Introduction | - | - | One integral part of any HAL must be to provide for the handling of
processor exceptions. This support provided here goes considerably further than most in
providing for exception handling. This includes:
|
||||||||||||||||||||||||||||||||||||||||
Callability |
|
||||||||||||||||||||||||||||||||||||||||||
HALError |
Note that when codes with bit 7 set (128-133) appear it is always in the form of a system halt |
||||||||||||||||||||||||||||||||||||||||||
Portability | API is portable Code not portable |
||||||||||||||||||||||||||||||||||||||||||
Constituents | Entirely written in ARM assembler for speed | ||||||||||||||||||||||||||||||||||||||||||
Porting notes | API will be very similar across platforms. This will ease porting of code which uses this module - hence the not including of ARM in the module's name or prefix. | ||||||||||||||||||||||||||||||||||||||||||
Other Notes | The following values in BoardDefines determine how many handlers exist
for each ARM vector:
|
||||||||||||||||||||||||||||||||||||||||||
General use | All calls to and from the exception handling module are prefixed by
ExceptH_. The module uses only zero page memory and hence may be called at times when main
memory is not functional. To initialise and finalise the operation of this module, the usual calls ExceptH_Initialise and ExceptH_Finalise are provided. The API's ExceptH_AddHandler and ExceptH_RemoveHandler permit the installation and removal of exception handlers. Control of the order in which handlers are called when the exception occurs is provided - through the use of bit 0 in the flags when calling ExceptH_AddHandler one may place the new handler at the top of the list or at the end of the list. The module also features an extremely low overhead when handling exceptions. It is hand-written in ARM assembler for speed and a handler can be called with as little as a twenty instruction overhead. The ExceptH end of the handling code is also reentrant, allowing (say) interrupts to be reenabled during the execution of an interrupt handler. To install the module over an existing exception handling system (eg; an OS or debugger like Demon or Angel) is easy. Initialise the module, perform an ExceptH_AddHandler on each of the relevent vectors with bit 2 set. This will unwind the stack and return entry registers to those of module entry before calling. This is done for you during the HAL startup code when DEMON or ANGEL builds are performed. On non-STANDALONE builds bit 0 in ExceptH_AddHandler is forced set as any former handler type entry prevents calls to those handlers after it in the list. It is good form to ensure your handler is capable of being called before and after any other handler and this has the benefit of not introducing an unneeded bug. You may also add the same handler many times for the same vector. It is your responsibility to remove the handler the same number of times. If you are unsure how many times a handler has been added, repeatedly call ExceptH_RemoveHandler until it returns an error. Handler format: The environment just prior to the call can be discovered via the contents of the stack as passed to the handler:
The SPSR is the Saved Processor Status Register and the CPSR is the Current Processor Status Register which holds the flags used by the ARM processor. This environment block allows a SWI handler to discover what SWI number it is or a virtual memory driver to discover what page of memory has faulted and needs loading. A routine wishing to alter R0-R3 on return may do so by altering the appropriate registers on the stack and altering R4-R7 on return may be done so directly as these are not used by the ExceptH handling routine. In addition, handlers must return a value in R0 to specify what is to be done by the system. These values can be:
Note that any errors returned via R0 this way have nowhere to go. Hence the ExceptH handler will perform an emergency halt to the system using NedHAL_EmergencyStop which is usually undesirable. Finally, for the benefit of code compiled with stack-checking enabled, the APCS stack limit register (sl=R10) is set to current stack less current stack size. This should catch any massive overflows of stack space, but as the routine cannot know the base of the stack for efficiency reasons, it may allow for corruption of memory. Besides, the APCS stack extending routine can not extend a supervisor stack so enabling stack checking on handler code is wasteful. Hence, we recommend the use of #pragma nocheck_stack around C functions which act as exception handlers. Example handler code:
|
Purpose | - | - | Initialises the exception handling module for subsequent use | |
Entry | None | |||
Exit | R0 = Null if no error occurred, pointer to HALError otherwise | |||
Interrupts | IRQ is disabled FIQ is unchanged |
|||
Processor Mode | SVC32 if User32 on entry, otherwise unchanged | |||
Staticity | Alters zero page memory only | |||
Use | None | |||
Notes | None |
Purpose | Deinitialises the exception handling module | |||
Entry | None | |||
Exit | R0 = Null if no error occurred, pointer to HALError otherwise | |||
Interrupts | IRQ is disabled FIQ is unchanged |
|||
Processor Mode | SVC32 if User32 on entry, otherwise unchanged | |||
Staticity | Alters zero page memory only | |||
Use | None | |||
Notes | None |
Purpose | Adds a handler for a particular vector | |||||||||||||
Entry | R0 = ARM vector number R1 = address of handler to add R2 = value to pass to handler (if bit 2 of R3 set then use is reserved, use zero)
|
|||||||||||||
Exit | R0 = Null if no error occurred, pointer to HALError otherwise | |||||||||||||
Interrupts | IRQ is disabled FIQ is unchanged |
|||||||||||||
Processor Mode | SVC32 if User32 on entry, otherwise unchanged | |||||||||||||
Staticity | Alters zero page memory only | |||||||||||||
Use | The ARM vector number specified by R0 is
determined by ResetV=0, UndefV=1, SWIV=2, PAbortV=3, DAbortV=4, IRQV=6, FIQV=7 - the same
way the ARM processor has them. There are macros equivalent to these names defined in
BoardDefines for your convenience. Placing a handler at the top of the calling list (bit 0 set) requires the list to be shifted downwards in memory first - this imposes an additional interrupt latency as interrupts are disabled during the duration of the move. Note that bit 0 is forced set on all non-STANDALONE builds as one always wishes for handlers to be added before the jump to the debugger - see the section below about bit 2/ Bit 1 is intended to be used when you wish R0-R7 to be preserved on entry to the handler from when the exception happened. This most likely occurs in the SWI exception handler when current register contents are used as parameters for the SWI call. If you wish to write the SWI handler in C, either use parameters which don't include R0 (easy as R0 usually contains the error status on SWI exit) or else write a function wrapper in assembler which moves R12 into a more convenient register or memory location. You could even embed assembler into the start of the function on some ARM compilers to retrieve R12 with the least efficiency loss. The use of bit 2 to add a former handler of this vector carries with it a number of caveats. Firstly, it will always claim any exceptions passed to it - if you don't want this, add it after you have added all your handlers or else add all handers with bit 0 set. You do not need to do this versions of the HAL built without the STANDALONE macro defined as bit 0 is always forced on on DEMON or ANGEL builds as these always install the Demon or Angel former handler during startup. The use of bit 2 handlers also adds a considerable latency to the execution of that handler. After all the ExceptH listed handlers preceding its entry have been called, ExceptH restores pre-exception context completely before it calls the former handler (hence appearing as though ExceptH had never been called). This requires considerable stack manipulation, and hence many cycles are used. |
|||||||||||||
Notes | The following values in BoardDefines determine how many handlers exist
for each ARM vector:
|
Purpose | Removes a handler for a particular vector | |||
Entry | R0 = ARM vector number R1 = address of handler |
|||
Exit | R0 = Null if no error occurred, pointer to HALError otherwise | |||
Interrupts | IRQ is disabled FIQ is unchanged |
|||
Processor Mode | SVC32 if User32 on entry, otherwise unchanged | |||
Staticity | Alters zero page memory only | |||
Use | The ARM vector number is determined by ResetV=0, UndefV=1, SWIV=2,
PAbortV=3, DAbortV=4, IRQV=6, FIQV=7 - the same way the ARM processor has them. There are
macros equivalent to these names defined in BoardDefines for your convenience. The removal of a handler at the top of the list will incur greater interrupt latency than one at the end of the list as the list onwards from the handler's entry must be copied upwards. |
|||
Notes | None |