Skip to content

Commit a24dc8c

Browse files
committed
esp32s3: improve exception handlers, add procPin/procUnpin, and linker wrap flags
Rewrite kernel and double exception handlers to save EXCCAUSE/EPC1 to RTC STORE registers before triggering a software reset, replacing the LED-blink diagnostic with post-mortem debug info that survives reset. Add user exception dispatch in the level-1 handler with a weak espradio_user_exception symbol so programs without espradio still link. Implement procPin/procUnpin for Xtensa using RSIL/WSR PS to properly disable interrupts during atomic operations. Fix abort() to use a waiti loop instead of bare spin. Add --wrap ldflags for malloc/calloc/free/realloc/ppCheckTxConnTrafficIdle to support espradio WiFi blob integration. Signed-off-by: deadprogram <ron@hybridgroup.com>
1 parent c3d514a commit a24dc8c

4 files changed

Lines changed: 126 additions & 29 deletions

File tree

src/runtime/arch_xtensa.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
package runtime
44

5+
import "device"
6+
57
const GOARCH = "arm" // xtensa pretends to be arm
68

79
// The bitness of the CPU (e.g. 8, 32, 64).
@@ -19,3 +21,21 @@ func align(ptr uintptr) uintptr {
1921
func getCurrentStackPointer() uintptr {
2022
return uintptr(stacksave())
2123
}
24+
25+
// Disable interrupts for procPin/procUnpin using the Xtensa RSIL/WSR PS
26+
// instructions. A global variable is safe here because accesses happen
27+
// with interrupts disabled.
28+
var procPinnedMask uintptr
29+
30+
//go:linkname procPin sync/atomic.runtime_procPin
31+
func procPin() {
32+
// rsil sets PS.INTLEVEL=15 (mask all) and returns the old PS.
33+
procPinnedMask = uintptr(device.AsmFull("rsil {}, 15", nil))
34+
}
35+
36+
//go:linkname procUnpin sync/atomic.runtime_procUnpin
37+
func procUnpin() {
38+
device.AsmFull("wsr {state}, PS", map[string]interface{}{
39+
"state": procPinnedMask,
40+
})
41+
}

src/runtime/runtime_esp32s3.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,11 @@ func init() {
9696
}
9797

9898
func abort() {
99-
// lock up forever
10099
print("abort called\n")
100+
// lock up forever
101+
for {
102+
device.Asm("waiti 0")
103+
}
101104
}
102105

103106
// interruptInit installs the Xtensa vector table by writing its address

targets/esp32s3-interrupts.S

Lines changed: 95 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ _nmi_vector:
173173

174174
// -----------------------------------------------------------------------
175175
// Offset 0x300 — Kernel exception
176-
// Diagnostic: steady 1s ON / 1s OFF blink.
176+
// Writes EXCCAUSE+EPC1 to RTC STORE regs, then triggers software reset.
177177
// -----------------------------------------------------------------------
178178
.org _vector_table + 0x300
179179
_kernel_vector:
@@ -192,17 +192,39 @@ _level1_vector:
192192

193193
// -----------------------------------------------------------------------
194194
// Offset 0x3C0 — Double exception
195-
// Diagnostic: solid LED ON (no blinking).
195+
// Diagnostic: write magic to RTC STORE regs, then halt.
196+
// RTC_CNTL_STORE0 = 0x60008050, STORE1 = 0x60008054
196197
// -----------------------------------------------------------------------
197198
.org _vector_table + 0x3C0
198199
_double_vector:
199-
movi a0, 1
200-
slli a0, a0, 21
201-
movi a1, 6
202-
slli a1, a1, 28
203-
addmi a1, a1, 0x4000
204-
s32i a0, a1, 0x0C // LED ON permanently
205-
1: j 1b // spin forever
200+
// Write 0x55550000 + EXCCAUSE to RTC_CNTL_STORE0 (0x60008050)
201+
// Build address: 0x60008050 = 6<<28 + 0x4000 + 0x4000 + 0x50
202+
movi a0, 6
203+
slli a0, a0, 28 // a0 = 0x60000000
204+
addmi a0, a0, 0x4000 // a0 = 0x60004000
205+
addmi a0, a0, 0x4000 // a0 = 0x60008000
206+
addi a0, a0, 0x50 // a0 = 0x60008050
207+
rsr a1, EXCCAUSE
208+
movi a2, 0x555
209+
slli a2, a2, 20 // a2 = 0x55500000
210+
movi a3, 5
211+
slli a3, a3, 16 // a3 = 0x00050000
212+
or a2, a2, a3 // a2 = 0x55550000
213+
or a1, a2, a1 // a1 = 0x5555xxxx
214+
s32i a1, a0, 0 // STORE0
215+
rsr a1, DEPC
216+
s32i a1, a0, 4 // STORE1 = DEPC
217+
rsr a1, EPC1
218+
s32i a1, a0, 8 // STORE2 = EPC1
219+
// Trigger software system reset (preserves RTC STORE regs).
220+
// RTC_CNTL_OPTIONS0_REG = 0x60008000, bit 31 = SW_SYS_RST
221+
addi a0, a0, -0x50 // a0 = 0x60008000
222+
l32i a1, a0, 0
223+
movi a2, 1
224+
slli a2, a2, 31
225+
or a1, a1, a2
226+
s32i a1, a0, 0 // trigger reset
227+
1: j 1b // wait for reset
206228

207229
// -----------------------------------------------------------------------
208230
// Level-1 interrupt handler — lives outside the vector table so there
@@ -232,6 +254,13 @@ _double_vector:
232254
.balign 4
233255
.LhandleInterrupt_addr:
234256
.word handleInterrupt
257+
.Luser_exception_addr:
258+
.word espradio_user_exception
259+
260+
// Make espradio_user_exception a weak reference so programs that don't
261+
// link espradio (e.g. blinky) still build. The default handler below
262+
// is used when no strong definition is provided.
263+
.weak espradio_user_exception
235264

236265
// -----------------------------------------------------------------------
237266
// Kernel exception handler (out-of-table).
@@ -243,25 +272,30 @@ _double_vector:
243272
// 29 blinks = cause 28 (LoadProhibitedCause)
244273
// -----------------------------------------------------------------------
245274
_handle_kernel_exc:
246-
// Steady 1-second ON / 1-second OFF blink.
247-
// This is the KERNEL exception handler.
248-
movi a0, 1
249-
slli a0, a0, 21 // a0 = GPIO21 bit
250-
movi a1, 6
251-
slli a1, a1, 28
252-
addmi a1, a1, 0x4000 // a1 = 0x60004000
253-
_hke_blink:
254-
s32i a0, a1, 0x0C // LED ON
255-
movi a2, 5
256-
slli a2, a2, 23 // ~1s @40MHz
257-
1: addi a2, a2, -1
258-
bnez a2, 1b
259-
s32i a0, a1, 0x08 // LED OFF
260-
movi a2, 5
261-
slli a2, a2, 23 // ~1s @40MHz
262-
1: addi a2, a2, -1
263-
bnez a2, 1b
264-
j _hke_blink
275+
// Write 0x55560000 + EXCCAUSE to RTC_CNTL_STORE0 (0x60008050)
276+
movi a0, 6
277+
slli a0, a0, 28
278+
addmi a0, a0, 0x4000
279+
addmi a0, a0, 0x4000
280+
addi a0, a0, 0x50 // a0 = 0x60008050
281+
rsr a1, EXCCAUSE
282+
movi a2, 0x555
283+
slli a2, a2, 20
284+
movi a3, 6
285+
slli a3, a3, 16
286+
or a2, a2, a3 // a2 = 0x55560000
287+
or a1, a2, a1
288+
s32i a1, a0, 0 // STORE0
289+
rsr a1, EPC1
290+
s32i a1, a0, 4 // STORE1 = EPC1
291+
// Trigger software system reset (preserves RTC STORE regs).
292+
addi a0, a0, -0x50 // a0 = 0x60008000
293+
l32i a1, a0, 0
294+
movi a2, 1
295+
slli a2, a2, 31
296+
or a1, a1, a2
297+
s32i a1, a0, 0 // trigger reset
298+
1: j 1b // wait for reset
265299

266300
.global _handle_level1
267301
_handle_level1:
@@ -312,6 +346,26 @@ _handle_level1:
312346
wsr a2, PS
313347
rsync
314348

349+
// Check if this is an exception (not an interrupt).
350+
// EXCCAUSE == 4 means level-1 interrupt; anything else is an exception.
351+
rsr a2, EXCCAUSE
352+
movi a3, 4
353+
beq a2, a3, .Lis_interrupt
354+
355+
// --- It's an exception, not an interrupt ---
356+
// Call the C exception handler: espradio_user_exception(cause, epc, excvaddr, frame)
357+
// Using callx4 (same convention as handleInterrupt).
358+
mov a6, a2 // a6 = EXCCAUSE (will be a2 after window rotate)
359+
l32i a7, a1, 68 // a7 = EPC1 (saved earlier)
360+
rsr a8, EXCVADDR // a8 = faulting address
361+
mov a9, a1 // a9 = frame pointer (callee a5)
362+
mov a5, a1 // callee stack
363+
l32r a2, .Luser_exception_addr
364+
callx4 a2
365+
// Does not return — loops forever in the C handler.
366+
367+
.Lis_interrupt:
368+
315369
// Call the Go interrupt dispatcher via callx4.
316370
// callx4 explicitly sets PS.CALLINC=1 and puts the return address
317371
// (with top 2 bits = 01) in a4. After entry rotates the window by
@@ -357,3 +411,16 @@ _handle_level1:
357411
l32i a1, a1, 4 // restores original SP (deallocates frame)
358412

359413
rfe
414+
415+
// -----------------------------------------------------------------------
416+
// Default weak espradio_user_exception: infinite loop halt.
417+
// Overridden by the strong definition in espradio's isr.c when linked.
418+
// -----------------------------------------------------------------------
419+
.section .iram1, "ax"
420+
.balign 4
421+
.weak espradio_user_exception
422+
.type espradio_user_exception, @function
423+
espradio_user_exception:
424+
waiti 0
425+
j espradio_user_exception
426+
.size espradio_user_exception, . - espradio_user_exception

targets/esp32s3.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@
77
"scheduler": "tasks",
88
"serial": "usb",
99
"linker": "ld.lld",
10+
"ldflags": [
11+
"--wrap=malloc",
12+
"--wrap=calloc",
13+
"--wrap=free",
14+
"--wrap=realloc",
15+
"--wrap=ppCheckTxConnTrafficIdle"
16+
],
1017
"default-stack-size": 8192,
1118
"rtlib": "compiler-rt",
1219
"libc": "picolibc",

0 commit comments

Comments
 (0)