ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_dtc.c
Go to the documentation of this file.
1
50
51#include <stdint.h>
52#include <stdio.h>
53
54#include "board_console.h"
55#include "board_periph_block.h"
57
59typedef enum : uint32_t {
62
64typedef enum : uint64_t {
65 k_dtc_base = 0x4000AC00UL,
66 k_dtc_span = 0x40UL,
67 k_dtc_off_dtccr = 0x00UL,
69 k_dtc_off_dtcst = 0x0CUL,
72
74typedef enum : uint64_t {
75 k_elc_base = 0x40201000UL,
76 k_elc_span = 0x100UL,
79
81typedef enum : uint32_t {
85
87typedef enum : uint32_t {
94
111
113typedef enum : uint32_t {
116
118typedef enum : uint32_t {
120 k_dtc_ti_addr_mask = 0xFFFFFFFEU,
121 k_dtc_max_units = 0x10000U,
124
126typedef enum : uint32_t {
129
131typedef struct {
132 uint8_t dtccr;
133 uint32_t dtcvbr;
134 uint8_t dtcst;
135 uint16_t dtcsts;
136 uint32_t activations;
137 uint32_t bytes;
139
141
150static uint8_t s_elc[k_elc_span];
151
163RA8_INTERNAL static uint32_t internal_dtc_unit_bytes(uint32_t sz_code)
164{
165 if (sz_code == (uint32_t)k_mra_sz_word) {
166 return 4U;
167 }
168 if (sz_code == (uint32_t)k_mra_sz_half) {
169 return 2U;
170 }
171 return 1U;
172}
173
187RA8_INTERNAL static uint32_t internal_dtc_mem_read(uc_engine* uc, uint64_t addr, uint32_t bytes)
188{
189 uint32_t v = 0U;
190 (void)emu_mem_read(uc, addr, &v, (size_t)bytes);
191 return v;
192}
193
203typedef struct {
204 uint32_t sar;
205 uint32_t dar;
206 uint32_t cra;
207 uint32_t crb;
208 uint32_t md;
209 uint32_t sz;
210 uint32_t sm;
211 uint32_t dm;
212} dtc_ti_t;
213
228RA8_INTERNAL static void internal_dtc_decode_ti(uc_engine* uc, uint32_t ti_addr, dtc_ti_t* out)
229{
230 const uint32_t mr = internal_dtc_mem_read(uc, (uint64_t)ti_addr + (uint64_t)k_ti_off_mr, 4U);
231 out->sar = internal_dtc_mem_read(uc, (uint64_t)ti_addr + (uint64_t)k_ti_off_sar, 4U);
232 out->dar = internal_dtc_mem_read(uc, (uint64_t)ti_addr + (uint64_t)k_ti_off_dar, 4U);
233 out->crb = internal_dtc_mem_read(uc, (uint64_t)ti_addr + (uint64_t)k_ti_off_crb, 2U);
234 out->cra = internal_dtc_mem_read(uc, (uint64_t)ti_addr + (uint64_t)k_ti_off_cra, 2U);
235
236 const uint32_t mra = (mr >> (uint32_t)k_mr_mra_shift) & (uint32_t)k_mr_byte_mask;
237 const uint32_t mrb = (mr >> (uint32_t)k_mr_mrb_shift) & (uint32_t)k_mr_byte_mask;
238 out->md = (mra >> (uint32_t)k_mra_md_pos) & (uint32_t)k_mr_2bit_mask;
239 out->sz = (mra >> (uint32_t)k_mra_sz_pos) & (uint32_t)k_mr_2bit_mask;
240 out->sm = (mra >> (uint32_t)k_mra_sm_pos) & (uint32_t)k_mr_2bit_mask;
241 out->dm = (mrb >> (uint32_t)k_mrb_dm_pos) & (uint32_t)k_mr_2bit_mask;
242}
243
264{
265 uint32_t per_block = ti->cra;
266 uint32_t blocks = 1U;
267 if (ti->md == (uint32_t)k_mra_md_block) {
268 if (ti->cra == 0U) {
269 per_block = (uint32_t)k_dtc_block_cra_zero;
270 }
271 blocks = (ti->crb == 0U) ? 1U : ti->crb;
272 }
273 const uint32_t units = per_block * blocks;
274 return (units > (uint32_t)k_dtc_max_units) ? 0U : units;
275}
276
296RA8_INTERNAL static void
297internal_dtc_copy_units(uc_engine* uc, const dtc_ti_t* ti, uint32_t units, uint32_t unit)
298{
299 uint64_t local_addr = (uint64_t)ti->sar;
300 uint64_t d_addr = (uint64_t)ti->dar;
301 const uint64_t local_step = (ti->sm == (uint32_t)k_mr_addr_inc) ? (uint64_t)unit : 0U;
302 const uint64_t d_step = (ti->dm == (uint32_t)k_mr_addr_inc) ? (uint64_t)unit : 0U;
303 for (uint32_t i = 0U; i < units; ++i) {
304 uint8_t tmp[4] = {};
305 (void)emu_mem_read(uc, local_addr, tmp, (size_t)unit);
306 (void)emu_mem_write(uc, d_addr, tmp, (size_t)unit);
307 local_addr += local_step;
308 d_addr += d_step;
309 }
310}
311
322RA8_INTERNAL static void internal_dtc_run_transfer(uc_engine* uc, uint32_t ti_addr)
323{
324 dtc_ti_t ti = {};
325 internal_dtc_decode_ti(uc, ti_addr, &ti);
326
327 const uint32_t units = internal_dtc_unit_count(&ti);
328 if (units == 0U) {
329 return;
330 }
331 const uint32_t unit = internal_dtc_unit_bytes(ti.sz);
332 internal_dtc_copy_units(uc, &ti, units, unit);
333
334 s_dtc.activations++;
335 s_dtc.bytes = units * unit;
336 /* Console DMA tab: one line per DTC activation (the DTC shares the DMA tab
337 * with the DMAC). Reports the unit count and unit size. */
338 char ln[k_dtc_console_line_cap];
339 (void)snprintf(ln, sizeof(ln), "DTC %u units x %uB", (unsigned)units, (unsigned)unit);
341}
342
353{
354 if (s_dtc.dtcvbr == 0U) {
355 return; /* Vector base not programmed: nothing to activate. */
356 }
357 const uint32_t slot = board_periph_icu_dtc_slot((uint16_t)k_elc_swevt0_event);
358 if (slot >= (uint32_t)k_dtc_ielsr_slots) {
359 return; /* No IELSR slot links the event with DTCE set. */
360 }
361 const uint64_t vt_entry =
362 (uint64_t)s_dtc.dtcvbr + ((uint64_t)slot * (uint64_t)k_dtc_vt_entry_bytes);
363 const uint32_t ti_addr = internal_dtc_mem_read(uc, vt_entry, 4U) & (uint32_t)k_dtc_ti_addr_mask;
364 if (ti_addr == 0U) {
365 return; /* No TI registered for the slot. */
366 }
367 internal_dtc_run_transfer(uc, ti_addr);
368 /* Real silicon clears the slot's DTCE and asserts its CPU interrupt on the
369 * last transfer (HUM Figure 18.5 p 801). The demo gates on the destination
370 * settling (a direct poll), not the ISR, so the model performs the copy and
371 * leaves interrupt delivery to the firmware's own polling path -- pending the
372 * NVIC line here is unnecessary for the transfer contract and depends on the
373 * app's vector wiring. The transfer (the modelled behaviour) is what flips the
374 * demo to match=Y. */
375}
376
386{
387 s_dtc = (dtc_state_t){};
388 for (uint32_t i = 0U; i < (uint32_t)k_elc_span; ++i) {
389 s_elc[i] = 0U;
390 }
391}
392
406RA8_INTERNAL static uint64_t internal_dtc_read(uc_engine* uc, uint64_t addr, unsigned size)
407{
408 (void)uc;
409 (void)size;
410 const uint64_t off = addr - (uint64_t)k_dtc_base;
411 if (off == (uint64_t)k_dtc_off_dtccr) {
412 return s_dtc.dtccr;
413 }
414 if (off == (uint64_t)k_dtc_off_dtcvbr) {
415 return s_dtc.dtcvbr;
416 }
417 if (off == (uint64_t)k_dtc_off_dtcst) {
418 return s_dtc.dtcst;
419 }
420 if (off == (uint64_t)k_dtc_off_dtcsts) {
421 return s_dtc.dtcsts;
422 }
423 return 0U;
424}
425
438RA8_INTERNAL static void
439internal_dtc_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
440{
441 (void)uc;
442 (void)size;
443 const uint64_t off = addr - (uint64_t)k_dtc_base;
444 if (off == (uint64_t)k_dtc_off_dtccr) {
445 s_dtc.dtccr = (uint8_t)value;
446 } else if (off == (uint64_t)k_dtc_off_dtcvbr) {
447 s_dtc.dtcvbr = (uint32_t)value;
448 } else if (off == (uint64_t)k_dtc_off_dtcst) {
449 s_dtc.dtcst = (uint8_t)value;
450 } else if (off == (uint64_t)k_dtc_off_dtcsts) {
451 s_dtc.dtcsts = (uint16_t)value;
452 }
453}
454
468RA8_INTERNAL static uint64_t internal_elc_read(uc_engine* uc, uint64_t addr, unsigned size)
469{
470 (void)uc;
471 const uint64_t off = addr - (uint64_t)k_elc_base;
472 uint64_t v = 0U;
473 for (unsigned i = 0U; (i < size) && ((off + (uint64_t)i) < (uint64_t)k_elc_span); ++i) {
474 v |= (uint64_t)s_elc[off + (uint64_t)i] << (8U * i);
475 }
476 return v;
477}
478
491RA8_INTERNAL static void
492internal_elc_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
493{
494 const uint64_t off = addr - (uint64_t)k_elc_base;
495 for (unsigned i = 0U; (i < size) && ((off + (uint64_t)i) < (uint64_t)k_elc_span); ++i) {
496 s_elc[off + (uint64_t)i] = (uint8_t)(value >> (8U * i));
497 }
498 /* The third write of the unlock/arm/trigger sequence (SEG=1) on ELSEGR0 fires
499 * software event 0, which activates the DTC if a DTCE-enabled slot links it. */
500 if ((off == (uint64_t)k_elc_off_elsegr0) &&
501 (((uint8_t)value & (uint8_t)k_elc_elsegr_trigger) == (uint8_t)k_elc_elsegr_trigger)) {
503 }
504}
505
515{
516 if (s_dtc.activations == 0U) {
517 return; /* Untouched: stay quiet. */
518 }
519 (void)priv_emu_io_errf(" DTC : activations=%u last_bytes=%u DTCVBR=0x%08X\n",
520 s_dtc.activations,
521 s_dtc.bytes,
522 s_dtc.dtcvbr);
523}
524
527 .base = (uint64_t)k_dtc_base,
528 .span = (uint64_t)k_dtc_span,
529 .order = (uint32_t)k_dtc_block_order,
530 .read = internal_dtc_read,
532 .tick = nullptr,
533 .reset = internal_dtc_reset,
534 .report = internal_dtc_report,
535 .name = "DTC",
536};
537
540 .base = (uint64_t)k_elc_base,
541 .span = (uint64_t)k_elc_span,
542 .order = (uint32_t)k_dtc_block_order,
543 .read = internal_elc_read,
545 .tick = nullptr,
546 .reset = nullptr,
547 .report = nullptr,
548 .name = "ELC/DTC-trig",
549};
550
Multi-channel console log store backing the board view's tabbed console.
void board_console_push(board_console_ch_t ch, const char *line)
Append one completed line to a channel ring and the ALL ring.
@ k_board_console_ch_dma
DMAC + DTC transfer-complete summaries.
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.
uint32_t board_periph_icu_dtc_slot(uint16_t event)
Find the IELSR slot that activates the DTC for an ELC event.
@ k_mrb_dm_pos
MRB.DM[3:2] dest-address mode.
@ k_mra_md_pos
MRA.MD[7:6] transfer mode.
@ k_mra_sz_half
SZ = 01b: 16-bit unit.
@ k_mra_sz_word
SZ = 10b: 32-bit unit.
@ k_mra_sm_pos
MRA.SM[3:2] source-address mode.
@ k_mr_mra_shift
MRA occupies MR[31:24].
@ k_mr_byte_mask
Eight-bit field mask (one MRA/MRB byte).
@ k_mra_md_block
MD = 10b: block transfer.
@ k_mr_addr_inc
SM / DM = 10b: increment the address.
@ k_mra_sz_byte
SZ = 00b: 8-bit unit.
@ k_mr_mrb_shift
MRB occupies MR[23:16].
@ k_mr_2bit_mask
Two-bit field mask.
@ k_mra_sz_pos
MRA.SZ[5:4] unit width.
static RA8_INTERNAL uint32_t internal_dtc_mem_read(uc_engine *uc, uint64_t addr, uint32_t bytes)
Read a little-endian word of bytes from emulated memory.
static const board_periph_block_t s_k_dtc_block
DTC control-window descriptor (owns reset + report).
static RA8_INTERNAL uint64_t internal_elc_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the ELC window (transparent byte shadow).
static RA8_INTERNAL void internal_dtc_block_register(void)
Register the DTC control + ELC ELSEGR windows before main runs.
static RA8_INTERNAL void internal_elc_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the ELC window: byte shadow + ELSEGR0 trigger.
static RA8_INTERNAL void internal_dtc_run_transfer(uc_engine *uc, uint32_t ti_addr)
Perform the SAR->DAR copy described by one TI block.
@ k_dtc_max_units
Sanity cap on a single activation.
@ k_dtc_vt_entry_bytes
One 4-byte pointer per IELSR slot.
@ k_dtc_ti_addr_mask
Strip bit0 (privilege attribute).
@ k_dtc_ielsr_slots
Number of IELSR slots (0..95).
dtc_console_t
Console-tap line buffer capacity for a DTC transfer summary.
@ k_dtc_console_line_cap
Max chars in a "DTC .. units" line.
static RA8_INTERNAL uint32_t internal_dtc_unit_count(const dtc_ti_t *ti)
Total unit count a descriptor moves, or 0 if it is out of range.
static RA8_INTERNAL void internal_dtc_decode_ti(uc_engine *uc, uint32_t ti_addr, dtc_ti_t *out)
Read and decode the TI block at ti_addr into out.
static RA8_INTERNAL void internal_dtc_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the DTC control window.
dtc_ti_off_t
DTC TI block field offsets (r_dtc_xfer_info_t, HUM Figure 18.4).
@ k_ti_off_sar
SAR source address.
@ k_ti_off_dar
DAR destination address.
@ k_ti_off_cra
CRA transfer count (16b).
@ k_ti_off_crb
CRB block count (16b).
@ k_ti_off_mr
MR mode word ([31:24] MRA, [23:16] MRB).
static RA8_INTERNAL uint32_t internal_dtc_unit_bytes(uint32_t sz_code)
Bytes per unit for an MRA.SZ code (defaults to byte width).
@ k_dtc_block_cra_zero
Block mode CRA = 0 encodes a 256-unit block.
static dtc_state_t s_dtc
static uint8_t s_elc[k_elc_span]
ELC register-window shadow (ELCR / ELSEGR / ELSR).
static RA8_INTERNAL void internal_dtc_activate_swevt0(uc_engine *uc)
Run the DTC for ELC software event 0 if a DTCE slot links it.
static RA8_INTERNAL void internal_dtc_copy_units(uc_engine *uc, const dtc_ti_t *ti, uint32_t units, uint32_t unit)
Copy units units of unit bytes from SAR to DAR.
static RA8_INTERNAL void internal_dtc_reset(void)
Reset the DTC model (and the ELC shadow) to power-on state.
elc_swevt_t
ELC software event 0 number and its ELSEGR trigger value.
@ k_elc_swevt0_event
ELC_SWEVT0 event (HUM Table 19.3).
@ k_elc_elsegr_trigger
WE=1, SEG=1: fires the software event.
static RA8_INTERNAL void internal_dtc_report(void)
End-of-run DTC section: activations and bytes moved.
dtc_geom_t
DTC control-window geometry (ra8_dtc_regs.h, r_dtc_regs_t).
@ k_dtc_off_dtcst
DTCST module start (8b).
@ k_dtc_base
DTC control-register base.
@ k_dtc_off_dtcvbr
DTCVBR vector base (32b).
@ k_dtc_off_dtcsts
DTCSTS status (16b).
@ k_dtc_span
Covers DTCCR..DTCSQE comfortably.
@ k_dtc_off_dtccr
DTCCR control (8b).
dtc_order_t
Per-tick order slot for the DTC block (relative order only).
@ k_dtc_block_order
Just after the DMAC block; report order.
elc_geom_t
ELC ELSEGR-window geometry (ra8_elc_regs.h).
@ k_elc_off_elsegr0
ELSEGR0 software-event gen (8b).
@ k_elc_span
Covers ELCR / ELSEGR[0..3] / ELSR.
@ k_elc_base
R_ELC base.
static RA8_INTERNAL uint64_t internal_dtc_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the DTC control window.
static const board_periph_block_t s_k_elc_block
ELC ELSEGR-window descriptor (the DTC activation trigger).
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.
uc_err emu_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t count)
Read guest memory through the central access seam.
uc_err emu_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t count)
Write guest memory through the central access seam.
dtc_mr_field_t
DTC Transfer-Information mode-bit field values.
Definition main.c:114
dtc_vt_geom_t
DTC vector-table geometry.
Definition main.c:160
dtc_count_t
DTC count-register values for one 256-word block.
Definition main.c:145
-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.
DTC control-register state plus an activation/byte tally.
uint16_t dtcsts
DTCSTS status shadow.
uint8_t dtccr
DTCCR shadow.
uint32_t activations
Successful DTC activations performed.
uint32_t dtcvbr
DTCVBR vector-table base address.
uint32_t bytes
Bytes moved on the last activation.
uint8_t dtcst
DTCST module-start shadow.
One Transfer Information block, decoded from emulated memory.
uint32_t cra
CRA transfer count (block size in block mode).
uint32_t sar
SAR source address.
uint32_t md
MRA.MD transfer mode (normal / repeat / block).
uint32_t crb
CRB block count.
uint32_t dar
DAR destination address.
uint32_t dm
MRB.DM destination address mode.
uint32_t sm
MRA.SM source address mode.
uint32_t sz
MRA.SZ unit-width code (see internal_dtc_unit_bytes).