FaultCortex.fth (17451B)
1 \ FaultCortex.fth - Exception fault handling 2 3 (( 4 Copyright (c) 2010, 2011 5 MicroProcessor Engineering 6 133 Hill Lane 7 Southampton SO15 5AF 8 England 9 10 tel: +44 (0)23 8063 1441 11 fax: +44 (0)23 8033 9691 12 email: mpe@mpeforth.com 13 tech-support@mpeforth.com 14 web: http://www.mpeforth.com 15 Skype: mpe_sfp 16 17 From North America, our telephone and fax numbers are: 18 011 44 23 8063 1441 19 011 44 23 8033 9691 20 901 313 4312 (North American access number to UK office) 21 22 23 To do 24 ===== 25 26 Change history 27 ============== 28 20110628 SFP001 Extended for Cortex-M0/M1. 29 )) 30 31 only forth definitions 32 decimal 33 34 \ ============== 35 \ *! faultcortex 36 \ *T Exception Fault handling 37 \ ============== 38 \ *P The code in *\i{Cortex/FaultCortex.fth} provides debugging 39 \ ** facilities for when a fault occurs that triggers an exception. 40 41 42 \ ************************** 43 \ *S Fault handler framework 44 \ ************************** 45 46 struct /AppFrame \ -- len 47 \ *G Structure defining what is saved on the application's 48 \ ** stack. 49 int exa.r0 50 int exa.r1 51 int exa.r2 52 int exa.r3 53 int exa.r12 54 int exa.r14 55 int exa.PC 56 int exa.PSR 57 end-struct 58 59 struct /MainFrame \ -- len 60 int exm.r4 61 int exm.r5 62 int exm.r6 63 int exm.r7 64 int exm.r8 65 int exm.r9 66 int exm.r10 67 int exm.r11 68 int exm.ExcRet \ LR after entry 69 end-struct 70 71 struct /ExData \ -- len 72 \ *G A structure holding data saved on entry to the exception 73 \ ** routine 74 int exd.R13flt \ Faulting stack on entry 75 int exd.R13main \ main stack on entry 76 int exd.R13process \ process stack on entry 77 int exd.R13state \ main stack after state save 78 int exd.LRmain \ LR of main stack on entry 79 end-struct 80 81 /ExData reserve constant ExData \ -- addr 82 \ *G Holds additional data about the fault. 83 84 Not-M0/M1? [if] 85 PROC FltEntry \ -- 86 \ *G This is the template code for Cortex-M3+ fault handlers. 87 \ ** R0..R3 have already been saved by the hardware 88 \ --- save fault data --- 89 mvl r3, # ExData \ point to save buffer 90 mrs r0, SP_main \ save stack pointers 91 str r0, [ r3, # 0 exd.R13main ] 92 mrs r1, SP_process 93 str r1, [ r3, # 0 exd.R13process ] 94 str lr, [ r3, # 0 exd.LRmain ] \ save LR 95 tst lr, # bit2 96 ite .eq 97 str r0, [ r3, # 0 exd.R13flt ] \ faulting stack is SP_main 98 str r1, [ r3, # 0 exd.R13flt ] \ faulting stack is SP_process 99 \ --- save state --- must match /MainFrame structure above 100 push { r4-r11, r14 } \ registers not saved by core + link 101 str r13, [ r3, # 0 exd.R13state ] 102 sub rsp, rsp, # up-size sp-size + \ make space for USER area and data stack 103 104 \ --- set up Forth --- 105 add up, rsp, # sp-size \ USER area at top 106 sub psp, up, # sp-guard cells \ generate new PSP 107 sub r4, psp, # tos-cached? cells \ generate new S0 108 str r4, [ up, # s0-offset ] \ that is compatible with SP! 109 str rsp, [ up, # r0-offset ] \ set R0 110 \ --- call high level handler --- 111 l: FltCall 112 bl ' noop \ call the high level handler 113 \ --- restore state --- 114 add rsp, rsp, # up-size sp-size + \ remove space for USER area and data stack 115 pop { r4-r11, pc } \ restore and exit 116 end-code 117 chere FltEntry - equ /FltEntry \ size of template 118 FltCall FltEntry - equ /FltCall \ offset to call instruction 119 [else] 120 PROC FltEntry \ -- 121 \ *G This is the template code for Cortex-M0/M1 fault handlers. 122 \ ** R0..R3 have already been saved by the hardware 123 \ --- save fault data --- 124 ldr r3, ^ExData \ point to save buffer 125 mrs r0, SP_main \ save stack pointers 126 str r0, [ r3, # 0 exd.R13main ] 127 mrs r1, SP_process 128 str r1, [ r3, # 0 exd.R13process ] 129 mov r2, lr 130 str r2, [ r3, # 0 exd.LRmain ] \ save LR 131 mov .s r0, # bit2 132 tst r2, r0 133 eq, if, 134 ldr r0, [ r3, # 0 exd.R13main ] \ faulting stack is SP_main 135 else, 136 ldr r0, [ r3, # 0 exd.R13Process ] \ faulting stack is SP_process 137 endif, 138 str r0, [ r3, # 0 exd.R13flt ] \ faulting stack pointer 139 \ --- save state --- must match /MainFrame structure above 140 mov r0, r8 141 mov r1, r9 142 mov r2, r10 143 mov r3, r11 144 push { r0-r3, r14 } \ R8-R11 not saved by core + link 145 push { r4-r7 } \ R4-R7 146 147 ldr r3, ^ExData \ point to save buffer 148 mov r0, r13 149 str r0, [ r3, # 0 exd.R13state ] 150 sub rsp, rsp, # up-size sp-size + \ make space for USER area and data stack 151 152 \ --- set up Forth --- 153 mov r0, rsp 154 add .s r0, r0, # sp-size \ USER area at top 155 mov up, r0 156 mov psp, r0 157 sub .s psp, psp, # sp-guard cells \ generate new PSP 158 sub .s r4, psp, # tos-cached? cells \ generate new S0 159 str r4, [ r0, # s0-offset ] \ that is compatible with SP! 160 mov r1, rsp 161 str r1, [ r0, # r0-offset ] \ set R0 162 \ --- call high level handler --- 163 l: FltCall 164 bl ' noop \ call the high level handler 165 \ --- restore state --- 166 add rsp, rsp, # up-size sp-size + \ remove space for USER area and data stack 167 pop { r4-r7 } \ original R4-R7 168 pop { r0-r3 } \ original R8-11 169 mov r8, r0 170 mov r9, r1 171 mov r10, r2 172 mov r11, r3 173 pop { pc } \ exit 174 end-code 175 align l: ^ExData 176 ExData , 177 chere FltEntry - equ /FltEntry \ size of template 178 FltCall FltEntry - equ /FltCall \ offset to call instruction 179 [then] 180 181 interpreter 182 : FLT: \ xt vec# -- ; -- isr 183 \ *G Creates a fault handler that runs the given Forth word. 184 \ ** At run time the entry point of the ISR is returned. Use in 185 \ ** the form: 186 \ *C ' <action> <vec#> FLT: <actionISR> 187 \ *P The example below will define a handler for the HardFault 188 \ ** exception that runs the Forth word *\fo{HFhandler}. 189 \ *C ' HFhandler 3 FLT: HFentry 190 swap align chere /FltEntry callot \ -- vec# xt isr ; reserve space 191 FltEntry over /FltEntry move \ copy template 192 tuck /FltCall + !call \ -- vec# isr ; form call instruction 193 tuck swap setExcVec 194 label \ returns entry point 195 ; 196 target 197 198 : .item \ addr -- addr+4 199 \ *G Display a cell item at addr and increment addr. 200 dup @ .dword space cell + ; 201 : .items \ addr n -- addr+4n 202 \ *G Display n items at addr and increment addr. 203 0 ?do .item loop ; 204 205 : AppItems \ -- addr 206 \ *G The address of fault data saved on the application's stack. 207 ExData exd.R13flt @ ; 208 : MainItems \ -- addr 209 \ *G The address of fault data saved on SP_main. 210 ExData exd.R13state @ ; 211 : AppRSP \ -- addr 212 \ *G The Forth return stack pointer at the fault. 213 AppItems 8 cells + ; 214 Cortex-M0/M1? [if] 215 : AppPSP \ -- addr 216 \ *G The Forth data stack pointer at the fault for Cortex-M3+. 217 MainItems exm.R6 @ ; 218 [else] 219 : AppPSP \ -- addr 220 \ *G The Forth data stack pointer at the fault for Cortex-M3+. 221 AppItems exa.R12 @ ; 222 [then] 223 : AppUP \ -- addr 224 \ *G The Forth UP at the fault. 225 MainItems exm.R11 @ ; 226 : AppTOS \ -- x 227 \ *G The Forth TOS at the fault. 228 MainItems exm.R7 @ ; 229 : AppPC \ -- x 230 \ *G The PC at the fault. 231 AppItems exa.PC @ ; 232 233 : .FltFrame \ -- 234 \ *G Display the CPU state pointed to by the data frame. 235 AppItems \ from the faulting stack 236 cr ." === Registers ===" 237 cr ." PSR = " dup exa.PSR @ .dword \ PSR 238 cr ." R0 = " dup exa.R0 4 .items \ R0..R3 239 drop 240 MainItems exm.R4 \ from SP_main 241 cr ." R4 = " 4 .items \ R4..R7 242 cr ." R8 = " 4 .items \ R8..R11 243 drop 244 AppItems \ from the faulting stack 245 cr ." R12 = " dup exa.r12 .item drop \ R12 246 dup 8 cells + .dword space \ R13 before fault 247 exa.R14 2 .items drop \ R14 R15 248 ; 249 250 [undefined] ip>nfa [if] 251 : name? \ addr -- flag 252 \ *G Check to see if the supplied address is a valid NFA. 253 \ ** This word is implementation dependent. 254 \ ** A valid NFA for MPE embedded systems satisfies the following: 255 \ *( 256 \ *B All characters within string are printable ASCII within range 33..126. 257 \ *B String Length is non-zero in range 1..31 and bit 7 is set, ignore bits 6, 5. 258 \ *) 259 dup aligned over <> 260 if drop FALSE exit endif 261 count \ c-addr u -- 262 dup $9F and $81 $A0 within 0= \ NFA first byte = 1SIxxxxx, count = xxxxx 263 \ mask = 10011111 264 if 2drop 0 exit then 265 $01F and bounds ?do 266 i c@ #33 #127 within 0= \ check all ascii chars 267 if unloop FALSE exit then 268 loop 269 TRUE 270 ; 271 272 : ip>nfa \ addr -- nfa 273 \ *G Attempt to move backwards from an address within a definition 274 \ ** to the relevant NFA. 275 2- \ NFA must be at least 'n' bytes backwards 276 begin 277 dup name? 0= 278 while 279 1- 280 repeat 281 ; 282 [then] 283 284 : check-aligned \ addr -- addr' 285 \ *G Check addr for cell alignment, report and correct if 286 \ ** misaligned. 287 dup aligned over <> 288 if ." (Misaligned) " aligned then 289 ; 290 291 : ?Clip32 \ xsp xspTop -- xsp xspTop' 292 \ *G Clip the stack display to 32 items. 293 2dup swap - #32 cells u> if 294 drop dup #32 cells + 295 cr ." Clipped to 32 items" 296 endif 297 ; 298 299 [defined] Umbilical? [if] 300 ' (do) equ start-of-code 301 [else] 302 ' and equ start-of-code 303 [then] 304 prog sec-end equ end-of-code 305 306 : .rsFault \ -- 307 \ *G Display the return stack of the fault, assuming 308 \ ** the Forth RSP=R13 and RUP=R11. 309 cr AppRSP \ -- rsp 310 cr ." RSP = " dup .dword space check-aligned 311 AppUP r0-offset + @ \ -- rsp rsTop 312 ." R0 = " dup .dword space check-aligned 313 \ Note that for negative steps, +LOOP processes the limit value. 314 cell - \ rsTop is not used 315 cr ." --- Return stack high ---" 316 ?Clip32 ?do 317 cr i .dword space i @ dup .dword space 318 dup start-of-code end-of-code within 319 if ip>nfa .name else drop endif 320 cell negate +loop 321 cr ." --- Return stack low --- " 322 ; 323 324 : .psFault \ -- 325 \ *G Display the data stack indicated by the frame, assuming 326 \ ** the Forth RSP=AppPSP and RUP=R11. 327 cr AppPSP \ -- psp 328 cr ." PSP = " dup .dword space check-aligned 329 AppUP s0-offset + @ \ -- psp psTop 330 ." S0 = " dup .dword space check-aligned 331 \ Because of the stack caching S0 is a cell LOWER than PSP 332 \ for an empty stack. 333 2dup cell + u> 334 if 2drop cr ." Data stack underflowed" exit endif 335 2dup cell + = 336 if 2drop cr ." Data stack empty" exit endif 337 2dup swap - SP-SIZE u> 338 if 2drop cr ." Data stack overflowed" exit endif 339 cr ." --- Data stack high ---" \ -- psp psTop/S0 340 2dup <> if 341 cell - ?Clip32 do \ for -ve steps +LOOP runs the limit value 342 cr i .dword space i @ .dword 343 cell negate 344 +loop 345 else 346 2drop 347 endif 348 cr ." --- Data stack low --- " 349 cr ." rTOS/R7 " AppTOS .dword 350 ; 351 352 353 \ ************************* 354 \ *S Default Fault handlers 355 \ ************************* 356 \ *P The interrupt structure of the Cortex-M3 is such that the 357 \ ** simplest way to display exception information without large 358 \ ** code and RAM overheads is to use polled operation of the 359 \ ** comms link without interrupts. By default, the fault handler 360 \ ** only transmits, it does not use *\fo{KEY}. If your serial 361 \ ** driver normally uses interrupt-driven transmit routines, you 362 \ ** must provide the word *\fo{+FaultConsole} which switches it 363 \ ** into polled operation. An example can be found in 364 \ ** *\i{Cortex/Drivers/serLPC17xxqi.fth}. 365 366 \ *P By default, there is no return from a fault handler, the 367 \ ** system is reset using *\fo{REBOOT}. From experience, 368 \ ** attempting to restart the system by executing the boot 369 \ ** vector is not enough. Rebooting by triggering the watchdog 370 \ ** always works. 371 372 : showFault \ -- 373 \ *G Generic fault handler. 374 console setConsole +FaultConsole \ use fault mode console 375 decimal 376 cr 377 cr ." Exception " IPSR sys@ . ." at " AppPC .dword 378 .FltFrame .rsFault .psFault 379 cr ." Restarting system ..." 380 [defined] reboot [if] reboot [else] ExcVecs 4 + @ execute [then] 381 0 382 ; 383 384 ' showFault 2 FLT: NMIisr 385 ' showFault 3 FLT: HFisr 386 Cortex-M0/M1? 0= [if] 387 ' showFault 4 FLT: MFisr 388 ' showFault 5 FLT: BFisr 389 ' showFault 6 FLT: UFisr 390 ' showFault #12 FLT: DFisr 391 392 : EnableFaults \ -- 393 \ Enable the fault handlers. 394 $0007:0000 _SCS scsSHCSR + ! ; 395 ' EnableFaults AtCold 396 [then] 397 398 399 \ ***************************** 400 \ *S Intepreting the crash dump 401 \ ***************************** 402 \ *P The example below comes from typing 403 \ *C 55 0 ! 404 \ *P on the Forth console. The device is an NXP LPC2388 and address 405 \ ** zero is Flash to which writes have not been permitted. There 406 \ ** are only minor differences between the ARM example here and the 407 \ ** fault display for Cortex-M devices. 408 409 \ *E 55 0 ! 410 \ ** DAbort exception at PC = 0000:1C64 411 \ ** === Registers === 412 \ ** CPSR = 6000:001F 413 \ ** R0 = 0000:0037 0000:1C60 0000:0021 0000:0021 414 \ ** R4 = 0000:0070 0005:FD8C 4000:0298 0000:000C 415 \ ** R8 = 0000:1C60 0000:0000 0000:0000 4000:FEE0 416 \ ** R12 = 4000:FED4 4000:FDCC 0000:4FAC 0000:1C6C 417 \ ** 418 \ ** RSP = 4000:FDCC R0 = 4000:FDE0 419 \ ** --- Return stack high --- 420 \ ** 4000:FDDC 0000:51E0 QUIT 421 \ ** 4000:FDD8 4000:FED0 422 \ ** 4000:FDD4 0000:0000 423 \ ** 4000:FDD0 4000:FD94 424 \ ** 4000:FDCC 0000:3244 CATCH 425 \ ** --- Return stack low --- 426 \ ** 427 \ ** PSP = 4000:FED4 S0 = 4000:FED4 428 \ ** --- Data stack high --- 429 \ ** --- Data stack low --- 430 \ ** rTOS/R10 0000:0000 431 \ ** Restarting system ... 432 433 \ *P The exception occurred in the instruction at $1C64. It was 434 \ ** a data abort exception, which means that it was a data 435 \ ** load or store at an invalid address. 436 437 \ *P Assuming that restart is to a Forth console, you can find 438 \ ** out where the fault occurred if you have compiled the file 439 \ ** *\i{Common\DebugTools.fth} or *\i{Powernet\DebugTools.fth}. 440 \ *C $1C64 ip>nfa .name<Enter> ! ok 441 442 \ *P The return stack dump shows that *\fo{CATCH} was used, in turn 443 \ ** called by *\fo{QUIT}, the text interpreter. 444 445 \ *P Further interpretation requires some knowledge of the use of 446 \ ** the CPU registers. 447 448 \ ***************** 449 \ *N Register usage 450 \ ***************** 451 \ --------- 452 \ *H Cortex 453 \ --------- 454 \ *P For Cortex-M the following register usage is the default: 455 \ *E r15 pc program counter 456 \ ** r14 link link register; bit0=1=Thumb, usually set 457 \ ** r13 rsp return stack pointer 458 \ ** r12 psp data stack pointer 459 \ ** r11 up user area pointer 460 \ ** r10 -- 461 \ ** r9 lp locals pointer 462 \ ** r8 -- 463 \ ** r7 tos cached top of stack 464 \ ** r0-r6 scratch 465 \ *P The VFX optimiser reserves R0 and R1 for internal operations. 466 \ ** *\fo{CODE} definitions must use R10 as TOS with NOS pointed to by 467 \ ** R12 as a full descending stack in ARM terminology. R0..R8 are 468 \ ** free for use by *\fo{CODE} definitions and need not be preserved or 469 \ ** restored. You should assume that any register can be affected 470 \ ** by other words. 471 \ --------- 472 \ *H ARM32 473 \ --------- 474 \ *P On the ARM the following register usage is the default: 475 \ *E r15 pc program counter 476 \ ** r14 link link register 477 \ ** r13 rsp return stack pointer 478 \ ** r12 psp data stack pointer 479 \ ** r11 up user area pointer 480 \ ** r10 tos cached top of stack 481 \ ** r9 lp locals pointer 482 \ ** r0-r8 scratch 483 \ *P The VFX optimiser reserves R0 and R1 for internal operations. 484 \ ** *\fo{CODE} definitions must use R10 as TOS with NOS pointed to by 485 \ ** R12 as a full descending stack in ARM terminology. R0..R8 are 486 \ ** free for use by *\fo{CODE} definitions and need not be preserved or 487 \ ** restored. You should assume that any register can be affected 488 \ ** by other words. 489 490 \ ============================= 491 \ *N Interpreting the registers 492 \ ============================= 493 \ *P Using the ARM example above, we can learn more. 494 \ *E DAbort exception at PC = 0000:1C64 495 \ ** === Registers === 496 \ ** CPSR = 6000:001F 497 \ ** R0 = 0000:0037 0000:1C60 0000:0021 0000:0021 498 \ ** R4 = 0000:0070 0005:FD8C 4000:0298 0000:000C 499 \ ** R8 = 0000:1C60 0000:0000 0000:0000 4000:FEE0 500 \ ** R12 = 4000:FED4 4000:FDCC 0000:4FAC 0000:1C6C 501 502 \ *P The exception occurred in the instruction at $1C64. It was 503 \ ** a data abort exception, which means that it was a data 504 \ ** load or store at an invalid address. 505 506 \ *E TOS = R10 = 0000:0000 507 \ ** UP = R11 = 4000:FEE0 508 \ ** PSP = R12 = 4000:FED4 509 \ ** RSP = R13 = 4000:FDCC 510 511 \ *P In general, UP > PSP > RSP. In this case that's good. TOS=0, 512 \ ** which we would expect from the phrase: 513 \ *C 55 0 ! 514 515 \ *P We now switch back to the cross compiler, which you did leave 516 \ ** running, didn't you? Since we now know that $1C64 is in *\fo{!}, 517 \ ** we can disassemble it. 518 \ *E dis ! 519 \ ** ! 520 \ ** ( 0000:1C60 0100BCE8 ..<h ) ldmia r12 ! { r0 } 521 \ ** ( 0000:1C64 00008AE5 ...e ) str r0, [ r10, # $00 ] 522 \ ** ( 0000:1C68 0004BCE8 ..<h ) ldmia r12 ! { r10 } 523 \ ** ( 0000:1C6C 0EF0A0E1 .p a ) mov PC, LR 524 \ ** 16 bytes, 4 instructions. 525 \ ** ok 526 527 \ *P From this, we can see that the offending instruction is 528 \ *C ( 0000:1C64 00008AE5 ...e ) str r0, [ r10, # $00 ] 529 \ *P Since R10 is 0, we now know that it was attempting a 530 \ ** write to 0, which is not permitted. 531 532 \ *P Provided that you keep words small, the register contents 533 \ ** at the crash point, with the stack contents and the disassembly 534 \ ** often provide enough information to reconstruct the state of 535 \ ** stack on entry to the word. 536 537 538 \ ====== 539 \ *> ### 540 \ ====== 541 542 543 decimal