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

Single-region MPU read-only partition demo with fault recovery. 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_err.h"
#include "ra8_isr.h"
#include "ra8_mpu.h"
#include "ra8_time.h"
Include dependency graph for main.c:

Go to the source code of this file.

Enumerations

enum  thumb_decode_t : uint16_t {
  k_thumb_hi5_shift = 11U ,
  k_thumb_hi5_mask = 0x1FU ,
  k_thumb2_prefix_11101 = 0x1DU ,
  k_thumb2_prefix_11110 = 0x1EU ,
  k_thumb2_prefix_11111 = 0x1FU
}
 Thumb-2 first-halfword decode constants. More...
enum  mpu_simple_region_t : uint32_t {
  k_mpu_simple_region_size = 32U ,
  k_mpu_simple_mair0_word = 0x000000FFU
}
enum  mpu_simple_attr_t : uint8_t {
  k_mpu_simple_attr_normal_wb = 0xFFU ,
  k_mpu_simple_probe_byte = 0x42U
}
 MAIR slot encoding + probe sentinel. More...
enum  mpu_simple_timing_t : uint32_t {
  k_mpu_simple_period_ms = 1000U ,
  k_mpu_simple_baud = 115200U
}
 Heartbeat cadence + UART line rate. More...
enum  mpu_simple_frame_t : uint8_t { k_mpu_simple_frame_pc_idx = 6U }
 Stacked-frame layout for an Armv8-M exception entry (no FP). More...

Functions

static void internal_mpu_simple_panic_halt (void)
 Park the CPU after a fatal UART, clock, LED, or MPU setup failure.
static void internal_mpu_simple_setup_or_halt (void)
 Bring CGC + SysTick + LEDs + the SCI8 console UART up.
static ra8_err_t internal_mpu_simple_probe (void)
 Probe the RO region with a write – expected to fault on silicon.
static void internal_mpu_simple_fault_recover (uint32_t *frame)
 Recovering MemManage handler.
void MemManage_Handler (void)
static void internal_mpu_simple_emit_banner (void)
 Emit the banner once after the recovering handler fires.
void main (void)
 The application entry point Reset_Handler hands control to.

Variables

static uint8_t s_ro_buffer [k_mpu_simple_region_size] = {}
 32-byte aligned scratch buffer the RO region will cover.
static volatile uint8_t s_fault_pending = 0U
 Set non-zero by the recovering MemManage handler so the main loop can emit the user-visible "fault handled" banner from thread context (the handler itself avoids UART I/O because SCI8 writes can block on TDR-empty).
volatile uint32_t g_mpu_simple_match = 0U
 HIL liveness counter – incremented by main() on every loop iteration after the RO probe + fault-recovery sequence has completed at least once.
volatile uint32_t g_mpu_simple_fault_count = 0U
 HIL diagnostic counter – incremented by the recovering MemManage handler every time the deliberate RO-write traps.
static const ra8_mpu_region_t s_regions []
 One-region RO descriptor covering s_ro_buffer.
static const ra8_mpu_cfg_t s_cfg
 Aggregate MPU configuration handed to ra8_mpu_configure.
static const uint8_t s_mpu_simple_banner [] = "mpu: fault handled, recovered\r\n"
 Greeting line for the "fault handled" banner.

Detailed Description

Single-region MPU read-only partition demo with fault recovery.

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

Bare-metal counterpart to threadx_mpu_partition_demo – no RTOS, no thread context, just one MPU region. The app:

  1. Allocates a 32-byte aligned scratch buffer in SRAM.
  2. Configures MPU region 0 covering that buffer as RO/RO with PRIVDEFENA = true so the rest of the address map remains RW (the default privileged-mode background region).
  3. Installs a recovering MemManage handler (strong override of the weak trampoline in vector_table.c). On the faulting store the handler latches a flag, rewrites the stacked Program Counter to skip past the offending instruction, and returns – execution resumes immediately after the write so the main loop can print the "fault handled" banner and keep iterating. The handler deliberately leaves the MMFSR half-word of CFSR latched so the HIL alive-mode check with HIL_FAULT_EXPECTED=1 still sees the non-zero CFSR it requires as positive proof the fault actually fired.
  4. From privileged code, attempts a write to the RO buffer. The write is expected to raise a MemManage fault, the recovering handler bumps g_mpu_simple_fault_count, sets s_fault_pending, and returns. The main loop then prints "mpu: fault handled, recovered" over SCI8 (the J-Link OB VCOM bridge), advances g_mpu_simple_match, and loops.

