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

Cross-reset crash-log: persist the last fault + a reset-loop guard. More...

#include <stdint.h>
#include "ra8_exception.h"
Include dependency graph for ra8_crashlog.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  ra8_crashlog_record_t
 The .noinit cross-reset post-mortem record. More...

Enumerations

enum  ra8_crashlog_magic_t : uint32_t { k_ra8_crashlog_magic_valid = 0x5AFEB007UL }
 Sentinel marking a fully-written ra8_crashlog_record_t. More...
enum  ra8_crashlog_limits_t : uint32_t {
  k_ra8_crashlog_loop_threshold = 3U ,
  k_ra8_crashlog_reserve_bytes = 256U
}
 Reset-loop threshold and the linker reservation size. More...

Functions

void ra8_crashlog_install (void)
 Arm the fault-persist hook so decoded faults are logged to .noinit.
void ra8_crashlog_record_fault (const volatile ra8_exception_last_t *decoded)
 Persist a decoded fault snapshot into the cross-reset record.
bool ra8_crashlog_peek (ra8_crashlog_record_t *out)
 Validate and copy out the last cross-reset record (non-destructive).
void ra8_crashlog_claim (void)
 Consume the record and reset the reset-loop guard (clean claim).
bool ra8_crashlog_safe_mode_requested (void)
 Whether accumulated crashes crossed the reset-loop threshold.

Detailed Description

Cross-reset crash-log: persist the last fault + a reset-loop guard.

ra8_exception.c decodes every CPU fault / NMI into the fixed-SRAM ra8_exception_last_t snapshot g_ra8_exception_last and then halts. That snapshot lives in ordinary .bss-adjacent SRAM, so the very next reset's Reset_Handler zero-fills it and the post-mortem is gone before a field technician can read it. This module adds the missing survival + guard layer ON TOP of that decode path:

  1. A .noinit record (ra8_crashlog_record_t) that the reset handler does NOT zero. It is pinned at the very top of SRAM, above the main stack, so it survives a warm / watchdog / pin reset (any reset that keeps SRAM powered) and never shifts the .data / .bss layout the TrustZone fixed windows depend on. The linker reservation lives in libs/ra8_board_ek_ra8d2/ld/linker_script.ld (.noinit).
  2. Integrity: a 32-bit sentinel magic plus a CRC-32 over the payload. A cold power-on reset randomises SRAM; the sentinel and CRC then fail validation and the log reads as empty – the intended fail-safe (there is no false post-mortem from garbage SRAM).
  3. A monotonic boot_loops counter incremented on every recorded fault. If it climbs past k_ra8_crashlog_loop_threshold without the application reaching a known-good checkpoint and calling ra8_crashlog_claim(), ra8_crashlog_safe_mode_requested() latches true so the app can boot a minimal safe mode instead of the code that keeps crashing.

Lifecycle

// Early in boot, before any risky bring-up:
ra8_crashlog_install(); // arm the fault-persist hook
if (ra8_crashlog_peek(&rec)) {
log_prior_crash(&rec); // exc_number, pc, boot_loops
ra8_crashlog_claim(); // reset the guard, boot safe mode
enter_safe_mode();
}
}
run_risky_bringup(); // a fault here bumps boot_loops
ra8_crashlog_claim(); // reached known-good: reset guard
void ra8_crashlog_claim(void)
Consume the record and reset the reset-loop guard (clean claim).
bool ra8_crashlog_peek(ra8_crashlog_record_t *out)
Validate and copy out the last cross-reset record (non-destructive).
bool ra8_crashlog_safe_mode_requested(void)
Whether accumulated crashes crossed the reset-loop threshold.
void ra8_crashlog_install(void)
Arm the fault-persist hook so decoded faults are logged to .noinit.
The .noinit cross-reset post-mortem record.

What survives which reset class

Reset class SRAM kept Record survives
Watchdog / IWDT underflow (warm) yes yes
Software reset (SYSRESETREQ) yes yes
Pin / debugger reset (no power cut) yes yes
Power-on reset / LVD brown-out no no (fail-safe)

