ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_ipc.c
Go to the documentation of this file.
1
49
50#include <stdint.h>
51#include <stdio.h>
52
53#include "board_periph_block.h"
55
64typedef enum : uint32_t {
67
69typedef enum : uint64_t {
70 k_ipc_base = 0x40020000UL,
71 k_ipc_span = 0x140UL,
73
75typedef enum : uint64_t {
76 k_ipc_ch0_off = 0xC0UL,
77 k_ipc_ch_stride = 0x20UL,
78 k_ipc_reg_sta = 0x00UL,
79 k_ipc_reg_iset = 0x04UL,
80 k_ipc_reg_txd = 0x08UL,
81 k_ipc_reg_rxd = 0x0CUL,
82 k_ipc_reg_clr = 0x10UL,
84
86typedef enum : uint32_t {
91
93typedef enum : uint32_t {
94 k_ipc_sta_irq_mask = 0x000000FFU,
95 k_ipc_sta_rdy = 0x00010000U,
96 k_ipc_sta_full = 0x00020000U,
97 k_ipc_sta_rerr = 0x01000000U,
98 k_ipc_sta_ferr = 0x02000000U,
99 k_ipc_clr_rst = 0x00010000U,
100 k_ipc_clr_rclr = 0x01000000U,
101 k_ipc_clr_fclr = 0x02000000U,
105
115typedef struct {
117 uint32_t rd;
118 uint32_t count;
119 bool rerr;
120 bool ferr;
121} ipc_fifo_t;
122
131typedef struct {
132 uint32_t sta[k_ipc_ch_count];
134 uint32_t sends;
135 uint32_t wakes;
136 uint32_t pushes;
137 uint32_t pops;
139
146
147/* =============================================================================
148 * Channel / register decode.
149 * =============================================================================
150 */
151
172RA8_INTERNAL static bool internal_ipc_decode(uint64_t off, uint32_t* ch, uint32_t* reg)
173{
174 if (off < (uint64_t)k_ipc_ch0_off) {
175 return false;
176 }
177 const uint64_t rel = off - (uint64_t)k_ipc_ch0_off;
178 const uint32_t channel = (uint32_t)(rel / (uint64_t)k_ipc_ch_stride);
179 if (channel >= (uint32_t)k_ipc_ch_count) {
180 return false;
181 }
182 *ch = channel;
183 *reg = (uint32_t)(rel % (uint64_t)k_ipc_ch_stride);
184 return true;
185}
186
187/* =============================================================================
188 * MMIO read / write.
189 * =============================================================================
190 */
191
210RA8_INTERNAL static uint32_t internal_ipc_sta_value(uint32_t ch)
211{
212 const ipc_fifo_t* f = &s_ipc.fifo[ch];
213 uint32_t sta = s_ipc.sta[ch];
214 if (f->count > 0U) {
215 sta |= (uint32_t)k_ipc_sta_rdy;
216 }
217 if (f->count >= (uint32_t)k_ipc_fifo_depth) {
218 sta |= (uint32_t)k_ipc_sta_full;
219 }
220 if (f->rerr) {
221 sta |= (uint32_t)k_ipc_sta_rerr;
222 }
223 if (f->ferr) {
224 sta |= (uint32_t)k_ipc_sta_ferr;
225 }
226 return sta;
227}
228
246RA8_INTERNAL static uint32_t internal_ipc_fifo_pop(uint32_t ch)
247{
248 ipc_fifo_t* f = &s_ipc.fifo[ch];
249 if (f->count == 0U) {
250 f->rerr = true; /* read-while-empty: latch RERR, data undefined (0). */
251 return 0U;
252 }
253 const uint32_t w = f->word[f->rd];
254 f->rd = (f->rd + 1U) % (uint32_t)k_ipc_fifo_depth;
255 f->count--;
256 s_ipc.pops++;
257 return w;
258}
259
273RA8_INTERNAL static uint64_t internal_ipc_read(uc_engine* uc, uint64_t addr, unsigned size)
274{
275 (void)uc;
276 (void)size;
277 const uint64_t off = addr - (uint64_t)k_ipc_base;
278 uint32_t ch = 0U;
279 uint32_t reg = 0U;
280 if (internal_ipc_decode(off, &ch, &reg)) {
281 if (reg == (uint32_t)k_ipc_reg_sta) {
282 return (uint64_t)internal_ipc_sta_value(ch);
283 }
284 if (reg == (uint32_t)k_ipc_reg_rxd) {
285 return (uint64_t)internal_ipc_fifo_pop(ch);
286 }
287 }
288 /* Semaphores and the NMI window are not modelled: read as 0. */
289 return 0U;
290}
291
310RA8_INTERNAL static void internal_ipc_fifo_push(uint32_t ch, uint32_t value)
311{
312 ipc_fifo_t* f = &s_ipc.fifo[ch];
313 if (f->count >= (uint32_t)k_ipc_fifo_depth) {
314 f->ferr = true; /* write-while-full: latch FERR, drop the word. */
315 return;
316 }
317 f->word[(f->rd + f->count) % (uint32_t)k_ipc_fifo_depth] = value;
318 f->count++;
319 s_ipc.pushes++;
320}
321
339RA8_INTERNAL static void internal_ipc_clr_write(uint32_t ch, uint32_t value)
340{
341 s_ipc.sta[ch] &= ~(value & (uint32_t)k_ipc_sta_irq_mask);
342 ipc_fifo_t* f = &s_ipc.fifo[ch];
343 if ((value & (uint32_t)k_ipc_clr_rst) != 0U) {
344 f->rd = 0U;
345 f->count = 0U; /* FIFO reset also drops RDY + FULL (they are computed). */
346 }
347 if ((value & (uint32_t)k_ipc_clr_rclr) != 0U) {
348 f->rerr = false;
349 }
350 if ((value & (uint32_t)k_ipc_clr_fclr) != 0U) {
351 f->ferr = false;
352 }
353}
354
367RA8_INTERNAL static void
368internal_ipc_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
369{
370 (void)size;
371 const uint64_t off = addr - (uint64_t)k_ipc_base;
372 uint32_t ch = 0U;
373 uint32_t reg = 0U;
374 if (!internal_ipc_decode(off, &ch, &reg)) {
375 return; /* semaphore / NMI window write: benign no-op */
376 }
377 if (reg == (uint32_t)k_ipc_reg_iset) {
378 const uint32_t bits = (uint32_t)value & (uint32_t)k_ipc_sta_irq_mask;
379 if (bits == 0U) {
380 return;
381 }
382 s_ipc.sta[ch] |= bits;
383 s_ipc.sends++;
384 /* Raise the receiving core's ELC event: the core's ICU walks its IELSR
385 * shadow, pends the mapped NVIC line, and the M85 takes it on its next
386 * instruction boundary (cpu1 never drains, so an IPC1 raise is inert). */
387 const uint16_t event =
388 (ch < (uint32_t)k_ipc_unit_split) ? (uint16_t)k_ipc0_irq_event : (uint16_t)k_ipc1_irq_event;
390 s_ipc.wakes++;
391 if (board_periph_trace()) {
392 (void)priv_emu_io_errf(" IPC : ch%u ISET 0x%02X -> raise 0x%03X\n",
393 ch,
394 bits,
395 (unsigned)event);
396 }
397 return;
398 }
399 if (reg == (uint32_t)k_ipc_reg_txd) {
400 internal_ipc_fifo_push(ch, (uint32_t)value);
401 return;
402 }
403 if (reg == (uint32_t)k_ipc_reg_clr) {
404 internal_ipc_clr_write(ch, (uint32_t)value);
405 }
406}
407
408/* =============================================================================
409 * Reset / report.
410 * =============================================================================
411 */
412
422{
423 s_ipc = (ipc_state_t){};
424}
425
435{
436 if ((s_ipc.sends == 0U) && (s_ipc.wakes == 0U) && (s_ipc.pushes == 0U) && (s_ipc.pops == 0U)) {
437 return;
438 }
439 (void)priv_emu_io_errf(" IPC : sends=%u wakes=%u fifo pushes=%u pops=%u\n",
440 s_ipc.sends,
441 s_ipc.wakes,
442 s_ipc.pushes,
443 s_ipc.pops);
444}
445
448 .base = (uint64_t)k_ipc_base,
449 .span = (uint64_t)k_ipc_span,
450 .order = (uint32_t)k_ipc_block_order,
451 .read = internal_ipc_read,
453 .tick = nullptr,
454 .reset = internal_ipc_reset,
455 .report = internal_ipc_report,
456 .name = "IPC",
457};
458
460[[gnu::constructor]] RA8_INTERNAL static void internal_board_periph_ipc_register(void)
461{
463}
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_ipc_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
Write an IPC register: ISET latches + raises, TXD pushes, CLR clears.
static RA8_INTERNAL void internal_ipc_clr_write(uint32_t ch, uint32_t value)
Apply a CLR write: W1C IRQ lines, FIFO reset, error-latch clears.
static RA8_INTERNAL uint32_t internal_ipc_fifo_pop(uint32_t ch)
Pop the oldest word off a channel's message FIFO (RXD read).
ipc_geom_t
IPC register window geometry (ra8_ipc_regs.h).
@ k_ipc_span
Semaphores + NMI + four channel windows.
@ k_ipc_base
IPC unit base (Secure alias).
static RA8_INTERNAL uint64_t internal_ipc_read(uc_engine *uc, uint64_t addr, unsigned size)
Read an IPC register: STA composes live status, RXD pops the FIFO.
static RA8_INTERNAL void internal_ipc_reset(void)
Clear the IPC channel shadows and the run counters.
static RA8_INTERNAL void internal_board_periph_ipc_register(void)
Self-register the IPC window before main runs (decentralised).
static ipc_state_t s_ipc
Single shared IPC model instance (both engines dispatch into it).
static const board_periph_block_t s_k_ipc_block
IPC register window + reset / report (no tick: event-driven only).
ipc_order_t
Per-tick order slot for the IPC block.
@ k_ipc_block_order
IPC reset / report order slot.
static RA8_INTERNAL void internal_ipc_fifo_push(uint32_t ch, uint32_t value)
Push one word onto a channel's message FIFO (TXD write).
static RA8_INTERNAL bool internal_ipc_decode(uint64_t off, uint32_t *ch, uint32_t *reg)
Decode a window offset to its channel index and register offset.
ipc_chan_t
Channel-count, unit-split and FIFO-depth constants.
@ k_ipc_unit_split
Channels < split are IPC0, the rest IPC1.
@ k_ipc_fifo_depth
4-stage message FIFO (HUM Ch 3.1 p 204).
@ k_ipc_ch_count
IPC0_0, IPC0_1, IPC1_0, IPC1_1.
static RA8_INTERNAL uint32_t internal_ipc_sta_value(uint32_t ch)
Compose one channel's live STA value from its shadows.
ipc_status_t
Status-register masks and the receiving-core ELC event ids.
@ k_ipc0_irq_event
ELC_EVENT_IPC_IRQ0 -> CPU0/M85.
@ k_ipc_sta_rdy
STA.RDY: receive FIFO non-empty.
@ k_ipc_sta_rerr
STA.RERR: read-while-empty sticky.
@ k_ipc_clr_fclr
CLR.FCLR: drop the FERR sticky.
@ k_ipc1_irq_event
ELC_EVENT_IPC_IRQ1 -> CPU1/M33.
@ k_ipc_clr_rclr
CLR.RCLR: drop the RERR sticky.
@ k_ipc_sta_ferr
STA.FERR: write-while-full sticky.
@ k_ipc_sta_irq_mask
Eight maskable IRQ-line bits.
@ k_ipc_clr_rst
CLR.RST: reset the message FIFO.
@ k_ipc_sta_full
STA.FULL: message FIFO full.
static RA8_INTERNAL void internal_ipc_report(void)
Print the IPC send / wake / FIFO totals when the path was exercised.
ipc_reg_off_t
Channel-window layout within the IPC register block.
@ k_ipc_ch_stride
Stride between consecutive channels.
@ k_ipc_reg_rxd
RXD FIFO receive register (read).
@ k_ipc_reg_txd
TXD FIFO transmit register (write).
@ k_ipc_reg_clr
CLR clear register (W1C).
@ k_ipc_reg_sta
STA pending-IRQ register (read).
@ k_ipc_reg_iset
ISET set-IRQ register (write).
@ k_ipc_ch0_off
IPC0 channel 0 (FIFO00) window base.
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 channel's 4-stage message FIFO plus its sticky error latches.
uint32_t word[k_ipc_fifo_depth]
Ring storage (oldest at rd).
bool rerr
Read-while-empty sticky latch.
uint32_t rd
Index of the oldest queued word.
uint32_t count
Queued words (0..depth).
bool ferr
Write-while-full sticky latch.
Modelled IPC state: per-channel IRQ bits + FIFOs plus run counters.
ipc_fifo_t fifo[k_ipc_ch_count]
Per-channel 4-stage message FIFO.
uint32_t wakes
Receiving-core ELC events raised.
uint32_t pops
RXD words drained from a FIFO.
uint32_t sta[k_ipc_ch_count]
Per-channel STA pending-IRQ shadow.
uint32_t pushes
TXD words accepted into a FIFO.
uint32_t sends
ISET writes that latched IRQ bits.