ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_exception.c
Go to the documentation of this file.
1
35
36#include "ra8_exception.h"
37
38#include <stdint.h>
39
40#include "ra8_attributes.h"
41#include "ra8_error_handler.h"
42#include "ra8_log.h"
43#include "ra8_scb.h"
44
45#ifdef RA8_OFF_TARGET
46/* Host-side test builds get the legacy halt-via-fatal-error path so
47 * tests can override `ra8_fatal_error` to longjmp out. */
49#define RA8_EXCEPTION_HALT(tag, msg, exc) ra8_fatal_error((tag), (msg), (exc))
50#else
52#define RA8_EXCEPTION_HALT(tag, msg, exc) \
53 do { \
54 (void)(tag); \
55 (void)(msg); \
56 (void)(exc); \
57 internal_exception_halt_loop(); \
58 } while (0)
59#endif
60
65typedef enum : uint32_t {
68
95{
96 if (out == nullptr) {
97 return;
98 }
101 out->cfsr = fs.cfsr;
102 out->hfsr = fs.hfsr;
103 out->dfsr = fs.dfsr;
104 out->mmfar = fs.mmfar;
105 out->bfar = fs.bfar;
106 out->afsr = fs.afsr;
107 out->sfsr = fs.sfsr;
108 out->sfar = fs.sfar;
109 }
110}
111
115
133static volatile uint32_t s_ra8_exception_nmi_stage;
134
152
159
160#ifndef RA8_OFF_TARGET
180[[noreturn, gnu::noinline]] RA8_INTERNAL static void internal_exception_halt_loop(void)
181{
182 __asm__ volatile("cpsid i" ::: "memory");
183 while (1) {
184 __asm__ volatile("wfi");
185 }
186}
187#endif
188
216 uint32_t exc_number,
217 const ra8_exception_diagnostics_t* diag,
218 uint32_t nmisr)
219{
220 ra8_log_error_val("EXC", "exception", exc_number);
221
222 if (frame != nullptr) {
223 ra8_log_error_val("EXC", "pc ", frame->pc);
224 ra8_log_error_val("EXC", "lr ", frame->lr);
225 ra8_log_error_val("EXC", "xpsr", frame->xpsr);
226 ra8_log_error_val("EXC", "r0 ", frame->r0);
227 ra8_log_error_val("EXC", "r1 ", frame->r1);
228 ra8_log_error_val("EXC", "r2 ", frame->r2);
229 ra8_log_error_val("EXC", "r3 ", frame->r3);
230 ra8_log_error_val("EXC", "r12 ", frame->r12);
231 }
232
233 ra8_log_error_val("EXC", "cfsr ", diag->cfsr);
234 ra8_log_error_val("EXC", "hfsr ", diag->hfsr);
235 ra8_log_error_val("EXC", "bfar ", diag->bfar);
236 ra8_log_error_val("EXC", "mmfar", diag->mmfar);
237 ra8_log_error_val("EXC", "sfsr ", diag->sfsr);
238 ra8_log_error_val("EXC", "sfar ", diag->sfar);
239 if (exc_number == (uint32_t)k_ra8_exc_num_nmi) {
240 ra8_log_error_val("EXC", "nmisr", nmisr);
241 }
242}
243
270void ra8_exception_report(const ra8_exception_frame_t* frame, uint32_t exc_number)
271{
272 /* Step 1: capture EVERYTHING into fixed SRAM FIRST -- before any
273 * function call that might itself fault. This guarantees that even
274 * if the log backend, ITM, or anything else takes a secondary fault,
275 * a debugger can still recover the original fault context from
276 * g_ra8_exception_last. */
277 g_ra8_exception_last.magic = 0U;
278 g_ra8_exception_last.exc_number = exc_number;
279 g_ra8_exception_last.frame_ptr = (uintptr_t)frame;
280 if (frame != nullptr) {
281 g_ra8_exception_last.frame.r0 = frame->r0;
282 g_ra8_exception_last.frame.r1 = frame->r1;
283 g_ra8_exception_last.frame.r2 = frame->r2;
284 g_ra8_exception_last.frame.r3 = frame->r3;
285 g_ra8_exception_last.frame.r12 = frame->r12;
286 g_ra8_exception_last.frame.lr = frame->lr;
287 g_ra8_exception_last.frame.pc = frame->pc;
288 g_ra8_exception_last.frame.xpsr = frame->xpsr;
289 }
292 g_ra8_exception_last.diag = diag;
293 /* Fold in the ICU cause staged by ra8_exception_report_nmi(), then
294 * clear the stage so a later non-NMI record cannot inherit it. */
298
299 /* Step 1b: persist the completed snapshot across the coming reset (if a
300 * sink is armed). Runs before any code that might take a secondary fault
301 * so a field unit's post-mortem + reset-loop count survive to the next
302 * boot. A plain SRAM copy -- fault-context safe. See ra8_crashlog.c. */
303 if (s_ra8_exception_persist != nullptr) {
305 }
306
307 /* Step 2: best-effort logging, strictly after the snapshot. */
308 internal_log_fault_dump(frame, exc_number, &diag, g_ra8_exception_last.nmisr);
309
310 /* Step 3: halt at a named symbol. On target we do NOT call
311 * ra8_fatal_error from a fault context -- that path issues
312 * `bkpt #0`, which on a board without an attached debugger re-enters
313 * HardFault and can escalate to LOCKUP at PC=0xEFFFFFFE. The
314 * dedicated halt loop parks PC at a symbol the debugger can name.
315 * On host (fake) builds we still go through the overridable
316 * fatal hook so unit tests can longjmp out. */
317 RA8_EXCEPTION_HALT("EXC", "fault", exc_number);
318}
319
322void ra8_exception_report_nmi(const ra8_exception_frame_t* frame, uint32_t nmisr)
323{
324 /* Plain SRAM store first (cannot fault), so even a secondary fault
325 * inside the common path leaves the cause recoverable: the stage is
326 * copied into g_ra8_exception_last.nmisr before magic is set. */
328 ra8_exception_report(frame, (uint32_t)k_ra8_exc_num_nmi);
329}
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
Centralised fatal-error sink.
void ra8_exception_set_persist_hook(ra8_exception_persist_fn hook)
Implementation of ra8_exception_set_persist_hook() – store the sink pointer.
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.
static ra8_exception_persist_fn s_ra8_exception_persist
Registered post-decode persistence sink, or nullptr when disarmed.
static void internal_exception_halt_loop(void)
Spin halt with a known PC at a named symbol.
ra8_exc_num_t
Architectural exception numbers this module special-cases.
@ k_ra8_exc_num_nmi
NMI vector slot – carries an ICU NMISR cause.
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_capture_diagnostics(ra8_exception_diagnostics_t *out)
Implementation of ra8_exception_capture_diagnostics() – routes the SCB fault-status read through the ...
static volatile uint32_t s_ra8_exception_nmi_stage
NMISR cause staged by ra8_exception_report_nmi() for the record.
void ra8_exception_report(const ra8_exception_frame_t *frame, uint32_t exc_number)
Implementation of ra8_exception_report() – fault dump + halt.
#define RA8_EXCEPTION_HALT(tag, msg, exc)
RA8 EXCEPTION HALT.
Cortex-M85 CPU exception diagnostic helpers.
void(* ra8_exception_persist_fn)(const volatile ra8_exception_last_t *decoded)
Post-decode persistence sink invoked once the snapshot is complete.
@ k_ra8_exc_magic_valid
Snapshot is fully populated.
volatile ra8_exception_last_t g_ra8_exception_last
The one fixed-SRAM ra8_exception_last_t snapshot instance.
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_error_val(tag, message, value)
RA8 log error val.
Definition ra8_log.h:337
Cortex-M85 System Control Block: VTOR relocation + fault-status decode.
ra8_err_t ra8_scb_read_fault_status(ra8_scb_fault_status_t *out)
Read the Cortex-M85 SCB fault-status registers into one struct.
Definition ra8_scb.c:86
SCB fault-status register snapshot.
uint32_t dfsr
Debug Fault Status Register.
uint32_t sfar
SecureFault Address Reg (if SFSR.SFARVALID).
uint32_t hfsr
HardFault Status Register.
uint32_t bfar
BusFault Address Register (if BFARVALID).
uint32_t sfsr
SecureFault Status Register (RAZ from NS).
uint32_t afsr
Auxiliary Fault Status Register.
uint32_t cfsr
Configurable Fault Status Register.
uint32_t mmfar
MemManage Fault Address Reg (if MMARVALID).
Stacked registers pushed by the Cortex-M exception entry.
uint32_t r1
General-purpose register R1.
uint32_t xpsr
Program status register.
uint32_t r0
General-purpose register R0.
uint32_t pc
Program counter at fault.
uint32_t r12
Scratch register R12.
uint32_t lr
Link register (return addr).
uint32_t r2
General-purpose register R2.
uint32_t r3
General-purpose register R3.
Fixed-SRAM post-mortem snapshot of the most recent fault or NMI.
One snapshot of the Cortex-M85 SCB fault-status registers.
Definition ra8_scb.h:86
uint32_t mmfar
MemManage Fault Address Register (if CFSR.MMARVALID).
Definition ra8_scb.h:90
uint32_t dfsr
Debug Fault Status Register.
Definition ra8_scb.h:89
uint32_t hfsr
HardFault Status Register.
Definition ra8_scb.h:88
uint32_t afsr
Auxiliary Fault Status Register.
Definition ra8_scb.h:92
uint32_t cfsr
Configurable Fault Status Register (MMFSR|BFSR|UFSR).
Definition ra8_scb.h:87
uint32_t sfsr
SecureFault Status Register (Secure-banked, RAZ NS).
Definition ra8_scb.h:93
uint32_t bfar
BusFault Address Register (if CFSR.BFARVALID).
Definition ra8_scb.h:91
uint32_t sfar
SecureFault Address Register (if SFSR.SFARVALID).
Definition ra8_scb.h:94