umouse

umouse.git
git clone git://git.lenczewski.org/umouse.git
Log | Files | Refs | Submodules | README

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