diff --git a/mbv/Makefile b/mbv/Makefile index c74145d..854fda4 100644 --- a/mbv/Makefile +++ b/mbv/Makefile @@ -10,6 +10,10 @@ helloworld: ## Build the helloworld app in docker wozmon: ## Build the wozmon app in docker 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 dev-image: docker build -t mbv-dev --target dev . diff --git a/mbv/apps/app.ld b/mbv/apps/app.ld index e92a2fb..decf721 100644 --- a/mbv/apps/app.ld +++ b/mbv/apps/app.ld @@ -1,6 +1,6 @@ MEMORY { - RAM (rwx) : ORIGIN = 0x00000800, LENGTH = 14336 + RAM (rwx) : ORIGIN = 0x80000000, LENGTH = 0x10000000 } _vector_table = 0x0; @@ -37,6 +37,6 @@ SECTIONS _heap_begin = .; - _initial_stack_pointer = 16384; - _heap_end = _initial_stack_pointer - 1024; + _initial_stack_pointer = 0x90000000; + _heap_end = 0x8f000000; /* leave 1M for the stack */ } diff --git a/mbv/apps/fromddr.ld b/mbv/apps/fromddr.ld deleted file mode 100644 index f62c61a..0000000 --- a/mbv/apps/fromddr.ld +++ /dev/null @@ -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; -} diff --git a/mbv/apps/helloworld/helloworld.cc b/mbv/apps/helloworld/helloworld.cc index b653dcb..2a9cd3c 100644 --- a/mbv/apps/helloworld/helloworld.cc +++ b/mbv/apps/helloworld/helloworld.cc @@ -1,10 +1,12 @@ #include +#include "pol0.h" + struct Gpio { volatile uint32_t data; }; -#define gpio0 ((Gpio*)0x40000000) +#define gpio0 ((Gpio*)GPIO0_BASE) void sleep(int ms) { for (int m = 0; m < ms; m++) { diff --git a/mbv/apps/timer/timer.cc b/mbv/apps/timer/timer.cc new file mode 100644 index 0000000..4f50025 --- /dev/null +++ b/mbv/apps/timer/timer.cc @@ -0,0 +1,47 @@ +#include + +#include "intc.h" +#include "interrupts.h" +#include "pol0.h" +#include "timer.h" + +namespace { + +struct Gpio { + volatile uint32_t data; +}; + +Gpio* leds = reinterpret_cast(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(); +} diff --git a/mbv/apps/wozmon/wozmon.cc b/mbv/apps/wozmon/wozmon.cc index f37b3ad..745d0b4 100644 --- a/mbv/apps/wozmon/wozmon.cc +++ b/mbv/apps/wozmon/wozmon.cc @@ -125,7 +125,6 @@ int main() { gpio0->data = 1; uint32_t cur_addr = 0; uint32_t cur_data = 0; - bool writing = false; char inbuf[64] = {}; char* inptr = inbuf; diff --git a/mbv/bootloader/bootloader.cc b/mbv/bootloader/bootloader.cc index 429008d..53d7b0c 100644 --- a/mbv/bootloader/bootloader.cc +++ b/mbv/bootloader/bootloader.cc @@ -1,19 +1,23 @@ #include +#include "pol0.h" #include "xuartlite.h" +uint8_t UartRead(); +void UartWrite(uint8_t); + struct Gpio { volatile uint32_t data; }; -#define gpio0 ((Gpio*)0x40000000) +#define gpio0 ((Gpio*)GPIO0_BASE) namespace { -constexpr uintptr_t kUart0BaseAddress = 0x40600000; + XUartLite uart0_inst; XUartLite_Config uart0_config = { .DeviceId = 0, - .RegBaseAddr = kUart0BaseAddress, + .RegBaseAddr = UART0_BASE, .BaudRate = 115200, .UseParity = false, .DataBits = 8, @@ -25,15 +29,6 @@ void InitUarts() { 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 val = 0; @@ -45,8 +40,21 @@ uint32_t UartRead32() { return val; } + } // 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() { gpio0->data = 1; diff --git a/mbv/bootloader/bootloader.ld b/mbv/bootloader/bootloader.ld index 7c9465d..70ddcc4 100644 --- a/mbv/bootloader/bootloader.ld +++ b/mbv/bootloader/bootloader.ld @@ -1,6 +1,6 @@ MEMORY { - BLRAM (rwx) : ORIGIN = 0x00000000, LENGTH = 2048 + BLRAM (rwx) : ORIGIN = 0x00000000, LENGTH = 0x1000 } SECTIONS @@ -30,5 +30,5 @@ SECTIONS __exidx_end = .; } > BLRAM - _initial_stack_pointer = 2048; + _initial_stack_pointer = 0x1000; } diff --git a/mbv/bootloader/wozmon.cc b/mbv/bootloader/wozmon.cc new file mode 100644 index 0000000..c66f882 --- /dev/null +++ b/mbv/bootloader/wozmon.cc @@ -0,0 +1,132 @@ +#include + +uint8_t UartRead(); +void UartWrite(uint8_t); + +namespace { + +void Jump(uint32_t addr) { + auto jump = reinterpret_cast(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(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(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; + } + } +} diff --git a/mbv/configure b/mbv/configure index ad8c65c..3acef53 100755 --- a/mbv/configure +++ b/mbv/configure @@ -18,6 +18,7 @@ hostcflags = "-g -std=c++20 -fprofile-instr-generate -fcoverage-mapping" hostlibs = "-lgtest -lgmock -lgtest_main" hostldflags = "-fprofile-instr-generate -fcoverage-mapping" include_dirs = [ + "hal", "hal/uart", "hal/lib/common", ] @@ -31,7 +32,7 @@ common_flags = [ "-Wall", "-Wextra", "-flto", - "-march=rv32i", + "-march=rv32i_zicsr", "-ffunction-sections", "-Oz", ] @@ -44,7 +45,7 @@ ldflags = [ "-Wl,--gc-sections", "-Wl,--print-memory-usage", "-flto", - "-march=rv32i", + "-march=rv32i_zicsr", ] @@ -244,6 +245,8 @@ def make_coverage(binaries): hal = source_set("hal", [ + "hal/intc.cc", + "hal/interrupts.cc", "hal/start.cc", "hal/lib/common/xil_assert.c", "hal/uart/xuartlite.c", @@ -252,9 +255,6 @@ hal = source_set("hal", [ ]) 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, dependencies=[hal], @@ -262,22 +262,22 @@ bootloader_image = build_image( linker_script="bootloader/bootloader.ld", ) -helloworld_image = build_image( - helloworld, - dependencies=[hal], - elf_out="out/helloworld.elf", - bin_out="out/helloworld.bin", -) +def app_image(app): + return build_image( + source_set(app, glob.glob(f"./apps/{app}/**/*.cc", recursive=True)), + dependencies=[hal], + elf_out=f"out/{app}.elf", + bin_out=f"out/{app}.bin", + ) -wozmon_image = build_image( - wozmon, - dependencies=[hal], - elf_out="out/wozmon.elf", - bin_out="out/wozmon.bin", - linker_script = "apps/fromddr.ld", -) +all = [ + build_source_set(hal), + bootloader_image, -all = [build_source_set(hal), bootloader_image, helloworld_image, wozmon_image] + app_image("helloworld"), + app_image("wozmon"), + app_image("timer"), +] def parse_args(): diff --git a/mbv/hal/intc.cc b/mbv/hal/intc.cc new file mode 100644 index 0000000..fe13989 --- /dev/null +++ b/mbv/hal/intc.cc @@ -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_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 + } + } +} diff --git a/mbv/hal/intc.h b/mbv/hal/intc.h new file mode 100644 index 0000000..156af0e --- /dev/null +++ b/mbv/hal/intc.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +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(); diff --git a/mbv/hal/interrupts.cc b/mbv/hal/interrupts.cc new file mode 100644 index 0000000..a9d777d --- /dev/null +++ b/mbv/hal/interrupts.cc @@ -0,0 +1,52 @@ +#include + +#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)); +} diff --git a/mbv/hal/interrupts.h b/mbv/hal/interrupts.h new file mode 100644 index 0000000..ec09d51 --- /dev/null +++ b/mbv/hal/interrupts.h @@ -0,0 +1,6 @@ +#pragma once + +using Isr = void(*)(); + +void SetExternalInterruptHandler(Isr handler); +void EnableExternalInterrupts(); diff --git a/mbv/hal/pol0.h b/mbv/hal/pol0.h new file mode 100644 index 0000000..f4b0f30 --- /dev/null +++ b/mbv/hal/pol0.h @@ -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) diff --git a/mbv/hal/timer.h b/mbv/hal/timer.h new file mode 100644 index 0000000..5637a9d --- /dev/null +++ b/mbv/hal/timer.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +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(base); + } +}; diff --git a/mbv/prog.py b/mbv/prog.py index 3e14b00..31f8d89 100644 --- a/mbv/prog.py +++ b/mbv/prog.py @@ -1,6 +1,8 @@ +import argparse import serial import struct import sys +import threading import time offset = 0x80000000 @@ -29,28 +31,46 @@ def write(s, offset, dat): def jump(s, offset): cmd = struct.pack(' 1.0: - print('Data timeout') - break - continue - last_dat = time.time() - sys.stdout.buffer.write(dat) + if args.monitor: + t = threading.Thread(target=lambda: stream_logs(s), daemon=True) + t.start() + + try: + while True: + dat = input("") + '\r' + s.write(dat.encode()) + except KeyboardInterrupt: + print("Bye.") if __name__ == "__main__":