ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_exception.c File Reference

Cortex-M85 CPU exception diagnostic implementation. More...

#include "ra8_exception.h"
#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_error_handler.h"
#include "ra8_log.h"
#include "ra8_scb.h"
Include dependency graph for ra8_exception.c:

Go to the source code of this file.

Macros

#define RA8_EXCEPTION_HALT(tag, msg, exc)
 RA8 EXCEPTION HALT.

Enumerations

enum  ra8_exc_num_t : uint32_t { k_ra8_exc_num_nmi = 2U }
 Architectural exception numbers this module special-cases. More...

Functions

void ra8_exception_capture_diagnostics (ra8_exception_diagnostics_t *out)
 Implementation of ra8_exception_capture_diagnostics() – routes the SCB fault-status read through the shared ra8_scb primitive.
void ra8_exception_set_persist_hook (ra8_exception_persist_fn hook)
 Implementation of ra8_exception_set_persist_hook() – store the sink pointer.
static void internal_exception_halt_loop (void)
 Spin halt with a known PC at a named symbol.
static void internal_log_fault_dump (const ra8_exception_frame_t *frame, uint32_t exc_number, const ra8_exception_diagnostics_t *diag, uint32_t nmisr)
 Best-effort log dump of a captured fault record.
void ra8_exception_report (const ra8_exception_frame_t *frame, uint32_t exc_number)
 Implementation of ra8_exception_report() – fault dump + halt.
void ra8_exception_report_nmi (const ra8_exception_frame_t *frame, uint32_t nmisr)
 Implementation of ra8_exception_report_nmi() – stage the ICU cause, then chain.

Variables

volatile ra8_exception_last_t g_ra8_exception_last
 Definition of g_ra8_exception_last – contract documented in ra8_exception.h.
static volatile uint32_t s_ra8_exception_nmi_stage
 NMISR cause staged by ra8_exception_report_nmi() for the record.
static ra8_exception_persist_fn s_ra8_exception_persist
 Registered post-decode persistence sink, or nullptr when disarmed.

Detailed Description

Cortex-M85 CPU exception diagnostic implementation.

Reads the SCB fault status registers and logs the stacked exception frame before halting via ra8_fatal_error().

Installing as the HardFault handler

The weak HardFault_Handler in src/boot/vector_table.c is replaced by a non-weak naked trampoline:

__attribute__((naked)) void HardFault_Handler(void)
{
__asm__ volatile(
"tst lr, #4 \n" // MSP or PSP?
"ite eq \n"
"mrseq r0, msp \n"
"mrsne r0, psp \n"
"mov r1, #3 \n" // exception number for HardFault
"b ra8_exception_report\n"
);
}
void HardFault_Handler(void)
Capture a HardFault and transfer to the common exception reporter.

The trampoline picks the stack pointer the fault was taken on (MSP if EXC_RETURN[2]=0, PSP otherwise) and tail-calls ra8_exception_report() with a pointer to the stacked frame.

Definition in file ra8_exception.c.

Macro Definition Documentation

◆ RA8_EXCEPTION_HALT

#define RA8_EXCEPTION_HALT ( tag,
msg,
exc )
Value:
do { \
(void)(tag); \
(void)(msg); \
(void)(exc); \
internal_exception_halt_loop(); \
} while (0)

RA8 EXCEPTION HALT.

Definition at line 52 of file ra8_exception.c.

Referenced by ra8_exception_report().

Enumeration Type Documentation

◆ ra8_exc_num_t

enum ra8_exc_num_t : uint32_t

Architectural exception numbers this module special-cases.

Enumerator
k_ra8_exc_num_nmi 

NMI vector slot – carries an ICU NMISR cause.

Definition at line 65 of file ra8_exception.c.

Function Documentation

◆ internal_exception_halt_loop()

void internal_exception_halt_loop ( void )
static

Spin halt with a known PC at a named symbol.

The fault handler MUST terminate at a symbol the debugger can name rather than escalate to LOCKUP at PC=0xEFFFFFFE. This function is a wfi loop in its own translation-unit-local symbol so a backtrace unambiguously points at "we got here from the fault handler" rather than at a random unmapped address.

Precondition
All maskable interrupts have been disabled by the caller.
g_ra8_exception_last has been populated with the fault snapshot.
Postcondition
Function never returns.
CPU is parked in a wfi loop with IRQs masked.
Note
noreturn. Trivially thread-safe.
Since
0.1.0

