ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_sci.c
Go to the documentation of this file.
1
30
31#include <stdint.h>
32#include <stdio.h>
33
34#include "board_console.h"
35#include "board_periph.h"
36#include "board_periph_block.h"
37#include "board_periph_modem.h"
38#include "board_periph_sd.h"
40
42typedef enum : uint64_t {
43 k_sci_base = 0x40358000UL,
44 k_sci_stride = 0x100UL,
45 k_sci_count = 10UL,
46 k_sci_span = 0x100UL * 10UL,
47 k_sci_off_rdr = 0x00UL,
48 k_sci_off_tdr = 0x04UL,
49 k_sci_off_ccr0 = 0x08UL,
50 k_sci_off_csr = 0x48UL,
51 k_sci_off_frsr = 0x50UL,
52 k_sci_off_ftsr = 0x54UL,
53 k_sci_off_cfclr = 0x68UL,
54 k_sci_off_ffclr = 0x70UL,
55} sci_map_t;
56
58typedef enum : uint32_t {
59 k_sci_ccr0_re = 0x00000001U,
60 k_sci_ccr0_te = 0x00000010U,
61 k_sci_ccr0_rie = 0x00010000U,
62 k_sci_ccr0_tie = 0x00100000U,
63 k_sci_ccr0_teie = 0x00200000U,
65
67typedef enum : uint32_t {
68 k_sci_csr_rxdmon = 0x00008000U,
69 k_sci_csr_tdre = 0x20000000U,
70 k_sci_csr_tend = 0x40000000U,
71 k_sci_csr_rdrf = 0x80000000U,
73
75typedef enum : uint32_t {
76 k_sci_frsr_dr = 0x00000001U,
77 k_sci_frsr_rdf = 0x00000040U,
78 k_sci_ftsr_tdfe = 0x00000040U,
80
82typedef enum : uint32_t {
83 k_sci_cfclr_rdrfc = 0x80000000U,
84 k_sci_ffclr_drc = 0x00000001U,
86
99typedef enum : uint16_t {
104
115
126typedef struct {
127 uint32_t ccr0;
128 uint32_t transmitted;
129 uint32_t received;
131 uint32_t rx_head;
132 uint32_t rx_tail;
133 uint32_t rx_dropped;
135
137
139static void (*s_sci_tx_sink)(uint8_t channel, uint8_t byte);
140
141/* Last complete console line, latched on newline so the board view can show
142 * what a non-display example printed (e.g. "hello, ra8d2!"). s_uart_pend
143 * accumulates the in-flight line; s_uart_last holds the last finished one. The
144 * deeper scrollback ring now lives in board_console (the UART channel), which
145 * the board view's tabbed console reads back -- this model just latches the last
146 * line and routes each completed line there. */
149static uint32_t s_uart_pend_len;
150
151void board_periph_sci_set_tx_sink(void (*sink)(uint8_t channel, uint8_t byte))
152{
153 s_sci_tx_sink = sink;
154}
155
157{
158 return (uint8_t)k_sci_console_ch;
159}
160
161void board_periph_sci_feed_rx(uint8_t channel, const uint8_t* data, uint32_t len)
162{
163 if ((channel >= (uint32_t)k_sci_count) || (data == nullptr)) {
164 return;
165 }
166 sci_state_t* s = &s_sci[channel];
167 for (uint32_t i = 0U; i < len; i++) {
168 const uint32_t next = (s->rx_tail + 1U) % (uint32_t)k_sci_rx_queue_len;
169 if (next == s->rx_head) {
170 s->rx_dropped += (len - i); /* ring full: drop the remaining bytes */
171 if (board_periph_trace()) {
172 (void)priv_emu_io_errf(" [trace] SCI%u RX queue full, dropped %u bytes\n",
173 channel,
174 len - i);
175 }
176 return;
177 }
178 s->rx[s->rx_tail] = data[i];
179 s->rx_tail = next;
180 }
181}
182
184{
185 return s_uart_last;
186}
187
189{
190 uint32_t total = 0U;
191 for (uint32_t ch = 0U; ch < (uint32_t)k_sci_count; ch++) {
192 total += s_sci[ch].transmitted;
193 }
194 return total;
195}
196
209{
210 return s->rx_head != s->rx_tail;
211}
212
225{
226 /* TDRE + TEND are held set so ra8_sci's "wait for transmit empty / end" polls
227 * (ra8_sci_putc_polling on TDRE, internal_wait_tx_end on TEND) fall through.
228 * RXDMON reads high because an idle UART line idles high. RDRF tracks the
229 * host RX queue so ra8_sci_getc_polling completes only when a byte is ready. */
230 uint32_t csr = (uint32_t)k_sci_csr_tdre | (uint32_t)k_sci_csr_tend | (uint32_t)k_sci_csr_rxdmon;
232 csr |= (uint32_t)k_sci_csr_rdrf;
233 }
234 return csr;
235}
236
249RA8_INTERNAL static uint64_t internal_sci_reg_read(uint32_t ch, uint64_t off)
250{
251 sci_state_t* s = &s_sci[ch];
252 if (off == (uint64_t)k_sci_off_rdr) {
254 return 0U; /* drained: nothing queued */
255 }
256 const uint8_t b = s->rx[s->rx_head];
257 s->rx_head = (s->rx_head + 1U) % (uint32_t)k_sci_rx_queue_len;
258 s->received++;
259 return (uint64_t)b;
260 }
261 if (off == (uint64_t)k_sci_off_csr) {
262 return internal_sci_csr_value(s);
263 }
264 if (off == (uint64_t)k_sci_off_ccr0) {
265 return s->ccr0;
266 }
267 if (off == (uint64_t)k_sci_off_frsr) {
268 return internal_sci_rx_available(s) ? ((uint32_t)k_sci_frsr_dr | (uint32_t)k_sci_frsr_rdf) : 0U;
269 }
270 if (off == (uint64_t)k_sci_off_ftsr) {
271 return (uint32_t)k_sci_ftsr_tdfe; /* TX FIFO is always empty in the model */
272 }
273 return 0U; /* CFCLR / FFCLR read as 0; other regs unmodelled -> 0 */
274}
275
293{
294 if (byte == (uint8_t)'\n') {
295 for (uint32_t i = 0U; i < s_uart_pend_len; i++) {
296 s_uart_last[i] = s_uart_pend[i];
297 }
299 /* Route the completed line into the board_console UART channel (which backs
300 * the tabbed board-view console + its deeper scrollback). */
302 s_uart_pend_len = 0U;
303 return;
304 }
305 if ((byte != (uint8_t)'\r') && (s_uart_pend_len < (uint32_t)(k_uart_line_cap - 1U))) {
306 s_uart_pend[s_uart_pend_len++] = (char)byte;
307 }
308}
309
321RA8_INTERNAL static void internal_sci_reg_write(uint32_t ch, uint64_t off, uint32_t value)
322{
323 sci_state_t* s = &s_sci[ch];
324 if (off == (uint64_t)k_sci_off_tdr) {
325 /* Both the polled (ra8_sci_putc_polling) and interrupt (ra8_sci_dispatch_txi)
326 * paths launch a frame by writing TDAT[7:0]; FIFO mode also writes TDR. */
327 const uint8_t byte = (uint8_t)(value & (uint32_t)k_sci_data_mask);
328 s->transmitted++;
329 /* Only the console channel carries UART text. Other channels (e.g. SCI0 in
330 * Simple-SPI mode for the microSD) move binary frames, so capturing + sinking
331 * their TX would spew the SPI traffic as "[uart] SCIn:" garbage -- skip it. */
332 if (ch == (uint32_t)k_sci_console_ch) {
334 if (s_sci_tx_sink != nullptr) {
335 s_sci_tx_sink((uint8_t)ch, byte);
336 }
337 }
338 /* Pmod2 microSD is SCI0 in Simple-SPI mode: every TDR write clocks a
339 * full-duplex frame, so feed the modelled card's response back into this
340 * channel's RX queue (RDR/RDRF) -- the real ra8_sci_spi + ra8_sdmmc_spi path
341 * then runs against the FAT image exactly as on hardware. */
342 if (ch == (uint32_t)k_sci_sd_ch) {
343 /* With a card attached, return its modelled response; with none, return
344 * 0xFF (CIPO idles high on an empty bus). Always feeding a byte is what
345 * keeps RDRF advancing -- otherwise the firmware's full-duplex read polls
346 * RDRF forever and every ra8_sdmmc probe burns its whole timeout budget
347 * (seconds, per byte), which is the dominant cold-boot stall for any app
348 * that probes the SD without a card present. With 0xFF the probe simply
349 * sees no valid R1 and fails "no card" promptly, as on real hardware. */
350 const uint8_t resp =
352 board_periph_sci_feed_rx((uint8_t)ch, &resp, 1U);
353 }
354 /* MikroBUS UART (SCI7) with a modelled AT modem attached: forward each
355 * transmitted byte to the modem line model. Most bytes buffer silently; the
356 * terminating CR closes an AT command and the model returns the modem's
357 * reply, which we feed back into this channel's RX queue so the firmware's
358 * genuine ra8_modem_at -> ra8_sci_getc_polling path drains it (EIL == HIL). */
359 if ((ch == (uint32_t)board_modem_channel()) && board_modem_attached()) {
360 uint8_t mresp[k_sci_modem_resp_cap] = {};
361 const uint32_t mn = board_modem_feed_tx(byte, mresp, (uint32_t)k_sci_modem_resp_cap);
362 if (mn > 0U) {
363 board_periph_sci_feed_rx((uint8_t)ch, mresp, mn);
364 }
365 }
366 } else if (off == (uint64_t)k_sci_off_ccr0) {
367 s->ccr0 = value;
368 }
369 /* CFCLR / FFCLR are write-1-to-clear: in this model TDRE/TEND stay asserted
370 * and RDRF is derived from the live RX queue, so clearing them is a no-op
371 * (the firmware re-reads the queue-backed state on its next poll). */
372}
373
384RA8_INTERNAL static void internal_sci_tick_channel(uc_engine* uc, uint32_t ch)
385{
386 if (ch != (uint32_t)k_sci_console_ch) {
387 return; /* only the console channel has modelled ELC event numbers */
388 }
389 const sci_state_t* s = &s_sci[ch];
390 /* TX is always empty/ended in the model: while the firmware keeps TIE/TEIE
391 * armed (an interrupt-driven ra8_sci_write in flight), re-pend TXI/TEI so the
392 * dispatcher pushes the next byte exactly as a real TDRE/TEND would. */
393 if ((s->ccr0 & (uint32_t)k_sci_ccr0_te) != 0U) {
394 if ((s->ccr0 & (uint32_t)k_sci_ccr0_tie) != 0U) {
396 }
397 if ((s->ccr0 & (uint32_t)k_sci_ccr0_teie) != 0U) {
399 }
400 }
401 /* RX: while RIE is armed and a host byte is queued, pend RXI so the firmware
402 * reads it from RDR in handler context (interrupt-driven receive). */
403 if (((s->ccr0 & (uint32_t)k_sci_ccr0_rie) != 0U) && internal_sci_rx_available(s)) {
405 }
406}
407
421RA8_INTERNAL static uint64_t internal_sci_read(uc_engine* uc, uint64_t addr, unsigned size)
422{
423 (void)uc;
424 (void)size;
425 const uint32_t ch = (uint32_t)((addr - (uint64_t)k_sci_base) / (uint64_t)k_sci_stride);
426 return internal_sci_reg_read(ch, (addr - (uint64_t)k_sci_base) % (uint64_t)k_sci_stride);
427}
428
441RA8_INTERNAL static void
442internal_sci_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
443{
444 (void)uc;
445 (void)size;
446 const uint32_t ch = (uint32_t)((addr - (uint64_t)k_sci_base) / (uint64_t)k_sci_stride);
448 (addr - (uint64_t)k_sci_base) % (uint64_t)k_sci_stride,
449 (uint32_t)value);
450}
451
461RA8_INTERNAL static void internal_sci_tick(uc_engine* uc)
462{
463 for (uint32_t ch = 0U; ch < (uint32_t)k_sci_count; ch++) {
465 }
466}
467
477{
478 for (uint32_t i = 0U; i < (uint32_t)k_sci_count; i++) {
479 s_sci[i] = (sci_state_t){};
480 }
481 s_uart_last[0] = '\0';
482 s_uart_pend[0] = '\0';
483 s_uart_pend_len = 0U;
484 /* Re-frame the AT modem's command accumulator on a block reset (--reboot),
485 * keeping its attached flag -- the SCI reset re-frames transport, not the
486 * modem's presence, mirroring board_periph_eink.c's reset. */
488}
489
499{
500 for (uint32_t ch = 0U; ch < (uint32_t)k_sci_count; ch++) {
501 if ((s_sci[ch].transmitted > 0U) || (s_sci[ch].received > 0U)) {
502 (void)priv_emu_io_errf(" SCI%u UART : TX %u bytes RX %u bytes (RX dropped %u)\n",
503 ch,
504 s_sci[ch].transmitted,
505 s_sci[ch].received,
506 s_sci[ch].rx_dropped);
507 }
508 }
509 /* An attached AT modem answers on the modem channel; summarise its traffic
510 * alongside the raw SCI byte counts (self-contained -- no main.c edit). */
512}
513
516 .base = (uint64_t)k_sci_base,
517 .span = (uint64_t)k_sci_span,
518 .order = (uint32_t)k_block_order_sci,
519 .read = internal_sci_read,
521 .tick = internal_sci_tick,
522 .reset = internal_sci_reset,
523 .report = internal_sci_report,
524 .name = "SCI_B UART",
525};
526
528[[gnu::constructor]] RA8_INTERNAL static void internal_board_periph_sci_register(void)
529{
531}
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_uart
SCI_B serial console ([uart] SCIn:).
Register-accurate peripheral-model framework for the board emulator.
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.
@ k_block_order_sci
SCI_B UART.
bool board_periph_trace(void)
Whether –trace is active (blocks log transitions when true).
Cellular AT-modem device model for ra8_emulator (attached to SCI7 UART).
void board_modem_reset(void)
Clear the modem's parser state (keeps the attached flag).
uint8_t board_modem_channel(void)
SCI channel the modem is wired to (RXD7 / TXD7 = SCI7).
uint32_t board_modem_feed_tx(uint8_t tx, uint8_t *out, uint32_t out_cap)
Feed one firmware-transmitted byte into the modem and drain a reply.
bool board_modem_attached(void)
Report whether the AT modem model is currently attached.
void board_modem_report(void)
Print a one-line end-of-run summary if the modem was exercised.
static RA8_INTERNAL void internal_sci_report(void)
Print one line per SCI channel that moved any bytes.
sci_tune_t
SCI_B model sizing and the EK-RA8D2 console channel.
@ k_sci_rx_queue_len
Per-channel host->firmware RX capacity.
@ k_sci_modem_resp_cap
Scratch for one AT-modem reply burst.
@ k_sci_sd_ch
EK-RA8D2 Pmod2 microSD = SCI0 Simple-SPI.
@ k_sci_data_mask
RDR/TDR data field is 8 bits.
@ k_sci_spi_cipo_idle
CIPO idles high (0xFF) on an empty SPI bus.
@ k_sci_console_ch
EK-RA8D2 console = SCI8 (PD02/PD03).
@ k_uart_line_cap
Captured last-TX-line buffer capacity.
sci_map_t
SCI_B block geometry (ra8_sci_regs.h, 32-bit-register variant).
@ k_sci_off_cfclr
CFCLR common flag clear (W1C).
@ k_sci_off_rdr
RDR receive data (RDAT[7:0]).
@ k_sci_off_ffclr
FFCLR FIFO flag clear (W1C).
@ k_sci_off_tdr
TDR transmit data (TDAT[7:0]).
@ k_sci_off_csr
CSR (TDRE/TEND/RDRF + errors).
@ k_sci_stride
Bytes per SCI channel.
@ k_sci_span
SCI span.
@ k_sci_count
SCI0..SCI9.
@ k_sci_off_ccr0
CCR0 (TE/RE/RIE/TIE/TEIE).
@ k_sci_off_frsr
FRSR FIFO receive status.
@ k_sci_off_ftsr
FTSR FIFO transmit status.
@ k_sci_base
SCI0 base (Secure alias).
void board_periph_sci_feed_rx(uint8_t channel, const uint8_t *data, uint32_t len)
Queue host->firmware bytes for a channel's receive path.
static char s_uart_pend[k_uart_line_cap]
Line being accumulated.
static RA8_INTERNAL void internal_sci_reset(void)
Clear all SCI channel state and the captured last-line buffers.
sci_elc_event_t
SCI8 ELC event numbers the console channel raises (FSP ra8d2 bsp_elc).
@ k_event_sci8_rxi
SCI8 RXI receive-data-full event.
@ k_event_sci8_txi
SCI8 TXI transmit-data-empty event.
@ k_event_sci8_tei
SCI8 TEI transmit-end event.
static RA8_INTERNAL uint64_t internal_sci_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the SCI window: route to the addressed channel.
static RA8_INTERNAL void internal_sci_tick_channel(uc_engine *uc, uint32_t ch)
Raise this channel's enabled SCI interrupts through the ICU.
static void(* s_sci_tx_sink)(uint8_t channel, uint8_t byte)
Host sink for transmitted bytes (main.c installs the descriptor-backed output sink).
const char * board_periph_uart_last_line(void)
The most recent complete line the firmware transmitted over any SCI.
static RA8_INTERNAL void internal_sci_capture_tx_line(uint8_t byte)
Accumulate one transmitted byte into the captured last-line buffer.
static char s_uart_last[k_uart_line_cap]
Last completed TX line.
uint8_t board_periph_sci_console_channel(void)
The SCI channel the EK-RA8D2 console (J-Link OB VCOM) uses.
sci_clr_bit_t
CFCLR / FFCLR write-1-to-clear bits this model honours.
@ k_sci_cfclr_rdrfc
RDRFC clear CSR.RDRF (bit 31).
@ k_sci_ffclr_drc
DRC clear FRSR.DR (bit 0).
static RA8_INTERNAL uint32_t internal_sci_csr_value(const sci_state_t *s)
Build the CSR value: TX always drained, RDRF reflects the RX queue.
void board_periph_sci_set_tx_sink(void(*sink)(uint8_t channel, uint8_t byte))
Wire a host sink that receives every byte the firmware transmits.
static sci_state_t s_sci[k_sci_count]
uint32_t board_periph_uart_tx_total(void)
Total bytes the firmware has transmitted over all SCI channels.
static RA8_INTERNAL void internal_sci_reg_write(uint32_t ch, uint64_t off, uint32_t value)
Dispatch an SCI write; TDR is captured, CCR0 shadowed, clears no-op.
sci_ccr0_bit_t
SCI_B CCR0 interrupt/enable bits (ra8_sci_ccr0_bit_t).
@ k_sci_ccr0_rie
RIE receive-interrupt enable (16).
@ k_sci_ccr0_teie
TEIE transmit-end int enable (21).
@ k_sci_ccr0_re
RE receive enable (bit 0).
@ k_sci_ccr0_tie
TIE transmit-interrupt enable (20).
@ k_sci_ccr0_te
TE transmit enable (bit 4).
static uint32_t s_uart_pend_len
Chars buffered in s_uart_pend.
sci_fifo_bit_t
SCI_B FIFO status bits used by reads of FRSR / FTSR.
@ k_sci_frsr_dr
FRSR.DR receive-data-ready (bit 0).
@ k_sci_ftsr_tdfe
FTSR.TDFE transmit-FIFO-empty (6).
@ k_sci_frsr_rdf
FRSR.RDF receive-FIFO-data-full (6).
static RA8_INTERNAL bool internal_sci_rx_available(const sci_state_t *s)
True iff the channel has a queued, unread host RX byte.
static RA8_INTERNAL void internal_sci_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the SCI window: route to the addressed channel.
static RA8_INTERNAL void internal_sci_tick(uc_engine *uc)
Service every SCI channel once per emulation chunk.
static const board_periph_block_t s_k_sci_block
This block's descriptor (static lifetime; the core keeps the pointer).
static RA8_INTERNAL void internal_board_periph_sci_register(void)
Self-register the SCI block before main runs (decentralized).
sci_csr_bit_t
SCI_B CSR status bits (ra8_sci_csr_bit_t).
@ k_sci_csr_tend
TEND transmit-end (bit 30).
@ k_sci_csr_rxdmon
RXDMON RXD pin monitor (bit 15).
@ k_sci_csr_tdre
TDRE transmit-data-empty (bit 29).
@ k_sci_csr_rdrf
RDRF receive-data-full (bit 31).
static RA8_INTERNAL uint64_t internal_sci_reg_read(uint32_t ch, uint64_t off)
Dispatch an SCI read for channel ch at byte offset off.
SD-card-over-SPI device model for ra8_emulator (attached to SPI_B).
bool board_sd_attached(void)
Report whether an SD-card image is currently attached.
uint8_t board_sd_exchange(uint8_t tx)
Exchange one full-duplex SPI byte with the modelled card.
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 SCI_B channel: control shadow + a host-fed RX byte queue.
uint32_t ccr0
CCR0 shadow (TE/RE/RIE/TIE/TEIE).
uint32_t rx_head
Next byte the firmware will read.
uint8_t rx[k_sci_rx_queue_len]
Host->firmware byte ring.
uint32_t received
Bytes the firmware read from RDR.
uint32_t transmitted
Bytes captured from TDR writes.
uint32_t rx_dropped
Bytes dropped on a full RX ring.
uint32_t rx_tail
Next free slot for a queued byte.