ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_timer.c
Go to the documentation of this file.
1
27
28#include <stdint.h>
29#include <stdio.h>
30
31#include "board_periph_block.h"
33
35typedef enum : uint64_t {
36 k_agt_base = 0x40221000UL,
37 k_agt_stride = 0x100UL,
38 k_agt_count = 10UL,
39 k_agt_span = 0x100UL * 10UL,
40 k_agt_off_agt = 0x00UL,
41 k_agt_off_cma = 0x02UL,
42 k_agt_off_cmb = 0x04UL,
43 k_agt_off_cr = 0x08UL,
44 k_agt_off_mr1 = 0x09UL,
45} agt_map_t;
46
48typedef enum : uint64_t {
49 k_gpt_base = 0x40322000UL,
50 k_gpt_stride = 0x100UL,
51 k_gpt_count = 14UL,
52 k_gpt_span = 0x100UL * 14UL,
53 k_gpt_off_gtstr = 0x04UL,
54 k_gpt_off_gtstp = 0x08UL,
55 k_gpt_off_gtclr = 0x0CUL,
56 k_gpt_off_gtcr = 0x2CUL,
57 k_gpt_off_gtst = 0x3CUL,
58 k_gpt_off_gtcnt = 0x48UL,
59 k_gpt_off_gtpr = 0x64UL,
60} gpt_map_t;
61
63typedef enum : uint32_t {
64 k_u16_max = 0xFFFFU,
66
68typedef enum : uint32_t {
70 k_agtcr_tcstf = 0x02U,
71 k_agtcr_tundf = 0x20U,
72 k_agtcr_tcmaf = 0x40U,
73 k_agtcr_tcmbf = 0x80U,
75
77typedef enum : uint32_t {
78 k_gtcr_cst = 0x00000001U,
79 k_gtst_tcfa = 0x00000001U,
80 k_gtst_tcfpo = 0x00000040U,
81 k_gtst_tcfpu = 0x00000080U,
82} gpt_bit_t;
83
93typedef enum : uint16_t {
97
122typedef enum : uint32_t {
124 k_gpt_step_per_chunk = 0x00004001U,
126
128typedef struct {
129 uint16_t counter;
130 uint16_t reload;
131 uint16_t cmpa;
132 uint16_t cmpb;
133 uint8_t cr;
134 uint8_t mr1;
135 uint32_t underflows;
137
139typedef struct {
140 uint32_t cnt;
141 uint32_t period;
142 uint32_t cr;
143 uint32_t st;
144 uint32_t overflows;
146
149
150/* =============================================================================
151 * AGT timer model -- 16-bit reloading down-counter (ra8_agt.c semantics).
152 * =============================================================================
153 */
154
167RA8_INTERNAL static uint64_t internal_agt_reg_read(uint32_t ch, uint64_t off)
168{
169 const agt_state_t* a = &s_agt[ch];
170 if (off == (uint64_t)k_agt_off_agt) {
171 return a->counter;
172 }
173 if (off == (uint64_t)k_agt_off_cma) {
174 return a->cmpa;
175 }
176 if (off == (uint64_t)k_agt_off_cmb) {
177 return a->cmpb;
178 }
179 if (off == (uint64_t)k_agt_off_cr) {
180 return a->cr;
181 }
182 if (off == (uint64_t)k_agt_off_mr1) {
183 return a->mr1;
184 }
185 return 0U;
186}
187
199RA8_INTERNAL static void internal_agt_reg_write(uint32_t ch, uint64_t off, uint32_t value)
200{
201 agt_state_t* a = &s_agt[ch];
202 if (off == (uint64_t)k_agt_off_agt) {
203 a->counter = (uint16_t)value; /* AGT write both reloads and seeds count */
204 a->reload = (uint16_t)value;
205 } else if (off == (uint64_t)k_agt_off_cma) {
206 a->cmpa = (uint16_t)value;
207 } else if (off == (uint64_t)k_agt_off_cmb) {
208 a->cmpb = (uint16_t)value;
209 } else if (off == (uint64_t)k_agt_off_cr) {
210 /* TUNDF/TCMAF/TCMBF are write-0-to-clear (ra8_agt clears by writing 0);
211 * TSTART is RW. TCSTF tracks TSTART. */
212 const uint8_t status_keep =
213 (uint8_t)(a->cr & (uint8_t)value & (uint8_t)(k_agtcr_tundf | k_agtcr_tcmaf | k_agtcr_tcmbf));
214 const uint8_t start = (uint8_t)(value & (uint8_t)k_agtcr_tstart);
215 a->cr = (uint8_t)(start | (start != 0U ? (uint8_t)k_agtcr_tcstf : 0U) | status_keep);
216 } else if (off == (uint64_t)k_agt_off_mr1) {
217 a->mr1 = (uint8_t)value;
218 }
219}
220
231RA8_INTERNAL static void internal_agt_tick_channel(uc_engine* uc, uint32_t ch)
232{
233 agt_state_t* a = &s_agt[ch];
234 if ((a->cr & (uint8_t)k_agtcr_tstart) == 0U) {
235 return; /* stopped: counter holds */
236 }
237 const uint32_t step = (uint32_t)k_agt_step_per_chunk;
238 if (a->counter > step) {
239 a->counter = (uint16_t)(a->counter - step);
240 return;
241 }
242 /* Underflow: reload and set TUNDF (and TCMAF if compare-match A is armed). */
243 const uint32_t span = (uint32_t)a->reload + 1U;
244 const uint32_t deficit = step - a->counter;
245 a->counter = (uint16_t)(a->reload - ((deficit - 1U) % span));
246 a->cr |= (uint8_t)k_agtcr_tundf;
247 a->underflows++;
248 if (ch == 0U) {
250 }
251}
252
266RA8_INTERNAL static uint64_t internal_agt_read(uc_engine* uc, uint64_t addr, unsigned size)
267{
268 (void)uc;
269 (void)size;
270 const uint32_t ch = (uint32_t)((addr - (uint64_t)k_agt_base) / (uint64_t)k_agt_stride);
271 return internal_agt_reg_read(ch, (addr - (uint64_t)k_agt_base) % (uint64_t)k_agt_stride);
272}
273
286RA8_INTERNAL static void
287internal_agt_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
288{
289 (void)uc;
290 (void)size;
291 const uint32_t ch = (uint32_t)((addr - (uint64_t)k_agt_base) / (uint64_t)k_agt_stride);
293 (addr - (uint64_t)k_agt_base) % (uint64_t)k_agt_stride,
294 (uint32_t)value);
295}
296
297/* =============================================================================
298 * GPT timer model -- 32-bit saw up-counter (ra8_gpt.c semantics).
299 * =============================================================================
300 */
301
314RA8_INTERNAL static uint64_t internal_gpt_reg_read(uint32_t ch, uint64_t off)
315{
316 const gpt_state_t* g = &s_gpt[ch];
317 if (off == (uint64_t)k_gpt_off_gtcnt) {
318 return g->cnt;
319 }
320 if (off == (uint64_t)k_gpt_off_gtpr) {
321 return g->period;
322 }
323 if (off == (uint64_t)k_gpt_off_gtcr) {
324 return g->cr;
325 }
326 if (off == (uint64_t)k_gpt_off_gtst) {
327 return g->st;
328 }
329 return 0U; /* GTWP / GTSTR / GTSTP / GTCLR read as 0 in this model */
330}
331
343RA8_INTERNAL static void internal_gpt_reg_write(uint32_t ch, uint64_t off, uint32_t value)
344{
345 gpt_state_t* g = &s_gpt[ch];
346 if (off == (uint64_t)k_gpt_off_gtcnt) {
347 g->cnt = value;
348 } else if (off == (uint64_t)k_gpt_off_gtpr) {
349 g->period = value;
350 } else if (off == (uint64_t)k_gpt_off_gtcr) {
351 g->cr = value; /* CST (bit0) starts / stops the count */
352 } else if (off == (uint64_t)k_gpt_off_gtstr) {
353 if ((value & 1U) != 0U) {
354 g->cr |= (uint32_t)k_gtcr_cst;
355 }
356 } else if (off == (uint64_t)k_gpt_off_gtstp) {
357 if ((value & 1U) != 0U) {
358 g->cr &= ~(uint32_t)k_gtcr_cst;
359 }
360 } else if (off == (uint64_t)k_gpt_off_gtclr) {
361 if ((value & 1U) != 0U) {
362 g->cnt = 0U;
363 }
364 } else if (off == (uint64_t)k_gpt_off_gtst) {
365 /* GTST bits are cleared by writing the value back with target bits zero
366 * (ra8_gpt_clear_status), so the model keeps only bits still set. */
367 g->st &= value;
368 }
369}
370
381RA8_INTERNAL static void internal_gpt_tick_channel(uc_engine* uc, uint32_t ch)
382{
383 gpt_state_t* g = &s_gpt[ch];
384 if ((g->cr & (uint32_t)k_gtcr_cst) == 0U) {
385 return; /* stopped */
386 }
387 const uint32_t period = (g->period == 0U) ? (uint32_t)k_u16_max : g->period;
388 const uint32_t step = (uint32_t)k_gpt_step_per_chunk;
389 if ((g->cnt + step) <= period) {
390 g->cnt += step;
391 return;
392 }
393 /* Overflow past GTPR in saw mode: wrap and set TCFPO. */
394 g->cnt = (uint32_t)((g->cnt + step) - period - 1U);
395 g->st |= (uint32_t)k_gtst_tcfpo;
396 g->overflows++;
397 if (ch == 0U) {
399 }
400}
401
415RA8_INTERNAL static uint64_t internal_gpt_read(uc_engine* uc, uint64_t addr, unsigned size)
416{
417 (void)uc;
418 (void)size;
419 const uint32_t ch = (uint32_t)((addr - (uint64_t)k_gpt_base) / (uint64_t)k_gpt_stride);
420 return internal_gpt_reg_read(ch, (addr - (uint64_t)k_gpt_base) % (uint64_t)k_gpt_stride);
421}
422
435RA8_INTERNAL static void
436internal_gpt_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
437{
438 (void)uc;
439 (void)size;
440 const uint32_t ch = (uint32_t)((addr - (uint64_t)k_gpt_base) / (uint64_t)k_gpt_stride);
442 (addr - (uint64_t)k_gpt_base) % (uint64_t)k_gpt_stride,
443 (uint32_t)value);
444}
445
446/* =============================================================================
447 * Shared tick / reset / report for both timer families (AGT then GPT).
448 * =============================================================================
449 */
450
460RA8_INTERNAL static void internal_timer_tick(uc_engine* uc)
461{
462 for (uint32_t ch = 0U; ch < (uint32_t)k_agt_count; ch++) {
464 }
465 for (uint32_t ch = 0U; ch < (uint32_t)k_gpt_count; ch++) {
467 }
468}
469
479{
480 for (uint32_t i = 0U; i < (uint32_t)k_agt_count; i++) {
481 s_agt[i] = (agt_state_t){};
482 }
483 for (uint32_t i = 0U; i < (uint32_t)k_gpt_count; i++) {
484 s_gpt[i] = (gpt_state_t){};
485 }
486}
487
497{
498 for (uint32_t ch = 0U; ch < (uint32_t)k_agt_count; ch++) {
499 if (s_agt[ch].underflows > 0U) {
500 (void)priv_emu_io_errf(" AGT%u : counter=0x%04X underflows=%u (running=%s)\n",
501 ch,
502 s_agt[ch].counter,
503 s_agt[ch].underflows,
504 (s_agt[ch].cr & (uint8_t)k_agtcr_tstart) ? "yes" : "no");
505 }
506 }
507 for (uint32_t ch = 0U; ch < (uint32_t)k_gpt_count; ch++) {
508 if (s_gpt[ch].overflows > 0U) {
509 (void)priv_emu_io_errf(" GPT%u : cnt=0x%08X period=0x%08X overflows=%u\n",
510 ch,
511 s_gpt[ch].cnt,
512 s_gpt[ch].period,
513 s_gpt[ch].overflows);
514 }
515 }
516}
517
518/* The AGT descriptor carries the tick / reset / report that cover BOTH timer
519 * families, so the historical AGT-then-GPT order is preserved; the GPT
520 * descriptor only owns its own register window. Both share the timer tick
521 * order, and registration order between them is irrelevant. */
522
525 .base = (uint64_t)k_agt_base,
526 .span = (uint64_t)k_agt_span,
527 .order = (uint32_t)k_block_order_timer,
528 .read = internal_agt_read,
530 .tick = internal_timer_tick,
531 .reset = internal_timer_reset,
532 .report = internal_timer_report,
533 .name = "AGT",
534};
535
538 .base = (uint64_t)k_gpt_base,
539 .span = (uint64_t)k_gpt_span,
540 .order = (uint32_t)k_block_order_timer,
541 .read = internal_gpt_read,
543 .tick = nullptr,
544 .reset = nullptr,
545 .report = nullptr,
546 .name = "GPT",
547};
548
Decentralized peripheral-block registry for the board emulator core.
void board_periph_icu_raise_event(uc_engine *uc, uint16_t event)
Raise a peripheral ELC event through the core's ICU -> NVIC path.
void board_periph_register_block(const board_periph_block_t *block)
Register a peripheral block's descriptor with the core registry.
@ k_block_order_timer
GPT + AGT timers.
static RA8_INTERNAL void internal_board_periph_timer_register(void)
Self-register both timer windows before main runs (decentralized).
static RA8_INTERNAL uint64_t internal_gpt_reg_read(uint32_t ch, uint64_t off)
Dispatch a GPT register read for channel ch at byte offset off.
static gpt_state_t s_gpt[k_gpt_count]
static RA8_INTERNAL void internal_agt_tick_channel(uc_engine *uc, uint32_t ch)
Advance one running AGT channel by one chunk; raise events on wrap.
elc_event_t
Canonical ELC event numbers the timer models emit (FSP ra8d2 bsp_elc).
@ k_event_gpt0_ovf
GPT0 GTCIV counter-overflow event.
@ k_event_agt0_int
AGT0 AGTI combined interrupt event.
static RA8_INTERNAL void internal_gpt_tick_channel(uc_engine *uc, uint32_t ch)
Advance one running GPT channel by one chunk; raise overflow events.
static RA8_INTERNAL void internal_agt_reg_write(uint32_t ch, uint64_t off, uint32_t value)
Dispatch an AGT register write; AGTCR.TSTART arms / status is RW1C.
static RA8_INTERNAL void internal_timer_tick(uc_engine *uc)
Advance every running AGT channel, then every running GPT channel.
static RA8_INTERNAL uint64_t internal_agt_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the AGT window: route to the addressed channel.
static RA8_INTERNAL uint64_t internal_gpt_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the GPT window: route to the addressed channel.
static RA8_INTERNAL void internal_agt_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the AGT window: route to the addressed channel.
static RA8_INTERNAL void internal_gpt_reg_write(uint32_t ch, uint64_t off, uint32_t value)
Dispatch a GPT register write; GTSTR/GTSTP gate the counter.
gpt_bit_t
GPT GTCR / GTST bits – ra8_gpt register notes.
@ k_gtst_tcfa
GTST.TCFA compare-match A.
@ k_gtst_tcfpo
GTST.TCFPO overflow.
@ k_gtcr_cst
GTCR.CST count-start.
@ k_gtst_tcfpu
GTST.TCFPU underflow.
static RA8_INTERNAL uint64_t internal_agt_reg_read(uint32_t ch, uint64_t off)
Dispatch an AGT register read for channel ch at byte offset off.
agtcr_bit_t
AGTCR (control/status) bits – ra8_agt_agtcr_bits_t.
@ k_agtcr_tundf
TUNDF underflow flag (RW1C).
@ k_agtcr_tcmaf
TCMAF compare-match A flag (RW1C).
@ k_agtcr_tstart
TSTART start request.
@ k_agtcr_tcmbf
TCMBF compare-match B flag (RW1C).
@ k_agtcr_tcstf
TCSTF count-status flag (RO).
agt_map_t
AGT block geometry (ra8_agt_regs.h, 16-bit view).
@ k_agt_off_cmb
AGTCMB compare-match B.
@ k_agt_off_agt
AGT counter (16-bit down).
@ k_agt_stride
Bytes per AGT channel.
@ k_agt_off_cma
AGTCMA compare-match A.
@ k_agt_off_mr1
AGTMR1 mode 1 (8-bit).
@ k_agt_count
AGT0..AGT9.
@ k_agt_span
AGT span.
@ k_agt_off_cr
AGTCR control/status (8-bit).
@ k_agt_base
AGT0 base.
gpt_map_t
GPT block geometry (ra8_gpt_regs.h, 32-bit channels).
@ k_gpt_base
GPT0 base.
@ k_gpt_off_gtpr
GTPR period.
@ k_gpt_span
GPT span.
@ k_gpt_off_gtstp
GTSTP software stop.
@ k_gpt_off_gtclr
GTCLR software clear.
@ k_gpt_off_gtst
GTST status.
@ k_gpt_stride
Bytes per GPT channel.
@ k_gpt_off_gtstr
GTSTR software start.
@ k_gpt_off_gtcr
GTCR control (CST bit0).
@ k_gpt_count
GPT0..GPT13.
@ k_gpt_off_gtcnt
GTCNT counter (32-bit up).
static const board_periph_block_t s_k_agt_block
AGT register window + the combined timer tick / reset / report.
static const board_periph_block_t s_k_gpt_block
GPT register window (tick / reset / report handled by the AGT block).
static RA8_INTERNAL void internal_gpt_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the GPT window: route to the addressed channel.
timer_field_t
16-bit counter wrap value (also the GPT default period).
@ k_u16_max
16-bit counter wrap value.
timer_tune_t
Per-chunk advance for the modelled counters (one chunk == 1 tick).
@ k_agt_step_per_chunk
AGT down-count per chunk.
@ k_gpt_step_per_chunk
GPT up-count per chunk; odd (.
static RA8_INTERNAL void internal_timer_report(void)
Print one line per AGT / GPT channel that raised any event.
static RA8_INTERNAL void internal_timer_reset(void)
Clear all AGT + GPT channel state.
static agt_state_t s_agt[k_agt_count]
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
-proof
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
One AGT channel: a 16-bit reloading down-counter + status.
uint32_t underflows
Underflow event count.
uint16_t cmpa
AGTCMA compare-match A.
uint16_t cmpb
AGTCMB compare-match B.
uint16_t reload
Value last written to AGT.
uint8_t mr1
AGTMR1 mode 1.
uint8_t cr
AGTCR control/status.
uint16_t counter
Live AGT count.
A modelled peripheral block's self-description for the core registry.
One GPT channel: a 32-bit saw up-counter + status.
uint32_t st
GTST status flags.
uint32_t cnt
GTCNT live count.
uint32_t period
GTPR period.
uint32_t overflows
Overflow event count.
uint32_t cr
GTCR (CST in bit0).