summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pardes/app.zig49
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