ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_gptp.c
Go to the documentation of this file.
1
61
62#include <stdint.h>
63#include <stdio.h>
64
65#include "board_periph_block.h"
67#include "ra8_ether_regs.h"
68
76typedef enum : uint64_t {
78 k_gptp_win_span = 0x1000UL,
80
82typedef enum : uint32_t {
85
104typedef enum : uint64_t {
105 k_gptp_eswclk_hz = 250000000ULL,
106 k_gptp_tick_hz = 1000ULL,
109 k_gptp_ns_per_sec = 1000000000ULL,
110 k_gptp_mask_nsec = 0x3FFFFFFFULL,
111 k_gptp_mask_sec_u = 0x0000FFFFULL,
112 k_gptp_mask_sec_48 = 0xFFFFFFFFFFFFULL,
114 k_gptp_ipv_reset = 0x00000003ULL,
115 k_gptp_hz_per_mhz = 1000000ULL,
117
124
138typedef struct {
139 bool enabled;
140 uint64_t off_sec;
141 uint32_t off_nsec;
142 uint64_t acc_sec;
143 uint64_t acc_fixed;
144 uint32_t latch_gptp_m;
145 uint32_t latch_gptp_u;
146 uint32_t latch_avtp_u;
148
162
165
166/* ------------------------------------------------------------------------- */
167/* Flat config-reflect shadow helpers. */
168/* ------------------------------------------------------------------------- */
169
182RA8_INTERNAL static uint64_t internal_gptp_shadow_read(uint64_t off, unsigned size)
183{
184 uint64_t v = 0U;
185 for (unsigned i = 0U; (i < size) && ((off + (uint64_t)i) < (uint64_t)k_gptp_win_span); ++i) {
186 v |= (uint64_t)s_gptp.win[off + (uint64_t)i] << (k_gptp_shift_byte * i);
187 }
188 return v;
189}
190
202RA8_INTERNAL static void internal_gptp_shadow_write(uint64_t off, unsigned size, uint64_t value)
203{
204 for (unsigned i = 0U; (i < size) && ((off + (uint64_t)i) < (uint64_t)k_gptp_win_span); ++i) {
205 s_gptp.win[off + (uint64_t)i] = (uint8_t)(value >> (k_gptp_shift_byte * i));
206 }
207}
208
220RA8_INTERNAL static uint32_t internal_gptp_shadow_u32(uint64_t off)
221{
222 return (uint32_t)internal_gptp_shadow_read(off, k_gptp_reg32_bytes);
223}
224
225/* ------------------------------------------------------------------------- */
226/* Per-timer offset decode. */
227/* ------------------------------------------------------------------------- */
228
246RA8_INTERNAL static bool
247internal_gptp_decode_timer(uint64_t off, uint32_t* out_timer, uint64_t* out_inoff)
248{
249 if (off < (uint64_t)k_ra8_gptp_off_timer0) {
250 return false;
251 }
252 const uint64_t rel = off - (uint64_t)k_ra8_gptp_off_timer0;
253 const uint32_t timer = (uint32_t)(rel / (uint64_t)k_ra8_gptp_timer_stride);
254 if (timer >= (uint32_t)k_ra8_gptp_timer_count) {
255 return false;
256 }
257 *out_timer = timer;
258 *out_inoff = rel % (uint64_t)k_ra8_gptp_timer_stride;
259 return true;
260}
261
262/* ------------------------------------------------------------------------- */
263/* Free-running counter -> reported (seconds, nanoseconds). */
264/* ------------------------------------------------------------------------- */
265
283RA8_INTERNAL static void internal_gptp_time_now(uint32_t t, uint64_t* out_sec, uint32_t* out_nsec)
284{
285 const gptp_timer_t* u = &s_gptp.timer[t];
286 if (!u->enabled) {
287 /* HUM Ch 35.3.2.1 "PTPTMEC : Timer Enable Configuration Register" p 1927 --
288 * a stopped unit's counters read 0. */
289 *out_sec = 0U;
290 *out_nsec = 0U;
291 return;
292 }
293 const uint32_t free_ns = (uint32_t)(u->acc_fixed >> (uint64_t)k_gptp_subns_shift);
294 uint64_t total_nsec = (uint64_t)u->off_nsec + (uint64_t)free_ns;
295 uint64_t carry_sec = 0U;
296 if (total_nsec >= (uint64_t)k_gptp_ns_per_sec) {
297 total_nsec -= (uint64_t)k_gptp_ns_per_sec;
298 carry_sec = 1U;
299 }
300 const uint64_t total_sec = (u->off_sec + u->acc_sec + carry_sec) & (uint64_t)k_gptp_mask_sec_48;
301 *out_sec = total_sec;
302 *out_nsec = (uint32_t)(total_nsec & (uint64_t)k_gptp_mask_nsec);
303}
304
322{
323 gptp_timer_t* u = &s_gptp.timer[t];
324 if (!u->enabled) {
325 return;
326 }
327 const uint64_t tiv_off = (uint64_t)k_ra8_gptp_off_timer0 +
328 ((uint64_t)t * (uint64_t)k_ra8_gptp_timer_stride) +
329 (uint64_t)k_ra8_gptp_off_t_ptptivc;
330 /* HUM Ch 35.3.2.3 "PTPTIVCt : Timer t Increment Value Configuration Register
331 * (t = 0, 1)" p 1928 -- 5.27 fixed-point nanoseconds per clk. */
332 const uint64_t tiv = (uint64_t)internal_gptp_shadow_u32(tiv_off);
333 if (tiv == 0U) {
334 return;
335 }
336 u->acc_fixed += tiv * (uint64_t)k_gptp_clk_per_tick;
337 const uint64_t one_sec_fixed = (uint64_t)k_gptp_ns_per_sec << (uint64_t)k_gptp_subns_shift;
338 const uint64_t whole = u->acc_fixed / one_sec_fixed;
339 u->acc_sec += whole;
340 u->acc_fixed -= whole * one_sec_fixed;
341}
342
343/* ------------------------------------------------------------------------- */
344/* Enable / disable / offset commit. */
345/* ------------------------------------------------------------------------- */
346
356RA8_INTERNAL static void internal_gptp_apply_tmec(uint32_t value)
357{
358 /* HUM Ch 35.3.2.1 "PTPTMEC : Timer Enable Configuration Register" p 1927 --
359 * writing 1 to bit q sets TEq; other bits are unaffected (write-1-to-set). */
360 for (uint32_t t = 0U; t < (uint32_t)k_ra8_gptp_timer_count; ++t) {
361 if ((value & (1UL << t)) != 0U) {
362 s_gptp.timer[t].enabled = true;
363 }
364 }
365}
366
376RA8_INTERNAL static void internal_gptp_apply_tmdc(uint32_t value)
377{
378 /* HUM Ch 35.3.2.2 "PTPTMDC : Timer Disable Configuration Register" p 1928 --
379 * writing 1 to bit q clears TEq; a stopped unit's counters read 0, so a stop
380 * is also a clear of the free-running accumulator. */
381 for (uint32_t t = 0U; t < (uint32_t)k_ra8_gptp_timer_count; ++t) {
382 if ((value & (1UL << t)) != 0U) {
383 s_gptp.timer[t].enabled = false;
384 s_gptp.timer[t].acc_sec = 0U;
385 s_gptp.timer[t].acc_fixed = 0U;
386 }
387 }
388}
389
401{
402 uint32_t v = 0U;
403 for (uint32_t t = 0U; t < (uint32_t)k_ra8_gptp_timer_count; ++t) {
404 if (s_gptp.timer[t].enabled) {
405 v |= (1UL << t);
406 }
407 }
408 return v;
409}
410
429{
430 const uint64_t base =
431 (uint64_t)k_ra8_gptp_off_timer0 + ((uint64_t)t * (uint64_t)k_ra8_gptp_timer_stride);
432 /* HUM Ch 35.3.2.6 "PTPTOVCtU : Timer t Offset Value Configuration Register U"
433 * p 1930 -- seconds [47:32]. */
434 const uint64_t ovcu =
435 (uint64_t)internal_gptp_shadow_u32(base + (uint64_t)k_ra8_gptp_off_t_ptptovcu);
436 /* HUM Ch 35.3.2.5 "PTPTOVCMt : Timer t Offset Value Configuration Register M"
437 * p 1929 -- seconds [31:0]. */
438 const uint64_t ovcm =
439 (uint64_t)internal_gptp_shadow_u32(base + (uint64_t)k_ra8_gptp_off_t_ptptovcm);
440 /* HUM Ch 35.3.2.4 "PTPTOVCtL : Timer t Offset Value Configuration Register L"
441 * p 1929 -- nanoseconds [29:0]; this write commits the whole offset. */
442 const uint64_t ovcl =
443 (uint64_t)internal_gptp_shadow_u32(base + (uint64_t)k_ra8_gptp_off_t_ptptovcl);
444 s_gptp.timer[t].off_sec =
445 ((ovcu & (uint64_t)k_gptp_mask_sec_u) << (uint64_t)k_gptp_shift_sec_u) | ovcm;
446 s_gptp.timer[t].off_nsec = (uint32_t)(ovcl & (uint64_t)k_gptp_mask_nsec);
447}
448
449/* ------------------------------------------------------------------------- */
450/* Monitoring reads (with the L-read M/U latch). */
451/* ------------------------------------------------------------------------- */
452
474RA8_INTERNAL static bool internal_gptp_read_monitor(uint32_t t, uint64_t inoff, uint32_t* out)
475{
476 gptp_timer_t* u = &s_gptp.timer[t];
477 if (inoff == (uint64_t)k_ra8_gptp_off_t_ptpgptptml) {
478 uint64_t sec = 0U;
479 uint32_t nsec = 0U;
480 internal_gptp_time_now(t, &sec, &nsec);
481 u->latch_gptp_m = (uint32_t)sec;
482 u->latch_gptp_u =
483 (uint32_t)((sec >> (uint64_t)k_gptp_shift_sec_u) & (uint64_t)k_gptp_mask_sec_u);
484 *out = nsec;
485 return true;
486 }
487 if (inoff == (uint64_t)k_ra8_gptp_off_t_ptpgptptmm) {
488 *out = u->latch_gptp_m;
489 return true;
490 }
491 if (inoff == (uint64_t)k_ra8_gptp_off_t_ptpgptptmu) {
492 *out = u->latch_gptp_u;
493 return true;
494 }
495 if (inoff == (uint64_t)k_ra8_gptp_off_t_ptpavtptml) {
496 uint64_t sec = 0U;
497 uint32_t nsec = 0U;
498 internal_gptp_time_now(t, &sec, &nsec);
499 const uint64_t avtp = (sec * (uint64_t)k_gptp_ns_per_sec) + (uint64_t)nsec;
500 u->latch_avtp_u = (uint32_t)(avtp >> (uint64_t)k_gptp_shift_sec_u);
501 *out = (uint32_t)avtp;
502 return true;
503 }
504 if (inoff == (uint64_t)k_ra8_gptp_off_t_ptpavtptmu) {
505 *out = u->latch_avtp_u;
506 return true;
507 }
508 return false;
509}
510
511/* ------------------------------------------------------------------------- */
512/* MMIO dispatch. */
513/* ------------------------------------------------------------------------- */
514
531RA8_INTERNAL static uint64_t internal_gptp_read(uc_engine* uc, uint64_t addr, unsigned size)
532{
533 (void)uc;
534 const uint64_t off = addr - (uint64_t)k_gptp_win_base;
535 if (off == (uint64_t)k_ra8_gptp_off_ptpipv) {
536 /* HUM Ch 35.3.1.1 "PTPIPV : IP Version Register" p 1927 -- read-only, reset
537 * value nonzero; the apps use it as the block-presence probe. */
538 return (uint64_t)k_gptp_ipv_reset;
539 }
540 if (off == (uint64_t)k_ra8_gptp_off_ptptmec) {
541 return (uint64_t)internal_gptp_read_tmec();
542 }
543 if (off == (uint64_t)k_ra8_gptp_off_ptptmdc) {
544 /* HUM Ch 35.3.2.2 "PTPTMDC : Timer Disable Configuration Register" p 1928 --
545 * write-only; reads back 0. */
546 return 0U;
547 }
548 uint32_t timer = 0U;
549 uint64_t inoff = 0U;
550 if (internal_gptp_decode_timer(off, &timer, &inoff)) {
551 uint32_t val = 0U;
552 if (internal_gptp_read_monitor(timer, inoff, &val)) {
553 return (uint64_t)val;
554 }
555 }
556 return internal_gptp_shadow_read(off, size);
557}
558
576RA8_INTERNAL static void
577internal_gptp_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
578{
579 (void)uc;
580 const uint64_t off = addr - (uint64_t)k_gptp_win_base;
581 if (off == (uint64_t)k_ra8_gptp_off_ptptmec) {
582 internal_gptp_apply_tmec((uint32_t)value);
583 return;
584 }
585 if (off == (uint64_t)k_ra8_gptp_off_ptptmdc) {
586 internal_gptp_apply_tmdc((uint32_t)value);
587 return;
588 }
589 if (off == (uint64_t)k_ra8_gptp_off_ptpipv) {
590 return; /* PTPIPV is read-only (HUM 35.3.1.1 p 1927) -- drop the write. */
591 }
592 internal_gptp_shadow_write(off, size, value);
593 uint32_t timer = 0U;
594 uint64_t inoff = 0U;
595 if (internal_gptp_decode_timer(off, &timer, &inoff) &&
596 (inoff == (uint64_t)k_ra8_gptp_off_t_ptptovcl)) {
598 }
599}
600
610RA8_INTERNAL static void internal_gptp_tick(uc_engine* uc)
611{
612 (void)uc;
613 for (uint32_t t = 0U; t < (uint32_t)k_ra8_gptp_timer_count; ++t) {
615 }
616}
617
627{
628 s_gptp = (gptp_state_t){};
629}
630
640{
641 for (uint32_t t = 0U; t < (uint32_t)k_ra8_gptp_timer_count; ++t) {
642 const gptp_timer_t* u = &s_gptp.timer[t];
643 if (!u->enabled && (u->acc_sec == 0U) && (u->acc_fixed == 0U)) {
644 continue; /* this unit never ran -- stay quiet. */
645 }
646 uint64_t sec = 0U;
647 uint32_t nsec = 0U;
648 internal_gptp_time_now(t, &sec, &nsec);
649 (void)priv_emu_io_errf(
650 " GPTP timer%u : gPTP=%llu.%09u s (ESWCLK %llu MHz free-run)\n",
651 (unsigned)t,
652 (unsigned long long)sec,
653 (unsigned)nsec,
654 (unsigned long long)((uint64_t)k_gptp_eswclk_hz / (uint64_t)k_gptp_hz_per_mhz));
655 }
656}
657
660 .base = (uint64_t)k_gptp_win_base,
661 .span = (uint64_t)k_gptp_win_span,
662 .order = (uint32_t)k_gptp_block_order,
663 .read = internal_gptp_read,
665 .tick = internal_gptp_tick,
666 .reset = internal_gptp_reset,
667 .report = internal_gptp_report,
668 .name = "GPTP",
669};
670
672[[gnu::constructor]] RA8_INTERNAL static void internal_gptp_block_register(void)
673{
675}
Decentralized peripheral-block registry for the board emulator core.
void board_periph_register_block(const board_periph_block_t *block)
Register a peripheral block's descriptor with the core registry.
gptp_access_const_t
Byte / word access constants for the flat config shadow.
@ k_gptp_reg32_bytes
One 32-bit register.
@ k_gptp_byte_mask
Low 8 bits of a value.
@ k_gptp_shift_byte
One-byte shift.
static bool internal_gptp_read_monitor(uint32_t t, uint64_t inoff, uint32_t *out)
Serve a read of a per-timer monitoring register, or report "not one".
static uint32_t internal_gptp_shadow_u32(uint64_t off)
Read a 32-bit shadow register at window offset off.
static void internal_gptp_apply_tmdc(uint32_t value)
Apply a PTPTMDC write: stop and clear each unit whose bit is set.
static uint64_t internal_gptp_shadow_read(uint64_t off, unsigned size)
Read size bytes little-endian from the config shadow at off.
static void internal_gptp_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write handler for the GPTP window.
static void internal_gptp_tick(uc_engine *uc)
Per-tick advance for every running timer unit.
static void internal_gptp_reset(void)
Reset the GPTP model to its power-on state.
static void internal_gptp_apply_tmec(uint32_t value)
Apply a PTPTMEC write: start each unit whose bit is set.
static const board_periph_block_t s_k_gptp_block
GPTP block descriptor.
static gptp_state_t s_gptp
Singleton GPTP model state.
static void internal_gptp_shadow_write(uint64_t off, unsigned size, uint64_t value)
Write size bytes little-endian into the config shadow at off.
gptp_order_t
Per-tick order slot: right after the R-Switch cluster (58).
@ k_gptp_block_order
Tick / reset / report order (just after ETH).
static uint32_t internal_gptp_read_tmec(void)
Read PTPTMEC back as the live enable bitmask across the units.
static void internal_gptp_report(void)
End-of-run GPTP section: report any unit that advanced.
gptp_time_const_t
Clock rate, fixed-point scale and field widths of the GPTP time.
@ k_gptp_ipv_reset
PTPIPV read-only reset (HUM 35.3.1.1).
@ k_gptp_mask_sec_48
48-bit seconds field.
@ k_gptp_clk_per_tick
ESWCLK cycles per emulator tick.
@ k_gptp_hz_per_mhz
Hz per MHz, for the report line.
@ k_gptp_eswclk_hz
ESWCLK = PLL1P/4 (HUM 9.10.23 p 398).
@ k_gptp_tick_hz
Ticks/s: one tick = one SysTick 1 ms.
gptp_win_geom_t
Absolute window the block owns.
@ k_gptp_win_span
Whole GPTP aperture length.
@ k_gptp_win_base
GPTP window base (0x403E_0000).
static void internal_gptp_commit_offset(uint32_t t)
Commit one unit's 78-bit offset from the staged PTPTOVCt registers.
static void internal_gptp_tick_timer(uint32_t t)
Advance one running unit's free-running accumulator by one tick.
static void internal_gptp_block_register(void)
Self-register the GPTP window before main runs (decentralized).
static uint64_t internal_gptp_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read handler for the GPTP window.
static bool internal_gptp_decode_timer(uint64_t off, uint32_t *out_timer, uint64_t *out_inoff)
Resolve a window offset to a timer unit and its in-block offset.
static void internal_gptp_time_now(uint32_t t, uint64_t *out_sec, uint32_t *out_nsec)
Compute one unit's current GPTP time (offset + free-running counter).
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.
@ k_gptp_ns_per_sec
Nanoseconds in one second.
Definition main.c:85
-proof
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_gptp_subns_shift
5.27 fixed-point scale of TIV.
@ k_gptp_mask_nsec
Mask for GPTPL / TOVL [29:0].
@ k_gptp_shift_sec_u
Seconds upper part starts at 2^32.
@ k_gptp_mask_sec_u
Mask for GPTPU / TOVU [15:0].
Ethernet controller base addresses for the Renesas RA8D2.
@ k_ra8_gptp_timer_count
Number of timer units on this part.
@ k_ra8_gptp_off_ptptmdc
PTPTMDC Timer Disable Cfg.
@ k_ra8_gptp_off_t_ptpgptptmu
PTPGPTPTMtU in-block offset.
@ k_ra8_gptp_off_t_ptptovcl
PTPTOVCtL in-block offset.
@ k_ra8_gptp_off_timer0
First per-timer block.
@ k_ra8_gptp_off_t_ptpgptptmm
PTPGPTPTMtM in-block offset.
@ k_ra8_gptp_off_t_ptpavtptmu
PTPAVTPTMtU in-block offset.
@ k_ra8_gptp_off_t_ptptivc
PTPTIVCt in-block offset.
@ k_ra8_gptp_off_t_ptpgptptml
PTPGPTPTMtL in-block offset.
@ k_ra8_gptp_off_t_ptpavtptml
PTPAVTPTMtL in-block offset.
@ k_ra8_gptp_off_ptpipv
PTPIPV IP Version.
@ k_ra8_gptp_off_t_ptptovcm
PTPTOVCtM in-block offset.
@ k_ra8_gptp_off_t_ptptovcu
PTPTOVCtU in-block offset.
@ k_ra8_gptp_timer_stride
Per-timer block stride (0x40 x t).
@ k_ra8_gptp_off_ptptmec
PTPTMEC Timer Enable Cfg.
@ k_ra8_gptp_base_addr
RA8 gptp base address.
A modelled peripheral block's self-description for the core registry.
Whole-block model state.
gptp_timer_t timer[k_ra8_gptp_timer_count]
Per-unit derived state.
uint8_t win[k_gptp_win_span]
Flat config-reflect shadow.
Runtime state of one GPTP timer unit.
uint64_t off_sec
Committed offset seconds (48-bit).
uint32_t latch_gptp_u
PTPGPTPTMtU latched on the L read.
bool enabled
PTPTMEC.TEq: unit is counting.
uint64_t acc_sec
Free-running whole seconds.
uint64_t acc_fixed
Free-running sub-second, 5.27 fixed-point ns.
uint32_t off_nsec
Committed offset nanoseconds (below 1e9).
uint32_t latch_gptp_m
PTPGPTPTMtM latched on the L read.
uint32_t latch_avtp_u
PTPAVTPTMtU latched on the L read.