ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_dmac.c
Go to the documentation of this file.
1
43
44#include <stdint.h>
45#include <stdio.h>
46
47#include "board_console.h"
48#include "board_periph_block.h"
50
52typedef enum : uint32_t {
55
57typedef enum : uint32_t { k_u16_mask = 0xFFFFU } dmac_lit_t;
58
70typedef enum : uint32_t {
73
75typedef enum : uint64_t {
76 k_dmac_base = 0x4000A000UL,
77 k_dmac_stride = 0x40UL,
79 k_dmac_span = 0x40UL * 8UL,
81
83typedef enum : uint64_t {
84 k_dma_shared_base = 0x4000A800UL,
87
89typedef enum : uint64_t {
101} dmac_off_t;
102
104typedef enum : uint8_t {
109} dmac_bit_t;
110
122
124typedef enum : uint32_t {
129} dmac_am_t;
130
132typedef enum : uint32_t {
135
137typedef enum : uint32_t {
138 k_dmac_max_units = 0x10000U,
140
151typedef enum : uint16_t {
154
156typedef struct {
157 uint32_t dmsar;
158 uint32_t dmdar;
159 uint32_t dmcra;
160 uint32_t dmcrb;
161 uint32_t dmofr;
162 uint16_t dmtmd;
163 uint16_t dmamd;
164 uint8_t dmint;
165 uint8_t dmcnt;
166 uint8_t dmsts;
167 uint32_t copies;
168 uint32_t units;
170
172
175
176/* =============================================================================
177 * DMAC channel transfer primitive (the part that actually moves memory).
178 * =============================================================================
179 */
180
192RA8_INTERNAL static uint32_t internal_dmac_unit_bytes(uint32_t sz_code)
193{
194 if (sz_code == (uint32_t)k_dmac_sz_word) {
195 return (uint32_t)k_dmac_unit_word;
196 }
197 if (sz_code == (uint32_t)k_dmac_sz_half) {
198 return (uint32_t)k_dmac_unit_half;
199 }
200 return (uint32_t)k_dmac_unit_byte;
201}
202
215{
216 uint32_t count = c->dmcra & (uint32_t)k_dmac_dmcra_low_mask;
217 if (count == 0U) {
218 count = (uint32_t)k_dmac_dmcra_low_mask + 1U; /* DMCRAL==0 means 1024 units */
219 }
220 uint32_t blocks = c->dmcrb & (uint32_t)k_u16_mask;
221 if (blocks == 0U) {
222 blocks = 1U; /* normal mode: a single block of `count` units */
223 }
224 uint64_t total = (uint64_t)count * (uint64_t)blocks;
225 if (total > (uint64_t)k_dmac_max_units) {
226 total = (uint64_t)k_dmac_max_units;
227 }
228 return (uint32_t)total;
229}
230
243RA8_INTERNAL static void
244internal_dmac_copy_units(uc_engine* uc, dmac_chan_t* c, uint32_t units, uint32_t unit)
245{
246 uint64_t src = (uint64_t)c->dmsar;
247 uint64_t dst = (uint64_t)c->dmdar;
248 const uint32_t sm = (c->dmamd >> (uint32_t)k_dmac_sm_pos) & (uint32_t)k_dmac_am_mask;
249 const uint32_t dm = (c->dmamd >> (uint32_t)k_dmac_dm_pos) & (uint32_t)k_dmac_am_mask;
250 const bool src_inc = (sm == (uint32_t)k_dmac_am_incr);
251 const bool dst_inc = (dm == (uint32_t)k_dmac_am_incr);
252 uint8_t word[k_dmac_unit_word] = {};
253 for (uint32_t i = 0U; i < units; i++) {
254 if (emu_mem_read(uc, src, word, unit) != UC_ERR_OK) {
255 break;
256 }
257 if (emu_mem_write(uc, dst, word, unit) != UC_ERR_OK) {
258 break;
259 }
260 if (src_inc) {
261 src += unit;
262 }
263 if (dst_inc) {
264 dst += unit;
265 }
266 c->units++;
267 }
268 c->dmsar = (uint32_t)src;
269 c->dmdar = (uint32_t)dst;
270}
271
282RA8_INTERNAL static void internal_dmac_run_transfer(uc_engine* uc, uint32_t ch)
283{
284 dmac_chan_t* c = &s_dmac[ch];
285 if ((c->dmcnt & (uint8_t)k_dmac_dte_mask) == 0U) {
286 return; /* channel disarmed: SWREQ does nothing */
287 }
288 const uint32_t sz = (c->dmtmd >> (uint32_t)k_dmac_sz_pos) & (uint32_t)k_dmac_sz_mask;
289 const uint32_t unit = internal_dmac_unit_bytes(sz);
290 const uint32_t units = internal_dmac_total_units(c);
291 c->units = 0U;
292 internal_dmac_copy_units(uc, c, units, unit);
293 c->dmcra &= ~(uint32_t)k_dmac_dmcra_low_mask; /* count drained to zero */
294 c->dmsts &= (uint8_t)~k_dmac_act_mask; /* synchronous: idle after copy */
295 c->dmsts |= (uint8_t)k_dmac_dtif_mask; /* transfer-end status latched */
296 c->copies++;
297 /* Console DMA tab: one line per completed DMAC transfer (not per unit). */
299 (void)snprintf(ln,
300 sizeof(ln),
301 "DMAC%u %u units x %uB",
302 (unsigned)ch,
303 (unsigned)c->units,
304 (unsigned)unit);
306 if (board_periph_trace()) {
307 (void)
308 priv_emu_io_errf(" DMAC%u : copied %u units x %uB (SWREQ)\n", ch, c->units, unit);
309 }
310 if ((c->dmint & (uint8_t)k_dmac_dtif_mask) != 0U) {
311 board_periph_icu_raise_event(uc, (uint16_t)((uint16_t)k_dmac_event_ch0 + (uint16_t)ch));
312 }
313}
314
315/* =============================================================================
316 * DMAC0 per-channel MMIO.
317 * =============================================================================
318 */
319
332RA8_INTERNAL static uint64_t internal_dmac_reg_read(uint32_t ch, uint64_t off)
333{
334 const dmac_chan_t* c = &s_dmac[ch];
335 switch (off) {
336 case (uint64_t)k_dmac_off_dmsar:
337 return c->dmsar;
338 case (uint64_t)k_dmac_off_dmdar:
339 return c->dmdar;
340 case (uint64_t)k_dmac_off_dmcra:
341 return c->dmcra;
342 case (uint64_t)k_dmac_off_dmcrb:
343 return c->dmcrb;
344 case (uint64_t)k_dmac_off_dmtmd:
345 return c->dmtmd;
346 case (uint64_t)k_dmac_off_dmamd:
347 return c->dmamd;
348 case (uint64_t)k_dmac_off_dmofr:
349 return c->dmofr;
350 case (uint64_t)k_dmac_off_dmint:
351 return c->dmint;
352 case (uint64_t)k_dmac_off_dmcnt:
353 return c->dmcnt;
354 case (uint64_t)k_dmac_off_dmsts:
355 return c->dmsts;
356 default:
357 return 0U;
358 }
359}
360
372RA8_INTERNAL static void internal_dmac_reg_store(dmac_chan_t* c, uint64_t off, uint32_t value)
373{
374 switch (off) {
375 case (uint64_t)k_dmac_off_dmsar:
376 c->dmsar = value;
377 break;
378 case (uint64_t)k_dmac_off_dmdar:
379 c->dmdar = value;
380 break;
381 case (uint64_t)k_dmac_off_dmcra:
382 c->dmcra = value;
383 break;
384 case (uint64_t)k_dmac_off_dmcrb:
385 c->dmcrb = value;
386 break;
387 case (uint64_t)k_dmac_off_dmtmd:
388 c->dmtmd = (uint16_t)value;
389 break;
390 case (uint64_t)k_dmac_off_dmamd:
391 c->dmamd = (uint16_t)value;
392 break;
393 case (uint64_t)k_dmac_off_dmofr:
394 c->dmofr = value;
395 break;
396 case (uint64_t)k_dmac_off_dmint:
397 c->dmint = (uint8_t)value;
398 break;
399 case (uint64_t)k_dmac_off_dmcnt:
400 c->dmcnt = (uint8_t)value;
401 break;
402 case (uint64_t)k_dmac_off_dmsts:
403 /* ESIF/DTIF are write-0-to-clear; keep only bits still set. */
404 c->dmsts &= (uint8_t)value;
405 break;
406 default:
407 break;
408 }
409}
410
423RA8_INTERNAL static void
424internal_dmac_reg_write(uc_engine* uc, uint32_t ch, uint64_t off, uint32_t value)
425{
426 if (off == (uint64_t)k_dmac_off_dmreq) {
427 if ((value & (uint32_t)k_dmac_swreq_mask) != 0U) {
428 internal_dmac_run_transfer(uc, ch); /* SWREQ auto-clears once accepted */
429 }
430 return;
431 }
432 internal_dmac_reg_store(&s_dmac[ch], off, value);
433}
434
448RA8_INTERNAL static uint64_t internal_dmac_read(uc_engine* uc, uint64_t addr, unsigned size)
449{
450 (void)uc;
451 (void)size;
452 const uint32_t ch = (uint32_t)((addr - (uint64_t)k_dmac_base) / (uint64_t)k_dmac_stride);
453 return internal_dmac_reg_read(ch, (addr - (uint64_t)k_dmac_base) % (uint64_t)k_dmac_stride);
454}
455
468RA8_INTERNAL static void
469internal_dmac_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
470{
471 (void)size;
472 const uint32_t ch = (uint32_t)((addr - (uint64_t)k_dmac_base) / (uint64_t)k_dmac_stride);
474 ch,
475 (addr - (uint64_t)k_dmac_base) % (uint64_t)k_dmac_stride,
476 (uint32_t)value);
477}
478
479/* =============================================================================
480 * Shared DMA module-control bank (transparent shadow).
481 * =============================================================================
482 */
483
498RA8_INTERNAL static uint64_t
499internal_shadow_read(const uint8_t* shadow, uint64_t span, uint64_t off, unsigned size)
500{
501 uint64_t v = 0U;
502 for (unsigned i = 0U; i < size; i++) {
503 const uint64_t b = off + (uint64_t)i;
504 if (b < span) {
505 v |= (uint64_t)shadow[b] << (8U * i);
506 }
507 }
508 return v;
509}
510
524RA8_INTERNAL static void
525internal_shadow_write(uint8_t* shadow, uint64_t span, uint64_t off, unsigned size, uint64_t value)
526{
527 for (unsigned i = 0U; i < size; i++) {
528 const uint64_t b = off + (uint64_t)i;
529 if (b < span) {
530 shadow[b] = (uint8_t)(value >> (8U * i));
531 }
532 }
533}
534
548RA8_INTERNAL static uint64_t internal_dma_shared_read(uc_engine* uc, uint64_t addr, unsigned size)
549{
550 (void)uc;
552 (uint64_t)k_dma_shared_span,
553 addr - (uint64_t)k_dma_shared_base,
554 size);
555}
556
569RA8_INTERNAL static void
570internal_dma_shared_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
571{
572 (void)uc;
574 (uint64_t)k_dma_shared_span,
575 addr - (uint64_t)k_dma_shared_base,
576 size,
577 value);
578}
579
580/* =============================================================================
581 * Reset / report (carried by the DMAC channel descriptor).
582 * =============================================================================
583 */
584
594{
595 for (uint32_t i = 0U; i < (uint32_t)k_dmac_count; i++) {
596 s_dmac[i] = (dmac_chan_t){};
597 }
598 for (uint32_t i = 0U; i < (uint32_t)k_dma_shared_span; i++) {
599 s_dma_shared[i] = 0U;
600 }
601}
602
612{
613 for (uint32_t ch = 0U; ch < (uint32_t)k_dmac_count; ch++) {
614 if (s_dmac[ch].copies > 0U) {
615 (void)priv_emu_io_errf(" DMAC%u : transfers=%u last_units=%u DMSTS=0x%02X\n",
616 ch,
617 s_dmac[ch].copies,
618 s_dmac[ch].units,
619 s_dmac[ch].dmsts);
620 }
621 }
622}
623
624/* The DMAC channel descriptor carries reset + report covering every DMAC
625 * window; the shared-bank and DTC descriptors only own their register windows.
626 * Registration order between them is irrelevant (dispatch is by disjoint
627 * range). */
628
631 .base = (uint64_t)k_dmac_base,
632 .span = (uint64_t)k_dmac_span,
633 .order = (uint32_t)k_dmac_block_order,
634 .read = internal_dmac_read,
636 .tick = nullptr,
637 .reset = internal_dmac_reset,
638 .report = internal_dmac_report,
639 .name = "DMAC",
640};
641
644 .base = (uint64_t)k_dma_shared_base,
645 .span = (uint64_t)k_dma_shared_span,
646 .order = (uint32_t)k_dmac_block_order,
649 .tick = nullptr,
650 .reset = nullptr,
651 .report = nullptr,
652 .name = "DMA",
653};
654
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.
@ k_u16_mask
16-bit field.
Definition board_net.c:70
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 uint8_t s_dma_shared[k_dma_shared_span]
Shared DMA module-control bank shadow (DMAST / DMCTL / DELSR).
dmac_am_t
DMAMD address-update fields (ra8_dmac_regs.h).
@ k_dmac_dm_pos
DMAMD.DM destination-update position.
@ k_dmac_am_incr
10b: increment after each unit.
@ k_dmac_am_mask
Address-update field width (2 bits).
@ k_dmac_sm_pos
DMAMD.SM source-update position.
static RA8_INTERNAL void internal_dmac_report(void)
Print one line per DMAC channel that completed any transfer.
static RA8_INTERNAL uint64_t internal_dmac_reg_read(uint32_t ch, uint64_t off)
Dispatch a DMAC channel register read for channel ch.
static RA8_INTERNAL void internal_dmac_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the DMAC0 channel window.
static RA8_INTERNAL void internal_board_periph_dmac_register(void)
Self-register the DMAC0 / shared module-control windows before main.
static RA8_INTERNAL void internal_dma_shared_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the shared DMA module-control bank.
dmac_console_t
Console-tap line buffer capacity for a DMAC transfer summary.
@ k_dmac_console_line_cap
Max chars in a "DMAC.. .. units" line.
dmac_dmcra_t
DMCRA low half holds the running transfer count (bits 0..9).
@ k_dmac_dmcra_low_mask
DMCRA.DMCRAL count field.
dmac_bit_t
DMCNT / DMREQ / DMSTS bit masks (ra8_dmac_regs.h).
@ k_dmac_swreq_mask
DMREQ.SWREQ software request.
@ k_dmac_dte_mask
DMCNT.DTE channel-enable.
@ k_dmac_act_mask
DMSTS.ACT active flag.
@ k_dmac_dtif_mask
DMSTS.DTIF transfer-end flag.
static RA8_INTERNAL uint64_t internal_dma_shared_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the shared DMA module-control bank.
static RA8_INTERNAL void internal_shadow_write(uint8_t *shadow, uint64_t span, uint64_t off, unsigned size, uint64_t value)
Write size bytes little-endian into a byte-array shadow.
dmac_off_t
Per-channel register byte offsets (subset the driver touches).
@ k_dmac_off_dmint
Interrupt setting (DTIE et al.).
@ k_dmac_off_dmreq
Software start (SWREQ / CLRS).
@ k_dmac_off_dmsts
Status (ESIF / DTIF / ACT).
@ k_dmac_off_dmdar
Destination address (32-bit).
@ k_dmac_off_dmamd
Address mode (SM / DM).
@ k_dmac_off_dmcnt
Transfer enable (DTE bit0).
@ k_dmac_off_dmofr
Offset register.
@ k_dmac_off_dmcrb
Block transfer count.
@ k_dmac_off_dmsar
Source address (32-bit).
@ k_dmac_off_dmtmd
Transfer mode (SZ / MD / DTS).
@ k_dmac_off_dmcra
Transfer count (low/high halves).
static RA8_INTERNAL void internal_dmac_copy_units(uc_engine *uc, dmac_chan_t *c, uint32_t units, uint32_t unit)
Copy units of unit bytes src->dst in emulated memory.
static RA8_INTERNAL void internal_dmac_run_transfer(uc_engine *uc, uint32_t ch)
Run one software-triggered transfer on channel ch.
static const board_periph_block_t s_k_dmac_block
DMAC0 channel window + the combined reset / report.
dmac_event_t
DMAC0 transfer-end ELC event the model emits (FSP ra8d2 bsp_elc).
@ k_dmac_event_ch0
DMAC0 channel-0 transfer-end event.
static RA8_INTERNAL uint64_t internal_dmac_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the DMAC0 channel window.
static RA8_INTERNAL uint32_t internal_dmac_total_units(const dmac_chan_t *c)
Total units one software trigger transfers for channel c.
dmac_order_t
Per-tick order slot for the DMAC / DTC windows.
@ k_dmac_block_order
DMAC / DMA / DTC reset+report order slot.
dmac_sz_t
DMTMD.SZ transfer-width field (bits 8..9) and unit sizes.
@ k_dmac_sz_pos
DMTMD.SZ field position.
@ k_dmac_sz_half
01b: 16-bit unit.
@ k_dmac_unit_half
Bytes moved per half-width unit.
@ k_dmac_sz_byte
00b: 8-bit unit.
@ k_dmac_sz_word
10b: 32-bit unit.
@ k_dmac_unit_byte
Bytes moved per byte-width unit.
@ k_dmac_unit_word
Bytes moved per word-width unit.
@ k_dmac_sz_mask
DMTMD.SZ field width (2 bits).
static RA8_INTERNAL uint32_t internal_dmac_unit_bytes(uint32_t sz_code)
Bytes moved per unit for a DMTMD.SZ code (defaults to byte width).
dmac_geom_t
DMAC0 per-channel window geometry (ra8_dmac_regs.h).
@ k_dmac_span
DMAC span.
@ k_dmac_count
DMAC0 channel 0..7.
@ k_dmac_stride
Bytes per DMAC channel.
@ k_dmac_base
DMAC0 channel 0 base.
static RA8_INTERNAL uint64_t internal_shadow_read(const uint8_t *shadow, uint64_t span, uint64_t off, unsigned size)
Read size bytes little-endian from a byte-array shadow.
static const board_periph_block_t s_k_dma_shared_block
Shared DMA module-control bank window (transparent shadow).
dmac_lit_t
Field mask for the DMAC block-count register.
static RA8_INTERNAL void internal_dmac_reg_store(dmac_chan_t *c, uint64_t off, uint32_t value)
Apply a DMAC channel register write (non-trigger registers).
static dmac_chan_t s_dmac[k_dmac_count]
static RA8_INTERNAL void internal_dmac_reset(void)
Clear all DMAC channel state plus the shared module-control shadow.
dma_shared_geom_t
Shared DMA module-control bank geometry (R_DMA at 0x4000A800).
@ k_dma_shared_span
FSP R_DMA_Type size.
@ k_dma_shared_base
DMAST / DMCTL / DMECHR / DELSR.
static RA8_INTERNAL void internal_dmac_reg_write(uc_engine *uc, uint32_t ch, uint64_t off, uint32_t value)
Dispatch a DMAC channel register write; DMREQ.SWREQ triggers a copy.
dmac_limit_t
A bounded ceiling on units copied per trigger (safety net).
@ k_dmac_max_units
Largest one-trigger unit 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.
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.
-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 DMAC channel's modelled register shadow.
uint32_t units
Units moved by the last copy.
uint32_t copies
Completed transfers (report).
uint8_t dmsts
DMSTS status flags.
uint32_t dmsar
DMSAR source address.
uint16_t dmtmd
DMTMD transfer mode.
uint16_t dmamd
DMAMD address mode.
uint8_t dmcnt
DMCNT transfer enable.
uint32_t dmcra
DMCRA transfer count.
uint8_t dmint
DMINT interrupt setting.
uint32_t dmdar
DMDAR destination address.
uint32_t dmcrb
DMCRB block transfer count.
uint32_t dmofr
DMOFR offset register.