ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_spi.c
Go to the documentation of this file.
1
36
37#include <stdint.h>
38#include <stdio.h>
39
40#include "board_console.h"
41#include "board_periph_block.h"
42#include "board_periph_eink.h"
43#include "board_periph_sd.h"
45
56typedef enum : uint64_t {
57 k_spi_base = 0x4035C000UL,
58 k_spi_stride = 0x100UL,
60 k_spi_span = 0x100UL * 2UL,
61 k_spi_off_spdr = 0x00UL,
62 k_spi_off_spcr = 0x08UL,
63 k_spi_off_spcr2 = 0x0CUL,
64 k_spi_off_spsr = 0x50UL,
65 k_spi_off_spsrc = 0x68UL,
66 k_spi_reg_words = 0x70UL / 4UL,
67} spi_map_t;
68
70typedef enum : uint32_t {
71 k_spi_spcr_spe = 0x00000001U,
73
75typedef enum : uint32_t {
76 k_spi_spcr2_splp = 0x00010000U,
77 k_spi_spcr2_splp2 = 0x00020000U,
79
81typedef enum : uint32_t {
82 k_spi_spsr_spdrf = 0x00800000U,
83 k_spi_spsr_sptef = 0x20000000U,
84 k_spi_spsr_cendf = 0x40000000U,
85 k_spi_spsr_sprf = 0x80000000U,
87
89typedef enum : uint32_t {
92
94typedef enum : uint32_t {
97
109typedef struct {
111 uint32_t spsr;
112 uint32_t rx;
113 bool enabled;
114 bool loopback;
115 uint32_t frames;
117
120
121/* =============================================================================
122 * SPI_B controller model -- the single-frame echo the ra8_spi_b.c polling
123 * driver clocks through SPDR / SPSR / SPSRC under internal loopback.
124 * =============================================================================
125 */
126
138RA8_INTERNAL static uint32_t internal_spi_word(uint64_t off)
139{
140 return (uint32_t)(off / 4U);
141}
142
153RA8_INTERNAL static void internal_spi_spcr_write(spi_state_t* s, uint32_t value)
154{
155 s->enabled = ((value & (uint32_t)k_spi_spcr_spe) != 0U);
156 if (s->enabled) {
157 /* Running: the model drains TX instantly, so SPTEF is always ready for
158 * the first frame; SPRF stays clear until a word is written. */
159 s->spsr |= (uint32_t)k_spi_spsr_sptef;
160 s->spsr &= ~(uint32_t)k_spi_spsr_sprf;
161 } else {
162 s->spsr = 0U;
163 }
164}
165
176RA8_INTERNAL static void internal_spi_spcr2_write(spi_state_t* s, uint32_t value)
177{
178 s->loopback = ((value & ((uint32_t)k_spi_spcr2_splp | (uint32_t)k_spi_spcr2_splp2)) != 0U);
179}
180
191RA8_INTERNAL static void internal_spi_spdr_write(spi_state_t* s, uint32_t value)
192{
193 /* SPLP2 ties the outgoing line back to the receive shifter (rx = tx);
194 * SPLP inverts it (rx = ~tx). Without loopback, if a `--sd` card is
195 * attached it answers the exchange (the genuine ra8_sdmmc_spi path); an
196 * `--eink` IT8951 controller answers the ra8_epaper path; else nothing
197 * drives the line and the receive shifter clocks in an idle 0. */
198 if ((s->reg[internal_spi_word((uint64_t)k_spi_off_spcr2)] & (uint32_t)k_spi_spcr2_splp) != 0U) {
199 s->rx = (~value) & (uint32_t)k_spi_byte_mask;
200 } else if (s->loopback) {
201 s->rx = value;
202 } else if (board_sd_attached()) {
203 s->rx = (uint32_t)board_sd_exchange((uint8_t)(value & (uint32_t)k_spi_byte_mask));
204 } else if (board_eink_attached()) {
205 s->rx = (uint32_t)board_eink_exchange((uint8_t)(value & (uint32_t)k_spi_byte_mask));
206 } else {
207 s->rx = 0U;
208 }
209 /* TX drains immediately (SPTEF stays ready) and the echoed word lands in
210 * the receive holding register, so SPRF asserts for the driver's RX poll. */
211 s->spsr |= ((uint32_t)k_spi_spsr_sptef | (uint32_t)k_spi_spsr_sprf | (uint32_t)k_spi_spsr_spdrf);
212 s->frames++;
213}
214
225RA8_INTERNAL static void internal_spi_spsrc_write(spi_state_t* s, uint32_t value)
226{
227 /* Every SPSRC clear bit sits at the same position as its SPSR flag, so a
228 * written 1 clears that flag; SPTEF is re-asserted because the model's TX
229 * buffer is always empty once the channel runs. */
230 s->spsr &= ~value;
231 if (s->enabled) {
232 s->spsr |= (uint32_t)k_spi_spsr_sptef;
233 }
234}
235
248RA8_INTERNAL static uint64_t internal_spi_reg_read(spi_state_t* s, uint64_t off)
249{
250 if (off == (uint64_t)k_spi_off_spsr) {
251 return s->spsr;
252 }
253 if (off == (uint64_t)k_spi_off_spdr) {
254 return s->rx; /* receive holding register: the echoed frame */
255 }
256 return s->reg[internal_spi_word(off)]; /* reflect every other register */
257}
258
270RA8_INTERNAL static void internal_spi_reg_write(spi_state_t* s, uint64_t off, uint32_t value)
271{
272 s->reg[internal_spi_word(off)] = value; /* shadow keeps "configure then verify" working */
273 if (off == (uint64_t)k_spi_off_spcr) {
274 internal_spi_spcr_write(s, value);
275 } else if (off == (uint64_t)k_spi_off_spcr2) {
276 internal_spi_spcr2_write(s, value);
277 } else if (off == (uint64_t)k_spi_off_spdr) {
278 internal_spi_spdr_write(s, value);
279 } else if (off == (uint64_t)k_spi_off_spsrc) {
280 internal_spi_spsrc_write(s, value);
281 }
282}
283
297RA8_INTERNAL static uint64_t internal_spi_read(uc_engine* uc, uint64_t addr, unsigned size)
298{
299 (void)uc;
300 (void)size;
301 const uint32_t ch = (uint32_t)((addr - (uint64_t)k_spi_base) / (uint64_t)k_spi_stride);
302 const uint64_t off = (addr - (uint64_t)k_spi_base) % (uint64_t)k_spi_stride;
303 return internal_spi_reg_read(&s_spi[ch], off);
304}
305
318RA8_INTERNAL static void
319internal_spi_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
320{
321 (void)uc;
322 (void)size;
323 const uint32_t ch = (uint32_t)((addr - (uint64_t)k_spi_base) / (uint64_t)k_spi_stride);
324 const uint64_t off = (addr - (uint64_t)k_spi_base) % (uint64_t)k_spi_stride;
325 internal_spi_reg_write(&s_spi[ch], off, (uint32_t)value);
326}
327
337{
338 for (uint32_t i = 0U; i < (uint32_t)k_spi_count; i++) {
339 s_spi[i] = (spi_state_t){};
340 }
341 board_sd_reset(); /* clear any attached SD card's command framing */
342 board_eink_reset(); /* clear any attached IT8951 controller's framing */
343}
344
361{
362 for (uint32_t ch = 0U; ch < (uint32_t)k_spi_count; ch++) {
363 if (s_spi[ch].frames > 0U) {
364 (void)priv_emu_io_errf(" SPI%u : %u frame(s) echoed (loopback=%s)\n",
365 ch,
366 s_spi[ch].frames,
367 s_spi[ch].loopback ? "yes" : "no");
368 char ln[k_spi_console_line_cap];
369 (void)snprintf(ln,
370 sizeof(ln),
371 "SPI%u: %u frames lb=%s",
372 ch,
373 s_spi[ch].frames,
374 s_spi[ch].loopback ? "on" : "off");
376 }
377 }
378 board_eink_report(); /* an attached IT8951 controller prints its own line */
379}
380
383 .base = (uint64_t)k_spi_base,
384 .span = (uint64_t)k_spi_span,
385 .order = (uint32_t)k_block_order_sci,
386 .read = internal_spi_read,
388 .tick = nullptr,
389 .reset = internal_spi_reset,
390 .report = internal_spi_report,
391 .name = "SPI_B",
392};
393
395[[gnu::constructor]] RA8_INTERNAL static void internal_board_periph_spi_register(void)
396{
398}
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_spi
SPI_B transfer 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.
@ k_block_order_sci
SCI_B UART.
IT8951 e-paper SPI-device model for ra8_emulator (attached to SPI_B).
uint8_t board_eink_exchange(uint8_t tx)
Exchange one full-duplex SPI byte with the modelled controller.
void board_eink_reset(void)
Reset the controller's command / read framing to power-on.
void board_eink_report(void)
Print the controller's end-of-run summary line (if attached).
bool board_eink_attached(void)
Report whether the IT8951 controller is currently attached.
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.
void board_sd_reset(void)
Reset the card's command / response framing to power-on.
static spi_state_t s_spi[k_spi_count]
The modelled SPI_B channels (SPI0 + SPI1).
spi_spsr_bit_t
SPSR (status) flags the driver polls (ra8_spi_regs.h).
@ k_spi_spsr_cendf
CENDF: communication end (bit 30).
@ k_spi_spsr_spdrf
SPDRF: receive data ready (bit 23).
@ k_spi_spsr_sptef
SPTEF: transmit empty (bit 29).
@ k_spi_spsr_sprf
SPRF: receive full (bit 31).
static RA8_INTERNAL void internal_spi_spcr2_write(spi_state_t *s, uint32_t value)
Apply an SPCR2 write: latch whether internal loopback is engaged.
spi_map_t
SPI_B block geometry (ra8_spi_regs.h, 32-bit register file).
@ k_spi_reg_words
Shadow word count for one channel.
@ k_spi_stride
Bytes per SPI channel.
@ k_spi_off_spdr
SPDR data register (FIFO front).
@ k_spi_span
Both channel windows.
@ k_spi_base
SPI0 base.
@ k_spi_off_spsr
SPSR status (SPTEF/SPRF/...).
@ k_spi_off_spcr
SPCR control 1 (SPE/MSTR).
@ k_spi_off_spcr2
SPCR2 control 2 (loopback bits).
@ k_spi_count
SPI0 + SPI1.
@ k_spi_off_spsrc
SPSRC status clear (write-1).
static RA8_INTERNAL uint64_t internal_spi_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the SPI_B window: route to the addressed channel.
static RA8_INTERNAL uint32_t internal_spi_word(uint64_t off)
Index of the shadow word for off within a channel (range-checked).
spi_spcr_bit_t
SPCR (control 1) bits the model observes (ra8_spi_regs.h).
@ k_spi_spcr_spe
SPE: SPI function enable (bit 0).
spi_console_dim_t
Sizing for the board-console SPI transfer-summary line.
@ k_spi_console_line_cap
Max chars in one SPI console summary line.
spi_spcr2_bit_t
SPCR2 (control 2) loopback bits (ra8_spi_regs.h).
@ k_spi_spcr2_splp2
SPLP2: non-inverting loopback.
@ k_spi_spcr2_splp
SPLP: inverting loopback (rx=~tx).
static RA8_INTERNAL uint64_t internal_spi_reg_read(spi_state_t *s, uint64_t off)
Read a register from one modelled SPI_B channel.
static RA8_INTERNAL void internal_spi_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the SPI_B window: route to the addressed channel.
static RA8_INTERNAL void internal_spi_reg_write(spi_state_t *s, uint64_t off, uint32_t value)
Write a register on one modelled SPI_B channel.
static const board_periph_block_t s_k_spi_block
This block's descriptor (static lifetime; the core keeps the pointer).
static RA8_INTERNAL void internal_spi_report(void)
Print one line per SPI channel that echoed any frame.
static RA8_INTERNAL void internal_spi_spcr_write(spi_state_t *s, uint32_t value)
Apply an SPCR write: SPE gates the channel and arms TX-empty.
static RA8_INTERNAL void internal_spi_reset(void)
Clear all SPI_B channel state to power-on.
spi_data_t
One-byte / one-word masks used by the echo path.
@ k_spi_byte_mask
Low data byte of an 8-bit frame.
static RA8_INTERNAL void internal_spi_spsrc_write(spi_state_t *s, uint32_t value)
Handle an SPSRC write (write-1-to-clear the matching SPSR flags).
static RA8_INTERNAL void internal_spi_spdr_write(spi_state_t *s, uint32_t value)
Handle a write to SPDR: echo the frame and assert receive-full.
static RA8_INTERNAL void internal_board_periph_spi_register(void)
Self-register the SPI_B block before main runs (decentralized).
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 SPI_B channel: register shadow + a single-frame echo engine.
uint32_t frames
Frames echoed through this channel.
uint32_t reg[k_spi_reg_words]
Reflect-on-read register shadow.
uint32_t rx
Receive holding register (echoed word).
uint32_t spsr
SPSR flags (SPTEF / SPRF / ...).
bool enabled
SPCR.SPE: channel is running.
bool loopback
SPCR2.SPLP/SPLP2: internal rx = tx tie.