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

Periodic-app deep-idle loop: wake-do-work-standby on ULPT0 self-wake. More...

#include <stddef.h>
#include <stdint.h>
#include "ra8_board_ek_ra8d2.h"
#include "ra8_boot_entry.h"
#include "ra8_cgc.h"
#include "ra8_elc_regs.h"
#include "ra8_err.h"
#include "ra8_isr.h"
#include "ra8_lpm.h"
#include "ra8_lpm_regs.h"
#include "ra8_time.h"
#include "ra8_ulpt.h"
#include "ra8_ulpt_regs.h"
Include dependency graph for main.c:

Go to the source code of this file.

Enumerations

enum  lpi_const_t : uint32_t {
  k_lpi_baud = 115200U ,
  k_lpi_period_ticks = 0x4000U ,
  k_lpi_tcstf_poll_limit = 0x100000U ,
  k_lpi_period_count = 8U
}
 Demo tunables for the periodic-idle loop. More...
enum  lpi_chan_t : uint8_t { k_lpi_channel = 0U }
 ULPT channel selector for this demo. More...
enum  lpi_led_t : uint8_t { k_lpi_led = (uint8_t)k_ra8_board_led1 }
 Heartbeat LED selector. More...

Functions

static void lpi_panic_halt (void)
 Park the CPU forever on an unrecoverable bring-up failure.
static void lpi_ulpt_isr (void *ctx)
 ULPT0 underflow interrupt service routine.
static ra8_err_t lpi_wait_count_started (uint8_t channel)
 Spin until the ULPT count operation has actually started.
static void lpi_setup_or_halt (void)
 Bring CGC + SysTick + SCI8 + LED + ULPT + LPM block up.
static ra8_err_t lpi_arm_wake (void)
 Wire the ULPT0 underflow as a Software-Standby wake source.
static ra8_err_t lpi_do_work (void)
 Do one period's unit of work: heartbeat LED + console banner.
static ra8_err_t lpi_idle_one_period (void)
 Arm ULPT0, drop to Software Standby, and clean up after wake.
void main (void)
 The application entry point Reset_Handler hands control to.

Variables

static const uint8_t k_lpi_boot_msg [] = "lpm_periodic_idle: boot\r\n"
 Boot banner – emitted once before the first work step.
static const uint8_t k_lpi_work_msg [] = "lpm_periodic_idle: work\r\n"
 Per-period work banner – one per active period.
static const uint8_t k_lpi_pass_msg [] = "lpm_periodic_idle PASS\r\n"
 Completion banner – emitted once after the last period.
volatile uint32_t g_lpm_periodic_wake_count = 0U
 Liveness counter – bumped in the ULPT0 underflow ISR.

Detailed Description

Periodic-app deep-idle loop: wake-do-work-standby on ULPT0 self-wake.

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

