Exception Handlers¶
CPU exceptions are the mechanism by which various execution and memory system errors are handled. When an exception occurs, Freedom Metal will call the corresponding exception handler function, if one has been registered by the application.
Initializing the CPU¶
When the user application enters the main()
function, the Freedom Metal
framework has not yet performed the initialization necessary to register
exception handlers. If this initialization is not performed before an exception
occurs, any exception will cause the CPU to spin in a tight loop until reset.
To initialize the Freedom Metal exception handlers, initialize CPU interrupts:
struct metal_cpu *cpu0 = metal_cpu_get(0);
if(!cpu) {
/* There was an error acquiring the CPU hart 0 handle */
}
struct metal_interrupt *cpu_int = metal_cpu_interrupt_controller(cpu0);
if(!cpu_int) {
/* There was an error acquiring the CPU interrupt controller */
}
metal_interrupt_init(cpu_int);
The Freedom Metal interrupt API is further documented in Interrupt Handlers and Interrupts.
Defining an Exception Handler¶
Exception handlers must conform to the following function signature:
-
typedef void (*
metal_exception_handler_t
)(struct metal_cpu *cpu, int ecode) Function signature for exception handlers.
Therefore, an example exception handler might look like:
void my_exception_handler(struct metal_cpu *cpu, int ecode) {
/* Contents of handler */
}
Registering an Exception Handler¶
Exception handlers are registered with a given CPU hart for an individual exception code.
/* CPU Hart 0's interrupt controller must be initialized
* if it is not already */
struct metal_cpu *cpu0 = metal_cpu_get(0);
int rc = metal_cpu_exception_register(cpu0,
<my_ecode>, /* Set to your desired value */
my_exception_handler);
if(rc != 0) {
/* Failed to register exception handler */
}
A single exception handler may be used for multiple exception codes. For this reason,
exception handlers receive the exception code as the ecode
parameter and may use
this to determine how to handle the exception.
Returing Execution after a Faulting Instruction¶
The default behavior of a RISC-V CPU is to return execution to the faulting instruction. If this is not the desired behavior, execution can be returned to the instruction after the faulting instruction using the following method:
void return_after_fault(struct metal_cpu *cpu, int ecode)
{
/* Get the faulting instruction address */
uintptr_t epc = metal_cpu_get_exception_pc(cpu);
/* Get the length of the faulting instruction */
size_t len = metal_cpu_get_instruction_length(cpu, epc);
/* Advance stored exception program counter by the
* instruction length */
metal_cpu_set_exception_pc(cpu, epc + len);
}
Additional Documentation¶
Additional documentation for the exception handler API can be found in The CPU API Reference.