The "fault handled" banner is the success signal for the HIL alive-mode UART scanner: the negative regex in scripts/hil/check_alive.sh does not match this phrase, and HIL_FAULT_EXPECTED=1 still requires the non-zero CFSR that the handler intentionally leaves latched.

Bare EK-RA8D2; no expansion board.

Since
0.1.0

Definition in file main.c.

Enumeration Type Documentation

◆ mpu_simple_attr_t

enum mpu_simple_attr_t : uint8_t

MAIR slot encoding + probe sentinel.

Enumerator
k_mpu_simple_attr_normal_wb 

MPU simple attr normal wb.

k_mpu_simple_probe_byte 

Sentinel byte written by internal_mpu_simple_probe.

Definition at line 72 of file main.c.

◆ mpu_simple_frame_t

enum mpu_simple_frame_t : uint8_t

Stacked-frame layout for an Armv8-M exception entry (no FP).

Enumerator
k_mpu_simple_frame_pc_idx 

r0..r3, r12, lr, PC, xPSR.

Definition at line 84 of file main.c.

◆ mpu_simple_region_t

enum mpu_simple_region_t : uint32_t
Enumerator
k_mpu_simple_region_size 

Smallest legal Armv8-M MPU region.

k_mpu_simple_mair0_word 

Slot 0 = Normal WB RW-allocate.

Definition at line 66 of file main.c.

◆ mpu_simple_timing_t

enum mpu_simple_timing_t : uint32_t

Heartbeat cadence + UART line rate.

Enumerator
k_mpu_simple_period_ms 

MPU simple period ms.

k_mpu_simple_baud 

MPU simple baud.

Definition at line 78 of file main.c.

◆ thumb_decode_t

enum thumb_decode_t : uint16_t

Thumb-2 first-halfword decode constants.

Enumerator
k_thumb_hi5_shift 

Shift to the top 5 bits of the halfword.

k_thumb_hi5_mask 

Mask for the top 5 bits.

k_thumb2_prefix_11101 

32-bit Thumb-2 first-halfword prefix 0b11101.

k_thumb2_prefix_11110 

32-bit Thumb-2 first-halfword prefix 0b11110.

k_thumb2_prefix_11111 

32-bit Thumb-2 first-halfword prefix 0b11111.

Definition at line 58 of file main.c.

Function Documentation

◆ internal_mpu_simple_emit_banner()

void internal_mpu_simple_emit_banner ( void )
static

Emit the banner once after the recovering handler fires.

Writes the fixed positive-recovery marker from thread context after the exception handler has deferred reporting through its flag.

Precondition
The board console has been initialized.
s_fault_pending was set by the recovering handler.
Postcondition
The complete recovery banner has been offered to the console.
No MPU or exception-frame state is modified.
Note
Runs in thread context, never from the MemManage handler.
Since
0.1.0

Definition at line 388 of file main.c.

References ra8_board_uart_console_write(), RA8_INTERNAL, and s_mpu_simple_banner.

Referenced by main().

◆ internal_mpu_simple_fault_recover()

void internal_mpu_simple_fault_recover ( uint32_t * frame)
static

Recovering MemManage handler.

Strong override of the weak MemManage_Handler trampoline in vector_table.c. Walks the exception stack frame (selected by the EXC_RETURN.SPSEL bit in LR on entry), inspects the half-word the stacked PC points at to decide whether the faulting store is the 16-bit or 32-bit Thumb encoding, advances the stacked PC by 2 or 4 bytes accordingly, bumps the fault counter, sets the thread-context banner flag, and returns. The exception-return mechanism then resumes execution past the offending store, and internal_mpu_simple_probe reads back 0x00 (the original buffer contents) and reports k_ra8_err_hw_error – which is the success signal here, not a real failure.

The MMFSR half-word of CFSR is intentionally LEFT LATCHED so the HIL alive-mode probe with HIL_FAULT_EXPECTED=1 continues to see CFSR != 0 as its positive proof that the fault fired. CFSR self-clears on the next chip reset.

The handler is __attribute__((naked)) and written in inline assembly so the unbounded compiler-generated prologue cannot clobber LR before we read EXC_RETURN.SPSEL, and so the function can perform a real exception return via bx lr rather than a synthesised C return.

