|
1 | 1 |
|
2 | 2 | // Xtensa interrupt/exception vector table for the ESP32-S3. |
3 | 3 | // |
| 4 | +// See Xtensa ISA Reference Manual for details on the vector table layout and exception handling. |
| 5 | +// https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/silicon-solutions/compute-ip/isa-summary.pdf |
| 6 | +// |
4 | 7 | // The ESP32-S3 uses an Xtensa LX7 core with the windowed register ABI. |
5 | 8 | // Interrupt vectors are placed at fixed offsets from the VECBASE special |
6 | 9 | // register. We only handle level-1 (user) interrupts for now. |
@@ -299,13 +302,26 @@ _handle_kernel_exc: |
299 | 302 |
|
300 | 303 | .global _handle_level1 |
301 | 304 | _handle_level1: |
302 | | - // --- allocate 96-byte exception frame on the interrupted stack --- |
| 305 | + // EXCCAUSE 5 (AllocaCause) is a MOVSP window-spill request, not a fault, so |
| 306 | + // it is checked before any state is touched: a0 is still in EXCSAVE1. |
| 307 | + // Xtensa ISA Reference Manual, Table 4-64 "Exception Causes". |
| 308 | + rsr a0, EXCCAUSE |
| 309 | + bnei a0, 5, 1f |
| 310 | + j _xt_alloca_exc |
| 311 | +1: |
| 312 | + rsr a0, EXCSAVE1 // restore a0 clobbered by the EXCCAUSE read |
| 313 | + |
| 314 | + // --- allocate the exception frame on the interrupted stack --- |
303 | 315 | // Layout (offsets from a1 after adjustment): |
304 | 316 | // 0: a0 4: a1(orig) 8: a2 12: a3 16: a4 20: a5 |
305 | 317 | // 24: a6 28: a7 32: a8 36: a9 40: a10 44: a11 |
306 | 318 | // 48: a12 52: a13 56: a14 60: a15 |
307 | | - // 64: SAR 68: EPC1 72: PS |
308 | | - addi a0, a1, -96 // a0 = new frame pointer |
| 319 | + // 64: SAR 68: EPC1 72: PS 76: WINDOWBASE 80: WINDOWSTART |
| 320 | + // |
| 321 | + // 128, not the 84 the layout needs: the low 16 bytes are the interruptee's |
| 322 | + // windowed-ABI base save area, as in ESP-IDF's XT_STK_FRMSZ (+0x20). |
| 323 | + // https://github.com/espressif/esp-idf/blob/master/components/xtensa/include/xtensa_context.h |
| 324 | + addi a0, a1, -128 // a0 = new frame pointer |
309 | 325 | s32i a1, a0, 4 // save original a1 (SP) |
310 | 326 | mov a1, a0 // a1 = frame pointer |
311 | 327 |
|
@@ -339,6 +355,14 @@ _handle_level1: |
339 | 355 | // level-1 interrupts. |
340 | 356 | rsr a2, PS |
341 | 357 | s32i a2, a1, 72 // save PS (with EXCM=1 set by hardware) |
| 358 | + |
| 359 | + // Captured here, not in the C handler: getting there costs a callx4 and the |
| 360 | + // printfs rotate and spill windows, so they would report their own state. |
| 361 | + rsr a3, WINDOWBASE |
| 362 | + s32i a3, a1, 76 |
| 363 | + rsr a3, WINDOWSTART |
| 364 | + s32i a3, a1, 80 |
| 365 | + |
342 | 366 | movi a3, ~0x1F // mask: clear INTLEVEL (bits 0-3) + EXCM (bit 4) |
343 | 367 | and a2, a2, a3 |
344 | 368 | movi a3, 1 // INTLEVEL = 1 |
@@ -412,6 +436,37 @@ _handle_level1: |
412 | 436 |
|
413 | 437 | rfe |
414 | 438 |
|
| 439 | +// Alloca (MOVSP) exception: rotate back to the MOVSP window, fix PS.OWB, then |
| 440 | +// fall into the matching window underflow handler, which spills and rfwu's back. |
| 441 | +// Ported from ESP-IDF _xt_alloca_exc: |
| 442 | +// https://github.com/espressif/esp-idf/blob/master/components/xtensa/xtensa_vectors.S |
| 443 | + .balign 4 |
| 444 | + .global _xt_alloca_exc |
| 445 | +_xt_alloca_exc: |
| 446 | + rsr a0, WINDOWBASE // grab WINDOWBASE before rotw changes it |
| 447 | + rotw -1 // WINDOWBASE goes to a4, new a0-a3 are scratch |
| 448 | + rsr a2, PS |
| 449 | + extui a3, a2, 8, 4 // a3 = PS.OWB (shift 8, 4 bits) |
| 450 | + xor a3, a3, a4 // bits that changed from old to current WB |
| 451 | + rsr a4, EXCSAVE1 // restore the interruptee's a0 (now in a4) |
| 452 | + slli a3, a3, 8 |
| 453 | + xor a2, a2, a3 // flip those bits in PS.OWB |
| 454 | + wsr a2, PS // PS.OWB now matches the new WINDOWBASE |
| 455 | + rsync |
| 456 | + |
| 457 | + // Dispatch on the call size in bits 31:30 of the interruptee's a0; plain `j` |
| 458 | + // because _bbci.l's 8-bit range cannot reach the underflow vectors. |
| 459 | + // Xtensa ISA Reference Manual, RETW, section 8.3.253, p.565 (a0 encoding). |
| 460 | + _bbsi.l a4, 31, 1f |
| 461 | + j _window_underflow4 |
| 462 | +1: |
| 463 | + rotw -1 // interruptee's a0 moves to a8 |
| 464 | + _bbsi.l a8, 30, 2f |
| 465 | + j _window_underflow8 |
| 466 | +2: |
| 467 | + rotw -1 |
| 468 | + j _window_underflow12 |
| 469 | + |
415 | 470 | // ----------------------------------------------------------------------- |
416 | 471 | // Default weak espradio_user_exception: infinite loop halt. |
417 | 472 | // Overridden by the strong definition in espradio's isr.c when linked. |
|
0 commit comments