tinygo
tinygo copied to clipboard
Enhance HardFault debugging
This MR updates the HardFault handler, allowing improved debugging for Cortex-M3 and Cortex-M4 based boards, using the fault status registers defined by those cores.
Changes
- Add
cortex-m0.json,cortex-m0p.json, andcortex-m3.jsontotargets/, and update Cortex-M targets to inherit from these, to allow targeting of M0/M0+/M3/M4 (targets/cortex-m4.jsonalready exists). - Move the definition of the system control block registers from
arm.goto core-specific files (arm_cortex_mX.go) to allow for core-specific registers. - Generalize the Teensy 3.6/NXP MK66F18's HardFault handler to work with any Cortex-M3/M4
- Capture
PSPandMSPinstead of justSP, in order to log the correct stack pointer (becauseSPin the ISR always refers toMSP) - Use
stackTop-stackSizeinstead of a hardcoded value (0x20000000) to detect stack overflow (on the Teensy 3.6, RAM starts at0x1fff0000)
Executable size does not increase greatly for M3/M4 cores, unless const hardFaultDebug bool is set to true. As of cd521eb5387fd8ab0e20820698a32ad2f831c220, sizes for src/examples/blinky1 are:
| Model | Version | Size |
|---|---|---|
| Feather M0 | Unmodified | 23764 |
| Feather M0 | New | 23852 (+88) |
| Feather M4 | Unmodified | 20596 |
| Feather M4 | New w/o debug | 20728 (+132) |
| Feather M4 | New w/ debug | 23456 (+2860) |
8 bytes of the change are due to Default_Handler:
<Default_Handler>:
bf20 wfe
e7fd b.n <Default_Handler>
d4d4 bmi.n <tinygo_getSystemStackPointer+0x2>
+d4d4 bmi.n <tinygo_getSystemStackPointer+0x4>
+d4d4 bmi.n <tinygo_switchToScheduler>
+d4d4 bmi.n <tinygo_switchToScheduler+0x2>
+d4d4 bmi.n <tinygo_switchToScheduler+0x4>
@firelizzard18 please make sure you rebase against the dev branch, that might get rid of the build errors.
@deadprogram Rebased
On interrupt, the link register is filled with an EXC_RETURN (exception return) value. Bit 2 indicates whether the interrupted code was using the PSP (bit 2 == 1) or the MSP (bit 2 == 0). I attempted to do all of this in assembly, defining handleHardFault(sp *interruptStack, system bool). But I couldn't get that to work, so I did the check in Go instead. I think doing it in assembly would result in a smaller increase in code size (when hardFaultDebug == false). Here is what I think should work, but demonstrably did not:
tst lr, #(1<<2)
itete eq
mrseq r0, PSP
mrsne r0, MSP
moveq r1, #0 ; system == false
movne r1, #1 ; system == true
I have no idea why this does not work but the Go implementation does. I thought maybe I misunderstood the conditionals, so I switched them, but that didn't help. I triggered a stack overflow (via dumb recursive fibonacci) in the scheduler and in main (separately). The Go implementation correctly reports PSP vs MSP; the assembly implementation does not.
The following works as expected, but fails to compile for the Feather M0:
//go:export HardFault_Handler
func hardFaultEntry() {
pspStacked := arm.AsmFull("ands.w {}, lr, #(1<<2)", nil)
sp := (*interruptStack)(unsafe.Pointer(arm.AsmFull(`
ite ne
mrsne {}, PSP
mrseq {}, MSP
`, nil)))
arm.AsmFull(`
movs r3, #0
ldr r3, [r3]
mov sp, r3
`, nil)
handleHardFault(sp, pspStacked)
}
func handleHardFault(sp *interruptStack, pspStacked uintptr) {
// ...
}
The error:
<inline asm>:1:2: error: instruction requires: thumb2
ands.w r0, lr, #(1<<2)
^
LLVM ERROR: Error parsing inline asm
Yes, the Cortex-M0 has implemented far fewer instructions. Many wide instructions (such as ands.w) and most/all conditional instructions (such as mrsne) are not supported.
This is what the error says:
error: instruction requires: thumb2
Cortex-M0 implements thumb, not thumb2.