mbv: now with functioning timer interrupts!
And a few other nice things. The bootloader now has an embedded wozmon! If you know its offset, you can jump to it from the app.
This commit is contained in:
parent
fa6ae7b667
commit
3db383d461
@ -10,6 +10,10 @@ helloworld: ## Build the helloworld app in docker
|
|||||||
wozmon: ## Build the wozmon app in docker
|
wozmon: ## Build the wozmon app in docker
|
||||||
docker build -o . --target export --build-arg TARGET=wozmon.bin .
|
docker build -o . --target export --build-arg TARGET=wozmon.bin .
|
||||||
|
|
||||||
|
.PHONY: timer
|
||||||
|
timer: ## Build the timer app in docker
|
||||||
|
docker build -o . --target export --build-arg TARGET=timer.bin .
|
||||||
|
|
||||||
.PHONY: dev-image
|
.PHONY: dev-image
|
||||||
dev-image:
|
dev-image:
|
||||||
docker build -t mbv-dev --target dev .
|
docker build -t mbv-dev --target dev .
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
RAM (rwx) : ORIGIN = 0x00000800, LENGTH = 14336
|
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x10000000
|
||||||
}
|
}
|
||||||
|
|
||||||
_vector_table = 0x0;
|
_vector_table = 0x0;
|
||||||
@ -37,6 +37,6 @@ SECTIONS
|
|||||||
|
|
||||||
_heap_begin = .;
|
_heap_begin = .;
|
||||||
|
|
||||||
_initial_stack_pointer = 16384;
|
_initial_stack_pointer = 0x90000000;
|
||||||
_heap_end = _initial_stack_pointer - 1024;
|
_heap_end = 0x8f000000; /* leave 1M for the stack */
|
||||||
}
|
}
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
MEMORY
|
|
||||||
{
|
|
||||||
/* RAM (rwx) : ORIGIN = 0x00000800, LENGTH = 14336 */
|
|
||||||
RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x10000000
|
|
||||||
}
|
|
||||||
|
|
||||||
_vector_table = 0x0;
|
|
||||||
|
|
||||||
SECTIONS
|
|
||||||
{
|
|
||||||
.text :
|
|
||||||
{
|
|
||||||
_text_begin = .;
|
|
||||||
KEEP(*(.start))
|
|
||||||
|
|
||||||
*(.text*)
|
|
||||||
_text_end = .;
|
|
||||||
|
|
||||||
*(.rodata*)
|
|
||||||
} > RAM
|
|
||||||
|
|
||||||
.bss (NOLOAD) :
|
|
||||||
{
|
|
||||||
_bss_begin = .;
|
|
||||||
*(.bss*)
|
|
||||||
*(COMMON)
|
|
||||||
_bss_end = .;
|
|
||||||
} > RAM
|
|
||||||
|
|
||||||
.data :
|
|
||||||
{
|
|
||||||
*(.data*)
|
|
||||||
|
|
||||||
__exidx_start = .;
|
|
||||||
*(.exidx*)
|
|
||||||
__exidx_end = .;
|
|
||||||
} > RAM
|
|
||||||
|
|
||||||
_heap_begin = .;
|
|
||||||
|
|
||||||
_initial_stack_pointer = 16384;
|
|
||||||
_heap_end = _initial_stack_pointer - 1024;
|
|
||||||
}
|
|
@ -1,10 +1,12 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "pol0.h"
|
||||||
|
|
||||||
struct Gpio {
|
struct Gpio {
|
||||||
volatile uint32_t data;
|
volatile uint32_t data;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define gpio0 ((Gpio*)0x40000000)
|
#define gpio0 ((Gpio*)GPIO0_BASE)
|
||||||
|
|
||||||
void sleep(int ms) {
|
void sleep(int ms) {
|
||||||
for (int m = 0; m < ms; m++) {
|
for (int m = 0; m < ms; m++) {
|
||||||
|
47
mbv/apps/timer/timer.cc
Normal file
47
mbv/apps/timer/timer.cc
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "intc.h"
|
||||||
|
#include "interrupts.h"
|
||||||
|
#include "pol0.h"
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct Gpio {
|
||||||
|
volatile uint32_t data;
|
||||||
|
};
|
||||||
|
|
||||||
|
Gpio* leds = reinterpret_cast<Gpio*>(GPIO0_BASE);
|
||||||
|
Timer* timer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timer0Isr() {
|
||||||
|
static int counter = 0;
|
||||||
|
leds->data = counter++;
|
||||||
|
|
||||||
|
timer->Pet();
|
||||||
|
timer->ClearInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupTimer() {
|
||||||
|
timer = Timer::Instance(TIMER0_BASE);
|
||||||
|
timer->SetupAsWdt(100'000'000);
|
||||||
|
timer->EnableT1();
|
||||||
|
|
||||||
|
SetIsr(TIMER0_IRQN, Timer0Isr);
|
||||||
|
SetIrqEnabled(TIMER0_IRQN, true);
|
||||||
|
EnableInterrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
leds->data = 0xa0;
|
||||||
|
|
||||||
|
SetupTimer();
|
||||||
|
SetExternalInterruptHandler(InterruptHandler);
|
||||||
|
EnableExternalInterrupts();
|
||||||
|
|
||||||
|
leds->data = 0xa1;
|
||||||
|
|
||||||
|
BiosWozmon();
|
||||||
|
}
|
@ -125,7 +125,6 @@ int main() {
|
|||||||
gpio0->data = 1;
|
gpio0->data = 1;
|
||||||
uint32_t cur_addr = 0;
|
uint32_t cur_addr = 0;
|
||||||
uint32_t cur_data = 0;
|
uint32_t cur_data = 0;
|
||||||
bool writing = false;
|
|
||||||
|
|
||||||
char inbuf[64] = {};
|
char inbuf[64] = {};
|
||||||
char* inptr = inbuf;
|
char* inptr = inbuf;
|
||||||
|
@ -1,19 +1,23 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "pol0.h"
|
||||||
#include "xuartlite.h"
|
#include "xuartlite.h"
|
||||||
|
|
||||||
|
uint8_t UartRead();
|
||||||
|
void UartWrite(uint8_t);
|
||||||
|
|
||||||
struct Gpio {
|
struct Gpio {
|
||||||
volatile uint32_t data;
|
volatile uint32_t data;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define gpio0 ((Gpio*)0x40000000)
|
#define gpio0 ((Gpio*)GPIO0_BASE)
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
constexpr uintptr_t kUart0BaseAddress = 0x40600000;
|
|
||||||
XUartLite uart0_inst;
|
XUartLite uart0_inst;
|
||||||
XUartLite_Config uart0_config = {
|
XUartLite_Config uart0_config = {
|
||||||
.DeviceId = 0,
|
.DeviceId = 0,
|
||||||
.RegBaseAddr = kUart0BaseAddress,
|
.RegBaseAddr = UART0_BASE,
|
||||||
.BaudRate = 115200,
|
.BaudRate = 115200,
|
||||||
.UseParity = false,
|
.UseParity = false,
|
||||||
.DataBits = 8,
|
.DataBits = 8,
|
||||||
@ -25,15 +29,6 @@ void InitUarts() {
|
|||||||
XUartLite_CfgInitialize(uart0, &uart0_config, uart0_config.RegBaseAddr);
|
XUartLite_CfgInitialize(uart0, &uart0_config, uart0_config.RegBaseAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t UartRead() {
|
|
||||||
uint8_t c;
|
|
||||||
while (XUartLite_Recv(uart0, &c, 1) < 1) {
|
|
||||||
}
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UartWrite(uint8_t c) { XUartLite_Send(uart0, &c, 1); }
|
|
||||||
|
|
||||||
uint32_t UartRead32() {
|
uint32_t UartRead32() {
|
||||||
uint32_t val = 0;
|
uint32_t val = 0;
|
||||||
|
|
||||||
@ -45,8 +40,21 @@ uint32_t UartRead32() {
|
|||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
uint8_t UartRead() {
|
||||||
|
uint8_t c;
|
||||||
|
while (XUartLite_Recv(uart0, &c, 1) < 1) {
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UartWrite(uint8_t c) {
|
||||||
|
XUartLite_Send(uart0, &c, 1);
|
||||||
|
while (XUartLite_IsSending(uart0)) {}
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
gpio0->data = 1;
|
gpio0->data = 1;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
BLRAM (rwx) : ORIGIN = 0x00000000, LENGTH = 2048
|
BLRAM (rwx) : ORIGIN = 0x00000000, LENGTH = 0x1000
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTIONS
|
SECTIONS
|
||||||
@ -30,5 +30,5 @@ SECTIONS
|
|||||||
__exidx_end = .;
|
__exidx_end = .;
|
||||||
} > BLRAM
|
} > BLRAM
|
||||||
|
|
||||||
_initial_stack_pointer = 2048;
|
_initial_stack_pointer = 0x1000;
|
||||||
}
|
}
|
||||||
|
132
mbv/bootloader/wozmon.cc
Normal file
132
mbv/bootloader/wozmon.cc
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
uint8_t UartRead();
|
||||||
|
void UartWrite(uint8_t);
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void Jump(uint32_t addr) {
|
||||||
|
auto jump = reinterpret_cast<void (*)()>(addr);
|
||||||
|
jump();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr uint8_t kBackspace = 0x7f;
|
||||||
|
constexpr uint8_t kOtherBackspace = 0x08;
|
||||||
|
|
||||||
|
uint8_t ReadHexNibble(uint8_t c) {
|
||||||
|
// lowercase only
|
||||||
|
if (c <= '9') {
|
||||||
|
return c - '0';
|
||||||
|
}
|
||||||
|
return 10 + (c - 'a');
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t ReadHex(const char* buf) {
|
||||||
|
uint32_t out = 0;
|
||||||
|
while (*buf == ' ') {
|
||||||
|
buf++;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
uint8_t c = ReadHexNibble(*buf);
|
||||||
|
if (c > 0xf) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
out = (out << 4) + c;
|
||||||
|
buf++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteHexNibble(uint8_t c) {
|
||||||
|
if (c > 9) {
|
||||||
|
UartWrite('a' + c - 10);
|
||||||
|
} else {
|
||||||
|
UartWrite('0' + c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UartWriteUint32(uint32_t a) {
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
WriteHexNibble((a >> 28) & 0xf);
|
||||||
|
a <<= 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UartWriteUint8(uint8_t a) {
|
||||||
|
WriteHexNibble(a >> 4);
|
||||||
|
WriteHexNibble(a & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UartDump(uint32_t addr, int count) {
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
UartWrite(' ');
|
||||||
|
UartWriteUint8(*reinterpret_cast<uint8_t*>(addr + i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpHex(uint32_t addr) {
|
||||||
|
addr &= 0xfffffffc;
|
||||||
|
UartWriteUint32(addr);
|
||||||
|
UartWrite(':');
|
||||||
|
UartDump(addr, 4);
|
||||||
|
UartWrite('\r');
|
||||||
|
UartWrite('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
int FindChar(const char* buf, uint8_t c) {
|
||||||
|
int found = 0;
|
||||||
|
while (*buf) {
|
||||||
|
if (*buf == c) {
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
found++;
|
||||||
|
buf++;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
__attribute__((used))
|
||||||
|
void wozmon() {
|
||||||
|
uint32_t cur_addr = 0;
|
||||||
|
uint32_t cur_data = 0;
|
||||||
|
|
||||||
|
char inbuf[64] = {};
|
||||||
|
char* inptr = inbuf;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
uint8_t c = UartRead();
|
||||||
|
UartWrite(c); // echo
|
||||||
|
if (c == '\r') {
|
||||||
|
*inptr = 0;
|
||||||
|
if (inptr == inbuf) {
|
||||||
|
cur_addr += 4;
|
||||||
|
} else if (FindChar(inbuf, 'r') >= 0) {
|
||||||
|
Jump(cur_addr);
|
||||||
|
} else {
|
||||||
|
cur_addr = ReadHex(inbuf);
|
||||||
|
UartWrite('\n');
|
||||||
|
}
|
||||||
|
DumpHex(cur_addr);
|
||||||
|
int assigned = FindChar(inbuf, ':');
|
||||||
|
if (assigned >= 0) {
|
||||||
|
cur_data = ReadHex(inbuf + assigned + 1);
|
||||||
|
*(reinterpret_cast<uint32_t*>(cur_addr)) = cur_data;
|
||||||
|
}
|
||||||
|
inptr = inbuf;
|
||||||
|
} else if (c == kBackspace) {
|
||||||
|
inptr--;
|
||||||
|
if (inptr < inbuf) {
|
||||||
|
inptr = inbuf;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
UartWrite(kOtherBackspace);
|
||||||
|
UartWrite(' ');
|
||||||
|
UartWrite(kOtherBackspace);
|
||||||
|
} else {
|
||||||
|
*inptr++ = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
mbv/configure
vendored
38
mbv/configure
vendored
@ -18,6 +18,7 @@ hostcflags = "-g -std=c++20 -fprofile-instr-generate -fcoverage-mapping"
|
|||||||
hostlibs = "-lgtest -lgmock -lgtest_main"
|
hostlibs = "-lgtest -lgmock -lgtest_main"
|
||||||
hostldflags = "-fprofile-instr-generate -fcoverage-mapping"
|
hostldflags = "-fprofile-instr-generate -fcoverage-mapping"
|
||||||
include_dirs = [
|
include_dirs = [
|
||||||
|
"hal",
|
||||||
"hal/uart",
|
"hal/uart",
|
||||||
"hal/lib/common",
|
"hal/lib/common",
|
||||||
]
|
]
|
||||||
@ -31,7 +32,7 @@ common_flags = [
|
|||||||
"-Wall",
|
"-Wall",
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
"-flto",
|
"-flto",
|
||||||
"-march=rv32i",
|
"-march=rv32i_zicsr",
|
||||||
"-ffunction-sections",
|
"-ffunction-sections",
|
||||||
"-Oz",
|
"-Oz",
|
||||||
]
|
]
|
||||||
@ -44,7 +45,7 @@ ldflags = [
|
|||||||
"-Wl,--gc-sections",
|
"-Wl,--gc-sections",
|
||||||
"-Wl,--print-memory-usage",
|
"-Wl,--print-memory-usage",
|
||||||
"-flto",
|
"-flto",
|
||||||
"-march=rv32i",
|
"-march=rv32i_zicsr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -244,6 +245,8 @@ def make_coverage(binaries):
|
|||||||
|
|
||||||
|
|
||||||
hal = source_set("hal", [
|
hal = source_set("hal", [
|
||||||
|
"hal/intc.cc",
|
||||||
|
"hal/interrupts.cc",
|
||||||
"hal/start.cc",
|
"hal/start.cc",
|
||||||
"hal/lib/common/xil_assert.c",
|
"hal/lib/common/xil_assert.c",
|
||||||
"hal/uart/xuartlite.c",
|
"hal/uart/xuartlite.c",
|
||||||
@ -252,9 +255,6 @@ hal = source_set("hal", [
|
|||||||
])
|
])
|
||||||
|
|
||||||
bootloader = source_set("bootloader", glob.glob("./bootloader/**/*.cc", recursive=True))
|
bootloader = source_set("bootloader", glob.glob("./bootloader/**/*.cc", recursive=True))
|
||||||
helloworld = source_set("helloworld", glob.glob("./apps/helloworld/**/*.cc", recursive=True))
|
|
||||||
wozmon = source_set("wozmon", glob.glob("./apps/wozmon/**/*.cc", recursive=True))
|
|
||||||
|
|
||||||
bootloader_image = build_image(
|
bootloader_image = build_image(
|
||||||
bootloader,
|
bootloader,
|
||||||
dependencies=[hal],
|
dependencies=[hal],
|
||||||
@ -262,22 +262,22 @@ bootloader_image = build_image(
|
|||||||
linker_script="bootloader/bootloader.ld",
|
linker_script="bootloader/bootloader.ld",
|
||||||
)
|
)
|
||||||
|
|
||||||
helloworld_image = build_image(
|
def app_image(app):
|
||||||
helloworld,
|
return build_image(
|
||||||
dependencies=[hal],
|
source_set(app, glob.glob(f"./apps/{app}/**/*.cc", recursive=True)),
|
||||||
elf_out="out/helloworld.elf",
|
dependencies=[hal],
|
||||||
bin_out="out/helloworld.bin",
|
elf_out=f"out/{app}.elf",
|
||||||
)
|
bin_out=f"out/{app}.bin",
|
||||||
|
)
|
||||||
|
|
||||||
wozmon_image = build_image(
|
all = [
|
||||||
wozmon,
|
build_source_set(hal),
|
||||||
dependencies=[hal],
|
bootloader_image,
|
||||||
elf_out="out/wozmon.elf",
|
|
||||||
bin_out="out/wozmon.bin",
|
|
||||||
linker_script = "apps/fromddr.ld",
|
|
||||||
)
|
|
||||||
|
|
||||||
all = [build_source_set(hal), bootloader_image, helloworld_image, wozmon_image]
|
app_image("helloworld"),
|
||||||
|
app_image("wozmon"),
|
||||||
|
app_image("timer"),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
|
62
mbv/hal/intc.cc
Normal file
62
mbv/hal/intc.cc
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include "intc.h"
|
||||||
|
|
||||||
|
#include "pol0.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
struct IntC {
|
||||||
|
volatile uint32_t ISR;
|
||||||
|
volatile uint32_t IPR;
|
||||||
|
volatile uint32_t IER;
|
||||||
|
volatile uint32_t IAR;
|
||||||
|
volatile uint32_t SIE;
|
||||||
|
volatile uint32_t CIE;
|
||||||
|
volatile uint32_t IVR;
|
||||||
|
volatile uint32_t MER;
|
||||||
|
volatile uint32_t IMR;
|
||||||
|
volatile uint32_t ILR;
|
||||||
|
|
||||||
|
// the rest is not enabled
|
||||||
|
};
|
||||||
|
|
||||||
|
IntC* intc = reinterpret_cast<IntC*>(INTC_BASE);
|
||||||
|
Isr isrs[NIRQ] = {};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SetIrqEnabled(uint8_t irqn, bool enabled) {
|
||||||
|
uint32_t mask = 1 << irqn;
|
||||||
|
uint32_t ier = intc->IER;
|
||||||
|
bool was_enabled = (ier & (~mask)) > 0;
|
||||||
|
if (enabled) {
|
||||||
|
intc->IER = ier | mask;
|
||||||
|
} else {
|
||||||
|
intc->IER = ier & (~mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
return was_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetIsr(uint8_t irqn, Isr isr) {
|
||||||
|
isrs[irqn] = isr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnableInterrupts() {
|
||||||
|
intc->MER = 0x3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterruptHandler() {
|
||||||
|
uint32_t ipr = intc->IPR;
|
||||||
|
|
||||||
|
for (int i = 0; i < NIRQ; i++) {
|
||||||
|
uint32_t mask = 1 << i;
|
||||||
|
|
||||||
|
if ((ipr & mask) > 0) {
|
||||||
|
// interrupt pending
|
||||||
|
if (isrs[i] != nullptr) {
|
||||||
|
isrs[i]();
|
||||||
|
}
|
||||||
|
intc->IAR = mask; // ack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
mbv/hal/intc.h
Normal file
16
mbv/hal/intc.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
using Isr = void(*)(void);
|
||||||
|
|
||||||
|
/// Returns: true if the IRQ was previously enabled
|
||||||
|
bool SetIrqEnabled(uint8_t irqn, bool enabled);
|
||||||
|
|
||||||
|
void SetIsr(uint8_t irqn, Isr isr);
|
||||||
|
|
||||||
|
// Call this once to enable all HW interrupts
|
||||||
|
void EnableInterrupts();
|
||||||
|
|
||||||
|
// Feed this to the CPU's interrupt handler
|
||||||
|
void InterruptHandler();
|
52
mbv/hal/interrupts.cc
Normal file
52
mbv/hal/interrupts.cc
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "interrupts.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr uint32_t kMstatusMieMask = 1 << 3;
|
||||||
|
constexpr uint32_t kMieExternalInterruptMask = 1 << 11;
|
||||||
|
constexpr uint32_t kExternalInterruptCause = 0x0b;
|
||||||
|
constexpr uint32_t kInterruptCauseMask = 0xff;
|
||||||
|
|
||||||
|
Isr external_handler = nullptr;
|
||||||
|
|
||||||
|
__attribute__((interrupt))
|
||||||
|
void TrapHandler() {
|
||||||
|
uint32_t mcause;
|
||||||
|
uint32_t mip;
|
||||||
|
asm volatile("csrr %0, mcause" : "=r"(mcause));
|
||||||
|
asm volatile("csrr %0, mip" : "=r"(mip));
|
||||||
|
|
||||||
|
// check for external interrupt
|
||||||
|
if ((mcause & kInterruptCauseMask) == kExternalInterruptCause) {
|
||||||
|
if (external_handler != nullptr) {
|
||||||
|
external_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
mip &= ~(kMieExternalInterruptMask);
|
||||||
|
asm volatile("csrw mip, %0" :: "r"(mip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetExternalInterruptHandler(Isr handler) {
|
||||||
|
external_handler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnableExternalInterrupts() {
|
||||||
|
uint32_t mstatus;
|
||||||
|
uint32_t mie;
|
||||||
|
Isr trap = TrapHandler;
|
||||||
|
|
||||||
|
asm volatile("csrr %0, mstatus" : "=r"(mstatus));
|
||||||
|
asm volatile("csrr %0, mie" : "=r"(mie));
|
||||||
|
|
||||||
|
asm volatile("csrw mtvec, %0" :: "r"(trap));
|
||||||
|
|
||||||
|
mie |= kMieExternalInterruptMask;
|
||||||
|
asm volatile("csrw mie, %0" :: "r"(mie));
|
||||||
|
mstatus |= kMstatusMieMask;
|
||||||
|
asm volatile("csrw mstatus, %0" :: "r"(mstatus));
|
||||||
|
}
|
6
mbv/hal/interrupts.h
Normal file
6
mbv/hal/interrupts.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
using Isr = void(*)();
|
||||||
|
|
||||||
|
void SetExternalInterruptHandler(Isr handler);
|
||||||
|
void EnableExternalInterrupts();
|
18
mbv/hal/pol0.h
Normal file
18
mbv/hal/pol0.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// Platform definitions for pol0
|
||||||
|
|
||||||
|
// LED output
|
||||||
|
#define GPIO0_BASE (0x40000000)
|
||||||
|
|
||||||
|
// /dev/ttyUSB1
|
||||||
|
#define UART0_BASE (0x40600000)
|
||||||
|
|
||||||
|
// Interrupt controller
|
||||||
|
#define INTC_BASE (0x41200000)
|
||||||
|
|
||||||
|
// It's uh.. a timer.
|
||||||
|
#define TIMER0_BASE (0x41c00000)
|
||||||
|
|
||||||
|
// IRQs
|
||||||
|
#define UART0_IRQN (0)
|
||||||
|
#define TIMER0_IRQN (1)
|
||||||
|
#define NIRQ (2)
|
69
mbv/hal/timer.h
Normal file
69
mbv/hal/timer.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
struct TimerControl {
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint32_t MDT0 : 1;
|
||||||
|
uint32_t UDT0 : 1;
|
||||||
|
uint32_t GENT0 : 1;
|
||||||
|
uint32_t CAPT0 : 1;
|
||||||
|
uint32_t ARHT0 : 1;
|
||||||
|
uint32_t LOAD0 : 1;
|
||||||
|
uint32_t ENIT0 : 1;
|
||||||
|
uint32_t ENT0 : 1;
|
||||||
|
uint32_t T0INT : 1;
|
||||||
|
uint32_t PWMA0 : 1;
|
||||||
|
uint32_t ENALL : 1;
|
||||||
|
uint32_t CASC : 1;
|
||||||
|
|
||||||
|
uint32_t reserved : 20;
|
||||||
|
};
|
||||||
|
uint32_t raw;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Timer {
|
||||||
|
volatile TimerControl TCSR0;
|
||||||
|
volatile uint32_t TLR0;
|
||||||
|
volatile uint32_t TCR0;
|
||||||
|
|
||||||
|
uint32_t _reserved;
|
||||||
|
|
||||||
|
volatile TimerControl TCSR1;
|
||||||
|
volatile uint32_t TLR1;
|
||||||
|
volatile uint32_t TCR1;
|
||||||
|
|
||||||
|
void EnableT1() {
|
||||||
|
TCSR1.ARHT0 = 1;
|
||||||
|
TCSR1.ENT0 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t GetT1Ticks() { return TCR1; }
|
||||||
|
|
||||||
|
void SetupAsWdt(uint32_t timeout_ticks) {
|
||||||
|
TLR0 = timeout_ticks;
|
||||||
|
TCSR0.LOAD0 = 1; // reset counter
|
||||||
|
TCSR0.UDT0 = 1; // count backwards from the load value
|
||||||
|
TCSR0.ENIT0 = 1; // enable interrupt
|
||||||
|
|
||||||
|
TCSR0.LOAD0 = 0; // allow counter to run
|
||||||
|
TCSR0.ENT0 = 1; // enable timer
|
||||||
|
}
|
||||||
|
|
||||||
|
void Pet() {
|
||||||
|
TCSR0.ENT0 = 0;
|
||||||
|
TCSR0.LOAD0 = 1;
|
||||||
|
TCSR0.LOAD0 = 0;
|
||||||
|
TCSR0.ENT0 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClearInterrupt() {
|
||||||
|
TCSR0.T0INT = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Timer* Instance(uint32_t base) {
|
||||||
|
return reinterpret_cast<Timer*>(base);
|
||||||
|
}
|
||||||
|
};
|
44
mbv/prog.py
44
mbv/prog.py
@ -1,6 +1,8 @@
|
|||||||
|
import argparse
|
||||||
import serial
|
import serial
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
|
import threading
|
||||||
import time
|
import time
|
||||||
|
|
||||||
offset = 0x80000000
|
offset = 0x80000000
|
||||||
@ -29,28 +31,46 @@ def write(s, offset, dat):
|
|||||||
|
|
||||||
def jump(s, offset):
|
def jump(s, offset):
|
||||||
cmd = struct.pack('<cI', b'j', offset)
|
cmd = struct.pack('<cI', b'j', offset)
|
||||||
|
print(f'Jumping to 0x{offset:04x}')
|
||||||
s.write(cmd)
|
s.write(cmd)
|
||||||
|
|
||||||
|
|
||||||
|
def stream_logs(s):
|
||||||
|
while True:
|
||||||
|
dat = s.read()
|
||||||
|
if not dat:
|
||||||
|
continue
|
||||||
|
sys.stdout.buffer.write(dat.replace(b'\r', b''))
|
||||||
|
sys.stdout.buffer.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("binary")
|
||||||
|
parser.add_argument("--monitor", action="store_true",
|
||||||
|
help="wait for and display program serial output")
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
binfile = sys.argv[1]
|
args = parse_args()
|
||||||
with open(binfile, 'rb') as f:
|
|
||||||
|
with open(args.binary, 'rb') as f:
|
||||||
dat = f.read()
|
dat = f.read()
|
||||||
|
|
||||||
s = serial.Serial(tty, baud, timeout=1)
|
s = serial.Serial(tty, baud, timeout=1)
|
||||||
write(s, offset, dat)
|
write(s, offset, dat)
|
||||||
jump(s, offset)
|
jump(s, offset)
|
||||||
last_dat = time.time()
|
|
||||||
|
|
||||||
while True:
|
if args.monitor:
|
||||||
dat = s.read()
|
t = threading.Thread(target=lambda: stream_logs(s), daemon=True)
|
||||||
if not dat:
|
t.start()
|
||||||
if time.time() - last_dat > 1.0:
|
|
||||||
print('Data timeout')
|
try:
|
||||||
break
|
while True:
|
||||||
continue
|
dat = input("") + '\r'
|
||||||
last_dat = time.time()
|
s.write(dat.encode())
|
||||||
sys.stdout.buffer.write(dat)
|
except KeyboardInterrupt:
|
||||||
|
print("Bye.")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
Loading…
Reference in New Issue
Block a user