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

SRAM ECC fault-injection + detection demo (EK-RA8D2, issue #130). More...

#include <stdint.h>
#include "ra8_attributes.h"
#include "ra8_board_ek_ra8d2.h"
#include "ra8_boot_entry.h"
#include "ra8_cgc.h"
#include "ra8_check.h"
#include "ra8_err.h"
#include "ra8_isr.h"
#include "ra8_mstp.h"
#include "ra8_sram.h"
#include "ra8_time.h"
Include dependency graph for main.c:

Go to the source code of this file.

Enumerations

enum  mecc_config_t : uint32_t {
  k_mecc_baud = 115200U ,
  k_mecc_period_ms = 1000U
}
 Compile-time settings. More...
enum  mecc_geom_t : uint32_t {
  k_mecc_bank = 2U ,
  k_mecc_probe_offset = 0U
}
 ECC target geometry. More...

Functions

static void internal_mecc_panic_halt (void)
 Park forever after a fatal initialization failure.
static void internal_mecc_setup_or_halt (void)
 Bring CGC + SysTick + SCI8 + LEDs + MSTP up.
static ra8_err_t internal_mecc_configure (void)
 Enable full ECC + zero-init on spare bank 2.
static ra8_err_t internal_mecc_inject (bool two_bit, uint8_t *out_caught)
 Inject one ECC fault via the decoder self-test and record the verdict.
static ra8_err_t internal_mecc_run_pass (uint8_t *out_ok)
 Run both fault injections and fold them into a single verdict.
void main (void)
 The application entry point Reset_Handler hands control to.

Variables

static const char * s_tag = "mem_ecc"
 Diagnostic / log tag.
static const uint8_t s_mecc_ok_msg [] = "ecc: sram2 1bit-inj=caught 2bit-inj=caught ok=Y\r\n"
 Success banner for the complete one-bit and two-bit injection pass.
static const uint8_t s_mecc_bad_msg [] = "ecc: sram2 fault-inject ok=N\r\n"
 Failure banner for an incomplete or rejected injection pass.
volatile uint32_t g_mecc_1bit_caught = 0U
 1 when the injected 1-bit fault was latched by SRAMESR.
volatile uint32_t g_mecc_2bit_caught = 0U
 1 when the injected 2-bit fault was latched by SRAMESR.
volatile uint32_t g_mecc_1bit_mask = 0U
 Per-bank 1-bit-error bitmap decoded after the 1-bit injection.
volatile uint32_t g_mecc_2bit_mask = 0U
 Per-bank 2-bit-error bitmap decoded after the 2-bit injection.
volatile uint32_t g_mecc_heartbeat = 0U
 Bumps once per main-loop pass – liveness for headless probes.

Detailed Description

SRAM ECC fault-injection + detection demo (EK-RA8D2, issue #130).

Tag
[Ring 6 / APP] {World: S}

Where ecc_monitor_demo only brings ECC up and reads a (clean) status, this demo closes the loop the SIL-3 / DAL-B bar actually cares about: it deliberately provokes a memory error and proves the hardware-error path latches it.

It enables full SECDED ECC on a spare SRAM bank (bank 2, which the linker leaves unused), then runs the HUM Ch 58.3.4 ECC decoder self-test twice via ra8_sram_self_test:

  1. A 1-bit (correctable) fault – ra8_sram_self_test(..., false, ...) reads the raw syndrome in bypass mode, flips one bit, writes it back, and the verify read latches SRAMESRout_caught confirms the latch.
  2. A 2-bit (uncorrectable) fault – the same path with two bits flipped, which on silicon also raises the NMI (see the silicon note below).

The ESR status is cleared between injections so each is independently proven. The detected per-bank 1-bit / 2-bit masks are published to globals for HIL probing. Once a second the headless banner is emitted:

"ecc: sram2 1bit-inj=caught 2bit-inj=caught ok=Y\r\n" (both latched), or "ecc: sram2 fault-inject ok=N\r\n" (any miss).

LED1 toggles while both injections are caught; LED2 toggles on a miss.

Note
Headless-emulator status. tools/ra8_emulator (board_periph_sram.c) models the decoder self-test: it latches SRAMESR on the bypass->verify CR sequence so out_caught is true headlessly, proving the detection/reporting plumbing end to end. It cannot observe the syndrome data write that distinguishes a 1-bit from a 2-bit fault, so it latches both ESR slots on any self-test; the precise per-slot fidelity (and 1-bit correction vs the 2-bit NMI) is silicon-only – hence hw_pending/.
Silicon note. The 2-bit (uncorrectable) injection raises a non-maskable ECC interrupt on real hardware (on_error = interrupt); the bench bring-up must install an NMI handler (or mask faults around the 2-bit self-test). ra8_emulator does not raise the NMI, so the headless run reads SRAMESR synchronously and reports cleanly.

Bare EK-RA8D2 only – no shields or external transceivers.

Since
0.1.0

Definition in file main.c.

Enumeration Type Documentation

◆ mecc_config_t

enum mecc_config_t : uint32_t

Compile-time settings.

Enumerator
k_mecc_baud 

SCI8 baud rate.

k_mecc_period_ms 

Delay between fault-injection passes.

Definition at line 71 of file main.c.

◆ mecc_geom_t

enum mecc_geom_t : uint32_t

ECC target geometry.

Enumerator
k_mecc_bank 

Spare SRAM bank 2 (program uses 0-1).

k_mecc_probe_offset 

8-byte-aligned probe line within the bank.

Definition at line 77 of file main.c.

Function Documentation

◆ internal_mecc_configure()

ra8_err_t internal_mecc_configure ( void )
staticnodiscard

Enable full ECC + zero-init on spare bank 2.

Bank 2 gets with-check ECC, the 1-bit latch, a 128 KiB ECC region, and the zero-init pass that lays down valid ECC across the bank (so a read of a never-written line does not raise a spurious 2-bit error before the deliberate injection). on_error is NMI – see the file's silicon note.

MC/DC:
Sequential init guards (no compound decision); 2 vectors – init OK / init rejects (covered by the host test).
Returns
ra8_err_t from ra8_sram_init.
Return values
k_ra8_okBank 2 configured for ECC with-check.
Precondition
CGC + MSTP up; IRQs masked or single-threaded init.
Bank 2 holds no program data (linker keeps it spare).
Postcondition
Bank 2's SRAMCRn is in ECC + check mode.
No ECC error is latched yet (zero-init laid down valid codes).
Note
Not thread-safe; boot-time use.
Since
0.1.0

Definition at line 215 of file main.c.

References ra8_sram_config_t::banks, ra8_sram_bank_cfg_t::ecc_mode, ra8_sram_bank_cfg_t::eccrgn, ra8_sram_bank_cfg_t::enable_1bit_latch, k_mecc_bank, k_ra8_sram_ecc_with_chk, k_ra8_sram_on_error_interrupt, k_ra8_sram_region_128kb, ra8_sram_bank_cfg_t::on_error, RA8_INTERNAL, ra8_sram_init(), and ra8_sram_bank_cfg_t::zero_init.

Referenced by main().

◆ internal_mecc_inject()

ra8_err_t internal_mecc_inject ( bool two_bit,
uint8_t * out_caught )
staticnodiscard

Inject one ECC fault via the decoder self-test and record the verdict.

Runs ra8_sram_self_test on bank 2 (which corrupts the syndrome of the probe line and confirms the SRAMESR latch), snapshots the decoded error masks for HIL probing, then clears the latched status so the next injection starts clean.

Parameters
[in]two_bittrue to inject a 2-bit (uncorrectable) fault.
[out]out_caughtReceives 1 when SRAMESR latched the injected fault.
Returns
ra8_err_t from the self-test / status calls.
Return values
k_ra8_okInjection ran; *out_caught set.
k_ra8_err_null_ptrout_caught was NULL.
k_ra8_err_invalid_argBank / probe rejected by ra8_sram_self_test.
Precondition
internal_mecc_configure succeeded.
out_caught is non-NULL.
Postcondition
SRAMESR latched status is cleared on success.
The decoded mask is published to the matching global.
Note
Not thread-safe.
Since
0.1.0

Definition at line 248 of file main.c.

References g_mecc_1bit_mask, g_mecc_2bit_mask, k_mecc_bank, k_mecc_probe_offset, k_ra8_ok, ra8_sram_status_t::one_bit_mask, RA8_CHECK_NULL_PTR, RA8_INTERNAL, ra8_sram_clear_status(), ra8_sram_get_status(), ra8_sram_self_test(), ra8_sram_status_t::raw_esr, s_tag, and ra8_sram_status_t::two_bit_mask.

Referenced by internal_mecc_run_pass().

◆ internal_mecc_panic_halt()

void internal_mecc_panic_halt ( void )
static

Park forever after a fatal initialization failure.

Retains ECC and setup state for inspection in a permanent WFI loop.

Precondition
Reset startup initialized the exception and stack environment.
A required setup step has failed.
Postcondition
Control never returns to the caller.
No deliberate ECC injection is attempted.
Note
An interrupt can wake one iteration, but the terminal loop resumes.
Since
0.1.0

Definition at line 149 of file main.c.

References RA8_INTERNAL.

Referenced by internal_mecc_setup_or_halt(), and main().

◆ internal_mecc_run_pass()

ra8_err_t internal_mecc_run_pass ( uint8_t * out_ok)
staticnodiscard

Run both fault injections and fold them into a single verdict.

Executes the correctable and uncorrectable self-tests sequentially, publishes both liveness flags, and sets the aggregate result only when both hardware latches were observed.

Parameters
[out]out_ok1 only when both the 1-bit and 2-bit faults were caught.
Returns
ra8_err_t from the injection helpers.
Return values
k_ra8_okBoth injections ran; *out_ok set.
k_ra8_err_null_ptrout_ok was NULL.
Precondition
internal_mecc_configure succeeded.
out_ok is non-NULL.
Postcondition
g_mecc_1bit_caught / g_mecc_2bit_caught updated.
The latched ECC status is cleared after both injections.
Note
Not thread-safe.
Since
0.1.0

Definition at line 297 of file main.c.

References g_mecc_1bit_caught, g_mecc_2bit_caught, internal_mecc_inject(), k_ra8_ok, RA8_CHECK_NULL_PTR, RA8_INTERNAL, and s_tag.

Referenced by main().

◆ internal_mecc_setup_or_halt()

void internal_mecc_setup_or_halt ( void )
static

Bring CGC + SysTick + SCI8 + LEDs + MSTP up.

Initializes each boot dependency in order and treats any failure as terminal before SRAM ECC configuration begins.

Precondition
Reset startup completed data and BSS initialization.
Board clocks and GPIO are in their reset-compatible state.
Postcondition
On return the timebase, console, module clocks, and two LEDs are ready.
Any failed prerequisite has entered the terminal panic loop.
Note
Single-shot boot helper; it is not reentrant.
Since
0.1.0

Definition at line 167 of file main.c.

References internal_mecc_panic_halt(), k_mecc_baud, k_ra8_board_led1, k_ra8_board_led2, k_ra8_clock_id_cpuclk0, k_ra8_ok, ra8_board_led_init(), ra8_board_uart_console_init(), ra8_cgc_get_clock_hz(), ra8_cgc_init(), RA8_INTERNAL, ra8_mstp_init(), and ra8_time_init().

Referenced by main().

◆ main()

void main ( void )

The application entry point Reset_Handler hands control to.

Returns void, not int. This is a freestanding image: there is no hosted C environment, no process and nothing to report an exit status to. ISO C fixes main at int only for a hosted implementation; for a freestanding one (C23 5.1.2.1) the startup function's name and type are implementation-defined, and this is that definition. Reset_Handler discards no value because there is none to discard, and if main ever does return, startup halts the CPU rather than resuming anything.

The firmware lane is compiled -ffreestanding (see cmake/ra8_add_app.cmake) and the flag and this signature travel together: without it both GCC and clang reject a non-int main (-Wmain / -Wmain-return-type). Do not remove one without the other.

That coupling is why the declaration sits behind __STDC_HOSTED__ == 0, which -ffreestanding sets and a hosted build does not. The guard is not defensive dressing: this header is reachable from host builds (the unit tests compile ra8_core natively), and an unguarded void main(void); makes every hosted translation unit that includes it fail with conflicting types for 'main' against its own ISO int main. The declaration therefore exists exactly where its contract does.

Hosted first-party code – everything under tests/ and tools/ – uses the ISO int main(...) contract instead, because it genuinely does run under an OS that reads the exit status. scripts/checks/check_entry_points.py holds each domain to its own contract (#707).

Declared here, once, for the same reason SystemInit is: every vector_table.c used to restate it as a local extern int32_t main(void);, sixteen copies that no compiler ever compared against the definition – and roughly thirty of them had silently drifted out of agreement with the main they called.

Precondition
Reset_Handler has copied .data and zeroed .bss.
SystemInit has configured the clock tree and VTOR.
Postcondition
Control does not return; the image runs until reset or halt.
Any value the application wanted to report has been logged, not returned.
Note
Not thread-safe; single-threaded startup context only.
Warning
Only valid while the translation unit is compiled -ffreestanding. A hosted build rejects this signature.
See also
SystemInit()
Since
0.1.0

The application entry point Reset_Handler hands control to.

Brings up CGC + BSP audio then plays blocks.

Precondition
Reset_Handler has copied .data and zeroed .bss.
SystemInit has set VTOR, FPU, and priority grouping.
Postcondition
On clean entry the CPU stays in the playback loop forever.
On any HAL init failure the function halts in WFI.
Since
0.1.0

The application entry point Reset_Handler hands control to.

Brings up CGC + GPT triple, runs sweep.

Precondition
Reset_Handler has copied .data and zeroed .bss.
SystemInit has set VTOR, FPU, and priority grouping.
Postcondition
On clean entry the CPU stays in the sweep loop forever.
On any HAL init failure the function halts in WFI.
Since
0.1.0

The application entry point Reset_Handler hands control to.

Brings up clocks + UART + RMII pins, then ThreadX.

Precondition
Reset_Handler has copied .data and zeroed .bss.
Postcondition
On clean entry the kernel runs the worker thread once.
Since
0.1.0

The application entry point Reset_Handler hands control to.

Brings up clocks + UART, then enters ThreadX.

Precondition
Reset_Handler has copied .data and zeroed .bss.
SystemInit has set VTOR, FPU, and priority grouping.
Postcondition
On clean entry the kernel runs the worker thread forever.
On any HAL init failure the function halts in WFI.
Since
0.1.0

The application entry point Reset_Handler hands control to.

Brings up LED, console, SDHI pins, then ThreadX.

Precondition
Reset_Handler has copied .data + zeroed .bss.
SystemInit has set VTOR, FPU, and priority grouping.
Postcondition
CPUCLK0 is raised to the PLL1 target before the kernel starts.
On clean entry the SD card thread runs forever.
On any HAL init failure the function halts in __WFI.
Since
0.1.0

The application entry point Reset_Handler hands control to.

Brings up CGC + USB-FS + UAC1, then enters the iso-IN feed loop forever.

Precondition
Reset_Handler has copied .data and zeroed .bss.
SystemInit has set VTOR, FPU, and priority grouping.
Postcondition
On clean entry the CPU stays in the iso-IN feed loop forever.
On any HAL init failure the function halts in WFI.
Since
0.1.0

The application entry point Reset_Handler hands control to.

Precondition
SystemInit set VTOR / FPU / priority grouping.

The application entry point Reset_Handler hands control to.

See file header.

Precondition
Boot init has completed.
The secure-boot library's BLXNS into NS image either failed or was skipped (the call site in ra8_trustzone_init is a no-op on host builds).
Postcondition
Diagnostic counter latched, CPU parked in a halt loop.
Function never returns.
Note
Single-threaded entry.
Since
0.1.0

The application entry point Reset_Handler hands control to.

Precondition
Reset_Handler copied .data and zeroed .bss.
SystemInit set VTOR / FPU / priority grouping.
Postcondition
On success g_eoh_chapters / g_eoh_crc hold the parsed results, the banner is emitted, and g_eoh_heartbeat advances once per frame.
On any failure g_eoh_err is non-zero and the CPU parks (no heartbeat).
Since
0.1.0

The application entry point Reset_Handler hands control to.

Precondition
Reset_Handler copied .data and zeroed .bss.
SystemInit set VTOR / FPU / priority grouping.
Postcondition
The chapters/ch0-CRC banner is emitted; the CPU then loops in WFI.
Since
0.1.0

The application entry point Reset_Handler hands control to.

Precondition
Reset_Handler copied .data and zeroed .bss.
SystemInit set VTOR / FPU / priority grouping.
Postcondition
On success the g_etoc_* result globals hold the parsed TOC values, the banner is emitted, and g_etoc_heartbeat advances once per frame.
On any failure g_etoc_err is non-zero and the CPU parks (no heartbeat).
Since
0.1.0

The application entry point Reset_Handler hands control to.

Precondition
Reset_Handler copied .data and zeroed .bss; SystemInit set VTOR/FPU.
Postcondition
The shelf scans on the panel; taps open books, browse, and read.
Since
0.1.0

Definition at line 326 of file main.c.

References g_mecc_heartbeat, internal_mecc_configure(), internal_mecc_panic_halt(), internal_mecc_run_pass(), internal_mecc_setup_or_halt(), k_mecc_period_ms, k_ra8_board_led1, k_ra8_board_led2, k_ra8_ok, ra8_board_led_toggle(), ra8_board_uart_console_write(), ra8_delay_ms(), ra8_isr_globals_enable(), s_mecc_bad_msg, and s_mecc_ok_msg.

Variable Documentation

◆ g_mecc_1bit_caught

volatile uint32_t g_mecc_1bit_caught = 0U

1 when the injected 1-bit fault was latched by SRAMESR.

Note
Read externally only (HIL / board emulator).
Since
0.1.0

Definition at line 105 of file main.c.

Referenced by internal_mecc_run_pass().

◆ g_mecc_1bit_mask

volatile uint32_t g_mecc_1bit_mask = 0U

Per-bank 1-bit-error bitmap decoded after the 1-bit injection.

Note
Read externally only. Full per-slot fidelity on silicon.
Since
0.1.0

Definition at line 121 of file main.c.

Referenced by internal_mecc_inject().

◆ g_mecc_2bit_caught

volatile uint32_t g_mecc_2bit_caught = 0U

1 when the injected 2-bit fault was latched by SRAMESR.

Note
Read externally only.
Since
0.1.0

Definition at line 113 of file main.c.

Referenced by internal_mecc_run_pass().

◆ g_mecc_2bit_mask

volatile uint32_t g_mecc_2bit_mask = 0U

Per-bank 2-bit-error bitmap decoded after the 2-bit injection.

Note
Read externally only. Full per-slot fidelity on silicon.
Since
0.1.0

Definition at line 129 of file main.c.

Referenced by internal_mecc_inject().

◆ g_mecc_heartbeat

volatile uint32_t g_mecc_heartbeat = 0U

Bumps once per main-loop pass – liveness for headless probes.

Note
Read externally only.
Since
0.1.0

Definition at line 137 of file main.c.

Referenced by main().

◆ s_mecc_bad_msg

const uint8_t s_mecc_bad_msg[] = "ecc: sram2 fault-inject ok=N\r\n"
static

Failure banner for an incomplete or rejected injection pass.

Provides a deterministic negative marker for HIL log matching.

Note
Immutable console bytes with an explicit compile-time length.
Since
0.1.0

Definition at line 97 of file main.c.

Referenced by main().

◆ s_mecc_ok_msg

const uint8_t s_mecc_ok_msg[] = "ecc: sram2 1bit-inj=caught 2bit-inj=caught ok=Y\r\n"
static

Success banner for the complete one-bit and two-bit injection pass.

Records both expected latches and the aggregate healthy verdict.

Note
Immutable console bytes with an explicit compile-time length.
Since
0.1.0

Definition at line 89 of file main.c.

Referenced by main().

◆ s_tag

const char* s_tag = "mem_ecc"
static

Diagnostic / log tag.

Definition at line 68 of file main.c.