ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
emu_mmio.c
Go to the documentation of this file.
1
15
16#include "emu_mmio.h"
17
18#include <stdio.h>
19
20#include "board_periph.h"
21#include "emu_cpu1.h"
23
25typedef enum : uint32_t {
26 k_mmio_slots = 2048U,
29 k_u32_all_ones = 0xFFFFFFFFU,
31
32/* Renesas peripheral quirks that the generic sparse model cannot reproduce.
33 *
34 * MRMS frequency latches: the CGC driver (libs/ra8_hal/src/ra8_cgc.c,
35 * internal_wait_mrm_freq) writes ``key | freq_mhz`` to MRCFREQ / MREFREQ and
36 * spins until the register reads back == freq_mhz. Real silicon validates the
37 * upper key byte then strips it, so the readback is the bare frequency. The
38 * generic model reflects the full written word (key still in bits[31:24]), so
39 * the readback never equals freq and the poll runs to its 0x40000 timeout ->
40 * lcd_panic_halt. Model the hardware: on readback of these two registers,
41 * return the stored value with the key byte masked off. */
42typedef enum : uint64_t {
43 k_mrms_mrcfreq = 0x4013C004UL,
44 k_mrms_mrefreq = 0x4013C008UL,
45 k_mrms_freq_mask = 0x00FFFFFFUL,
47
48/* Sparse model of the Renesas peripheral space. Each touched address gets a
49 * slot: control writes are reflected back on read so "configure then verify"
50 * works, but once the firmware spins reading one address (a "wait for
51 * ready/idle" poll) past k_mmio_settle, reads alternate 0 / all-ones so a
52 * single-bit poll for either edge (flag set OR flag clear) completes instead
53 * of running to its timeout. */
54static uint64_t s_mmio_addr[k_mmio_slots];
55static uint32_t s_mmio_val[k_mmio_slots];
57static uint32_t s_mmio_rcount[k_mmio_slots];
58static uint32_t s_mmio_wcount[k_mmio_slots];
59static uint32_t s_mmio_n;
60static uint32_t s_mmio_reads;
61static uint32_t s_mmio_writes;
62static uint32_t s_mmio_toggle;
63
64static int s_mmio_cache = -1;
65static int s_mmio_run_slot = -1;
66static uint32_t s_mmio_run;
67
68/* BG_BGC colour-cycle witness: total writes and the distinct values seen. */
69static uint32_t s_bgc_writes;
71static uint32_t s_bgc_distinct_n;
72
73static uint32_t s_bgc_writes;
74static uint32_t s_bgc_distinct[k_bgc_track_max];
75static uint32_t s_bgc_distinct_n;
76
86RA8_INTERNAL static void internal_bgc_track(uint32_t value)
87{
89 for (uint32_t i = 0U; i < s_bgc_distinct_n; i++) {
90 if (s_bgc_distinct[i] == value) {
91 return;
92 }
93 }
94 if (s_bgc_distinct_n < (uint32_t)k_bgc_track_max) {
96 }
97}
98
110RA8_INTERNAL static int internal_mmio_index(uint64_t addr)
111{
112 if ((s_mmio_cache >= 0) && (s_mmio_addr[s_mmio_cache] == addr)) {
113 return s_mmio_cache;
114 }
115 for (uint32_t i = 0U; i < s_mmio_n; i++) {
116 if (s_mmio_addr[i] == addr) {
117 s_mmio_cache = (int)i;
118 return (int)i;
119 }
120 }
121 if (s_mmio_n < (uint32_t)k_mmio_slots) {
122 s_mmio_addr[s_mmio_n] = addr;
123 s_mmio_cache = (int)s_mmio_n;
124 return (int)(s_mmio_n++);
125 }
126 return -1;
127}
128
129uint64_t mmio_read(uc_engine* uc, uint64_t offset, unsigned size, void* user)
130{
131 (void)user;
132 s_mmio_reads++;
133 /* A modelled peripheral block answers first; the sparse fallback below is
134 * only reached for addresses no block in board_periph owns. */
135 bool handled = false;
136 const uint64_t modeled = board_periph_read(uc, (uint64_t)k_periph_base + offset, size, &handled);
137 if (handled) {
138 return modeled;
139 }
140 const int idx = internal_mmio_index((uint64_t)k_periph_base + offset);
141 if (idx >= 0) {
142 s_mmio_rcount[idx]++;
143 if (idx == s_mmio_run_slot) {
144 s_mmio_run++;
145 } else {
146 s_mmio_run_slot = idx;
147 s_mmio_run = 1U;
148 }
149 /* Reflect a written control value until a spin-poll forces it to settle. */
150 if (s_mmio_written[idx] && (s_mmio_run <= (uint32_t)k_mmio_settle)) {
151 const uint64_t addr = (uint64_t)k_periph_base + offset;
152 /* MRMS frequency latches strip the write key byte on readback so the
153 * driver's "wait until reg == freq" poll completes (see mrms_quirk_t). */
154 if ((addr == (uint64_t)k_mrms_mrcfreq) || (addr == (uint64_t)k_mrms_mrefreq)) {
155 return (uint64_t)(s_mmio_val[idx] & (uint32_t)k_mrms_freq_mask);
156 }
157 return (uint64_t)s_mmio_val[idx];
158 }
159 }
160 s_mmio_toggle ^= (uint32_t)k_u32_all_ones;
161 return (uint64_t)s_mmio_toggle;
162}
163
177uint32_t mmio_peek(uint64_t addr)
178{
179 for (uint32_t i = 0U; i < s_mmio_n; i++) {
180 if (s_mmio_addr[i] == addr) {
181 return s_mmio_written[i] ? s_mmio_val[i] : 0U;
182 }
183 }
184 return 0U;
185}
186
187void mmio_write(uc_engine* uc, uint64_t offset, unsigned size, uint64_t value, void* user)
188{
189 (void)user;
191 const uint64_t mmio_abs = (uint64_t)k_periph_base + offset;
192 /* Dual-core release: the firmware stages cpu1's vector table in
193 * CPU1INITVTOR, then asserts CPU1ACTCSR.ACTREQ to start cpu1. The watcher
194 * captures both so the run loop can boot the second engine (see emu_cpu1). */
195 emu_cpu1_notify_mmio_write(mmio_abs, value);
196 if (mmio_abs == (uint64_t)k_glcdc_bg_bgc) {
197 internal_bgc_track((uint32_t)value);
198 }
199 /* A modelled peripheral block consumes the write first (so GPIO latches,
200 * timer control, and ICU event links take real effect); the sparse fallback
201 * still records the write for the MMIO table and unmodelled blocks. */
202 bool handled = false;
203 board_periph_write(uc, (uint64_t)k_periph_base + offset, size, value, &handled);
204 if (handled) {
205 return;
206 }
207 const int idx = internal_mmio_index((uint64_t)k_periph_base + offset);
208 if (idx >= 0) {
209 s_mmio_wcount[idx]++;
210 s_mmio_val[idx] = (uint32_t)value;
211 s_mmio_written[idx] = true;
212 if (idx == s_mmio_run_slot) {
213 s_mmio_run++; /* same-addr read-modify-write spin accumulates toward settle */
214 } else {
215 s_mmio_run_slot = idx; /* new register -> following reads should see its value */
216 s_mmio_run = 1U;
217 }
218 }
219}
220
222uint32_t emu_mmio_reads(void)
223{
224 return s_mmio_reads;
225}
226
228uint32_t emu_mmio_writes(void)
229{
230 return s_mmio_writes;
231}
232
235{
236 (void)priv_emu_io_errf(" MMIO reads : %u writes: %u distinct addrs: %u\n",
239 s_mmio_n);
240}
241
244{
245 /* GLCDC colour-cycle witness: BG_BGC write count + the distinct colours. */
246 (void)priv_emu_io_errf(" BG_BGC writes : %u distinct colours: %u [",
249 for (uint32_t i = 0U; i < s_bgc_distinct_n; i++) {
250 (void)priv_emu_io_errf("%s0x%06X", (i == 0U) ? "" : " ", s_bgc_distinct[i]);
251 }
252 (void)priv_emu_io_errf("]\n");
253 (void)priv_emu_io_errf(" %-12s %10s %10s %12s\n", "addr", "reads", "writes", "last-write");
254 const bool truncated = (s_mmio_n > (uint32_t)k_mmio_print_max);
255 const uint32_t shown = truncated ? (uint32_t)k_mmio_print_max : s_mmio_n;
256 for (uint32_t i = 0U; i < shown; i++) {
257 if (s_mmio_written[i]) {
258 (void)priv_emu_io_errf(" 0x%08llX %10u %10u 0x%08X\n",
259 (unsigned long long)s_mmio_addr[i],
260 s_mmio_rcount[i],
261 s_mmio_wcount[i],
262 s_mmio_val[i]);
263 } else {
264 (void)priv_emu_io_errf(" 0x%08llX %10u %10u %12s\n",
265 (unsigned long long)s_mmio_addr[i],
266 s_mmio_rcount[i],
267 s_mmio_wcount[i],
268 "-");
269 }
270 }
271 if (truncated) {
272 (void)priv_emu_io_errf(" ... (%u more)\n", s_mmio_n - shown);
273 }
274}
Register-accurate peripheral-model framework for the board emulator.
void board_periph_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value, bool *handled)
Dispatch an MMIO write to the owning block, if any.
uint64_t board_periph_read(uc_engine *uc, uint64_t addr, unsigned size, bool *handled)
Dispatch an MMIO read to the owning block, if any.
Second-core (cpu1, Cortex-M33) engine: release watch, boot, stepping.
void emu_cpu1_notify_mmio_write(uint64_t mmio_abs, uint64_t value)
Watch a peripheral MMIO write for the cpu1 release sequence.
Definition emu_cpu1.c:68
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.
static uint32_t s_bgc_distinct_n
Definition emu_mmio.c:71
void emu_mmio_print_counts(void)
Implementation of emu_mmio_print_counts() – run-end report line.
Definition emu_mmio.c:234
static uint32_t s_mmio_n
Definition emu_mmio.c:59
static uint32_t s_mmio_wcount[k_mmio_slots]
Definition emu_mmio.c:58
static uint32_t s_bgc_distinct[k_bgc_track_max]
Definition emu_mmio.c:70
static uint32_t s_mmio_val[k_mmio_slots]
Definition emu_mmio.c:55
void mmio_write(uc_engine *uc, uint64_t offset, unsigned size, uint64_t value, void *user)
UC_MMIO write callback for the peripheral window.
Definition emu_mmio.c:187
static uint32_t s_mmio_reads
Definition emu_mmio.c:60
static uint32_t s_mmio_rcount[k_mmio_slots]
Definition emu_mmio.c:57
uint32_t mmio_peek(uint64_t addr)
Side-effect-free read of the last value written to a peripheral reg.
Definition emu_mmio.c:177
mrms_quirk_t
Definition emu_mmio.c:42
@ k_mrms_freq_mask
Key byte (bits[31:24]) stripped.
Definition emu_mmio.c:45
@ k_mrms_mrefreq
MRPCLK freq latch (write key 0xE1).
Definition emu_mmio.c:44
@ k_mrms_mrcfreq
MRICLK freq latch (write key 0x1E).
Definition emu_mmio.c:43
uint32_t emu_mmio_writes(void)
Implementation of emu_mmio_writes() – plain counter read.
Definition emu_mmio.c:228
static int s_mmio_cache
1-entry address->slot lookup cache.
Definition emu_mmio.c:64
static uint32_t s_bgc_writes
Definition emu_mmio.c:69
emu_mmio_cfg_t
Sparse-model sizing and settle thresholds.
Definition emu_mmio.c:25
@ k_mmio_slots
Distinct MMIO addresses tracked.
Definition emu_mmio.c:26
@ k_mmio_print_max
Max MMIO rows printed in the summary.
Definition emu_mmio.c:28
@ k_mmio_settle
Same-addr reads before a poll "settles".
Definition emu_mmio.c:27
@ k_u32_all_ones
All bits set (MMIO read toggle).
Definition emu_mmio.c:29
static uint32_t s_mmio_toggle
Definition emu_mmio.c:62
static RA8_INTERNAL int internal_mmio_index(uint64_t addr)
Find (or add) a slot for a distinct MMIO address; -1 if table full.
Definition emu_mmio.c:110
static bool s_mmio_written[k_mmio_slots]
Definition emu_mmio.c:56
static int s_mmio_run_slot
Slot of the current read run.
Definition emu_mmio.c:65
static RA8_INTERNAL void internal_bgc_track(uint32_t value)
Record a BG_BGC write; remember the value if it is a new colour.
Definition emu_mmio.c:86
uint64_t mmio_read(uc_engine *uc, uint64_t offset, unsigned size, void *user)
UC_MMIO read callback for the peripheral window.
Definition emu_mmio.c:129
static uint32_t s_mmio_writes
Definition emu_mmio.c:61
void emu_mmio_print_bgc_and_table(void)
Implementation of emu_mmio_print_bgc_and_table() – run-end report.
Definition emu_mmio.c:243
static uint64_t s_mmio_addr[k_mmio_slots]
Definition emu_mmio.c:54
static uint32_t s_mmio_run
Consecutive reads of that slot.
Definition emu_mmio.c:66
uint32_t emu_mmio_reads(void)
Implementation of emu_mmio_reads() – plain counter read.
Definition emu_mmio.c:222
Sparse MMIO model of the Renesas peripheral space.
@ k_periph_base
Peripheral window base.
Definition emu_mmio.h:47
@ k_bgc_track_max
Distinct BG_BGC values remembered.
Definition emu_mmio.h:58
@ k_glcdc_bg_bgc
GLCDC BG.BGC background colour.
Definition emu_mmio.h:57
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).