MC/DC:
The C helper internal_mpu_simple_fault_recover carries one compound decision: (lr & 4U) == 0U (was MSP active at exception entry?). MC/DC vectors: thread-mode MSP (vector 1, kernel/handler stack), thread-mode PSP (vector 2, application stack). Both vectors are exercised in unit tests that mock the LR value.
Parameters
[in,out]frameCortex-M exception frame whose stacked PC is advanced.
Precondition
A MemManage fault has been entered.
frame addresses a complete writable basic exception frame.
Postcondition
The stacked PC has been advanced past the faulting instruction, g_mpu_simple_fault_count has been incremented, and CFSR.MMFSR is intentionally left latched.
s_fault_pending is set for deferred thread-context reporting.
Note
Runs at the architecturally fixed MemManage priority.
Since
0.1.0

Definition at line 316 of file main.c.

References g_mpu_simple_fault_count, k_mpu_simple_frame_pc_idx, k_thumb2_prefix_11101, k_thumb2_prefix_11110, k_thumb2_prefix_11111, k_thumb_hi5_mask, k_thumb_hi5_shift, RA8_INTERNAL, and s_fault_pending.

◆ internal_mpu_simple_panic_halt()

void internal_mpu_simple_panic_halt ( void )
static

Park the CPU after a fatal UART, clock, LED, or MPU setup failure.

Retains the failing register and diagnostic state 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.
The deliberate read-only probe is not attempted after setup failure.
Note
An interrupt can wake one iteration, but the terminal loop resumes.
Since
0.1.0

Definition at line 147 of file main.c.

References RA8_INTERNAL.

Referenced by internal_mpu_simple_setup_or_halt(), and main().

◆ internal_mpu_simple_probe()

ra8_err_t internal_mpu_simple_probe ( void )
staticnodiscard

Probe the RO region with a write – expected to fault on silicon.

