ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
ra8_time.c
Go to the documentation of this file.
1
18
19#include "ra8_time.h"
20
21#include <stdint.h>
22
23#include "ra8_err.h"
24#include "ra8_log.h"
25#include "ra8_systick.h"
26#include "ra8_time_constants.h"
27
28static const char* s_tag = "TIME";
29
30static volatile uint32_t s_tick_ms = 0U;
31
33static volatile uint32_t s_cycles_per_ms = 0U;
34
35void SysTick_Handler(void);
36
59ra8_err_t ra8_time_init(uint32_t cpu_hz)
60{
61 if (cpu_hz == 0U) {
62 ra8_log_error(s_tag, "cpu_hz must be non-zero");
64 }
65
66 const uint32_t reload = (cpu_hz / k_ra8_ms_per_sec) - 1U;
67 if (reload == 0U) {
68 ra8_log_error(s_tag, "cpu_hz too low for 1kHz tick");
70 }
71
72#ifndef RA8_OFF_TARGET
73 /* Arm SysTick (CPU clock + tick IRQ) and enable the DWT cycle counter
74 * through the shared timebase primitive, which owns every raw SYST_CSR /
75 * SYST_RVR / SYST_CVR / DEMCR / DWT access. On the host build these calls
76 * are skipped, exactly as the raw pokes were. */
77 const ra8_err_t cfg_err = ra8_systick_configure(reload, k_ra8_systick_clk_cpu, true);
78 if (cfg_err != k_ra8_ok) {
79 ra8_log_error(s_tag, "systick configure failed");
80 return cfg_err;
81 }
83#endif
84
86 s_tick_ms = 0U;
87 ra8_log_info_val(s_tag, "systick reload", reload);
88 return k_ra8_ok;
89}
90
108uint32_t ra8_time_ms(void)
109{
110 return s_tick_ms;
111}
112
129void ra8_delay_ms(uint32_t ms)
130{
131#ifdef RA8_OFF_TARGET
132 (void)ms; /* No SysTick off-target -- s_tick_ms never advances. */
133#else
134 /* If PRIMASK is set the SysTick IRQ cannot dispatch and s_tick_ms */
135 /* never advances -- a wfi-loop on s_tick_ms would hang forever. */
136 /* Fall back to DWT_CYCCNT, which ticks every CPU cycle regardless */
137 /* of PRIMASK. Once IRQs are globally enabled, the cheaper */
138 /* SysTick path is used. */
139 uint32_t primask;
140 __asm__ volatile("mrs %0, primask" : "=r"(primask));
141 if ((primask & 1U) != 0U) {
142 const uint32_t target = ms * s_cycles_per_ms;
143 const uint32_t start = ra8_dwt_cyccnt_read();
144 while ((uint32_t)(ra8_dwt_cyccnt_read() - start) < target) {
145 __asm__ volatile("nop");
146 }
147 } else {
148 const uint32_t start = s_tick_ms;
149 while ((uint32_t)(s_tick_ms - start) < ms) {
150 __asm__ volatile("wfi");
151 }
152 }
153#endif
154}
155
171{
172 s_tick_ms++;
173}
174
175/*
176 * Per-subsystem tick callouts. These are declared as WEAK EXTERNS: if
177 * the firmware image links a subsystem that defines the symbol, the
178 * weak reference resolves to that strong defn and the function pointer
179 * is non-null; if the subsystem isn't linked the reference remains a
180 * null pointer and the call is skipped.
181 *
182 * - `_tx_timer_interrupt` lives in libthreadx.a. Advances
183 * the ThreadX time bases so
184 * tx_thread_sleep / TX_TIMER /
185 * semaphore-wait timeouts fire.
186 * GUARDED by `g_ra8_threadx_systick_ready`
187 * (also in libthreadx.a) -- the kernel
188 * timer state is not safe to call
189 * into until the project's
190 * `_tx_initialize_low_level` has run.
191 * - `ux_dcd_ra8_usb_irq_reenable` lives in port/usbx/src/ux_dcd_ra8_usb.c.
192 * Re-arms the USBFS NVIC line that
193 * the bridge's storm guard masks.
194 *
195 * Apps that need additional tick work can still publish a strong
196 * SysTick_Handler in their own main.c -- it wins over the weak default
197 * below. The weak-extern trick is what lets a per-app handler go away
198 * once its work is just "tick threadx and tick usb".
199 */
200/* The default SysTick_Handler below and its weak externs are firmware-only.
201 * The host unit-test build (RA8_OFF_TARGET) has no SysTick ISR, never calls
202 * this handler, and must not drag in the ThreadX / USB weak externs: the macOS
203 * test linker (ld64) does NOT resolve an undefined `weak` reference to NULL the
204 * way the ELF firmware linker does, so leaving them visible breaks every host
205 * test that links ra8_time.c. Compile the whole ISR + its externs for the
206 * firmware target only; on ELF the `weak` references still fold to NULL when a
207 * subsystem is absent and to its strong defn when present. */
208#ifndef RA8_OFF_TARGET
209
210[[gnu::weak]] extern void _tx_timer_interrupt(void);
211[[gnu::weak]] extern void ux_dcd_ra8_usb_irq_reenable(void);
212
213/* When a non-ThreadX app is being built nothing pulls in the storage
214 * for this flag and the weak reference resolves to a null pointer at
215 * link time; the runtime check below short-circuits the conditional.
216 * When ThreadX IS linked the pointer is non-null and we read the
217 * underlying byte every SysTick. */
218[[gnu::weak]] extern volatile uint32_t g_ra8_threadx_systick_ready;
219
220/*
221 * Default SysTick_Handler -- weak so an app can still supply a strong
222 * override when needed. The default already routes ticks into ThreadX
223 * and USB via the weak externs above, so most apps no longer need a
224 * per-app SysTick_Handler at all.
225 *
226 * The vector_table.c weak alias to Default_Handler is overridden by
227 * this stronger weak symbol; an even stronger non-weak one in the
228 * application file wins above both.
229 */
244[[gnu::weak]] void SysTick_Handler(void)
245{
247 /* Only dispatch into the ThreadX timer ISR after the project's
248 * `_tx_initialize_low_level` has flagged the kernel as ready.
249 * Between `ra8_time_init()` (which arms SysTick at 1 kHz from C)
250 * and `tx_kernel_enter()` (which runs ThreadX init), the kernel
251 * timer state is uninitialised; an early call into
252 * `_tx_timer_interrupt` here HardFaults with UFSR.INVPC=1 -- the
253 * bench symptom that bricks `threadx_netx_tcp_echo` boot when its
254 * intervening `ra8_board_ethernet_init` runs long enough for
255 * SysTick to fire. Non-ThreadX apps see both externs as NULL
256 * weak references and the nested-if folds away. Written as
257 * nested ifs (no compound boolean operator) so the project's
258 * MC/DC gate does not demand an extra test vector for this
259 * ISR-only code path. */
260 if (&g_ra8_threadx_systick_ready != ((void*)0)) {
261 if (g_ra8_threadx_systick_ready != 0U) {
262 if (_tx_timer_interrupt != ((void*)0)) {
264 }
265 }
266 }
267 if (ux_dcd_ra8_usb_irq_reenable != ((void*)0)) {
269 }
270}
271
272#else /* RA8_OFF_TARGET */
273
274/* Host unit-test build: test_ra8_time exercises SysTick_Handler directly, so the
275 * symbol must exist -- but there is no ThreadX kernel or USB bridge to tick,
276 * and the macOS test linker (ld64) cannot resolve the firmware handler's
277 * undefined weak externs. Provide a minimal handler that does only the
278 * tick-counter work the test observes. */
279[[gnu::weak]] void SysTick_Handler(void)
280{
282}
283
284#endif /* RA8_OFF_TARGET -- firmware vs host-test SysTick_Handler */
static const char * s_tag
Logging / check tag.
Definition ra8_app.c:17
Error Code Definitions for ra8-firmware.
@ k_ra8_err_invalid_arg
Invalid function argument.
Definition ra8_err.h:152
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_info_val(tag, message, value)
RA8 log info val.
Definition ra8_log.h:366
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
Cortex-M85 SysTick + DWT cycle-counter timebase primitive.
@ k_ra8_systick_clk_cpu
CLKSOURCE = 1: processor (CPU) clock.
Definition ra8_systick.h:75
ra8_err_t ra8_systick_configure(uint32_t reload, ra8_systick_clock_source_t src, bool tick_irq)
Arm and start SysTick with a reload value and clock source.
uint32_t ra8_dwt_cyccnt_read(void)
Sample the DWT cycle counter (DWT_CYCCNT).
void ra8_dwt_cyccnt_enable(void)
Enable the DWT free-running cycle counter (DWT_CYCCNT).
static volatile uint32_t s_cycles_per_ms
CPU cycles per millisecond, stamped by ra8_time_init.
Definition ra8_time.c:33
void ux_dcd_ra8_usb_irq_reenable(void)
Storm-guard recovery: zero the run counter and re-enable the USB IRQ.
static volatile uint32_t s_tick_ms
Definition ra8_time.c:30
ra8_err_t ra8_time_init(uint32_t cpu_hz)
Implementation of ra8_time_init() – programme SysTick.
Definition ra8_time.c:59
void ra8_time_on_tick(void)
Implementation of ra8_time_on_tick() – SysTick IRQ tick.
Definition ra8_time.c:170
void ra8_delay_ms(uint32_t ms)
Implementation of ra8_delay_ms() – busy-wait with wfi.
Definition ra8_time.c:129
void _tx_timer_interrupt(void)
void SysTick_Handler(void)
Service one SysTick event for the core timebase.
Definition ra8_time.c:244
uint32_t ra8_time_ms(void)
Implementation of ra8_time_ms() – read SysTick counter.
Definition ra8_time.c:108
volatile uint32_t g_ra8_threadx_systick_ready
0 until _tx_initialize_low_level has armed ThreadX's SysTick + timer state; 1 afterwards.
SysTick-based tick counter, delay and timestamp helpers.
Named Time Unit Constants for Delay / Timeout / Tick Math.
@ k_ra8_ms_per_sec
Milliseconds per second.