ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_ulpt.c
Go to the documentation of this file.
1
44
45#include <stdint.h>
46#include <stdio.h>
47
48#include "board_periph_block.h"
50
60typedef enum : uint32_t {
63
65typedef enum : uint64_t {
66 k_ulpt_base = 0x40220000UL,
67 k_ulpt_stride = 0x100UL,
69 k_ulpt_span = 0x100UL * 2UL,
71
73typedef enum : uint64_t {
74 k_ulpt_off_cnt = 0x00UL,
75 k_ulpt_off_cma = 0x04UL,
76 k_ulpt_off_cmb = 0x08UL,
77 k_ulpt_off_cr = 0x0CUL,
78 k_ulpt_off_mr1 = 0x0DUL,
79 k_ulpt_off_mr2 = 0x0EUL,
80 k_ulpt_off_mr3 = 0x0FUL,
81 k_ulpt_off_ioc = 0x10UL,
83
91typedef enum : uint8_t {
97
112typedef enum : uint32_t {
114 0x40000U,
116
127typedef enum : uint16_t {
130
132typedef enum : uint32_t {
136
138typedef struct {
139 uint32_t counter;
140 uint32_t reload;
141 uint32_t cmpa;
142 uint32_t cmpb;
143 uint8_t cr;
144 uint8_t mr1;
145 uint8_t mr2;
146 uint8_t mr3;
147 uint8_t ioc;
148 uint32_t underflows;
150
152
153/* =============================================================================
154 * Register-cell access (route a byte access to the addressed channel field).
155 * =============================================================================
156 */
157
170RA8_INTERNAL static uint32_t internal_ulpt_cell_value(const ulpt_channel_t* c, uint64_t off)
171{
172 /* HUM Ch 25.2.6 "ULPT : ULPT Counter Register" p 1198 */
173 if (off < (uint64_t)k_ulpt_off_cma) {
174 return c->counter;
175 }
176 /* HUM Ch 25 "Ultra-Low-Power Timer (ULPT)" p 1187 -- ULPTCMA */
177 if (off < (uint64_t)k_ulpt_off_cmb) {
178 return c->cmpa;
179 }
180 /* HUM Ch 25 "Ultra-Low-Power Timer (ULPT)" p 1187 -- ULPTCMB */
181 if (off < (uint64_t)k_ulpt_off_cr) {
182 return c->cmpb;
183 }
184 /* HUM Ch 25.2.1 "ULPTCR : ULPT Control Register" p 1190 */
185 if (off == (uint64_t)k_ulpt_off_cr) {
186 return c->cr;
187 }
188 /* HUM Ch 25.2.2 "ULPTMR1 : ULPT Mode Register 1" p 1192 */
189 if (off == (uint64_t)k_ulpt_off_mr1) {
190 return c->mr1;
191 }
192 /* HUM Ch 25.2.3 "ULPTMR2 : ULPT Mode Register 2" p 1192 */
193 if (off == (uint64_t)k_ulpt_off_mr2) {
194 return c->mr2;
195 }
196 /* HUM Ch 25.2.4 "ULPTMR3 : ULPT Mode Register 3" p 1195 */
197 if (off == (uint64_t)k_ulpt_off_mr3) {
198 return c->mr3;
199 }
200 /* HUM Ch 25.2.5 "ULPTIOC : ULPT I/O Control Register" p 1195 */
201 return c->ioc;
202}
203
215RA8_INTERNAL static uint64_t internal_ulpt_cell_base(uint64_t off)
216{
217 if (off < (uint64_t)k_ulpt_off_cma) {
218 return (uint64_t)k_ulpt_off_cnt;
219 }
220 if (off < (uint64_t)k_ulpt_off_cmb) {
221 return (uint64_t)k_ulpt_off_cma;
222 }
223 if (off < (uint64_t)k_ulpt_off_cr) {
224 return (uint64_t)k_ulpt_off_cmb;
225 }
226 return off; /* the 8-bit control / mode / io cells are one byte each */
227}
228
229/* =============================================================================
230 * MMIO read / write.
231 * =============================================================================
232 */
233
247RA8_INTERNAL static uint64_t internal_ulpt_read(uc_engine* uc, uint64_t addr, unsigned size)
248{
249 (void)uc;
250 const uint64_t rel = addr - (uint64_t)k_ulpt_base;
251 const uint32_t ch = (uint32_t)(rel / (uint64_t)k_ulpt_stride);
252 if (ch >= (uint32_t)k_ulpt_count) {
253 return 0U;
254 }
255 const uint64_t off = rel % (uint64_t)k_ulpt_stride;
256 const uint32_t cell = internal_ulpt_cell_value(&s_ulpt[ch], off);
257 const uint32_t lane = (uint32_t)(off - internal_ulpt_cell_base(off));
258 uint64_t v = 0U;
259 for (unsigned i = 0U; i < size; i++) {
260 const uint32_t b = lane + (uint32_t)i;
261 if (b < (uint32_t)sizeof(uint32_t)) {
262 const uint32_t lane_byte =
263 (cell >> ((uint32_t)k_ulpt_byte_bits * b)) & (uint32_t)k_ulpt_byte_mask;
264 v |= (uint64_t)lane_byte << ((uint32_t)k_ulpt_byte_bits * (uint32_t)i);
265 }
266 }
267 return v;
268}
269
281{
282 /* HUM Ch 25.2.1 "ULPTCR : ULPT Control Register" p 1190 -- TUNDF is
283 * write-0-to-clear (ra8_ulpt_stop clears it by writing 0); TSTART is RW and
284 * TCSTF tracks TSTART. Mirrors the AGT AGTCR model. */
285 const uint8_t status_keep = (uint8_t)(c->cr & value & (uint8_t)k_ulpt_cr_tundf);
286 const uint8_t start = (uint8_t)(value & (uint8_t)k_ulpt_cr_tstart);
287 c->cr = (uint8_t)(start | (start != 0U ? (uint8_t)k_ulpt_cr_tcstf : 0U) | status_keep);
288}
289
302RA8_INTERNAL static void
303internal_ulpt_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
304{
305 (void)uc;
306 const uint64_t rel = addr - (uint64_t)k_ulpt_base;
307 const uint32_t ch = (uint32_t)(rel / (uint64_t)k_ulpt_stride);
308 if (ch >= (uint32_t)k_ulpt_count) {
309 return;
310 }
311 ulpt_channel_t* c = &s_ulpt[ch];
312 const uint64_t off = rel % (uint64_t)k_ulpt_stride;
313 /* The ra8_ulpt driver only ever issues aligned, full-width stores: a 32-bit
314 * write to ULPTCNT/CMA/CMB and an 8-bit write to ULPTCR/MRn/IOC. */
315 if (off < (uint64_t)k_ulpt_off_cma) {
316 /* HUM Ch 25.2.6 "ULPT : ULPT Counter Register" p 1198 -- a write seeds both
317 * the live counter and the reload value (ra8_ulpt_start writes the period). */
318 c->counter = (uint32_t)value;
319 c->reload = (uint32_t)value;
320 } else if (off < (uint64_t)k_ulpt_off_cmb) {
321 /* HUM Ch 25 "Ultra-Low-Power Timer (ULPT)" p 1187 -- ULPTCMA */
322 c->cmpa = (uint32_t)value;
323 } else if (off < (uint64_t)k_ulpt_off_cr) {
324 /* HUM Ch 25 "Ultra-Low-Power Timer (ULPT)" p 1187 -- ULPTCMB */
325 c->cmpb = (uint32_t)value;
326 } else if (off == (uint64_t)k_ulpt_off_cr) {
327 internal_ulpt_write_cr(c, (uint8_t)value);
328 } else if (off == (uint64_t)k_ulpt_off_mr1) {
329 /* HUM Ch 25.2.2 "ULPTMR1 : ULPT Mode Register 1" p 1192 */
330 c->mr1 = (uint8_t)value;
331 } else if (off == (uint64_t)k_ulpt_off_mr2) {
332 /* HUM Ch 25.2.3 "ULPTMR2 : ULPT Mode Register 2" p 1192 */
333 c->mr2 = (uint8_t)value;
334 } else if (off == (uint64_t)k_ulpt_off_mr3) {
335 /* HUM Ch 25.2.4 "ULPTMR3 : ULPT Mode Register 3" p 1195 */
336 c->mr3 = (uint8_t)value;
337 } else if (off == (uint64_t)k_ulpt_off_ioc) {
338 /* HUM Ch 25.2.5 "ULPTIOC : ULPT I/O Control Register" p 1195 */
339 c->ioc = (uint8_t)value;
340 }
341 (void)size;
342}
343
344/* =============================================================================
345 * Per-tick down-count + edge-triggered underflow event.
346 * =============================================================================
347 */
348
359RA8_INTERNAL static void internal_ulpt_tick_channel(uc_engine* uc, uint32_t ch)
360{
361 ulpt_channel_t* c = &s_ulpt[ch];
362 /* HUM Ch 25.2.1 "ULPTCR : ULPT Control Register" p 1190 -- TSTART gate */
363 if ((c->cr & (uint8_t)k_ulpt_cr_tstart) == 0U) {
364 return; /* stopped: counter holds */
365 }
366 const uint32_t step = (uint32_t)k_ulpt_step_per_chunk;
367 if (c->counter > step) {
368 c->counter -= step;
369 return;
370 }
371 /* Underflow: reload from the latched period and continue (periodic). */
372 const uint32_t span = c->reload + 1U;
373 const uint32_t deficit = step - c->counter;
374 c->counter = c->reload - ((deficit - 1U) % span);
375 /* Raise only on the rising edge of TUNDF -- one wake per armed period, until
376 * the firmware clears it by stopping/re-arming the channel. */
377 if ((c->cr & (uint8_t)k_ulpt_cr_tundf) == 0U) {
378 /* HUM Ch 25.2.1 "ULPTCR : ULPT Control Register" p 1190 -- TUNDF set */
379 c->cr |= (uint8_t)k_ulpt_cr_tundf;
380 c->underflows++;
381 if (ch == 0U) {
383 }
384 if (board_periph_trace()) {
385 (void)priv_emu_io_errf(" ULPT%u : underflow (reload=0x%08X)\n", ch, c->reload);
386 }
387 }
388}
389
399RA8_INTERNAL static void internal_ulpt_tick(uc_engine* uc)
400{
401 for (uint32_t ch = 0U; ch < (uint32_t)k_ulpt_count; ch++) {
403 }
404}
405
406/* =============================================================================
407 * Reset / report.
408 * =============================================================================
409 */
410
420{
421 for (uint32_t ch = 0U; ch < (uint32_t)k_ulpt_count; ch++) {
422 s_ulpt[ch] = (ulpt_channel_t){};
423 }
424}
425
435{
436 for (uint32_t ch = 0U; ch < (uint32_t)k_ulpt_count; ch++) {
437 const ulpt_channel_t* c = &s_ulpt[ch];
438 if (c->underflows == 0U) {
439 continue;
440 }
441 (void)priv_emu_io_errf(" ULPT%u : counter=0x%08X underflows=%u (running=%s)\n",
442 ch,
443 c->counter,
444 c->underflows,
445 (c->cr & (uint8_t)k_ulpt_cr_tstart) ? "yes" : "no");
446 }
447}
448
451 .base = (uint64_t)k_ulpt_base,
452 .span = (uint64_t)k_ulpt_span,
453 .order = (uint32_t)k_ulpt_block_order,
454 .read = internal_ulpt_read,
456 .tick = internal_ulpt_tick,
457 .reset = internal_ulpt_reset,
458 .report = internal_ulpt_report,
459 .name = "ULPT",
460};
461
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.
bool board_periph_trace(void)
Whether –trace is active (blocks log transitions when true).
static RA8_INTERNAL void internal_ulpt_tick_channel(uc_engine *uc, uint32_t ch)
Advance one running ULPT channel by one chunk; raise its underflow event.
ulpt_event_t
ULPT0 underflow ELC event the model emits (HUM Ch 19 ELC).
@ k_ulpt_event_ch0_underflow
ULPT0_ULPTI underflow event.
ulpt_order_t
Per-tick order slot for the ULPT block.
@ k_ulpt_block_order
ULPT tick / reset / report order slot.
static RA8_INTERNAL void internal_ulpt_report(void)
Print each ULPT channel that underflowed at least once.
static RA8_INTERNAL uint64_t internal_ulpt_cell_base(uint64_t off)
Byte offset of the start of the register cell that contains off.
static RA8_INTERNAL void internal_ulpt_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
Write size bytes little-endian into the addressed ULPT channel.
static RA8_INTERNAL void internal_board_periph_ulpt_register(void)
Self-register the ULPT window before main runs (decentralized).
ulpt_off_t
ULPT per-channel register byte offsets (ra8_ulpt_regs.h).
@ k_ulpt_off_mr2
ULPTMR2 mode 2 (8-bit).
@ k_ulpt_off_cma
ULPTCMA compare match A (32-bit).
@ k_ulpt_off_mr3
ULPTMR3 mode 3 (8-bit).
@ k_ulpt_off_cmb
ULPTCMB compare match B (32-bit).
@ k_ulpt_off_cnt
ULPTCNT counter / reload (32-bit).
@ k_ulpt_off_ioc
ULPTIOC I/O control (8-bit).
@ k_ulpt_off_cr
ULPTCR control / status (8-bit).
@ k_ulpt_off_mr1
ULPTMR1 mode 1 (8-bit).
static const board_periph_block_t s_k_ulpt_block
ULPT register window + the down-count tick / reset / report.
ulpt_geom_t
ULPT register window geometry (ra8_ulpt_regs.h).
@ k_ulpt_stride
Bytes per channel (ULPT0 -> ULPT1).
@ k_ulpt_span
Whole-block window (both channels).
@ k_ulpt_base
ULPT0 base (FSP R_ULPT0_BASE).
@ k_ulpt_count
ULPT0 and ULPT1.
static RA8_INTERNAL void internal_ulpt_tick(uc_engine *uc)
Advance every running ULPT channel one chunk.
ulpt_rate_t
Down-count step applied per emulation chunk while a channel runs.
@ k_ulpt_step_per_chunk
256 Ki ticks/chunk: example periods underflow on the first idle tick.
static RA8_INTERNAL void internal_ulpt_reset(void)
Clear every ULPT channel to its power-on state.
static RA8_INTERNAL uint32_t internal_ulpt_cell_value(const ulpt_channel_t *c, uint64_t off)
Current 32-bit value of the register cell that contains off.
ulpt_byte_t
Sub-word access constants for the byte-granular MMIO path.
@ k_ulpt_byte_bits
Bits per byte lane.
@ k_ulpt_byte_mask
Low 8 bits of a register lane.
static RA8_INTERNAL void internal_ulpt_write_cr(ulpt_channel_t *c, uint8_t value)
Apply a write to ULPTCR: TSTART arms, TCSTF tracks it, TUNDF clears on 0.
static RA8_INTERNAL uint64_t internal_ulpt_read(uc_engine *uc, uint64_t addr, unsigned size)
Read size bytes little-endian from the addressed ULPT channel.
static ulpt_channel_t s_ulpt[k_ulpt_count]
ulpt_cr_bit_t
ULPTCR control / status bit masks.
@ k_ulpt_cr_tstop
TSTOP forced-stop request (bit 2, W).
@ k_ulpt_cr_tundf
TUNDF underflow flag (bit 5, write-0-clear).
@ k_ulpt_cr_tcstf
TCSTF count-status flag (bit 1, RO).
@ k_ulpt_cr_tstart
TSTART start request (bit 0, RW).
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).
A modelled peripheral block's self-description for the core registry.
One modelled ULPT channel: a 32-bit reloading down-counter + status.
uint32_t cmpa
ULPTCMA compare match A.
uint8_t mr1
ULPTMR1 mode 1.
uint32_t cmpb
ULPTCMB compare match B.
uint8_t ioc
ULPTIOC I/O control.
uint8_t mr3
ULPTMR3 mode 3.
uint32_t reload
Value last written to ULPTCNT.
uint8_t cr
ULPTCR control / status.
uint32_t underflows
Underflow event count (report).
uint32_t counter
Live ULPTCNT count.
uint8_t mr2
ULPTMR2 mode 2.