Implement custom abort and fault handler for debugging
Этот коммит содержится в:
родитель
4750635a20
коммит
04d097f4ea
5 изменённых файлов: 390 добавлений и 43 удалений
|
@ -3,7 +3,6 @@
|
|||
package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
|
@ -40,16 +39,6 @@ func preinit() {
|
|||
}
|
||||
}
|
||||
|
||||
func abort() {
|
||||
// disable all interrupts
|
||||
arm.DisableInterrupts()
|
||||
|
||||
// lock up forever
|
||||
for {
|
||||
arm.Asm("wfi")
|
||||
}
|
||||
}
|
||||
|
||||
// The stack layout at the moment an interrupt occurs.
|
||||
// Registers can be accessed if the stack pointer is cast to a pointer to this
|
||||
// struct.
|
||||
|
@ -63,35 +52,3 @@ type interruptStack struct {
|
|||
PC uintptr
|
||||
PSR uintptr
|
||||
}
|
||||
|
||||
// This function is called at HardFault.
|
||||
// Before this function is called, the stack pointer is reset to the initial
|
||||
// stack pointer (loaded from addres 0x0) and the previous stack pointer is
|
||||
// passed as an argument to this function. This allows for easy inspection of
|
||||
// the stack the moment a HardFault occurs, but it means that the stack will be
|
||||
// corrupted by this function and thus this handler must not attempt to recover.
|
||||
//
|
||||
// For details, see:
|
||||
// https://community.arm.com/developer/ip-products/system/f/embedded-forum/3257/debugging-a-cortex-m0-hard-fault
|
||||
// https://blog.feabhas.com/2013/02/developing-a-generic-hard-fault-handler-for-arm-cortex-m3cortex-m4/
|
||||
//export handleHardFault
|
||||
func handleHardFault(sp *interruptStack) {
|
||||
print("fatal error: ")
|
||||
if uintptr(unsafe.Pointer(sp)) < 0x20000000 {
|
||||
print("stack overflow")
|
||||
} else {
|
||||
// TODO: try to find the cause of the hard fault. Especially on
|
||||
// Cortex-M3 and higher it is possible to find more detailed information
|
||||
// in special status registers.
|
||||
print("HardFault")
|
||||
}
|
||||
print(" with sp=", sp)
|
||||
if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 {
|
||||
// Only print the PC if it points into memory.
|
||||
// It may not point into memory during a stack overflow, so check that
|
||||
// first before accessing the stack.
|
||||
print(" pc=", sp.PC)
|
||||
}
|
||||
println()
|
||||
abort()
|
||||
}
|
||||
|
|
17
src/runtime/runtime_cortexm_abort.go
Обычный файл
17
src/runtime/runtime_cortexm_abort.go
Обычный файл
|
@ -0,0 +1,17 @@
|
|||
// +build cortexm,!nxp
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
)
|
||||
|
||||
func abort() {
|
||||
// disable all interrupts
|
||||
arm.DisableInterrupts()
|
||||
|
||||
// lock up forever
|
||||
for {
|
||||
arm.Asm("wfi")
|
||||
}
|
||||
}
|
39
src/runtime/runtime_cortexm_hardfault.go
Обычный файл
39
src/runtime/runtime_cortexm_hardfault.go
Обычный файл
|
@ -0,0 +1,39 @@
|
|||
// +build cortexm,!nxp
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// This function is called at HardFault.
|
||||
// Before this function is called, the stack pointer is reset to the initial
|
||||
// stack pointer (loaded from addres 0x0) and the previous stack pointer is
|
||||
// passed as an argument to this function. This allows for easy inspection of
|
||||
// the stack the moment a HardFault occurs, but it means that the stack will be
|
||||
// corrupted by this function and thus this handler must not attempt to recover.
|
||||
//
|
||||
// For details, see:
|
||||
// https://community.arm.com/developer/ip-products/system/f/embedded-forum/3257/debugging-a-cortex-m0-hard-fault
|
||||
// https://blog.feabhas.com/2013/02/developing-a-generic-hard-fault-handler-for-arm-cortex-m3cortex-m4/
|
||||
//export handleHardFault
|
||||
func handleHardFault(sp *interruptStack) {
|
||||
print("fatal error: ")
|
||||
if uintptr(unsafe.Pointer(sp)) < 0x20000000 {
|
||||
print("stack overflow")
|
||||
} else {
|
||||
// TODO: try to find the cause of the hard fault. Especially on
|
||||
// Cortex-M3 and higher it is possible to find more detailed information
|
||||
// in special status registers.
|
||||
print("HardFault")
|
||||
}
|
||||
print(" with sp=", sp)
|
||||
if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 {
|
||||
// Only print the PC if it points into memory.
|
||||
// It may not point into memory during a stack overflow, so check that
|
||||
// first before accessing the stack.
|
||||
print(" pc=", sp.PC)
|
||||
}
|
||||
println()
|
||||
abort()
|
||||
}
|
304
src/runtime/runtime_cortexm_hardfault_debug.go
Обычный файл
304
src/runtime/runtime_cortexm_hardfault_debug.go
Обычный файл
|
@ -0,0 +1,304 @@
|
|||
// +build nxp
|
||||
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"device/nxp"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
SystemControl_CFSR_KnownFault = nxp.SystemControl_CFSR_IACCVIOL | nxp.SystemControl_CFSR_DACCVIOL | nxp.SystemControl_CFSR_MUNSTKERR | nxp.SystemControl_CFSR_MSTKERR | nxp.SystemControl_CFSR_MLSPERR | nxp.SystemControl_CFSR_IBUSERR | nxp.SystemControl_CFSR_PRECISERR | nxp.SystemControl_CFSR_IMPRECISERR | nxp.SystemControl_CFSR_UNSTKERR | nxp.SystemControl_CFSR_STKERR | nxp.SystemControl_CFSR_LSPERR | nxp.SystemControl_CFSR_UNDEFINSTR | nxp.SystemControl_CFSR_INVSTATE | nxp.SystemControl_CFSR_INVPC | nxp.SystemControl_CFSR_NOCP | nxp.SystemControl_CFSR_UNALIGNED | nxp.SystemControl_CFSR_DIVBYZERO
|
||||
)
|
||||
|
||||
// See runtime_cortexm_hardfault.go
|
||||
//go:export handleHardFault
|
||||
func handleHardFault(sp *interruptStack) {
|
||||
fault := GetFaultStatus()
|
||||
spValid := !fault.Bus().ImpreciseDataBusError()
|
||||
|
||||
print("fatal error: ")
|
||||
if spValid && uintptr(unsafe.Pointer(sp)) < 0x20000000 {
|
||||
print("stack overflow? ")
|
||||
}
|
||||
if fault.Mem().InstructionAccessViolation() {
|
||||
print("instruction access violation")
|
||||
}
|
||||
if fault.Mem().DataAccessViolation() {
|
||||
print("data access violation")
|
||||
}
|
||||
if fault.Mem().WhileUnstackingException() {
|
||||
print(" while unstacking exception")
|
||||
}
|
||||
if fault.Mem().WileStackingException() {
|
||||
print(" while stacking exception")
|
||||
}
|
||||
if fault.Mem().DuringFPLazyStatePres() {
|
||||
print(" during floating-point lazy state preservation")
|
||||
}
|
||||
|
||||
if fault.Bus().InstructionBusError() {
|
||||
print("instruction bus error")
|
||||
}
|
||||
if fault.Bus().PreciseDataBusError() {
|
||||
print("data bus error (precise)")
|
||||
}
|
||||
if fault.Bus().ImpreciseDataBusError() {
|
||||
print("data bus error (imprecise)")
|
||||
}
|
||||
if fault.Bus().WhileUnstackingException() {
|
||||
print(" while unstacking exception")
|
||||
}
|
||||
if fault.Bus().WhileStackingException() {
|
||||
print(" while stacking exception")
|
||||
}
|
||||
if fault.Bus().DuringFPLazyStatePres() {
|
||||
print(" during floating-point lazy state preservation")
|
||||
}
|
||||
|
||||
if fault.Usage().UndefinedInstruction() {
|
||||
print("undefined instruction")
|
||||
}
|
||||
if fault.Usage().IllegalUseOfEPSR() {
|
||||
print("illegal use of the EPSR")
|
||||
}
|
||||
if fault.Usage().IllegalExceptionReturn() {
|
||||
print("illegal load of EXC_RETURN to the PC")
|
||||
}
|
||||
if fault.Usage().AttemptedToAccessCoprocessor() {
|
||||
print("coprocessor access violation")
|
||||
}
|
||||
if fault.Usage().UnalignedMemoryAccess() {
|
||||
print("unaligned memory access")
|
||||
}
|
||||
if fault.Usage().DivideByZero() {
|
||||
print("divide by zero")
|
||||
}
|
||||
|
||||
if fault.Unknown() {
|
||||
print("unknown hard fault")
|
||||
}
|
||||
|
||||
if addr, ok := fault.Mem().Address(); ok {
|
||||
print(" with fault address ", addr)
|
||||
}
|
||||
|
||||
if addr, ok := fault.Bus().Address(); ok {
|
||||
print(" with bus fault address ", addr)
|
||||
}
|
||||
if spValid {
|
||||
print(" with sp=", sp)
|
||||
if uintptr(unsafe.Pointer(&sp.PC)) >= 0x20000000 {
|
||||
// Only print the PC if it points into memory.
|
||||
// It may not point into memory during a stack overflow, so check that
|
||||
// first before accessing the stack.
|
||||
print(" pc=", sp.PC)
|
||||
}
|
||||
}
|
||||
println()
|
||||
abort()
|
||||
}
|
||||
|
||||
// Descriptions are sourced from the K66 SVD and
|
||||
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Cihcfefj.html
|
||||
|
||||
// GetFaultStatus reads the System Control Block Configurable Fault Status
|
||||
// Register and returns it as a FaultStatus.
|
||||
func GetFaultStatus() FaultStatus { return FaultStatus(nxp.SystemControl.CFSR.Get()) }
|
||||
|
||||
type FaultStatus uint32
|
||||
type MemFaultStatus uint32
|
||||
type BusFaultStatus uint32
|
||||
type UsageFaultStatus uint32
|
||||
|
||||
func (fs FaultStatus) Mem() MemFaultStatus { return MemFaultStatus(fs) }
|
||||
func (fs FaultStatus) Bus() BusFaultStatus { return BusFaultStatus(fs) }
|
||||
func (fs FaultStatus) Usage() UsageFaultStatus { return UsageFaultStatus(fs) }
|
||||
|
||||
// Unknown returns true if the cause of the fault is not know
|
||||
func (fs FaultStatus) Unknown() bool { return fs&SystemControl_CFSR_KnownFault == 0 }
|
||||
|
||||
// InstructionAccessViolation: the processor attempted an instruction fetch from
|
||||
// a location that does not permit execution
|
||||
//
|
||||
// "This fault occurs on any access to an XN region, even when the MPU is
|
||||
// disabled or not present.
|
||||
//
|
||||
// When this bit is 1, the PC value stacked for the exception return points to
|
||||
// the faulting instruction. The processor has not written a fault address to
|
||||
// the MMAR."
|
||||
func (fs MemFaultStatus) InstructionAccessViolation() bool {
|
||||
return fs&nxp.SystemControl_CFSR_IACCVIOL != 0
|
||||
}
|
||||
|
||||
// DataAccessViolation: the processor attempted a load or store at a location
|
||||
// that does not permit the operation
|
||||
//
|
||||
// "When this bit is 1, the PC value stacked for the exception return points to
|
||||
// the faulting instruction. The processor has loaded the MMAR with the address
|
||||
// of the attempted access."
|
||||
func (fs MemFaultStatus) DataAccessViolation() bool { return fs&nxp.SystemControl_CFSR_DACCVIOL != 0 }
|
||||
|
||||
// WhileUnstackingException: unstack for an exception return has caused one or
|
||||
// more access violations
|
||||
//
|
||||
// "This fault is chained to the handler. This means that when this bit is 1,
|
||||
// the original return stack is still present. The processor has not adjusted
|
||||
// the SP from the failing return, and has not performed a new save. The
|
||||
// processor has not written a fault address to the MMAR."
|
||||
func (fs MemFaultStatus) WhileUnstackingException() bool {
|
||||
return fs&nxp.SystemControl_CFSR_MUNSTKERR != 0
|
||||
}
|
||||
|
||||
// WileStackingException: stacking for an exception entry has caused one or more
|
||||
// access violations
|
||||
//
|
||||
// "When this bit is 1, the SP is still adjusted but the values in the context
|
||||
// area on the stack might be incorrect. The processor has not written a fault
|
||||
// address to the MMAR."
|
||||
func (fs MemFaultStatus) WileStackingException() bool { return fs&nxp.SystemControl_CFSR_MSTKERR != 0 }
|
||||
|
||||
// DuringFPLazyStatePres: A MemManage fault occurred during floating-point lazy
|
||||
// state preservation
|
||||
func (fs MemFaultStatus) DuringFPLazyStatePres() bool { return fs&nxp.SystemControl_CFSR_MLSPERR != 0 }
|
||||
|
||||
// InstructionBusError: instruction bus error
|
||||
//
|
||||
// "The processor detects the instruction bus error on prefetching an
|
||||
// instruction, but it sets the IBUSERR flag to 1 only if it attempts to issue
|
||||
// the faulting instruction.
|
||||
//
|
||||
// When the processor sets this bit is 1, it does not write a fault address to
|
||||
// the BFAR."
|
||||
func (fs BusFaultStatus) InstructionBusError() bool { return fs&nxp.SystemControl_CFSR_IBUSERR != 0 }
|
||||
|
||||
// PreciseDataBusError: a data bus error has occurred, and the PC value stacked
|
||||
// for the exception return points to the instruction that caused the fault
|
||||
func (fs BusFaultStatus) PreciseDataBusError() bool { return fs&nxp.SystemControl_CFSR_PRECISERR != 0 }
|
||||
|
||||
// ImpreciseDataBusError: a data bus error has occurred, but the return address
|
||||
// in the stack frame is not related to the instruction that caused the error
|
||||
//
|
||||
// "When the processor sets this bit to 1, it does not write a fault address to
|
||||
// the BFAR.
|
||||
//
|
||||
// This is an asynchronous fault. Therefore, if it is detected when the priority
|
||||
// of the current process is higher than the BusFault priority, the BusFault
|
||||
// becomes pending and becomes active only when the processor returns from all
|
||||
// higher priority processes. If a precise fault occurs before the processor
|
||||
// enters the handler for the imprecise BusFault, the handler detects both
|
||||
// IMPRECISERR set to 1 and one of the precise fault status bits set to 1."
|
||||
func (fs BusFaultStatus) ImpreciseDataBusError() bool {
|
||||
return fs&nxp.SystemControl_CFSR_IMPRECISERR != 0
|
||||
}
|
||||
|
||||
// WhileUnstackingException: unstack for an exception return has caused one or
|
||||
// more BusFaults
|
||||
//
|
||||
// "This fault is chained to the handler. This means that when the processor
|
||||
// sets this bit to 1, the original return stack is still present. The processor
|
||||
// does not adjust the SP from the failing return, does not performed a new
|
||||
// save, and does not write a fault address to the BFAR."
|
||||
func (fs BusFaultStatus) WhileUnstackingException() bool {
|
||||
return fs&nxp.SystemControl_CFSR_UNSTKERR != 0
|
||||
}
|
||||
|
||||
// WhileStackingException: stacking for an exception entry has caused one or
|
||||
// more BusFaults
|
||||
//
|
||||
// "When the processor sets this bit to 1, the SP is still adjusted but the
|
||||
// values in the context area on the stack might be incorrect. The processor
|
||||
// does not write a fault address to the BFAR."
|
||||
func (fs BusFaultStatus) WhileStackingException() bool { return fs&nxp.SystemControl_CFSR_STKERR != 0 }
|
||||
|
||||
// DuringFPLazyStatePres: A bus fault occurred during floating-point lazy state
|
||||
// preservation
|
||||
func (fs BusFaultStatus) DuringFPLazyStatePres() bool { return fs&nxp.SystemControl_CFSR_LSPERR != 0 }
|
||||
|
||||
// UndefinedInstruction: the processor has attempted to execute an undefined
|
||||
// instruction
|
||||
//
|
||||
// "When this bit is set to 1, the PC value stacked for the exception return
|
||||
// points to the undefined instruction.
|
||||
//
|
||||
// An undefined instruction is an instruction that the processor cannot decode."
|
||||
func (fs UsageFaultStatus) UndefinedInstruction() bool {
|
||||
return fs&nxp.SystemControl_CFSR_UNDEFINSTR != 0
|
||||
}
|
||||
|
||||
// IllegalUseOfEPSR: the processor has attempted to execute an instruction that
|
||||
// makes illegal use of the EPSR
|
||||
//
|
||||
// "When this bit is set to 1, the PC value stacked for the exception return
|
||||
// points to the instruction that attempted the illegal use of the EPSR.
|
||||
//
|
||||
// This bit is not set to 1 if an undefined instruction uses the EPSR."
|
||||
func (fs UsageFaultStatus) IllegalUseOfEPSR() bool { return fs&nxp.SystemControl_CFSR_INVSTATE != 0 }
|
||||
|
||||
// IllegalExceptionReturn: the processor has attempted an illegal load of
|
||||
// EXC_RETURN to the PC
|
||||
//
|
||||
// "When this bit is set to 1, the PC value stacked for the exception return
|
||||
// points to the instruction that tried to perform the illegal load of the PC."
|
||||
func (fs UsageFaultStatus) IllegalExceptionReturn() bool { return fs&nxp.SystemControl_CFSR_INVPC != 0 }
|
||||
|
||||
// AttemptedToAccessCoprocessor: the processor has attempted to access a
|
||||
// coprocessor
|
||||
func (fs UsageFaultStatus) AttemptedToAccessCoprocessor() bool {
|
||||
return fs&nxp.SystemControl_CFSR_NOCP != 0
|
||||
}
|
||||
|
||||
// UnalignedMemoryAccess: the processor has made an unaligned memory access
|
||||
//
|
||||
// "Enable trapping of unaligned accesses by setting the UNALIGN_TRP bit in the
|
||||
// CCR to 1.
|
||||
//
|
||||
// Unaligned LDM, STM, LDRD, and STRD instructions always fault irrespective of
|
||||
// the setting of UNALIGN_TRP."
|
||||
func (fs UsageFaultStatus) UnalignedMemoryAccess() bool {
|
||||
return fs&nxp.SystemControl_CFSR_UNALIGNED != 0
|
||||
}
|
||||
|
||||
// DivideByZero: the processor has executed an SDIV or UDIV instruction with a
|
||||
// divisor of 0
|
||||
//
|
||||
// "When the processor sets this bit to 1, the PC value stacked for the
|
||||
// exception return points to the instruction that performed the divide by zero.
|
||||
//
|
||||
// Enable trapping of divide by zero by setting the DIV_0_TRP bit in the CCR to
|
||||
// 1."
|
||||
func (fs UsageFaultStatus) DivideByZero() bool { return fs&nxp.SystemControl_CFSR_DIVBYZERO != 0 }
|
||||
|
||||
// Address returns the MemManage Fault Address Register if the fault status
|
||||
// indicates the address is valid.
|
||||
//
|
||||
// "If a MemManage fault occurs and is escalated to a HardFault because of
|
||||
// priority, the HardFault handler must set this bit to 0. This prevents
|
||||
// problems on return to a stacked active MemManage fault handler whose MMAR
|
||||
// value has been overwritten."
|
||||
func (fs MemFaultStatus) Address() (uintptr, bool) {
|
||||
if fs&nxp.SystemControl_CFSR_MMARVALID == 0 {
|
||||
return 0, false
|
||||
} else {
|
||||
return uintptr(nxp.SystemControl.MMFAR.Get()), true
|
||||
}
|
||||
}
|
||||
|
||||
// Address returns the BusFault Address Register if the fault status
|
||||
// indicates the address is valid.
|
||||
//
|
||||
// "The processor sets this bit to 1 after a BusFault where the address is
|
||||
// known. Other faults can set this bit to 0, such as a MemManage fault
|
||||
// occurring later.
|
||||
//
|
||||
// If a BusFault occurs and is escalated to a hard fault because of priority,
|
||||
// the hard fault handler must set this bit to 0. This prevents problems if
|
||||
// returning to a stacked active BusFault handler whose BFAR value has been
|
||||
// overwritten.""
|
||||
func (fs BusFaultStatus) Address() (uintptr, bool) {
|
||||
if fs&nxp.SystemControl_CFSR_BFARVALID == 0 {
|
||||
return 0, false
|
||||
} else {
|
||||
return uintptr(nxp.SystemControl.BFAR.Get()), true
|
||||
}
|
||||
}
|
|
@ -235,3 +235,33 @@ func putchar(c byte) {
|
|||
|
||||
// ???
|
||||
const asyncScheduler = false
|
||||
|
||||
func abort() {
|
||||
println("!!! ABORT !!!")
|
||||
|
||||
m := arm.DisableInterrupts()
|
||||
arm.Asm("mov r12, #1")
|
||||
arm.Asm("msr basepri, r12") // only execute interrupts of priority 0
|
||||
nxp.SystemControl.SHPR3.ClearBits(nxp.SystemControl_SHPR3_PRI_15_Msk) // set systick to priority 0
|
||||
arm.EnableInterrupts(m)
|
||||
|
||||
machine.LED.Configure(machine.PinConfig{Mode: machine.PinOutput})
|
||||
|
||||
var v bool
|
||||
for {
|
||||
machine.LED.Set(v)
|
||||
v = !v
|
||||
|
||||
t := millisSinceBoot()
|
||||
for millisSinceBoot()-t < 60 {
|
||||
arm.Asm("wfi")
|
||||
}
|
||||
|
||||
// keep polling some communication while in fault
|
||||
// mode, so we don't completely die.
|
||||
// machine.PollUSB(&machine.USB0)
|
||||
machine.PollUART(&machine.UART0)
|
||||
machine.PollUART(&machine.UART1)
|
||||
machine.PollUART(&machine.UART2)
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче