diff --git a/builder/sizes_test.go b/builder/sizes_test.go index 4b0d6301e..fd985a897 100644 --- a/builder/sizes_test.go +++ b/builder/sizes_test.go @@ -42,7 +42,7 @@ func TestBinarySize(t *testing.T) { // This is a small number of very diverse targets that we want to test. tests := []sizeTest{ // microcontrollers - {"hifive1b", "examples/echo", 3668, 280, 0, 2244}, + {"hifive1b", "examples/echo", 3680, 280, 0, 2252}, {"microbit", "examples/serial", 2694, 342, 8, 2248}, {"wioterminal", "examples/pininterrupt", 7074, 1510, 120, 7248}, diff --git a/src/device/riscv/handleinterrupt.S b/src/device/riscv/handleinterrupt.S index c206c0159..79031423f 100644 --- a/src/device/riscv/handleinterrupt.S +++ b/src/device/riscv/handleinterrupt.S @@ -75,6 +75,10 @@ handleInterruptASM: SFREG f30,(30 + 16)*REGSIZE(sp) SFREG f31,(31 + 16)*REGSIZE(sp) #endif + // Save ra to a global so handleException can print the caller of the NULL + // function pointer. + la t0, tinygo_saved_ra + SREG ra, 0(t0) call handleInterrupt #ifdef __riscv_flen LFREG f0, (31 + 16)*REGSIZE(sp) @@ -128,3 +132,10 @@ handleInterruptASM: LREG ra, 0*REGSIZE(sp) addi sp, sp, NREG*REGSIZE mret + +.section .bss.tinygo_saved_ra +.global tinygo_saved_ra +.type tinygo_saved_ra,@object +.align 2 +tinygo_saved_ra: + .space REGSIZE diff --git a/src/runtime/interrupt/interrupt_esp32c3.go b/src/runtime/interrupt/interrupt_esp32c3.go index 27b7ae1b2..82e3d13ea 100644 --- a/src/runtime/interrupt/interrupt_esp32c3.go +++ b/src/runtime/interrupt/interrupt_esp32c3.go @@ -10,6 +10,9 @@ import ( "unsafe" ) +//go:extern tinygo_saved_ra +var tinygo_saved_ra uintptr + // Enable register CPU interrupt with interrupt.Interrupt. // The ESP32-C3 has 31 CPU independent interrupts. // The Interrupt.New(x, f) (x = [1..31]) attaches CPU interrupt to function f. @@ -88,7 +91,13 @@ const ( const ( defaultThreshold = 5 - disableThreshold = 10 + // Priority 0 disables an interrupt on ESP32-C3 (per the TRM, + // priority 0 = masked). During handling we set the current + // interrupt's priority to 0 so it cannot re-fire while MIE is + // re-enabled for nesting. This is critical for level-triggered + // interrupts where the hardware line stays asserted until the + // peripheral source is serviced by the handler. + disableThreshold = 0 ) //go:inline @@ -225,6 +234,7 @@ func handleException(mcause uintptr) { println("*** Exception: pc:", riscv.MEPC.Get()) println("*** Exception: code:", uint32(mcause&0x1f)) println("*** Exception: mcause:", mcause) + println("*** Exception: ra:", tinygo_saved_ra) switch uint32(mcause & 0x1f) { case riscv.InstructionAccessFault: println("*** virtual address:", riscv.MTVAL.Get())