ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
lx_nor_driver_ra8_xspi.c
Go to the documentation of this file.
1
38
40
41#include <stdint.h>
42
43#include "lx_api.h"
44#include "ra8_attributes.h"
45#include "ra8_board_ek_ra8d2.h"
46#include "ra8_err.h"
47#include "ra8_log.h"
48#include "ra8_pfs_regs.h"
49#include "ra8_port_constants.h"
50#include "ra8_time.h"
51#include "ra8_xspi.h"
52
54static const char* s_lx_xspi_tag = "LX_XSPI";
55
71typedef enum : uint32_t {
77
103
116typedef enum : uintptr_t {
120
135static ULONG s_ra8_lx_nor_sector_buffer[LX_NOR_SECTOR_SIZE];
136
137/* Convert a LevelX ``ULONG*`` flash pointer to a byte offset -- see implementation for details. */
138RA8_INTERNAL static uint32_t internal_flash_offset_bytes(ULONG* flash_address)
139{
140 /* LevelX adds a number-of-ULONGs offset onto base_address and hands
141 * the result back to us. Recover the byte offset by subtracting the
142 * base in pointer-difference units (each unit is sizeof(ULONG) = 4
143 * bytes on this target). */
144 uintptr_t addr = (uintptr_t)flash_address;
145 uintptr_t base = k_ra8_lx_nor_base_addr;
146 return (uint32_t)(addr - base);
147}
148
178RA8_INTERNAL static UINT internal_nor_read(ULONG* flash_address, ULONG* destination, ULONG words)
179{
180 if ((flash_address == LX_NULL) || (destination == LX_NULL) || (words == 0U)) {
181 return (UINT)LX_ERROR;
182 }
183 uint32_t offset = internal_flash_offset_bytes(flash_address);
184 uint32_t bytes = (uint32_t)words * k_ra8_lx_nor_word_bytes;
185 ra8_err_t err =
186 ra8_xspi_flash_read((uint8_t)k_ra8_lx_xspi_instance, offset, (uint8_t*)destination, bytes);
187 if (err != k_ra8_ok) {
188 return (UINT)LX_ERROR;
189 }
190 return (UINT)LX_SUCCESS;
191}
192
223RA8_INTERNAL static UINT internal_nor_write(ULONG* flash_address, ULONG* source, ULONG words)
224{
225 if ((flash_address == LX_NULL) || (source == LX_NULL) || (words == 0U)) {
226 return (UINT)LX_ERROR;
227 }
228 uint32_t offset = internal_flash_offset_bytes(flash_address);
229 uint32_t bytes = (uint32_t)words * k_ra8_lx_nor_word_bytes;
230 ra8_err_t err =
231 ra8_xspi_flash_program((uint8_t)k_ra8_lx_xspi_instance, offset, (const uint8_t*)source, bytes);
232 if (err != k_ra8_ok) {
233 return (UINT)LX_ERROR;
234 }
235 return (UINT)LX_SUCCESS;
236}
237
268{
269 LX_PARAMETER_NOT_USED(erase_count);
270 if (block >= (ULONG)k_ra8_lx_nor_total_blocks) {
271 return (UINT)LX_ERROR;
272 }
273 uint32_t offset = (uint32_t)block * k_ra8_lx_nor_block_bytes;
275 if (err != k_ra8_ok) {
276 return (UINT)LX_ERROR;
277 }
278 return (UINT)LX_SUCCESS;
279}
280
309{
310 if (block >= (ULONG)k_ra8_lx_nor_total_blocks) {
311 return (UINT)LX_ERROR;
312 }
313 uint32_t offset = (uint32_t)block * k_ra8_lx_nor_block_bytes;
314 uint32_t remain_words = k_ra8_lx_nor_words_per_block;
315 while (remain_words > 0U) {
316 uint32_t chunk_words = remain_words;
317 if (chunk_words > (uint32_t)LX_NOR_SECTOR_SIZE) {
318 chunk_words = (uint32_t)LX_NOR_SECTOR_SIZE;
319 }
320 uint32_t chunk_bytes = chunk_words * k_ra8_lx_nor_word_bytes;
322 offset,
324 chunk_bytes);
325 if (err != k_ra8_ok) {
326 return (UINT)LX_ERROR;
327 }
328 for (uint32_t i = 0U; i < chunk_words; i++) {
329 if (s_ra8_lx_nor_sector_buffer[i] != (ULONG)LX_ALL_ONES) {
330 return (UINT)LX_ERROR;
331 }
332 }
333 offset += chunk_bytes;
334 remain_words -= chunk_words;
335 }
336 return (UINT)LX_SUCCESS;
337}
338
375typedef struct {
376 uint32_t magic;
377 uint32_t call_count;
378 uint32_t rid_err;
379 uint32_t jedec_id;
380 uint32_t reset_8d_err;
381 uint32_t reset_1s_err;
382 uint32_t stage;
383 uint32_t reserved;
385
404
414typedef enum : uint32_t {
415 k_ra8_xspi_rdid_magic = 0x44494452UL,
417
448
459typedef enum : uint8_t {
462
481typedef struct {
482 uint8_t port;
483 uint8_t pin;
485 uint8_t pmr_observed;
486 uint32_t pfs_raw;
488
514
531typedef struct {
532 uint8_t port;
533 uint8_t pin;
535
549 {(uint8_t)k_ra8_port_1, (uint8_t)k_ra8_pin_4},
550 {(uint8_t)k_ra8_port_8, (uint8_t)k_ra8_pin_8},
551 {(uint8_t)k_ra8_port_8, (uint8_t)k_ra8_pin_1},
552 {(uint8_t)k_ra8_port_1, (uint8_t)k_ra8_pin_0},
553 {(uint8_t)k_ra8_port_8, (uint8_t)k_ra8_pin_3},
554 {(uint8_t)k_ra8_port_1, (uint8_t)k_ra8_pin_3},
555 {(uint8_t)k_ra8_port_1, (uint8_t)k_ra8_pin_1},
556 {(uint8_t)k_ra8_port_1, (uint8_t)k_ra8_pin_2},
557 {(uint8_t)k_ra8_port_8, (uint8_t)k_ra8_pin_0},
558 {(uint8_t)k_ra8_port_8, (uint8_t)k_ra8_pin_2},
559 {(uint8_t)k_ra8_port_8, (uint8_t)k_ra8_pin_4},
560 {0xFFU, 0xFFU},
561};
562
564typedef enum : uint8_t {
568
589{
590 for (uint8_t i = 0U; i < (uint8_t)k_ra8_xspi_pin_count; i++) {
591 const uint8_t port = s_ra8_xspi_pin_refs[i].port;
592 const uint8_t pin = s_ra8_xspi_pin_refs[i].pin;
593 g_ra8_xspi_pin_observed[i].port = port;
595 g_ra8_xspi_pin_observed[i].psel_observed = (uint8_t)k_ra8_lx_pin_unobserved;
596 g_ra8_xspi_pin_observed[i].pmr_observed = (uint8_t)k_ra8_lx_pin_unobserved;
597 g_ra8_xspi_pin_observed[i].pfs_raw = 0U;
598 if ((port > (uint8_t)k_ra8_port_max) || (pin > (uint8_t)k_ra8_pin_max)) {
599 continue;
600 }
601 volatile uint32_t* pfs = ra8_pfs_pmn((ra8_port_t)port, (ra8_pin_t)pin);
602 if (pfs == nullptr) {
603 continue;
604 }
605 const uint32_t raw = *pfs;
606 g_ra8_xspi_pin_observed[i].pfs_raw = raw;
607 g_ra8_xspi_pin_observed[i].psel_observed =
608 (uint8_t)((raw >> (uint32_t)k_ra8_pfs_bit_psel0) & (uint32_t)k_ra8_lx_pfs_psel_mask);
609 g_ra8_xspi_pin_observed[i].pmr_observed =
610 (uint8_t)(((raw & (uint32_t)k_ra8_pfs_mask_pmr) != 0U) ? 1U : 0U);
611 }
612}
613
625static bool s_xspi_bus_ready = false;
626
638typedef enum : uint32_t {
641
656
678{
680 g_ra8_xspi_rdid_observed.call_count += 1U;
681 g_ra8_xspi_rdid_observed.rid_err = (uint32_t)k_ra8_ok;
682 g_ra8_xspi_rdid_observed.jedec_id = 0U;
683 g_ra8_xspi_rdid_observed.reset_8d_err = (uint32_t)k_ra8_ok;
684 g_ra8_xspi_rdid_observed.reset_1s_err = (uint32_t)k_ra8_ok;
686}
687
719{
722 ra8_log_error(s_lx_xspi_tag, "ra8_xspi_init 8d failed");
723 return (UINT)LX_ERROR;
724 }
726 const ra8_err_t reset_8d =
728 g_ra8_xspi_rdid_observed.reset_8d_err = (uint32_t)reset_8d;
729 /* Tolerate timeouts here: if the chip is in 1S mode, the 8D bytes
730 * never form a recognised opcode and CMDCMP may legitimately stall.
731 * The Phase-B 1S reset will recover regardless. */
733 return (UINT)LX_SUCCESS;
734}
735
760{
763 ra8_log_error(s_lx_xspi_tag, "ra8_xspi_init 1s failed");
764 return (UINT)LX_ERROR;
765 }
767 const ra8_err_t reset_1s =
769 g_ra8_xspi_rdid_observed.reset_1s_err = (uint32_t)reset_1s;
771 return (UINT)LX_SUCCESS;
772}
773
800{
802 uint32_t jedec_id = 0U;
803 const ra8_err_t rid_err = ra8_xspi_flash_read_id((uint8_t)k_ra8_lx_xspi_instance, &jedec_id);
804 g_ra8_xspi_rdid_observed.rid_err = (uint32_t)rid_err;
805 g_ra8_xspi_rdid_observed.jedec_id = jedec_id;
806 if (rid_err != k_ra8_ok) {
807 ra8_log_error_val(s_lx_xspi_tag, "RDID transfer failed err", (uint32_t)rid_err);
808 return (UINT)LX_ERROR;
809 }
810 ra8_log_info_val(s_lx_xspi_tag, "RDID returned", jedec_id);
811 if (jedec_id != (uint32_t)k_ra8_lx_jedec_expected) {
812 ra8_log_error_val(s_lx_xspi_tag, "RDID mismatch (expected 0x9D5A1A); got", jedec_id);
813 return (UINT)LX_ERROR;
814 }
815 return (UINT)LX_SUCCESS;
816}
817
845{
846 if (s_xspi_bus_ready) {
847 return (UINT)LX_SUCCESS;
848 }
849
850 /* Stamp the magic + bump the counter BEFORE any HAL call so even a
851 * hang inside the controller leaves a visible breadcrumb at the
852 * known SRAM address. */
854
856 /* Capture whatever pin state we have BEFORE bailing so JLink can
857 * see how far the BSP got even on partial-failure paths. */
859 ra8_log_error(s_lx_xspi_tag, "xspi pin init failed");
860 return (UINT)LX_ERROR;
861 }
862 /* Snapshot post-init PSEL on every OCTA pad. Any row where
863 * psel_observed != 0x1C or pmr_observed != 1 means the BSP enum is
864 * wrong for that pin -- the chip will not see that signal. */
866
867 const UINT phase_a = internal_reset_phase_8d();
868 if (phase_a != (UINT)LX_SUCCESS) {
869 return phase_a;
870 }
871 const UINT phase_b = internal_reset_phase_1s();
872 if (phase_b != (UINT)LX_SUCCESS) {
873 return phase_b;
874 }
875 const UINT probe = internal_probe_rdid();
876 if (probe != (UINT)LX_SUCCESS) {
877 return probe;
878 }
879
881 s_xspi_bus_ready = true;
882 return (UINT)LX_SUCCESS;
883}
884
885/* Lx nor driver ra xspi initialize -- see implementation for details. */
887{
888 if (nor_flash == LX_NULL) {
889 return (UINT)LX_ERROR;
890 }
891
892 /* Idempotent bus bring-up: pins + xSPI controller. Done here (not
893 * in main) so the LevelX driver is self-contained and apps don't
894 * have to remember the order of init calls. */
895 const UINT bus = internal_bus_init_once();
896 if (bus != (UINT)LX_SUCCESS) {
897 return bus;
898 }
899
900 /* Programme the synthetic base + geometry. LevelX uses the base
901 * pointer as a cookie when calling back into our read / write
902 * callbacks; we recover the byte offset from it inside
903 * internal_flash_offset_bytes(). */
904 nor_flash->lx_nor_flash_base_address = (ULONG*)(uintptr_t)k_ra8_lx_nor_base_addr;
905 nor_flash->lx_nor_flash_total_blocks = (ULONG)k_ra8_lx_nor_total_blocks;
906 nor_flash->lx_nor_flash_words_per_block = (ULONG)k_ra8_lx_nor_words_per_block;
907
908 /* Wire up the four LevelX driver callbacks. */
909 nor_flash->lx_nor_flash_driver_read = internal_nor_read;
910 nor_flash->lx_nor_flash_driver_write = internal_nor_write;
911 nor_flash->lx_nor_flash_driver_block_erase = internal_nor_block_erase;
912 nor_flash->lx_nor_flash_driver_block_erased_verify = internal_nor_block_erased_verify;
913
914 /* Hand LevelX a 512-byte ULONG-aligned RAM scratch buffer for its
915 * sector-mapping reads. */
916 nor_flash->lx_nor_flash_sector_buffer = &s_ra8_lx_nor_sector_buffer[0];
917
918 return (UINT)LX_SUCCESS;
919}
ra8_xspi_rdid_magic_t
Sentinel value distinguishing a written observation from cold-boot zero / stale RAM.
@ k_ra8_xspi_rdid_magic
'R','D','I','D' little-endian.
static const ra8_xspi_pin_ref_t s_ra8_xspi_pin_refs[k_ra8_xspi_pin_count]
Static reference list of OCTA (port, pin) pairs.
ra8_lx_nor_base_t
Synthetic flash base address handed to LevelX.
@ k_ra8_lx_nor_base_addr
Synthetic base for LevelX-managed window inside the flash.
static UINT internal_reset_phase_1s(void)
Phase B – switch the controller to 1S-1S-1S and reset again.
static UINT internal_nor_write(ULONG *flash_address, ULONG *source, ULONG words)
LevelX write callback: program words ULONGs from RAM to flash.
static UINT internal_nor_read(ULONG *flash_address, ULONG *destination, ULONG words)
LevelX read callback: copy words ULONGs from flash to RAM.
ra8_xspi_reset_timing_t
Post-software-reset settle time required by the IS25LX512M.
@ k_ra8_xspi_reset_settle_ms
>= tRPH (50 us).
ra8_lx_nor_constants_t
Compile-time constants for the xSPI <-> LevelX bridge.
@ k_ra8_lx_nor_word_bytes
Bytes per ULONG word (LevelX uses 4-byte ULONGs on this target).
@ k_ra8_lx_xspi_instance
xSPI instance routed to the EK-RA8D2 IS25LX512M flash chip.
@ k_ra8_lx_nor_words_per_block
LevelX words per block (= block_bytes / sizeof(ULONG)).
@ k_ra8_lx_nor_block_bytes
IS25LX512M erase-sector size (opcode 0x20 / sector erase).
@ k_ra8_lx_nor_total_blocks
Number of LevelX blocks exposed to wear-levelling.
static UINT internal_nor_block_erased_verify(ULONG block)
LevelX block-erased-verify callback: confirm a sector is 0xFF.
ra8_xspi_rdid_observation_t g_ra8_xspi_rdid_observed
JLink-readable observation of the last RDID probe.
ra8_xspi_reset_form_t
Width of the opcode pair shipped to ra8_xspi_software_reset.
@ k_ra8_xspi_reset_form_8d
Opcode + complement for 8D-8D-8D.
@ k_ra8_xspi_reset_form_1s
1-byte opcode for 1S-1S-1S.
static UINT internal_bus_init_once(void)
Bring the OCTA bus up (pins + controller) on first call.
static uint32_t internal_flash_offset_bytes(ULONG *flash_address)
ra8_xspi_pin_observation_t g_ra8_xspi_pin_observed[k_ra8_xspi_pin_count]
JLink-readable PSEL readback for the 12 OCTA pin slots.
ra8_lx_jedec_id_t
Expected JEDEC ID triplet for the EK-RA8D2 IS25LX512M-JHLE chip.
@ k_ra8_lx_jedec_type
IS25LX memory-type code.
@ k_ra8_lx_jedec_mfr
ISSI manufacturer ID.
@ k_ra8_lx_jedec_expected
Packed RDID expected.
@ k_ra8_lx_jedec_capacity
512 Mbit capacity code.
ra8_xspi_pin_count_t
Number of OCTA bus pin slots in g_ra8_xspi_pin_observed.
@ k_ra8_xspi_pin_count
CS + CK + DQS + DQ0..DQ7 + 1 spare.
static UINT internal_reset_phase_8d(void)
Phase A – punch the IS25LX512M out of any 8D-OPI mode.
UINT lx_nor_driver_ra8_xspi_initialize(LX_NOR_FLASH *nor_flash)
LevelX NOR driver-initialise entry point bridging onto ra8_xspi.
ra8_lx_pin_diag_t
Constants for the diagnostic xSPI pin-state capture.
@ k_ra8_lx_pin_unobserved
Sentinel before PFS is read.
@ k_ra8_lx_pfs_psel_mask
PSEL is the low 5 bits of the PFS.
ra8_xspi_stage_t
Bring-up stage markers stamped into g_ra8_xspi_rdid_observed.stage.
@ k_ra8_xspi_stage_init_1s
xSPI init in 1S mode.
@ k_ra8_xspi_stage_done
Bring-up succeeded.
@ k_ra8_xspi_stage_pins
Pin routing in progress.
@ k_ra8_xspi_stage_reset_1s
Software reset in 1S.
@ k_ra8_xspi_stage_reset_8d
Software reset in 8D.
@ k_ra8_xspi_stage_init_8d
xSPI init in 8D mode.
@ k_ra8_xspi_stage_rdid
RDID probe in flight.
static void internal_stamp_observed_start(void)
Stamp the observation breadcrumb at the start of a bring-up call.
static void internal_capture_xspi_pin_state(void)
Capture the post-init PFS state of every OCTA pin.
static ULONG s_ra8_lx_nor_sector_buffer[LX_NOR_SECTOR_SIZE]
Per-driver scratch buffer LevelX uses for sector mapping reads.
static UINT internal_nor_block_erase(ULONG block, ULONG erase_count)
LevelX block-erase callback: erase one 4 KiB sector by index.
static bool s_xspi_bus_ready
One-shot guard so we only run the bus bring-up once.
static const char * s_lx_xspi_tag
Logging tag for the LevelX <-> ra8_xspi bridge.
static UINT internal_probe_rdid(void)
Probe RDID and confirm the IS25LX512M is responsive.
LevelX NOR-flash driver shim that bridges LevelX onto the RA8 ra8_xspi HAL (EK-RA8D2 ISSI IS25LX512M ...
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
Board-support layer for the Renesas EK-RA8D2 v1 evaluation kit.
ra8_board_eth_pin_t pin
Pin.
ra8_err_t ra8_board_xspi_pins_init(void)
Route the 12 OCTA bus pins to the xSPI peripheral and pulse RESET_L.
Error Code Definitions for ra8-firmware.
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
Lightweight Logging Interface for ra8-firmware.
#define ra8_log_info_val(tag, message, value)
RA8 log info val.
Definition ra8_log.h:366
#define ra8_log_error_val(tag, message, value)
RA8 log error val.
Definition ra8_log.h:337
#define ra8_log_error(tag, message)
RA8 log error.
Definition ra8_log.h:335
@ k_ra8_xspi_lio_1s1s1s
1S-1S-1S extended SPI.
@ k_ra8_xspi_lio_8d8d8d
8D-8D-8D octal DDR (OPI).
Pin Function Select (PFS) register layout for the Renesas RA8D2.
@ k_ra8_pfs_mask_pmr
RA8 PFS mask pmr.
@ k_ra8_pfs_bit_psel0
Peripheral select bit 0.
static volatile uint32_t * ra8_pfs_pmn(ra8_port_t port, ra8_pin_t pin)
Get pointer to the PmnPFS register for a single pin.
Typed Port / Pin Constants for the RA8D2 IOPORT Module.
ra8_port_t
Port indices for the RA8D2 IOPORT module.
@ k_ra8_port_max
Highest valid port index.
@ k_ra8_port_8
RA8 port 8.
@ k_ra8_port_1
RA8 port 1.
ra8_pin_t
Pin indices within an IOPORT port (0..15).
@ k_ra8_pin_4
RA8 pin 4.
@ k_ra8_pin_max
Highest valid pin index.
@ k_ra8_pin_1
RA8 pin 1.
@ k_ra8_pin_2
RA8 pin 2.
@ k_ra8_pin_8
RA8 pin 8.
@ k_ra8_pin_0
RA8 pin 0.
@ k_ra8_pin_3
RA8 pin 3.
SysTick-based tick counter, delay and timestamp helpers.
void ra8_delay_ms(uint32_t ms)
Busy-wait for at least ms milliseconds.
Definition ra8_time.c:129
unsigned int UINT
ThreadX-compatible unsigned int (host stub).
unsigned long ULONG
ThreadX-compatible unsigned long (host stub).
xSPI / Octo-SPI driver (flash read/program/erase + ID/status)
ra8_err_t ra8_xspi_flash_program(uint8_t instance, uint32_t flash_addr, const uint8_t *data, uint32_t len)
Program (write) len bytes at flash_addr.
ra8_err_t ra8_xspi_software_reset(uint8_t instance, uint8_t cmd_bytes)
Issue the JEDEC software-reset pair (RSTEN 0x66 + RST 0x99).
Definition ra8_xspi.c:756
ra8_err_t ra8_xspi_init(uint8_t instance, ra8_xspi_lio_mode_t mode)
Initialise an xSPI instance in the chosen link-layer IO mode.
Definition ra8_xspi.c:372
ra8_err_t ra8_xspi_flash_read(uint8_t instance, uint32_t flash_addr, uint8_t *buf, uint32_t len)
Read len bytes from external flash into buf.
ra8_err_t ra8_xspi_flash_erase_sector(uint8_t instance, uint32_t flash_addr)
Erase the 4 KiB sector containing flash_addr.
ra8_err_t ra8_xspi_flash_read_id(uint8_t instance, uint32_t *out_id)
Read the JEDEC ID (opcode 0x9F), returning 24 bits in a uint32_t.
One row per OCTA pin captured AFTER ra8_board_xspi_pins_init.
uint8_t pmr_observed
1 if PFS.PMR == 1 after init.
uint8_t psel_observed
PFS PSEL[28..24] >> 24.
uint8_t pin
Pin index within the port.
uint32_t pfs_raw
Full 32-bit PmnPFS readback.
uint8_t port
Port index from ra8_port_t.
Board-side reference (port, pin) for the 12 OCTA pad slots.
JLink-readable snapshot of the most recent RDID probe.
uint32_t jedec_id
Last raw RDID triplet from the HAL.
uint32_t magic
k_ra8_xspi_rdid_magic once written.
uint32_t call_count
Number of probe attempts since reset.
uint32_t reset_8d_err
ra8_err_t from 8D-form software reset.
uint32_t rid_err
Last ra8_err_t from read_id (uint).
uint32_t stage
Last stage reached (see ra8_xspi_stage_t).
uint32_t reset_1s_err
ra8_err_t from 1S-form software reset.
uint32_t reserved
Reserved for future diagnostics.