From e8ec3bfc3b4a636c91f26d198b3c3d6f75214ddf Mon Sep 17 00:00:00 2001 From: Farjad Alam Date: Mon, 26 Oct 2026 17:02:28 -0700 Subject: [PATCH 1/3] replace IP with ROM API --- examples/mcxa-577app/Cargo.lock | 35 ++- examples/mcxa-577app/Cargo.toml | 4 +- libs/Cargo.toml | 4 +- libs/ec-slimloader-mcxa/Cargo.toml | 1 - libs/ec-slimloader-mcxa/src/board.rs | 311 ++++++++++----------------- 5 files changed, 137 insertions(+), 218 deletions(-) diff --git a/examples/mcxa-577app/Cargo.lock b/examples/mcxa-577app/Cargo.lock index 78e25c4..d8f52b1 100644 --- a/examples/mcxa-577app/Cargo.lock +++ b/examples/mcxa-577app/Cargo.lock @@ -354,11 +354,10 @@ dependencies = [ [[package]] name = "embassy-embedded-hal" version = "0.6.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "embassy-futures", - "embassy-hal-internal", - "embassy-sync 0.8.0 (git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e)", + "embassy-sync 0.8.0 (git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c)", "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", @@ -384,12 +383,12 @@ dependencies = [ [[package]] name = "embassy-executor" version = "0.10.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "cordyceps", "critical-section", "document-features", - "embassy-executor-macros 0.8.0 (git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e)", + "embassy-executor-macros 0.8.0 (git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c)", "embassy-executor-timer-queue 0.1.1", "unitrait", ] @@ -409,7 +408,7 @@ dependencies = [ [[package]] name = "embassy-executor-macros" version = "0.8.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "darling", "proc-macro2", @@ -426,7 +425,7 @@ checksum = "2fc328bf943af66b80b98755db9106bf7e7471b0cf47dc8559cd9a6be504cc9c" [[package]] name = "embassy-executor-timer-queue" version = "0.1.1" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "unitrait", ] @@ -434,12 +433,12 @@ dependencies = [ [[package]] name = "embassy-futures" version = "0.1.2" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" [[package]] name = "embassy-hal-internal" version = "0.5.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "cortex-m", "critical-section", @@ -449,7 +448,7 @@ dependencies = [ [[package]] name = "embassy-mcxa" version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "bbqueue", "convert_case", @@ -458,11 +457,11 @@ dependencies = [ "critical-section", "defmt 1.1.1", "embassy-embedded-hal", - "embassy-executor 0.10.0 (git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e)", + "embassy-executor 0.10.0 (git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c)", "embassy-futures", "embassy-hal-internal", - "embassy-sync 0.8.0 (git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e)", - "embassy-time 0.5.1 (git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e)", + "embassy-sync 0.8.0 (git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c)", + "embassy-time 0.5.1 (git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c)", "embassy-time-driver", "embassy-time-queue-utils", "embedded-can", @@ -506,7 +505,7 @@ dependencies = [ [[package]] name = "embassy-sync" version = "0.8.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "cfg-if", "critical-section", @@ -536,7 +535,7 @@ dependencies = [ [[package]] name = "embassy-time" version = "0.5.1" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "cfg-if", "critical-section", @@ -551,7 +550,7 @@ dependencies = [ [[package]] name = "embassy-time-driver" version = "0.2.2" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "document-features", ] @@ -559,7 +558,7 @@ dependencies = [ [[package]] name = "embassy-time-queue-utils" version = "0.3.2" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "embassy-executor-timer-queue 0.1.1", "heapless 0.9.3", @@ -946,7 +945,7 @@ dependencies = [ [[package]] name = "nxp-pac" version = "0.1.0" -source = "git+https://github.com/embassy-rs/nxp-pac.git?rev=0c2b68a1c1badce2cf09ba8bb3aae25e72776b2b#0c2b68a1c1badce2cf09ba8bb3aae25e72776b2b" +source = "git+https://github.com/embassy-rs/nxp-pac.git?rev=2bfe368b9ad974d03a41279bddb1ef05705ae9d8#2bfe368b9ad974d03a41279bddb1ef05705ae9d8" dependencies = [ "cfg_aliases", "cortex-m", diff --git a/examples/mcxa-577app/Cargo.toml b/examples/mcxa-577app/Cargo.toml index a69e638..bcc4d50 100644 --- a/examples/mcxa-577app/Cargo.toml +++ b/examples/mcxa-577app/Cargo.toml @@ -9,12 +9,12 @@ members = [ partition-manager = { git = "https://github.com/OpenDevicePartnership/embedded-services.git", tag = "v0.1.0", default-features = false } partition-manager-macros = { git = "https://github.com/OpenDevicePartnership/embedded-services.git", tag = "v0.1.0" } embassy-sync = "0.8.0" -embassy-mcxa = { git = "https://github.com/embassy-rs/embassy", rev = "f0fd6e2a8e9c505290498e316ad2bdda8b94da5e", default-features = false } +embassy-mcxa = { git = "https://github.com/embassy-rs/embassy", rev = "2b34c20e968bbb12baba58aded23b7c1061e444c", default-features = false } embassy-executor = "0.10.0" embassy-time = "0.5.0" [patch.crates-io] -embassy-time-driver = { git = "https://github.com/embassy-rs/embassy", rev = "f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" } +embassy-time-driver = { git = "https://github.com/embassy-rs/embassy", rev = "2b34c20e968bbb12baba58aded23b7c1061e444c" } [profile.dev] panic = "abort" diff --git a/libs/Cargo.toml b/libs/Cargo.toml index 48e4310..15ba404 100644 --- a/libs/Cargo.toml +++ b/libs/Cargo.toml @@ -36,7 +36,7 @@ partition-manager = { git = "https://github.com/OpenDevicePartnership/embedded-s partition-manager-macros = { git = "https://github.com/OpenDevicePartnership/embedded-services.git", tag = "v0.1.0" } embassy-sync = "0.8.0" embassy-futures = "0.1.0" -embassy-mcxa = { git = "https://github.com/embassy-rs/embassy", rev = "f0fd6e2a8e9c505290498e316ad2bdda8b94da5e", default-features = false } +embassy-mcxa = { git = "https://github.com/embassy-rs/embassy", rev = "2b34c20e968bbb12baba58aded23b7c1061e444c", default-features = false } [patch.crates-io] -embassy-time-driver = { git = "https://github.com/embassy-rs/embassy", rev = "f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" } +embassy-time-driver = { git = "https://github.com/embassy-rs/embassy", rev = "2b34c20e968bbb12baba58aded23b7c1061e444c" } diff --git a/libs/ec-slimloader-mcxa/Cargo.toml b/libs/ec-slimloader-mcxa/Cargo.toml index 7cc62a1..8d724be 100644 --- a/libs/ec-slimloader-mcxa/Cargo.toml +++ b/libs/ec-slimloader-mcxa/Cargo.toml @@ -31,7 +31,6 @@ fault-rejection = { path = "../fault-rejection" } default = ["mcxa5xx", "defmt"] defmt = ["dep:defmt", "defmt-or-log/defmt", "ec-slimloader/defmt", "partition-manager/defmt", "mcxa-security-provisioning/defmt"] log = ["dep:log", "defmt-or-log/log", "ec-slimloader/log", "mcxa-security-provisioning/log"] -flexspi-selftest = [] mcxa5xx = ["embassy-mcxa/mcxa5xx"] # Use this when targeting the FRDM-MCXA577 EVK (flash on FlexSPI Port A). # Without this flag the driver targets Port B (custom board wiring for end target HW). diff --git a/libs/ec-slimloader-mcxa/src/board.rs b/libs/ec-slimloader-mcxa/src/board.rs index e8ebb19..112fb31 100644 --- a/libs/ec-slimloader-mcxa/src/board.rs +++ b/libs/ec-slimloader-mcxa/src/board.rs @@ -29,8 +29,8 @@ use crate::{jump, verification}; /// External flash total size (2 MB). const EXTERNAL_FLASH_SIZE: usize = 0x0020_0000; const READ_ALIGNMENT: usize = 1; -// 1-byte logical writes; write bridges to FlexSPI's 8-byte-aligned -// `page_program` with a read-modify-write. +// 1-byte logical writes; write bridges to the ROM `partial_program`, split on +// page boundaries. const WRITE_ALIGNMENT: usize = 1; const ERASE_ALIGNMENT: usize = 4096; const MAX_SLOT_COUNT: usize = 2; @@ -55,78 +55,19 @@ fn in_emulation_window(offset: usize, len: usize) -> bool { // ─── FlexSPI NOR backend (MCXA hardware only) ───────────────────────────── #[cfg(all(target_os = "none", feature = "mcxa5xx"))] -use embassy_mcxa::clocks::PoweredClock; -#[cfg(all(target_os = "none", feature = "mcxa5xx"))] use embassy_mcxa::clocks::config::{FircConfig, FircFreqSel, VddDriveStrength, VddLevel}; #[cfg(all(target_os = "none", feature = "mcxa5xx"))] -use embassy_mcxa::flexspi::lookup::opcodes::sdr::{CMD, RADDR, READ, WRITE}; -#[cfg(all(target_os = "none", feature = "mcxa5xx"))] -use embassy_mcxa::flexspi::lookup::{Command, Instr, LookupTable, Pads, SequenceBuilder}; -#[cfg(all(target_os = "none", feature = "mcxa5xx"))] -use embassy_mcxa::flexspi::{Blocking, ClockConfig, FlashConfig, Flexspi, NorFlash as FlexspiNor}; +use embassy_mcxa::rom::{ + FlexspiNor, FlexspiNorPins, FlexspiSerialClockFrequency, SerialNorConfigOption, SerialNorDeviceType, + SerialNorFlashConnection, SerialNorMiscMode, SerialNorOptionPadEncoding, SerialNorOptionSize, + SerialNorQuadModeSetting, pack_serial_nor_option0, pack_serial_nor_option1, +}; #[cfg(all(target_os = "none", feature = "mcxa5xx"))] use embassy_mcxa::{Peri, peripherals}; -/// Single-lane (1-bit SPI) LUT + geometry for the Macronix MX25U. -/// Port A (EVK, `mcxa5xxevk` feature): flash starts at SFAR=0, use actual size. -/// Port B (custom board, default): A1 is disabled in post-init so B1 starts at SFAR=0. +/// External NOR page size; ROM reads/programs are chunked to at most one page. #[cfg(all(target_os = "none", feature = "mcxa5xx"))] -const MX25U_FLASH_CONFIG: FlashConfig = FlashConfig { - flash_size_kbytes: EXTERNAL_FLASH_SIZE as u32 / 1024, - page_size: 256, - busy_status_polarity: true, - busy_status_offset: 0, - lookup_table: LookupTable::new() - .command( - Command::Read, - SequenceBuilder::new() - .instr(Instr::new(CMD, Pads::One, 0x03)) - .instr(Instr::new(RADDR, Pads::One, 0x18)) - .instr(Instr::new(READ, Pads::One, 0x00)) - .build(), - ) - .command( - Command::ReadStatus, - SequenceBuilder::new() - .instr(Instr::new(CMD, Pads::One, 0x05)) - .instr(Instr::new(READ, Pads::One, 0x00)) - .build(), - ) - .command( - Command::WriteEnable, - SequenceBuilder::new().instr(Instr::new(CMD, Pads::One, 0x06)).build(), - ) - .command( - Command::ReadId, - SequenceBuilder::new() - .instr(Instr::new(CMD, Pads::One, 0x9F)) - .instr(Instr::new(READ, Pads::One, 0x00)) - .build(), - ) - .command( - Command::EraseSector, - SequenceBuilder::new() - .instr(Instr::new(CMD, Pads::One, 0x20)) - .instr(Instr::new(RADDR, Pads::One, 0x18)) - .build(), - ) - .command( - Command::PageProgram, - SequenceBuilder::new() - .instr(Instr::new(CMD, Pads::One, 0x02)) - .instr(Instr::new(RADDR, Pads::One, 0x18)) - .instr(Instr::new(WRITE, Pads::One, 0x00)) - .build(), - ), - read_seq: Command::Read as u8, - read_status_seq: Command::ReadStatus as u8, - write_enable_seq: Command::WriteEnable as u8, - read_id_seq: Command::ReadId as u8, - erase_sector_seq: Command::EraseSector as u8, - page_program_seq: Command::PageProgram as u8, - reset_sequence: None, - device_mode_command: None, -}; +const FLASH_PAGE_SIZE: usize = 256; pub type StatePartition = Partition<'static, ExternalStorage, RW, NoopRawMutex>; pub type SlotPartition = Partition<'static, ExternalStorage, RO, NoopRawMutex>; @@ -152,20 +93,20 @@ pub struct Partitions { /// MCXA external flash storage backend. /// -/// On MCXA hardware (`mcxa5xx`) this drives the external MX25U QSPI NOR through -/// the FlexSPI controller (Port B) with single-lane IP commands (read / -/// sector-erase / page-program), so the boot journal is really persisted in +/// On MCXA hardware (`mcxa5xx`) this drives the external QSPI NOR through the +/// FlexSPI controller using the MCXA ROM FlexSPI NOR driver (read / +/// sector-erase / partial-program), so the boot journal is really persisted in /// external flash. On host builds it falls back to a RAM shadow so the boot /// state machine can be unit-tested without hardware. pub struct ExternalStorage { #[cfg(all(target_os = "none", feature = "mcxa5xx"))] - nor: FlexspiNor<'static, Blocking>, + nor: FlexspiNor<'static>, } impl ExternalStorage { - /// Wrap an initialized FlexSPI NOR driver (real external flash). + /// Wrap an initialized ROM FlexSPI NOR handle (real external flash). #[cfg(all(target_os = "none", feature = "mcxa5xx"))] - pub fn new(nor: FlexspiNor<'static, Blocking>) -> Self { + pub fn new(nor: FlexspiNor<'static>) -> Self { Self { nor } } @@ -199,12 +140,26 @@ impl ReadNorFlash for ExternalStorage { return Err(NorFlashErrorKind::OutOfBounds); } - // Real FlexSPI IP read from external NOR. + // ROM read needs a 32-bit aligned destination; bounce through a word + // buffer one page at a time. #[cfg(all(target_os = "none", feature = "mcxa5xx"))] { - self.nor - .blocking_read(offset, bytes) - .map_err(|_| NorFlashErrorKind::Other)?; + let mut start: u32 = offset; + let mut curr: usize = 0; + let mut words = [0u32; FLASH_PAGE_SIZE / 4]; + while curr < bytes.len() { + let read_len = core::cmp::min(FLASH_PAGE_SIZE, bytes.len() - curr); + + self.nor + .read(&mut words, start, read_len as u32) + .map_err(|_| NorFlashErrorKind::Other)?; + for (dst, word) in bytes[curr..curr + read_len].chunks_mut(4).zip(words.iter()) { + dst.copy_from_slice(&word.to_le_bytes()[..dst.len()]); + } + + start += read_len as u32; + curr += read_len; + } } // Host / non-hardware: RAM-shadow emulation. @@ -249,9 +204,7 @@ impl NorFlash for ExternalStorage { { let mut addr = from; while addr < to { - self.nor - .blocking_erase_sector(addr) - .map_err(|_| NorFlashErrorKind::Other)?; + self.nor.erase_sector(addr).map_err(|_| NorFlashErrorKind::Other)?; addr += Self::ERASE_SIZE as u32; } } @@ -272,7 +225,7 @@ impl NorFlash for ExternalStorage { Ok(()) } - // Slicing in the RMW and host RAM-shadow paths is bounds-guaranteed. + // Slicing in the page-split and host RAM-shadow paths is bounds-guaranteed. #[allow(clippy::indexing_slicing)] async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { let offset_usize = offset as usize; @@ -282,28 +235,29 @@ impl NorFlash for ExternalStorage { } // WRITE_SIZE is 1, so every offset/length is aligned (no check needed). - // page_program needs 8-byte-aligned address+length + // ROM `partial_program` programs an arbitrary byte span from a 32-bit + // aligned source. Bounce through a word buffer and never cross a page + // boundary in a single program operation. #[cfg(all(target_os = "none", feature = "mcxa5xx"))] { - const UNIT: usize = 8; - let mut pos = offset_usize; - let mut src = bytes; - while !src.is_empty() { - let block = pos & !(UNIT - 1); - let in_block = pos - block; - let n = core::cmp::min(UNIT - in_block, src.len()); - - let mut buf = [0u8; UNIT]; - self.nor - .blocking_read(block as u32, &mut buf) - .map_err(|_| NorFlashErrorKind::Other)?; - buf[in_block..in_block + n].copy_from_slice(&src[..n]); + let mut start: u32 = offset; + let mut curr: usize = 0; + let mut words = [u32::MAX; FLASH_PAGE_SIZE / 4]; + while curr < bytes.len() { + let page_remaining = FLASH_PAGE_SIZE - start as usize % FLASH_PAGE_SIZE; + let write_len = core::cmp::min(page_remaining, bytes.len() - curr); + + for (word, chunk) in words.iter_mut().zip(bytes[curr..curr + write_len].chunks(4)) { + let mut bounce = [0xFF; 4]; + bounce[..chunk.len()].copy_from_slice(chunk); + *word = u32::from_le_bytes(bounce); + } self.nor - .blocking_page_program(block as u32, &buf) + .partial_program(start, &words, write_len as u32) .map_err(|_| NorFlashErrorKind::Other)?; - pos += n; - src = &src[n..]; + start += write_len as u32; + curr += write_len; } } @@ -402,110 +356,77 @@ impl Board for Mcxa { #[cfg(all(target_os = "none", feature = "mcxa5xx"))] let external = { - // FIRC is 192 MHz; use div=16 → 12 MHz serial clock, matching the - // known-good speed from the original 48 MHz FIRC + div=4 default. - // The MX25U standard-SPI READ (0x03) max is ~50 MHz but board - // capacitance probably makes 48 MHz (192/4) unreliable. - use embassy_mcxa::clocks::periph_helpers::{Div4, FlexspiClockSel}; - const FLEXSPI_DIV: Div4 = match Div4::from_divisor(16) { - Some(d) => d, - None => panic!("invalid FlexSPI divisor"), - }; - const FLEXSPI_CLOCK: ClockConfig = ClockConfig { - power: PoweredClock::NormalEnabledDeepSleepDisabled, - source: FlexspiClockSel::FroHf, - div: FLEXSPI_DIV, - }; + // Reset FlexSPI so the ROM get_config/init start from the hardware + // default rather than inheriting leftover controller state. + // SAFETY: FLEXSPI0 is not otherwise in use at this point in boot. + unsafe { + embassy_mcxa::clocks::pulse_reset::(); + } #[cfg(not(feature = "mcxa5xxevk"))] - let flexspi = Flexspi::new_blocking( - p.FLEXSPI0, - p.P3_17, // Port B: B_SS0 (CS0) - p.P3_16, // Port B: B_SCLK - p.P3_1, // Port B: B_DQS - p.P3_15, // Port B: B_DATA0 - p.P3_14, // Port B: B_DATA1 - p.P3_13, // Port B: B_DATA2 - p.P3_12, // Port B: B_DATA3 - FLEXSPI_CLOCK, - MX25U_FLASH_CONFIG, - ) - .unwrap_or_else(|_| panic!("FlexSPI init failed")); + let (pins, connection) = ( + FlexspiNorPins::new( + p.P3_17, // Port B: B_SS0 (CS0) + p.P3_16, // Port B: B_SCLK + p.P3_1, // Port B: B_DQS + p.P3_15, // Port B: B_DATA0 + p.P3_14, // Port B: B_DATA1 + p.P3_13, // Port B: B_DATA2 + p.P3_12, // Port B: B_DATA3 + ), + SerialNorFlashConnection::SinglePortB, + ); #[cfg(feature = "mcxa5xxevk")] - let flexspi = Flexspi::new_blocking( - p.FLEXSPI0, - p.P3_0, // Port A EVK: A_SS0 (CS0) - p.P3_7, // Port A EVK: A_SCLK - p.P3_6, // Port A EVK: A_DQS - p.P3_8, // Port A EVK: A_DATA0 - p.P3_9, // Port A EVK: A_DATA1 - p.P3_10, // Port A EVK: A_DATA2 - p.P3_11, // Port A EVK: A_DATA3 - FLEXSPI_CLOCK, - MX25U_FLASH_CONFIG, - ) - .unwrap_or_else(|_| panic!("FlexSPI init failed")); - let external = ExternalStorage::new(FlexspiNor::new(flexspi)); - // Port B only: disable A1 (size=0) so B1 starts at SFAR=0, - // matching the driver's write_enable/read_status which use SFAR=0. - // Port A (EVK) needs no post-init — A1 is chip_index=0 and SFAR=0 - // already targets it natively. - #[cfg(not(feature = "mcxa5xxevk"))] - { - use embassy_mcxa::pac; - pac::FLEXSPI0.flshcr1(2).write_value(pac::FLEXSPI0.flshcr1(1).read()); - pac::FLEXSPI0.flshcr2(2).write_value(pac::FLEXSPI0.flshcr2(1).read()); - pac::FLEXSPI0.dllcr(1).write_value(pac::FLEXSPI0.dllcr(0).read()); - pac::FLEXSPI0.flshcr0(0).write(|r| r.set_flshsz(0)); - pac::FLEXSPI0.mcr0().modify(|r| r.set_mdis(pac::flexspi::Mdis::Val1)); - pac::FLEXSPI0 - .mcr0() - .modify(|r| r.set_rxclksrc(pac::flexspi::Rxclksrc::Val0)); - pac::FLEXSPI0.mcr0().modify(|r| r.set_mdis(pac::flexspi::Mdis::Val0)); - pac::FLEXSPI0 - .mcr0() - .modify(|r| r.set_swreset(pac::flexspi::Swreset::Val1)); - while pac::FLEXSPI0.mcr0().read().swreset() == pac::flexspi::Swreset::Val1 {} - } - external + let (pins, connection) = ( + FlexspiNorPins::new( + p.P3_0, // Port A EVK: A_SS0 (CS0) + p.P3_7, // Port A EVK: A_SCLK + p.P3_6, // Port A EVK: A_DQS + p.P3_8, // Port A EVK: A_DATA0 + p.P3_9, // Port A EVK: A_DATA1 + p.P3_10, // Port A EVK: A_DATA2 + p.P3_11, // Port A EVK: A_DATA3 + ), + SerialNorFlashConnection::SinglePortA, + ); + + // SFDP SDR auto-probe (non-destructive; DDR probing would write test + // patterns), 1-pad query/cmd, quad mode left to SFDP, 30 MHz. + let option = SerialNorConfigOption { + option0: pack_serial_nor_option0( + SerialNorOptionSize::Option0AndOption1, + SerialNorDeviceType::ReadSfdpSdr, + SerialNorOptionPadEncoding::One, + SerialNorOptionPadEncoding::One, + SerialNorQuadModeSetting::NotConfigured, + SerialNorMiscMode::Disabled, + FlexspiSerialClockFrequency::MHz30, + ), + option1: pack_serial_nor_option1(connection, 0, 0, 0, 0, 0), + }; + + // Keep the ROM-probed LUT; override only what the ROM can't infer: + // - read_sample_clk_src = 0: internal dummy-read strobe (no-DQS board). + // - Port B: disable A1/A2 so B1 sits at SFAR 0, and size B1. + let nor = embassy_mcxa::rom::get() + .flex_spi_with_options(p.FLEXSPI0, pins, option, |cfg| { + cfg.mem_config.read_sample_clk_src = 0; + #[cfg(not(feature = "mcxa5xxevk"))] + { + cfg.mem_config.sflash_a1_size = 0; + cfg.mem_config.sflash_a2_size = 0; + cfg.mem_config.sflash_b1_size = EXTERNAL_FLASH_SIZE as u32; + cfg.mem_config.sflash_b2_size = 0; + } + }) + .unwrap_or_else(|_| panic!("FlexSPI init failed")); + + ExternalStorage::new(nor) }; #[cfg(not(all(target_os = "none", feature = "mcxa5xx")))] let external = ExternalStorage::init(); - // FlexSPI external-flash READ probe (read-only, modifies nothing): dumps - // the JEDEC id + boot-journal bytes so you can compare the journal - // before/after an external write done by another tool (e.g. SurfDbg - // 0xFF'ing the journal). If the bytes change, that tool really wrote to - // external flash; if they don't, it didn't. - #[cfg(all(target_os = "none", feature = "mcxa5xx", feature = "flexspi-selftest"))] - let external = { - let mut external = external; - use embassy_mcxa::pac; - // JEDEC/vendor id: proves the MCU can actually reach the external NOR. - flexspi_ip_command(0, Command::ReadId as u8, 3); - let jedec = pac::FLEXSPI0.rfdr(0).read().rxdata(); - defmt_or_log::info!( - "flexspi-probe: JEDEC id = {:#010x} (MX25U low byte should be 0xc2)", - jedec - ); - // Boot-journal bytes at external offset 0x1000 (read-only). - let mut j = [0u8; 16]; - let _ = external.read(0x1000, &mut j).await; - defmt_or_log::info!( - "flexspi-probe: journal @0x1000 = [{:#04x} {:#04x} {:#04x} {:#04x} {:#04x} {:#04x} {:#04x} {:#04x}]", - j[0], - j[1], - j[2], - j[3], - j[4], - j[5], - j[6], - j[7] - ); - external - }; - let ext_flash_manager = EXT_FLASH.init_with(|| PartitionManager::new(external)); let Partitions { state, slots } = config.partitions(ext_flash_manager); From bb60a08c9112604ae50beec17120f71e43db6508 Mon Sep 17 00:00:00 2001 From: Farjad Alam Date: Mon, 26 Oct 2026 17:47:24 -0700 Subject: [PATCH 2/3] update lock file --- libs/Cargo.lock | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/libs/Cargo.lock b/libs/Cargo.lock index 60e0f1f..2960105 100644 --- a/libs/Cargo.lock +++ b/libs/Cargo.lock @@ -563,11 +563,10 @@ dependencies = [ [[package]] name = "embassy-embedded-hal" version = "0.6.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ - "embassy-futures 0.1.2 (git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e)", - "embassy-hal-internal 0.5.0", - "embassy-sync 0.8.0 (git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e)", + "embassy-futures 0.1.2 (git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c)", + "embassy-sync 0.8.0 (git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c)", "embedded-hal 0.2.7", "embedded-hal 1.0.0", "embedded-hal-async", @@ -579,7 +578,7 @@ dependencies = [ [[package]] name = "embassy-executor" version = "0.10.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "cordyceps", "critical-section", @@ -592,7 +591,7 @@ dependencies = [ [[package]] name = "embassy-executor-macros" version = "0.8.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "darling", "proc-macro2", @@ -603,7 +602,7 @@ dependencies = [ [[package]] name = "embassy-executor-timer-queue" version = "0.1.1" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "unitrait", ] @@ -617,7 +616,7 @@ checksum = "dc2d050bdc5c21e0862a89256ed8029ae6c290a93aecefc73084b3002cdebb01" [[package]] name = "embassy-futures" version = "0.1.2" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" [[package]] name = "embassy-hal-internal" @@ -643,7 +642,7 @@ dependencies = [ [[package]] name = "embassy-hal-internal" version = "0.5.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "cortex-m", "critical-section", @@ -689,7 +688,7 @@ dependencies = [ [[package]] name = "embassy-mcxa" version = "0.1.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "bbqueue", "convert_case 0.11.0", @@ -697,11 +696,11 @@ dependencies = [ "cortex-m-rt", "critical-section", "defmt 1.1.1", - "embassy-embedded-hal 0.6.0 (git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e)", + "embassy-embedded-hal 0.6.0 (git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c)", "embassy-executor", - "embassy-futures 0.1.2 (git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e)", + "embassy-futures 0.1.2 (git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c)", "embassy-hal-internal 0.5.0", - "embassy-sync 0.8.0 (git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e)", + "embassy-sync 0.8.0 (git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c)", "embassy-time", "embassy-time-driver", "embassy-time-queue-utils", @@ -761,7 +760,7 @@ dependencies = [ [[package]] name = "embassy-sync" version = "0.8.0" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "cfg-if", "critical-section", @@ -774,7 +773,7 @@ dependencies = [ [[package]] name = "embassy-time" version = "0.5.1" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "cfg-if", "critical-section", @@ -789,7 +788,7 @@ dependencies = [ [[package]] name = "embassy-time-driver" version = "0.2.2" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "document-features", ] @@ -797,7 +796,7 @@ dependencies = [ [[package]] name = "embassy-time-queue-utils" version = "0.3.2" -source = "git+https://github.com/embassy-rs/embassy?rev=f0fd6e2a8e9c505290498e316ad2bdda8b94da5e#f0fd6e2a8e9c505290498e316ad2bdda8b94da5e" +source = "git+https://github.com/embassy-rs/embassy?rev=2b34c20e968bbb12baba58aded23b7c1061e444c#2b34c20e968bbb12baba58aded23b7c1061e444c" dependencies = [ "embassy-executor-timer-queue", "heapless 0.9.3", @@ -1282,7 +1281,7 @@ dependencies = [ [[package]] name = "nxp-pac" version = "0.1.0" -source = "git+https://github.com/embassy-rs/nxp-pac.git?rev=0c2b68a1c1badce2cf09ba8bb3aae25e72776b2b#0c2b68a1c1badce2cf09ba8bb3aae25e72776b2b" +source = "git+https://github.com/embassy-rs/nxp-pac.git?rev=2bfe368b9ad974d03a41279bddb1ef05705ae9d8#2bfe368b9ad974d03a41279bddb1ef05705ae9d8" dependencies = [ "cfg_aliases", "cortex-m", From 1f9740a0ac4bc24ed3182bb4dced9e81afa42d75 Mon Sep 17 00:00:00 2001 From: Farjad Alam Date: Tue, 27 Oct 2026 08:56:42 -0700 Subject: [PATCH 3/3] address comments --- libs/ec-slimloader-mcxa/src/board.rs | 52 +++++++++++----------------- 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/libs/ec-slimloader-mcxa/src/board.rs b/libs/ec-slimloader-mcxa/src/board.rs index 112fb31..aa308a9 100644 --- a/libs/ec-slimloader-mcxa/src/board.rs +++ b/libs/ec-slimloader-mcxa/src/board.rs @@ -58,8 +58,8 @@ fn in_emulation_window(offset: usize, len: usize) -> bool { use embassy_mcxa::clocks::config::{FircConfig, FircFreqSel, VddDriveStrength, VddLevel}; #[cfg(all(target_os = "none", feature = "mcxa5xx"))] use embassy_mcxa::rom::{ - FlexspiNor, FlexspiNorPins, FlexspiSerialClockFrequency, SerialNorConfigOption, SerialNorDeviceType, - SerialNorFlashConnection, SerialNorMiscMode, SerialNorOptionPadEncoding, SerialNorOptionSize, + FlexspiClockSource, FlexspiNor, FlexspiNorPins, FlexspiSerialClockFrequency, SerialNorConfigOption, + SerialNorDeviceType, SerialNorFlashConnection, SerialNorMiscMode, SerialNorOptionPadEncoding, SerialNorOptionSize, SerialNorQuadModeSetting, pack_serial_nor_option0, pack_serial_nor_option1, }; #[cfg(all(target_os = "none", feature = "mcxa5xx"))] @@ -144,21 +144,15 @@ impl ReadNorFlash for ExternalStorage { // buffer one page at a time. #[cfg(all(target_os = "none", feature = "mcxa5xx"))] { - let mut start: u32 = offset; - let mut curr: usize = 0; let mut words = [0u32; FLASH_PAGE_SIZE / 4]; - while curr < bytes.len() { - let read_len = core::cmp::min(FLASH_PAGE_SIZE, bytes.len() - curr); - + for (page, chunk) in bytes.chunks_mut(FLASH_PAGE_SIZE).enumerate() { + let start = offset + (page * FLASH_PAGE_SIZE) as u32; self.nor - .read(&mut words, start, read_len as u32) + .read(&mut words, start, chunk.len() as u32) .map_err(|_| NorFlashErrorKind::Other)?; - for (dst, word) in bytes[curr..curr + read_len].chunks_mut(4).zip(words.iter()) { + for (dst, word) in chunk.chunks_mut(4).zip(words.iter()) { dst.copy_from_slice(&word.to_le_bytes()[..dst.len()]); } - - start += read_len as u32; - curr += read_len; } } @@ -199,15 +193,9 @@ impl NorFlash for ExternalStorage { return Err(NorFlashErrorKind::NotAligned); } - // Real FlexSPI: erase each 4 KB sector in [from, to). + // Real FlexSPI: erase [from, to). #[cfg(all(target_os = "none", feature = "mcxa5xx"))] - { - let mut addr = from; - while addr < to { - self.nor.erase_sector(addr).map_err(|_| NorFlashErrorKind::Other)?; - addr += Self::ERASE_SIZE as u32; - } - } + self.nor.erase(from, to - from).map_err(|_| NorFlashErrorKind::Other)?; // Host / non-hardware: RAM-shadow emulation. #[cfg(not(all(target_os = "none", feature = "mcxa5xx")))] @@ -240,24 +228,24 @@ impl NorFlash for ExternalStorage { // boundary in a single program operation. #[cfg(all(target_os = "none", feature = "mcxa5xx"))] { - let mut start: u32 = offset; - let mut curr: usize = 0; + let first_len = bytes.len().min(FLASH_PAGE_SIZE - offset as usize % FLASH_PAGE_SIZE); + let (first, rest) = bytes.split_at(first_len); + let mut start = offset; let mut words = [u32::MAX; FLASH_PAGE_SIZE / 4]; - while curr < bytes.len() { - let page_remaining = FLASH_PAGE_SIZE - start as usize % FLASH_PAGE_SIZE; - let write_len = core::cmp::min(page_remaining, bytes.len() - curr); - - for (word, chunk) in words.iter_mut().zip(bytes[curr..curr + write_len].chunks(4)) { + for chunk in core::iter::once(first) + .chain(rest.chunks(FLASH_PAGE_SIZE)) + .filter(|chunk| !chunk.is_empty()) + { + for (word, src) in words.iter_mut().zip(chunk.chunks(4)) { let mut bounce = [0xFF; 4]; - bounce[..chunk.len()].copy_from_slice(chunk); + bounce[..src.len()].copy_from_slice(src); *word = u32::from_le_bytes(bounce); } self.nor - .partial_program(start, &words, write_len as u32) + .partial_program(start, &words, chunk.len() as u32) .map_err(|_| NorFlashErrorKind::Other)?; - start += write_len as u32; - curr += write_len; + start += chunk.len() as u32; } } @@ -410,7 +398,7 @@ impl Board for Mcxa { // - read_sample_clk_src = 0: internal dummy-read strobe (no-DQS board). // - Port B: disable A1/A2 so B1 sits at SFAR 0, and size B1. let nor = embassy_mcxa::rom::get() - .flex_spi_with_options(p.FLEXSPI0, pins, option, |cfg| { + .flex_spi_with_options_and_clock(p.FLEXSPI0, pins, option, FlexspiClockSource::FroHf, |cfg| { cfg.mem_config.read_sample_clk_src = 0; #[cfg(not(feature = "mcxa5xxevk"))] {