Definition at line 180 of file ra8_exception.c.

References RA8_INTERNAL.

◆ internal_log_fault_dump()

void internal_log_fault_dump ( const ra8_exception_frame_t * frame,
uint32_t exc_number,
const ra8_exception_diagnostics_t * diag,
uint32_t nmisr )
static

Best-effort log dump of a captured fault record.

Emits the exception number, the stacked frame (when present), the SCB diagnostics, and – for the NMI class only – the ICU NMISR cause, all via ra8_log_error_val. Runs strictly AFTER the fixed-SRAM snapshot is complete, so a secondary fault inside the log backend can no longer lose the record.

Parameters
[in]frameStacked exception frame; may be nullptr.
[in]exc_numberArchitectural exception number.
[in]diagCaptured SCB diagnostics (never nullptr; sole caller passes a stack local).
[in]nmisrRecorded ICU NMI cause (logged only for exc 2).
Precondition
g_ra8_exception_last.magic is already k_ra8_exc_magic_valid.
diag points at the diagnostics captured for this event.
Postcondition
Every field above was offered to the log backend (which may drop bytes in fault context – see ra8_log.c).
No snapshot state is modified.
Note
Not thread-safe (single fault context by construction).
Since
0.1.0

Definition at line 215 of file ra8_exception.c.

References ra8_exception_diagnostics_t::bfar, ra8_exception_diagnostics_t::cfsr, ra8_exception_diagnostics_t::hfsr, k_ra8_exc_num_nmi, ra8_exception_frame_t::lr, ra8_exception_diagnostics_t::mmfar, ra8_exception_frame_t::pc, ra8_exception_frame_t::r0, ra8_exception_frame_t::r1, ra8_exception_frame_t::r12, ra8_exception_frame_t::r2, ra8_exception_frame_t::r3, RA8_INTERNAL, ra8_log_error_val, ra8_exception_diagnostics_t::sfar, ra8_exception_diagnostics_t::sfsr, and ra8_exception_frame_t::xpsr.

Referenced by ra8_exception_report().

◆ ra8_exception_capture_diagnostics()

void ra8_exception_capture_diagnostics ( ra8_exception_diagnostics_t * out)

Implementation of ra8_exception_capture_diagnostics() – routes the SCB fault-status read through the shared ra8_scb primitive.

Read the current SCB fault-status registers.

Delegates the CFSR / HFSR / DFSR / MMFAR / BFAR / AFSR + Secure SFSR / SFAR reads to ra8_scb_read_fault_status, then copies the snapshot into the exception-record layout. NULL argument is tolerated and returns silently (the fault path must never log from here), so the shared primitive is only ever handed a stack local and its null-guard return cannot trip. Behaviourally identical to the previous inline reads: the same eight registers in the same order, no register written.

Parameters
[out]outDestination buffer. May be nullptr.
Precondition
out is either nullptr or points to writable storage.
SCB MMIO at 0xE000ED00 is accessible.
Postcondition
On non-nullptr input, every field of *out reflects the SCB at the moment of the call.
No SCB register is modified.
Note
Not reentrant w.r.t. concurrent SCB writes.
Since
0.1.0

Definition at line 94 of file ra8_exception.c.

References ra8_exception_diagnostics_t::afsr, ra8_scb_fault_status_t::afsr, ra8_exception_diagnostics_t::bfar, ra8_scb_fault_status_t::bfar, ra8_exception_diagnostics_t::cfsr, ra8_scb_fault_status_t::cfsr, ra8_exception_diagnostics_t::dfsr, ra8_scb_fault_status_t::dfsr, ra8_exception_diagnostics_t::hfsr, ra8_scb_fault_status_t::hfsr, k_ra8_ok, ra8_exception_diagnostics_t::mmfar, ra8_scb_fault_status_t::mmfar, ra8_scb_read_fault_status(), ra8_exception_diagnostics_t::sfar, ra8_scb_fault_status_t::sfar, ra8_exception_diagnostics_t::sfsr, and ra8_scb_fault_status_t::sfsr.

Referenced by ra8_exception_report().

◆ ra8_exception_report()

void ra8_exception_report ( const ra8_exception_frame_t * frame,
uint32_t exc_number )

Implementation of ra8_exception_report() – fault dump + halt.

Emit a full fault dump over the log backend.