VBATT-backed / MRAM persistence across power loss is out of scope here (VBATT ra8_bkup is silicon-blocked – see issue #131) and is the named follow-up for surviving a cold power cycle.

Definition in file ra8_crashlog.h.

Enumeration Type Documentation

◆ ra8_crashlog_limits_t

enum ra8_crashlog_limits_t : uint32_t

Reset-loop threshold and the linker reservation size.

k_ra8_crashlog_loop_threshold is the number of recorded faults that may accumulate before ra8_crashlog_safe_mode_requested() latches; the app resets the count by reaching a known-good state and calling ra8_crashlog_claim(). k_ra8_crashlog_reserve_bytes MUST equal the LENGTH(NOINIT) region size in libs/ra8_board_ek_ra8d2/ld/linker_script.ld; a compile-time assertion in ra8_crashlog.c proves the record fits it.

Since
0.1.0
Enumerator
k_ra8_crashlog_loop_threshold 

Safe mode when boot_loops exceeds this.

k_ra8_crashlog_reserve_bytes 

Bytes reserved for .noinit at SRAM top.

Definition at line 110 of file ra8_crashlog.h.

◆ ra8_crashlog_magic_t

enum ra8_crashlog_magic_t : uint32_t

Sentinel marking a fully-written ra8_crashlog_record_t.

Written LAST when a record is complete and cleared FIRST when it is consumed, so neither a secondary fault mid-write nor random cold-boot SRAM can be mistaken for a valid post-mortem. The value is distinct from k_ra8_exc_magic_valid so the two fixed-SRAM records are never confused by a debugger.

See also
ra8_crashlog_record_t
Since
0.1.0
Enumerator
k_ra8_crashlog_magic_valid 

"SAFE-BOOT": record is valid.

Definition at line 91 of file ra8_crashlog.h.

Function Documentation

◆ ra8_crashlog_claim()

void ra8_crashlog_claim ( void )

Consume the record and reset the reset-loop guard (clean claim).

Marks the current record handled: clears magic (so a following ra8_crashlog_peek() reports empty) and zeroes boot_loops (so the reset-loop guard re-arms from a clean slate). Call it only after the application has reached a known-good checkpoint – claiming before the risky work would defeat the loop guard. Idempotent.

Returns
Nothing.
Precondition
The .noinit record is in writable SRAM.
The application has decided the prior record is handled.
Postcondition
A subsequent ra8_crashlog_peek() returns false.
boot_loops reads back zero; the guard is re-armed.
Note
Not thread-safe.
See also
ra8_crashlog_peek()
Since
0.1.0

Consume the record and reset the reset-loop guard (clean claim).

Definition at line 239 of file ra8_crashlog.c.

References s_ra8_crashlog_record.

Referenced by main().

◆ ra8_crashlog_install()

void ra8_crashlog_install ( void )

Arm the fault-persist hook so decoded faults are logged to .noinit.

Registers ra8_crashlog_record_fault() as the exception module's post-decode persist sink (see ra8_exception_set_persist_hook()). After this call, every fault/NMI that reaches ra8_exception_report() copies its decoded snapshot into the cross-reset record and bumps the reset-loop counter BEFORE the CPU halts. Must be called early each boot: the hook pointer lives in .bss and is zeroed by every reset, so it has to be re-armed before any code that might fault.

Returns
Nothing.
Precondition
Called from single-threaded boot context (before interrupts that could fault are enabled).
The .noinit record is in writable, powered SRAM.
Postcondition
ra8_exception_report() will persist subsequent faults.
The existing record (from a prior boot) is left untouched for a following ra8_crashlog_peek().
Note
Not thread-safe; single-threaded boot use only.
See also
ra8_crashlog_peek()
ra8_crashlog_record_fault()
Since
0.1.0

Arm the fault-persist hook so decoded faults are logged to .noinit.

Definition at line 197 of file ra8_crashlog.c.

References ra8_crashlog_record_fault(), and ra8_exception_set_persist_hook().

Referenced by main().

◆ ra8_crashlog_peek()

bool ra8_crashlog_peek ( ra8_crashlog_record_t * out)

Validate and copy out the last cross-reset record (non-destructive).

Recomputes the payload CRC and compares it plus the magic sentinel. On a match, copies the whole record into out and returns true; the stored record is left intact (call ra8_crashlog_claim() to consume it). On any mismatch – including the all-random SRAM of a cold power-on – returns false and leaves out unmodified.

Parameters
[out]outDestination for the validated record. Must not be nullptr. Untouched when the function returns false.
Returns
Whether a valid record was found.
Return values
trueA valid record existed and was copied to out.
falseNo valid record (empty, corrupted, or out == nullptr).
Precondition
out is either nullptr or points to writable storage.
The .noinit record is in readable SRAM.
Postcondition
On true, *out holds the validated record; the store is intact.
On false, no caller state is modified.
Note
Not thread-safe.
See also
ra8_crashlog_claim()
ra8_crashlog_safe_mode_requested()
Since
0.1.0

Validate and copy out the last cross-reset record (non-destructive).

Definition at line 225 of file ra8_crashlog.c.

References internal_crashlog_is_valid(), and s_ra8_crashlog_record.

Referenced by main(), and ra8_crashlog_safe_mode_requested().

◆ ra8_crashlog_record_fault()

void ra8_crashlog_record_fault ( const volatile ra8_exception_last_t * decoded)

Persist a decoded fault snapshot into the cross-reset record.

The write path installed by ra8_crashlog_install(): reads the prior boot_loops (only if the existing record still validates), invalidates the record (magic = 0), copies decoded into the embedded snapshot, increments the loop counter (saturating so it can never wrap back below the threshold), stamps the CRC, and finally re-arms magic LAST so a secondary fault mid-write leaves the record invalid rather than half-written. Also directly callable by an application to record a software-detected fatal condition (assertion, corruption, watchdog arming) in the same field-readable format.

Parameters
[in]decodedDecoded fault snapshot to persist. Must not be nullptr; typically &g_ra8_exception_last.
Returns
Nothing.
Precondition
decoded points at a populated ra8_exception_last_t.
The .noinit record is in writable SRAM.
Postcondition
On non-nullptr input the record validates and boot_loops grew by one (or held at its saturation ceiling).
magic == k_ra8_crashlog_magic_valid once the write completes.
Note
Not thread-safe; single fault/boot context by construction.
See also
ra8_crashlog_install()
ra8_crashlog_peek()
Since
0.1.0

Persist a decoded fault snapshot into the cross-reset record.

Definition at line 204 of file ra8_crashlog.c.

References internal_crashlog_is_valid(), internal_crashlog_payload_crc(), k_ra8_crashlog_loops_max, k_ra8_crashlog_magic_valid, and s_ra8_crashlog_record.

Referenced by main(), and ra8_crashlog_install().

◆ ra8_crashlog_safe_mode_requested()

bool ra8_crashlog_safe_mode_requested ( void )

Whether accumulated crashes crossed the reset-loop threshold.

Returns true only when a valid record exists AND its boot_loops exceeds k_ra8_crashlog_loop_threshold – i.e. the device has recorded more than the allowed number of faults without a clean ra8_crashlog_claim() in between. The application reads this early in boot to divert into a minimal safe mode instead of re-running the code that keeps faulting.

Returns
Whether safe mode is requested.
Return values
trueValid record and boot_loops > k_ra8_crashlog_loop_threshold.
falseNo valid record, or the count is within the threshold.
Precondition
The .noinit record is in readable SRAM.
Called from boot context to gate risky bring-up.
Postcondition
No state is modified (query only).
The record, if any, is left intact for ra8_crashlog_peek().
Note
Not thread-safe.
See also
ra8_crashlog_claim()
Since
0.1.0

Whether accumulated crashes crossed the reset-loop threshold.

Definition at line 248 of file ra8_crashlog.c.

References ra8_crashlog_record_t::boot_loops, k_ra8_crashlog_loop_threshold, and ra8_crashlog_peek().

Referenced by main().