diff options
| -rw-r--r-- | src/pardes/app.zig | 49 |
1 files changed, 39 insertions, 10 deletions
diff --git a/src/pardes/app.zig b/src/pardes/app.zig index cb0851e..9fbe19e 100644 --- a/src/pardes/app.zig +++ b/src/pardes/app.zig @@ -213,37 +213,27 @@ export fn zig_main() noreturn { // FIRST, before a single byte of `.rodata` is touched - which means before the marker below, // because that marker IS a string literal in flash and would read as machine code without this. flushFlashCache(); - uart.write("\r\nMARK PARDES_ENTRY direct-fifo\r\n"); - uart.write("MARK B1 entry ok\r\n"); const heap = heapSpan(); - uart.write("MARK B2 heapSpan ok\r\n"); soc.rom.print("\r\nMARK B3 rom.print heap 0x%08x..0x%08x %u KiB\r\n", .{ @as(u32, @intFromPtr(heap.ptr)), @as(u32, @intFromPtr(heap.ptr)) + @as(u32, @intCast(heap.len)), @as(u32, @intCast(heap.len / 1024)), }); - uart.write("MARK B4 rom.print returned\r\n"); const rwdt_was_armed = hal.rwdt.disable(); - uart.write("MARK B5 rwdt ok\r\n"); hal.systimer.init(); - uart.write("MARK B6 systimer ok\r\n"); _ = rwdt_was_armed; const their_abi = pardes_p4_abi_version(); - uart.write("MARK B7 abi call returned\r\n"); if (their_abi != abi_version) { uart.write("MARK PARDES_ABI_MISMATCH\r\n"); while (true) {} } gpa_heap = heapmod.Heap.init(heap); - uart.write("MARK B8 heap init ok\r\n"); _ = uart.drainInput(); - uart.write("MARK B9 drain ok, calling pardes_p4_init\r\n"); const rc = pardes_p4_init(&editor_allocator, writeOut, null, 80, 24); - uart.write("MARK B10 pardes_p4_init returned\r\n"); if (rc != 0) { soc.rom.print("MARK PARDES_INIT_FAIL rc=%u\r\n", .{rc}); @@ -274,6 +264,43 @@ export fn zig_main() noreturn { while (true) {} } +// ------------------------------------------------------------------------------------ the trap + +/// A trap handler, because the absence of one is why this port has been guessing. +/// +/// The mask ROM prints "Guru Meditation" for a trap only while ITS handler is still installed; +/// anything this image does that replaces or outgrows that path fails silently instead, and a silent +/// fault is indistinguishable from an infinite loop over a serial line. This one reports the three +/// registers that name the fault and then stops, using the direct-FIFO writer so it shares nothing +/// with the editor's buffered output. +/// +/// `mtvec` is set in DIRECT mode (low two bits zero), so every trap and every interrupt lands on +/// `trapEntry` regardless of cause - which is what a diagnostic wants. +export fn trapEntry() linksection(".text.entry") callconv(.naked) noreturn { + asm volatile ("j trapReport"); +} + +export fn trapReport() noreturn { + const mcause = asm volatile ("csrr %[o], mcause" + : [o] "=r" (-> u32), + ); + const mepc = asm volatile ("csrr %[o], mepc" + : [o] "=r" (-> u32), + ); + const mtval = asm volatile ("csrr %[o], mtval" + : [o] "=r" (-> u32), + ); + uart.write("\r\nMARK TRAP mcause="); + uart.dumpWord(mcause); + uart.write("MARK TRAP mepc="); + uart.dumpWord(mepc); + uart.write("MARK TRAP mtval="); + uart.dumpWord(mtval); + uart.write("MARK TRAP dropped="); + uart.dumpWord(uart.dropped); + while (true) {} +} + // --------------------------------------------------------------------------- the root's own duties /// `page_size_min`/`max`: the board has no MMU and no pages, but std derives allocator alignment @@ -324,6 +351,8 @@ export fn _start() linksection(".text.entry") callconv(.naked) noreturn { \\ csrs mstatus, t0 \\ la sp, __stack_top \\ mv fp, sp + \\ la t0, trapEntry + \\ csrw mtvec, t0 \\ la t0, __bss_start \\ la t1, __bss_end \\ bgeu t0, t1, 2f |
