ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
board_periph_xspi.c
Go to the documentation of this file.
1
32
33#include <stdint.h>
34#include <stdio.h>
35#include <string.h>
36
37#include "board_console.h"
38#include "board_periph_block.h"
40
42typedef enum : uint32_t {
45
47typedef enum : uint64_t {
48 k_xspi_base = 0x40268000UL,
49 k_xspi_span = 0x200UL,
51 k_xspi_off_cdbuf = 0x080UL,
52 k_xspi_off_ints = 0x190UL,
53 k_xspi_off_intc = 0x194UL,
55
66
76
78typedef enum : uint32_t {
80 k_xspi_op_pp = 0x02U,
85} xspi_op_t;
86
88typedef enum : uint32_t {
89 k_xspi_flash_size = 0x4000000UL,
92 k_xspi_sector_len = 0x1000UL,
93 k_xspi_words = 128U,
94 k_xspi_erased = 0xFFU,
96 k_xspi_jedec_cdd0 = 0x001A5A9DUL,
99
101typedef enum : uint32_t {
104
106typedef struct {
107 uint32_t regs[k_xspi_words];
108 uint32_t reads;
109 uint32_t writes;
110 uint32_t erases;
112
114
133
145static uint32_t s_xspi_dirty_hi;
146
158RA8_INTERNAL static uint8_t internal_xspi_flash_byte(uint32_t addr)
159{
160 if (addr >= (uint32_t)k_xspi_flash_size) {
161 return (uint8_t)k_xspi_erased;
162 }
163 return (uint8_t)~s_xspi_flash_neg[addr];
164}
165
175RA8_INTERNAL static void internal_xspi_mark_dirty(uint32_t end_excl)
176{
177 if (end_excl > s_xspi_dirty_hi) {
178 s_xspi_dirty_hi = end_excl;
179 }
180}
181
193RA8_INTERNAL static uint32_t internal_xspi_cdbuf_word(uint32_t w)
194{
195 return (uint32_t)((k_xspi_off_cdbuf / 4U) + w);
196}
197
209RA8_INTERNAL static uint32_t internal_xspi_opcode(uint32_t cdt)
210{
211 const uint32_t cmdsize =
212 (cdt >> (uint32_t)k_xspi_cdt_pos_cmdsize) & (uint32_t)k_xspi_cdt_mask_cmdsize;
213 const uint32_t cmdfield = (cdt >> (uint32_t)k_xspi_cdt_pos_cmd) & (uint32_t)k_xspi_cdt_mask_cmd;
214 /* internal_make_cdt left-justifies the opcode in the 16-bit CMD field:
215 * shift = 8 * (2 - cmdsize). Recover the low opcode byte. */
216 const uint32_t shift =
217 (uint32_t)k_xspi_byte_bits * (2U - (cmdsize & (uint32_t)k_xspi_cdt_mask_cmdsize));
218 return (cmdfield >> shift) & (uint32_t)k_xspi_cdt_byte;
219}
220
231RA8_INTERNAL static void internal_xspi_do_read(uint32_t addr, uint32_t n)
232{
233 uint32_t d0 = 0U;
234 uint32_t d1 = 0U;
235 for (uint32_t i = 0U; i < n; i++) {
236 const uint8_t b = internal_xspi_flash_byte(addr + i);
237 if (i < 4U) {
238 d0 |= (uint32_t)b << (i * (uint32_t)k_xspi_byte_bits);
239 } else {
240 d1 |= (uint32_t)b << ((i - 4U) * (uint32_t)k_xspi_byte_bits);
241 }
242 }
245 s_xspi.reads++;
246 /* Console OSPI tab: one line per manual read command. */
248 (void)snprintf(ln, sizeof(ln), "OSPI rd @0x%06X %uB", (unsigned)addr, (unsigned)n);
250}
251
262RA8_INTERNAL static void internal_xspi_do_program(uint32_t addr, uint32_t n)
263{
264 const uint32_t d0 = s_xspi.regs[internal_xspi_cdbuf_word((uint32_t)k_xspi_cdbuf_data0)];
265 const uint32_t d1 = s_xspi.regs[internal_xspi_cdbuf_word((uint32_t)k_xspi_cdbuf_data1)];
266 for (uint32_t i = 0U; i < n; i++) {
267 if ((addr + i) >= (uint32_t)k_xspi_flash_size) {
268 continue;
269 }
270 const uint32_t w = (i < 4U) ? d0 : d1;
271 const uint32_t s = (i < 4U) ? i : (i - 4U);
272 const uint8_t b =
273 (uint8_t)((w >> (s * (uint32_t)k_xspi_byte_bits)) & (uint32_t)k_xspi_cdt_byte);
274 /* NOR program clears flash bits only: flash &= b. In the inverted store
275 * that is neg = ~(~neg & b) = neg | ~b. */
276 s_xspi_flash_neg[addr + i] |= (uint8_t)~b;
277 internal_xspi_mark_dirty(addr + i + 1U);
278 }
279 s_xspi.writes++;
280 /* Console OSPI tab: one line per page-program command. */
282 (void)snprintf(ln, sizeof(ln), "OSPI pp @0x%06X %uB", (unsigned)addr, (unsigned)n);
284}
285
295RA8_INTERNAL static void internal_xspi_do_erase(uint32_t addr)
296{
297 const uint32_t base = addr & ~((uint32_t)k_xspi_sector_len - 1U);
298 if (base >= (uint32_t)k_xspi_flash_size) {
299 return;
300 }
301 /* Erased flash = 0xFF = all-zero bytes in the inverted store. */
302 (void)memset(&s_xspi_flash_neg[base], 0, (size_t)k_xspi_sector_len);
304 s_xspi.erases++;
305}
306
316{
317 const uint32_t cdt = s_xspi.regs[internal_xspi_cdbuf_word((uint32_t)k_xspi_cdbuf_cdt)];
318 const uint32_t addr = s_xspi.regs[internal_xspi_cdbuf_word((uint32_t)k_xspi_cdbuf_addr)];
319 const uint32_t n =
320 (cdt >> (uint32_t)k_xspi_cdt_pos_datasize) & (uint32_t)k_xspi_cdt_mask_datasize;
321 switch (internal_xspi_opcode(cdt)) {
322 case (uint32_t)k_xspi_op_rdid:
324 (uint32_t)k_xspi_jedec_cdd0;
325 break;
326 case (uint32_t)k_xspi_op_rdsr:
328 (uint32_t)k_xspi_status_wel;
329 break;
330 case (uint32_t)k_xspi_op_read:
331 internal_xspi_do_read(addr, n);
332 break;
333 case (uint32_t)k_xspi_op_pp:
335 break;
336 case (uint32_t)k_xspi_op_erase:
338 break;
339 default:
340 break; /* WREN, 8D/1S software reset, mode switches: no flash effect. */
341 }
342 s_xspi.regs[(uint32_t)(k_xspi_off_ints / 4U)] |= (uint32_t)k_xspi_ints_cmdcmp;
343}
344
354{
355 (void)memset(s_xspi.regs, 0, sizeof(s_xspi.regs));
356 /* Only the dirty prefix ever left the erased state; sweeping the whole
357 * 64 MiB would needlessly commit resident pages in every emulator run. */
358 (void)memset(s_xspi_flash_neg, 0, (size_t)s_xspi_dirty_hi);
359 s_xspi_dirty_hi = 0U;
360 s_xspi.reads = 0U;
361 s_xspi.writes = 0U;
362 s_xspi.erases = 0U;
363}
364
378RA8_INTERNAL static uint64_t internal_xspi_read(uc_engine* uc, uint64_t addr, unsigned size)
379{
380 (void)uc;
381 (void)size;
382 const uint64_t off = addr - (uint64_t)k_xspi_base;
383 if ((off / 4U) >= (uint64_t)k_xspi_words) {
384 return 0U;
385 }
386 return s_xspi.regs[off / 4U];
387}
388
401RA8_INTERNAL static void
402internal_xspi_write(uc_engine* uc, uint64_t addr, unsigned size, uint64_t value)
403{
404 (void)uc;
405 (void)size;
406 const uint64_t off = addr - (uint64_t)k_xspi_base;
407 if ((off / 4U) >= (uint64_t)k_xspi_words) {
408 return;
409 }
410 if (off == (uint64_t)k_xspi_off_intc) {
411 /* Write-1-to-clear against INTS. */
412 s_xspi.regs[(uint32_t)(k_xspi_off_ints / 4U)] &= ~(uint32_t)value;
413 return;
414 }
415 s_xspi.regs[off / 4U] = (uint32_t)value;
416 if ((off == (uint64_t)k_xspi_off_cdctl0) &&
417 (((uint32_t)value & (uint32_t)k_xspi_cdctl0_trreq) != 0U)) {
419 /* TRREQ self-clears on completion. */
420 s_xspi.regs[off / 4U] &= ~(uint32_t)k_xspi_cdctl0_trreq;
421 }
422}
423
433{
434 if ((s_xspi.reads == 0U) && (s_xspi.writes == 0U) && (s_xspi.erases == 0U)) {
435 return;
436 }
437 (void)priv_emu_io_errf(" XSPI flash : %u reads %u programs %u erases\n",
438 s_xspi.reads,
439 s_xspi.writes,
440 s_xspi.erases);
441}
442
445 .base = (uint32_t)k_xspi_base,
446 .span = (uint32_t)k_xspi_span,
447 .order = (uint32_t)k_xspi_block_order,
448 .read = internal_xspi_read,
450 .tick = nullptr,
451 .reset = internal_xspi_reset,
452 .report = internal_xspi_report,
453 .name = "XSPI",
454};
455
457[[gnu::constructor]] RA8_INTERNAL static void internal_xspi_block_register(void)
458{
460}
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_ospi
Octal-SPI (xSPI) command 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.
static RA8_INTERNAL void internal_xspi_block_register(void)
Register the xSPI block before main (host constructor).
static RA8_INTERNAL void internal_xspi_do_program(uint32_t addr, uint32_t n)
Page-program n bytes (<= 8) of CDD0/CDD1 into flash@addr (NOR AND).
static RA8_INTERNAL uint64_t internal_xspi_read(uc_engine *uc, uint64_t addr, unsigned size)
MMIO read inside the xSPI window.
xspi_op_t
JEDEC opcodes the engine decodes (ra8_xspi.c).
@ k_xspi_op_read
Normal read.
@ k_xspi_op_rdid
Read JEDEC ID.
@ k_xspi_op_rdsr
Read status.
@ k_xspi_op_pp
Page program.
@ k_xspi_op_erase
4 KiB sector erase.
@ k_xspi_op_wren
Write enable.
xspi_ctl_field_t
CDCTL0 / INTS bits + CDBUF slot-0 word indices.
@ k_xspi_cdbuf_addr
CDBUF[1] flash address.
@ k_xspi_ints_cmdcmp
CMDCMP: command complete.
@ k_xspi_cdbuf_data1
CDBUF[3] data bytes 4..7.
@ k_xspi_cdbuf_cdt
CDBUF[0] command descriptor.
@ k_xspi_cdctl0_trreq
TRREQ: kick the manual transfer.
@ k_xspi_cdbuf_data0
CDBUF[2] data bytes 0..3.
static RA8_INTERNAL uint32_t internal_xspi_opcode(uint32_t cdt)
Extract the 1/2-byte opcode from a CDT descriptor word.
static xspi_state_t s_xspi
static uint32_t s_xspi_dirty_hi
Exclusive high-water mark of bytes ever programmed/erased this run.
static RA8_INTERNAL void internal_xspi_exec_command(void)
Execute the slot-0 manual command, then raise INTS.CMDCMP.
xspi_console_t
Console-tap line buffer capacity for an xSPI command summary.
@ k_xspi_console_line_cap
Max chars in an "OSPI .. @0x.." line.
static RA8_INTERNAL uint32_t internal_xspi_cdbuf_word(uint32_t w)
Word index of CDBUF slot-0 entry w inside the window shadow.
xspi_map_t
XSPI0 register-window geometry (ra8_ospi_regs.h).
@ k_xspi_off_cdctl0
CDCTL0 (TRREQ bit 0 / CSSEL bit 3).
@ k_xspi_off_ints
INTS (CMDCMP bit 0).
@ k_xspi_off_cdbuf
CDBUF[0..3] for manual slot 0.
@ k_xspi_off_intc
INTC (write-1-to-clear).
@ k_xspi_base
XSPI0 register window base.
@ k_xspi_span
Window covering WRAPCFG..INTE.
static RA8_INTERNAL void internal_xspi_write(uc_engine *uc, uint64_t addr, unsigned size, uint64_t value)
MMIO write inside the xSPI window (TRREQ kicks; INTC is W1C).
static RA8_INTERNAL void internal_xspi_mark_dirty(uint32_t end_excl)
Raise the dirty high-water mark to cover [0, end_excl).
static RA8_INTERNAL void internal_xspi_report(void)
End-of-run xSPI section: op counts (only if the bus was used).
xspi_lit_t
Backing-flash + register-window sizing and constants.
@ k_xspi_status_wel
RDSR result: WEL=1, WIP=0.
@ k_xspi_flash_size
64 MiB: the full IS25LX512M part (512 Mbit; usb_selftest_ospi_rw's scratch window sits at +2 MiB).
@ k_xspi_words
0x200-byte window as 32-bit words.
@ k_xspi_byte_bits
Bits per byte.
@ k_xspi_erased
NOR erased byte value.
@ k_xspi_jedec_cdd0
{0x9D,0x5A,0x1A} packed for CDD0.
@ k_xspi_sector_len
4 KiB sector (opcode 0x20).
xspi_cdt_field_t
Command-descriptor (CDT) field positions / masks (ra8_ospi_regs.h).
@ k_xspi_cdt_pos_cmdsize
CMDSIZE[1:0] command bytes.
@ k_xspi_cdt_pos_cmd
CMD[31:16] opcode field.
@ k_xspi_cdt_mask_cmdsize
CMDSIZE width.
@ k_xspi_cdt_mask_cmd
CMD field width.
@ k_xspi_cdt_byte
One opcode/data byte.
@ k_xspi_cdt_mask_datasize
DATASIZE width.
@ k_xspi_cdt_pos_datasize
DATASIZE[8:5] data bytes.
static uint8_t s_xspi_flash_neg[k_xspi_flash_size]
Backing NOR array for the full 64 MiB part, stored INVERTED.
static RA8_INTERNAL void internal_xspi_do_erase(uint32_t addr)
Erase the 4 KiB sector containing addr (restore 0xFF).
static RA8_INTERNAL void internal_xspi_do_read(uint32_t addr, uint32_t n)
Read n bytes (<= 8) from flash@addr into CDD0/CDD1.
static RA8_INTERNAL void internal_xspi_reset(void)
Reset the xSPI model: zero the window, erase the touched flash prefix.
xspi_order_t
Per-tick / report order slot (after the SD block, before SPI).
@ k_xspi_block_order
Report ordering only.
static const board_periph_block_t s_k_xspi_block
xSPI block descriptor (self-registered with the core).
static RA8_INTERNAL uint8_t internal_xspi_flash_byte(uint32_t addr)
Current flash content byte at addr (0xFF beyond the part).
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).
void * memset(void *dst, int value, size_t n)
Fill memory with a constant byte value.
static ra8_err_t internal_xspi_write(void *ctx, uint32_t lba, uint32_t count, const uint8_t *buf)
xSPI backend: write count blocks from buf at lba.
static ra8_err_t internal_xspi_read(void *ctx, uint32_t lba, uint32_t count, uint8_t *buf)
xSPI backend: read count blocks at lba into buf.
A modelled peripheral block's self-description for the core registry.
xSPI model state: register-window shadow + op counters.
uint32_t erases
Sector erases.
uint32_t reads
READ commands executed.
uint32_t regs[k_xspi_words]
0x200-byte window shadow.
uint32_t writes
Page-program commands.