Captures the stacked frame and SCB diagnostics into the fixed-SRAM snapshot g_ra8_exception_last BEFORE any function call that might itself fault. Then best-effort logs them via internal_log_fault_dump (ra8_log silently drops every byte from a fault context on the default ITM backend; a registered byte sink still emits, see libs/ra8_core/src/ra8_log.c). Finally parks the CPU at the named internal_exception_halt_loop symbol on target so the debugger can give the halt a clean backtrace instead of escalating to LOCKUP at PC=0xEFFFFFFE.

Parameters
[in]frameStacked exception frame; may be nullptr.
[in]exc_numberArchitectural exception number.
Precondition
Invoked from a fault context (IPSR != 0).
g_ra8_exception_last is writable SRAM.
Postcondition
g_ra8_exception_last.magic == 0xFA17DEAD once snapshot is complete.
Control never returns; CPU is halted at a named symbol.
Note
Marked noreturn. Not thread-safe.
Since
0.1.0

Definition at line 270 of file ra8_exception.c.

References g_ra8_exception_last, internal_log_fault_dump(), k_ra8_exc_magic_valid, ra8_exception_frame_t::lr, ra8_exception_frame_t::pc, ra8_exception_frame_t::r0, ra8_exception_frame_t::r1, ra8_exception_frame_t::r12, ra8_exception_frame_t::r2, ra8_exception_frame_t::r3, ra8_exception_capture_diagnostics(), RA8_EXCEPTION_HALT, s_ra8_exception_nmi_stage, s_ra8_exception_persist, and ra8_exception_frame_t::xpsr.

Referenced by blc_fault_dispatch(), and ra8_exception_report_nmi().

◆ ra8_exception_report_nmi()

void ra8_exception_report_nmi ( const ra8_exception_frame_t * frame,
uint32_t nmisr )

Implementation of ra8_exception_report_nmi() – stage the ICU cause, then chain.

Report an NMI with its ICU cause register, then halt.

Definition at line 322 of file ra8_exception.c.

References k_ra8_exc_num_nmi, ra8_exception_report(), and s_ra8_exception_nmi_stage.

Referenced by internal_nmi_report(), and internal_ra8_board_nmi_report().

◆ ra8_exception_set_persist_hook()

void ra8_exception_set_persist_hook ( ra8_exception_persist_fn hook)

Implementation of ra8_exception_set_persist_hook() – store the sink pointer.

Register (or clear) the post-decode fault-persistence hook.

Definition at line 155 of file ra8_exception.c.

References s_ra8_exception_persist.

Referenced by ra8_crashlog_install().

Variable Documentation

◆ g_ra8_exception_last

volatile ra8_exception_last_t g_ra8_exception_last

Definition of g_ra8_exception_last – contract documented in ra8_exception.h.

The one fixed-SRAM ra8_exception_last_t snapshot instance.

Definition at line 114 of file ra8_exception.c.

Referenced by ra8_exception_report().

◆ s_ra8_exception_nmi_stage

volatile uint32_t s_ra8_exception_nmi_stage
static

NMISR cause staged by ra8_exception_report_nmi() for the record.

ra8_exception_report() owns the fixed-SRAM snapshot write order (everything first, magic last). The NMI entry point cannot write the record directly without racing that ordering, so it stages the ICU cause here (a plain SRAM store that cannot fault) and the common path copies it into g_ra8_exception_last.nmisr – then clears the stage so a later non-NMI record never inherits a stale cause.

Note
Written only by ra8_exception_report_nmi(); consumed and cleared by ra8_exception_report().
Warning
Do not read outside this translation unit.
Since
0.1.0

Definition at line 133 of file ra8_exception.c.

Referenced by ra8_exception_report(), and ra8_exception_report_nmi().

◆ s_ra8_exception_persist

ra8_exception_persist_fn s_ra8_exception_persist
static

Registered post-decode persistence sink, or nullptr when disarmed.

Set by ra8_exception_set_persist_hook() and invoked by ra8_exception_report() once the fixed-SRAM snapshot is complete. Lives in .bss (zeroed by every reset), so a consumer must re-arm it early on each boot. Left nullptr unless an app opts into fault persistence, so the default fault path pulls in no crash-log code.

Note
Written only by ra8_exception_set_persist_hook(); read only by ra8_exception_report().
Warning
Do not read or write outside this translation unit.
Since
0.1.0

Definition at line 151 of file ra8_exception.c.

Referenced by ra8_exception_report(), and ra8_exception_set_persist_hook().