The "periodic apps" increment of the deep-idle foundation (roadmap issue #153). Where lpm_ulpt_standby proves the bare wake path, this app wraps it into the shape a real periodic application takes: each period it does a small unit of work, then sleeps the CPU in Software Standby until the on-chip Ultra-Low-Power Timer wakes it, and repeats for a fixed number of periods before parking.

Per-period sequence:

  1. Work – toggle the user LED (a visible heartbeat) and emit "lpm_periodic_idle: work" over the SCI8 J-Link console.
  2. Arm – start ULPT0 counting down a fixed period and confirm ULPTCR.TCSTF = 1 (count actually started).
  3. Standbyra8_lpm_enter_sleep drops to Software Standby (LPSCR.LPMD = 0x5); every clock domain gates except LOCO and the always-on wake detectors.
  4. Self-wake – the ULPT0 underflow cancels Software Standby through the same two-halves ICU path the standby demo proves.
  5. Repeat for k_lpi_period_count periods, then emit "lpm_periodic_idle PASS" and park.

The exact, bench-proven wake path is reused verbatim from lpm_ulpt_standby – no new wake mechanism is invented:

  1. ra8_isr_register(k_ra8_elc_event_ulpt0_ulpti, ...) links the ULPT0 underflow event into an ICU IELSRn slot and enables the matching NVIC line – HUM 11.6.2.1 (p 482): "corresponding IELSRn register must be set before executing a WFI instruction", and Table 11.3 footnote *28 (p 434): the interrupt "must be enabled by NVIC_ISERn".
  2. ra8_lpm_arm_wupen1_bits(k_ra8_lpm_wupen1_ulpt0u) arms the async WUPEN1.ULP0U standby-cancel detector (HUM Ch 14.2.20 p 552). ULPT0_ULPTI is a valid SSTBY cancel source per Table 11.4 (p 434).

Why ULPT and not the RTC: with ULPTMR1.TCK1 = 0 the ULPT counts from ULPTLCLK – the LOCO-derived 32.768 kHz clock (HUM Table 9.2 p 320) – which keeps running through Software Standby while LOCOCR.LCSTP = 0 (HUM Table 11.3 footnote *2 p 433). LOCO is internal, so unlike the RTC alarm (which depends on the bare-board sub-clock crystal) this wake source does not rely on the intermittent SOSC.

Note
ra8_emulator does not model Software-Standby clock-gating: it fast-forwards WFI to the next SysTick, so the periodic loop advances in the fake (boot, the work banners, and PASS all print) but the genuine ULPT0 self-wake – the LOCO-clocked underflow cancelling Software Standby – can only be confirmed on the bench, exactly as lpm_ulpt_standby was. This example is correct-by-construction against that proven sibling pending that bench run.
Since
0.1.0

Definition in file main.c.

Enumeration Type Documentation

◆ lpi_chan_t

enum lpi_chan_t : uint8_t

ULPT channel selector for this demo.

Only ULPT0 is used; its underflow is the wake source.

Invariant
The selected channel matches the ELC event armed in lpi_arm_wake (k_ra8_elc_event_ulpt0_ulpti).
See also
lpi_arm_wake
Since
0.1.0
Enumerator
k_lpi_channel 

ULPT0 (its underflow is the wake source).

Definition at line 120 of file main.c.

◆ lpi_const_t

enum lpi_const_t : uint32_t

Demo tunables for the periodic-idle loop.

Groups every integer constant the loop needs so no magic numbers leak into the control flow.

Invariant
k_lpi_period_ticks fits the 32-bit ULPT counter.
k_lpi_period_count is small enough that the bounded outer loop terminates in well under a second of wall time on the bench.
See also
lus_const_t in lpm_ulpt_standby for the wake-path twin.
Since
0.1.0
Enumerator
k_lpi_baud 

SCI8 console baud rate.

k_lpi_period_ticks 

Lpi period ticks.

k_lpi_tcstf_poll_limit 

Lpi tcstf poll limit.

k_lpi_period_count 

Lpi period count.

Definition at line 94 of file main.c.

◆ lpi_led_t

enum lpi_led_t : uint8_t

Heartbeat LED selector.

The blue user LED (LED1) is toggled once per period so a bench observer sees the wake cadence without the UART.

Invariant
The id is a valid ra8_board_led_id_t.
See also
ra8_board_led_toggle
Since
0.1.0
Enumerator
k_lpi_led 

Blue user LED heartbeat.

Definition at line 136 of file main.c.

Function Documentation

◆ lpi_arm_wake()

ra8_err_t lpi_arm_wake ( void )
staticnodiscard

Wire the ULPT0 underflow as a Software-Standby wake source.

Both halves of the RA8D2 wake path are required (see file header): the IELSRn/NVIC link via ra8_isr_register AND the async WUPEN1.ULP0U detector via ra8_lpm_arm_wupen1_bits. This mirrors the bench-proven lpm_ulpt_standby arm step exactly.

MC/DC:
Compound decision: ra8_isr_init != ok || ra8_isr_register != ok || ra8_lpm_arm_wupen1_bits != ok. Three atomic conditions x N+1 = 4 vectors – the all-ok runtime path plus each step's error path (the underlying steps are covered in the HAL host unit tests).
Returns
k_ra8_ok on success, else the first failing step's error.
Return values
k_ra8_okAll three wake-path steps succeeded.
Precondition
lpi_setup_or_halt has run; interrupts not yet enabled.
LPM block initialised so WUPEN writes take effect.
Postcondition
On success ULPT0_ULPTI vectors to lpi_ulpt_isr and WUPEN1.ULP0U is asserted.
Note
Single-threaded init context; not interrupt-safe.
See also
lpi_ulpt_isr
Since
0.1.0

Definition at line 337 of file main.c.

References k_ra8_elc_event_ulpt0_ulpti, k_ra8_isr_prio_default, k_ra8_lpm_wupen1_ulpt0u, k_ra8_ok, lpi_ulpt_isr(), ra8_isr_init(), ra8_isr_register(), and ra8_lpm_arm_wupen1_bits().

Referenced by main().

◆ lpi_do_work()

ra8_err_t lpi_do_work ( void )
staticnodiscard

Do one period's unit of work: heartbeat LED + console banner.

The "application payload" stand-in for a real periodic task. Toggling the LED gives a bench observer a visible cadence; the banner gives the UART/HIL scrape a per-period marker.

Returns
k_ra8_ok on success, else the first failing step's error.
Return values
k_ra8_okLED toggled and banner transmitted.
Precondition
lpi_setup_or_halt initialised the LED and console.
Postcondition
On k_ra8_ok the LED state is inverted and the work banner is queued for transmission.
Note
Runs with interrupts enabled; touches no shared ISR state.
See also
lpi_idle_one_period
Since
0.1.0

Definition at line 373 of file main.c.

References k_lpi_led, k_lpi_work_msg, k_ra8_ok, ra8_board_led_toggle(), and ra8_board_uart_console_write().

Referenced by main().

◆ lpi_idle_one_period()

ra8_err_t lpi_idle_one_period ( void )
staticnodiscard

Arm ULPT0, drop to Software Standby, and clean up after wake.

Re-arms the ULPT countdown, confirms ULPTCR.TCSTF = 1, enters Software Standby until the underflow cancels it, then stops ULPT0 (ra8_ulpt_stop clears ULPTCR.TUNF) so the next period starts clean. The standby entry is the bench-proven sequence from lpm_ulpt_standby.

Returns
k_ra8_ok on a clean sleep/wake/cleanup, else the first failing step's error.
Return values
k_ra8_okSlept and woke cleanly.
k_ra8_err_hw_timeoutTCSTF never confirmed (see lpi_wait_count_started).
Precondition
lpi_arm_wake armed the ULPT0 wake path and IRQs are enabled.
LOCO is running so ULPTLCLK survives Software Standby.
Postcondition
On k_ra8_ok ULPT0 is stopped with TUNF cleared, ready to re-arm.
Note
Blocks the caller in Software Standby until the ULPT0 wake.
See also
lpi_wait_count_started
Since
0.1.0

Definition at line 407 of file main.c.

References k_lpi_channel, k_lpi_period_ticks, k_ra8_ok, k_ra8_sleep_mode_software_std, lpi_wait_count_started(), ra8_lpm_enter_sleep(), ra8_ulpt_start(), and ra8_ulpt_stop().

Referenced by main().

◆ lpi_panic_halt()

void lpi_panic_halt ( void )
static

Park the CPU forever on an unrecoverable bring-up failure.

Spins in WFI so an attached debugger can inspect state. Reached only when a precondition of the deep-idle loop cannot be met.

Precondition
A required init or loop step returned a non-k_ra8_ok status.
Postcondition
Control never leaves this function.
Note
Not interrupt-safe by intent; it is a terminal trap.
Since
0.1.0

Definition at line 179 of file main.c.

Referenced by lpi_setup_or_halt(), and main().

◆ lpi_setup_or_halt()

void lpi_setup_or_halt ( void )
static

Bring CGC + SysTick + SCI8 + LED + ULPT + LPM block up.

Same shape as lpm_ulpt_standby but also initialises the heartbeat LED. The SCI8 J-Link console (PD02 TXD / PD03 RXD) is brought up via the EK-RA8D2 board-support console API. LOCO is kept running so ULPTLCLK survives Software Standby.

Precondition
IRQs disabled (Reset_Handler default).
Reset_Handler has copied .data and zeroed .bss.
Postcondition
On success every sub-system is armed; on failure the function panic-halts and never returns.
LPM block has LPSCR.LPMD = 0 (System Active) until ra8_lpm_enter_sleep is called.
Note
Single-threaded init context; not interrupt-safe.
See also
lpi_arm_wake
Since
0.1.0

Definition at line 272 of file main.c.

References k_lpi_baud, k_lpi_led, k_ra8_clock_id_cpuclk0, k_ra8_lpm_clock_loco, k_ra8_lpm_dcssmode_128us, k_ra8_lpm_ss2lp_default, k_ra8_ok, lpi_panic_halt(), ra8_board_led_init(), ra8_board_uart_console_init(), ra8_cgc_get_clock_hz(), ra8_cgc_init(), ra8_lpm_init(), ra8_lpm_set_clock_stop(), ra8_time_init(), and ra8_ulpt_init().

Referenced by main().

◆ lpi_ulpt_isr()

void lpi_ulpt_isr ( void * ctx)
static

ULPT0 underflow interrupt service routine.

Real path: ULPT0 counter underflow -> ULPT0_ULPTI ELC event -> IELSRn link -> NVIC pend -> ra8_isr_dispatch -> here. Cancelling Software Standby is handled by the WUPEN1.ULP0U detector + the NVIC pend; this handler only records liveness. The underflow source flag (ULPTCR.TUNF) is cleared by the ra8_ulpt_stop in the loop's re-arm step, so the ISR stays minimal and allocation-free.

Parameters
[in]ctxUnused registration context.
Precondition
Registered for k_ra8_elc_event_ulpt0_ulpti via ra8_isr_register.
Postcondition
g_lpm_periodic_wake_count has advanced by exactly one.
Note
Not re-entrant; a single ULPT channel drives it.
Since
0.1.0

Definition at line 205 of file main.c.

References g_lpm_periodic_wake_count.

Referenced by lpi_arm_wake().

◆ lpi_wait_count_started()

ra8_err_t lpi_wait_count_started ( uint8_t channel)
staticnodiscard

Spin until the ULPT count operation has actually started.

HUM Section 25.4.7 (p 1214) requires confirming ULPTCR.TCSTF = 1 (count operation started) before entering a standby mode – otherwise the standby transition can gate the sync clock before the counter has begun, leaving it stalled and unable to underflow. ra8_ulpt_start only sets TSTART; this closes the gap from the application side. The spin is bounded by k_lpi_tcstf_poll_limit (NASA P10 Rule 2).

Parameters
[in]channelULPT channel index (0 or 1).
Returns
k_ra8_ok once TCSTF = 1, else the status-read error or k_ra8_err_hw_timeout if the bound is reached.
Return values
k_ra8_okCount confirmed running.
k_ra8_err_hw_timeoutBound reached without TCSTF set.
Precondition
ra8_ulpt_start has set TSTART = 1 on channel.
Postcondition
On k_ra8_ok the ULPT counter is confirmed running.
Note
Polls only; performs no register writes, so it is re-entrant.
See also
ra8_ulpt_get_status
Since
0.1.0

Definition at line 236 of file main.c.

References k_lpi_tcstf_poll_limit, k_ra8_err_hw_timeout, k_ra8_ok, k_ra8_ulpt_bit_tcstf, and ra8_ulpt_get_status().

Referenced by lpi_idle_one_period().

◆ 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 424 of file main.c.

References k_lpi_boot_msg, k_lpi_pass_msg, k_lpi_period_count, k_ra8_ok, lpi_arm_wake(), lpi_do_work(), lpi_idle_one_period(), lpi_panic_halt(), lpi_setup_or_halt(), ra8_board_uart_console_write(), and ra8_isr_globals_enable().

Variable Documentation

◆ g_lpm_periodic_wake_count

volatile uint32_t g_lpm_periodic_wake_count = 0U

Liveness counter – bumped in the ULPT0 underflow ISR.

Incremented from lpi_ulpt_isr each time a ULPT0 underflow cancels Software Standby. Exposed (non-static, volatile) so a J-Link session can watch the genuine wake cadence independent of the loop's own period counter.

Note
Written only by the ISR; a single 32-bit load elsewhere is atomic on the Cortex-M85.
Warning
Do not modify outside the ISR.
Since
0.1.0

Definition at line 164 of file main.c.

Referenced by lpi_ulpt_isr().

◆ k_lpi_boot_msg

const uint8_t k_lpi_boot_msg[] = "lpm_periodic_idle: boot\r\n"
static

Boot banner – emitted once before the first work step.

Definition at line 141 of file main.c.

Referenced by main().

◆ k_lpi_pass_msg

const uint8_t k_lpi_pass_msg[] = "lpm_periodic_idle PASS\r\n"
static

Completion banner – emitted once after the last period.

Definition at line 147 of file main.c.

Referenced by main().

◆ k_lpi_work_msg

const uint8_t k_lpi_work_msg[] = "lpm_periodic_idle: work\r\n"
static

Per-period work banner – one per active period.

Definition at line 144 of file main.c.

Referenced by lpi_do_work().