MC/DC:
Compound decision: probe_byte != 0x42. One atomic condition x 2 vectors – match (host fake: write succeeded, MPU not actually trapping) vs mismatch (impossible because the write either lands or faults). Test driver covers both branches by pre-seeding the buffer.
Returns
k_ra8_ok if the readback matches (no fault took place); k_ra8_err_hw_error if the byte changed value unexpectedly. Note: on real silicon control returns here only because the recovering MemManage handler rewrote the stacked PC to skip past the offending store – the readback sees the original sentinel (0x00, the buffer's initial value), so the function returns k_ra8_err_hw_error and the caller knows the fault path executed.
Return values
k_ra8_okThe host/off-target store completed and read back.
k_ra8_err_hw_errorSilicon recovery skipped the protected store.
Precondition
s_ro_buffer is covered by the configured read-only MPU region.
The recovering MemManage handler is installed on silicon.
Postcondition
The probe byte is unchanged when a silicon MemManage fault is recovered.
No address outside s_ro_buffer is written.
Note
The hardware-error code is the expected positive signal on silicon.
Since
0.1.0

Definition at line 257 of file main.c.

References k_mpu_simple_probe_byte, k_ra8_err_hw_error, k_ra8_ok, RA8_INTERNAL, and s_ro_buffer.

Referenced by main().

◆ internal_mpu_simple_setup_or_halt()

void internal_mpu_simple_setup_or_halt ( void )
static

Bring CGC + SysTick + LEDs + the SCI8 console UART up.

MC/DC:
Sequence of single-condition checks; each guard's MC/DC pair is covered by the test harness via mock-injected failures.

Adds ra8_board_uart_console_init to the original setup so the recovering MemManage handler can emit a "fault handled" banner (via the main loop, not from exception context) that the HIL alive-mode UART scanner picks up. PCLKA must be post-PLL at the point ra8_board_uart_console_init runs, so ra8_cgc_init is called first and the BSP-private BRR computation reads the live PCLKA value.

Precondition
Reset startup completed data and BSS initialization.
Board clocks and GPIO are in their reset-compatible state.
Postcondition
On return the timebase, console, and all three diagnostic 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 201 of file main.c.

References internal_mpu_simple_panic_halt(), k_mpu_simple_baud, k_ra8_board_led1, k_ra8_board_led2, k_ra8_board_led3, 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, 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

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 slab/arena/tile/vmem banner is emitted; the CPU then loops in WFI.
Since
0.1.0

Definition at line 393 of file main.c.

References g_mpu_simple_match, internal_mpu_simple_emit_banner(), internal_mpu_simple_panic_halt(), internal_mpu_simple_probe(), internal_mpu_simple_setup_or_halt(), k_mpu_simple_period_ms, k_ra8_board_led1, k_ra8_board_led3, k_ra8_ok, ra8_board_led_on(), ra8_board_led_toggle(), ra8_delay_ms(), ra8_isr_globals_enable(), ra8_mpu_configure(), s_cfg, and s_fault_pending.

◆ MemManage_Handler()

void MemManage_Handler ( void )

Definition at line 347 of file main.c.

Variable Documentation

◆ g_mpu_simple_fault_count

volatile uint32_t g_mpu_simple_fault_count = 0U

HIL diagnostic counter – incremented by the recovering MemManage handler every time the deliberate RO-write traps.

Expected steady-state value: 1 (the probe runs once before the main loop and the loop never re-arms it). Values > 1 indicate the handler failed to advance the stacked PC and the offending store re-executed on return.

Note
Read externally by J-Link only; firmware never reads back.
Since
0.1.0

Definition at line 135 of file main.c.

Referenced by internal_mpu_simple_fault_recover().

◆ g_mpu_simple_match

volatile uint32_t g_mpu_simple_match = 0U

HIL liveness counter – incremented by main() on every loop iteration after the RO probe + fault-recovery sequence has completed at least once.

Read externally by scripts/hil/jlink_memprobe.sh via SWD (when the app uses jlink_memprobe mode) and additionally readable when the harness uses HIL_MODE=alive with HIL_FAULT_EXPECTED=1 – in the latter case the alive check still passes (CycleCnt advances + PC is in code region + no negative UART banner) and this counter supplements the proof by demonstrating execution continued past the deliberately-triggered MPU fault.

Note
Read externally by J-Link only; firmware never reads back.
Since
0.1.0

Definition at line 121 of file main.c.

Referenced by main().

◆ s_cfg

const ra8_mpu_cfg_t s_cfg
static
Initial value:
= {
.regions = s_regions,
.region_count = 1U,
.mair0 = (uint32_t)k_mpu_simple_mair0_word,
.mair1 = 0U,
.privdefena = true,
.hfnmiena = false,
}
@ k_mpu_simple_mair0_word
Slot 0 = Normal WB RW-allocate.
Definition main.c:68
static const ra8_mpu_region_t s_regions[]
One-region RO descriptor covering s_ro_buffer.
Definition main.c:155

Aggregate MPU configuration handed to ra8_mpu_configure.

Definition at line 168 of file main.c.

◆ s_fault_pending

volatile uint8_t s_fault_pending = 0U
static

Set non-zero by the recovering MemManage handler so the main loop can emit the user-visible "fault handled" banner from thread context (the handler itself avoids UART I/O because SCI8 writes can block on TDR-empty).

Note
Written by the MemManage handler; read by main().
Since
0.1.0

Definition at line 101 of file main.c.

Referenced by internal_mpu_simple_fault_recover(), and main().

◆ s_mpu_simple_banner

const uint8_t s_mpu_simple_banner[] = "mpu: fault handled, recovered\r\n"
static

Greeting line for the "fault handled" banner.

The negative regex in scripts/hil/check_alive.sh rejects FAIL|panic|NAK|ERROR|HardFault|MemFault|BusFault|UsageFault| stack overflow (case-insensitive, word-bounded). The lower- case word "fault" on its own (and "handled", "recovered") are all outside that set, so this banner reads as a positive signal to the HIL alive checker.

Note
Immutable console bytes written after exception return.
Since
0.1.0

Definition at line 375 of file main.c.

Referenced by internal_mpu_simple_emit_banner().

◆ s_regions

const ra8_mpu_region_t s_regions[]
static
Initial value:
= {
{
.base = (uintptr_t)s_ro_buffer,
.size = (uint32_t)k_mpu_simple_region_size,
.unpriv = k_ra8_mpu_perm_ro,
.executable = false,
.shareable = k_ra8_mpu_share_inner,
.attr_idx = k_ra8_mpu_attr_idx_0,
},
}
@ k_mpu_simple_region_size
Smallest legal Armv8-M MPU region.
Definition main.c:67
static uint8_t s_ro_buffer[k_mpu_simple_region_size]
32-byte aligned scratch buffer the RO region will cover.
Definition main.c:89
@ k_ra8_mpu_perm_ro
Read-only at this privilege level.
Definition ra8_mpu.h:43
@ k_ra8_mpu_attr_idx_0
MAIR0 byte 0.
Definition ra8_mpu.h:66
@ k_ra8_mpu_share_inner
Inner shareable.
Definition ra8_mpu.h:56

One-region RO descriptor covering s_ro_buffer.

Definition at line 155 of file main.c.

Referenced by emu_memmap_mram_base(), emu_memmap_regions(), and internal_map_regions().

◆ s_ro_buffer

uint8_t s_ro_buffer[k_mpu_simple_region_size] = {}
static

32-byte aligned scratch buffer the RO region will cover.

Definition at line 89 of file main.c.

Referenced by internal_mpu_simple_probe().