From b2d9203af77821931dc79a70d4c42c2a03d82c27 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 4 Apr 2025 15:19:54 +0800 Subject: [PATCH 1/8] Bump stm32-data --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8204a0fea..4d0555d4a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -73,7 +73,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "16" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a964af03b298de30ff9f84fcfa890bcab4ce609" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a7a30c9d54e7415709c463a537501691784672db" } vcell = "0.1.3" nb = "1.0.0" @@ -102,7 +102,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "16", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-4a964af03b298de30ff9f84fcfa890bcab4ce609", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-a7a30c9d54e7415709c463a537501691784672db", default-features = false, features = ["metadata"] } [features] default = ["rt"] From 6d384a1a39f586c86cabe912fef639336cdc1ac7 Mon Sep 17 00:00:00 2001 From: Rick Rogers Date: Fri, 31 Jan 2025 18:12:30 -0500 Subject: [PATCH 2/8] introduce stm32h7rs xspi --- embassy-stm32/build.rs | 78 ++ embassy-stm32/src/lib.rs | 2 + embassy-stm32/src/xspi/enums.rs | 385 +++++++++ embassy-stm32/src/xspi/mod.rs | 1427 +++++++++++++++++++++++++++++++ 4 files changed, 1892 insertions(+) create mode 100644 embassy-stm32/src/xspi/enums.rs create mode 100644 embassy-stm32/src/xspi/mod.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 798133162..13ef74f16 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -116,6 +116,7 @@ fn main() { "peri_usb_otg_fs", "peri_usb_otg_hs", "peri_octospi2", + "peri_xspi2", ]); cfgs.declare_all(&["mco", "mco1", "mco2"]); @@ -1098,6 +1099,72 @@ fn main() { (("octospim", "P2_NCS"), quote!(crate::ospi::NSSPin)), (("octospim", "P2_CLK"), quote!(crate::ospi::SckPin)), (("octospim", "P2_NCLK"), quote!(crate::ospi::NckPin)), + (("xspi", "IO0"), quote!(crate::xspi::D0Pin)), + (("xspi", "IO1"), quote!(crate::xspi::D1Pin)), + (("xspi", "IO2"), quote!(crate::xspi::D2Pin)), + (("xspi", "IO3"), quote!(crate::xspi::D3Pin)), + (("xspi", "IO4"), quote!(crate::xspi::D4Pin)), + (("xspi", "IO5"), quote!(crate::xspi::D5Pin)), + (("xspi", "IO6"), quote!(crate::xspi::D6Pin)), + (("xspi", "IO7"), quote!(crate::xspi::D7Pin)), + (("xspi", "IO8"), quote!(crate::xspi::D8Pin)), + (("xspi", "IO9"), quote!(crate::xspi::D9Pin)), + (("xspi", "IO10"), quote!(crate::xspi::D10Pin)), + (("xspi", "IO11"), quote!(crate::xspi::D11Pin)), + (("xspi", "IO12"), quote!(crate::xspi::D12Pin)), + (("xspi", "IO13"), quote!(crate::xspi::D13Pin)), + (("xspi", "IO14"), quote!(crate::xspi::D14Pin)), + (("xspi", "IO15"), quote!(crate::xspi::D15Pin)), + (("xspi", "DQS0"), quote!(crate::xspi::DQS0Pin)), + (("xspi", "DQS1"), quote!(crate::xspi::DQS1Pin)), + (("xspi", "NCS1"), quote!(crate::xspi::NCS1Pin)), + (("xspi", "NCS2"), quote!(crate::xspi::NCS2Pin)), + (("xspi", "CLK"), quote!(crate::xspi::CLKPin)), + (("xspi", "NCLK"), quote!(crate::xspi::NCLKPin)), + (("xspim", "P1_IO0"), quote!(crate::xspi::D0Pin)), + (("xspim", "P1_IO1"), quote!(crate::xspi::D1Pin)), + (("xspim", "P1_IO2"), quote!(crate::xspi::D2Pin)), + (("xspim", "P1_IO3"), quote!(crate::xspi::D3Pin)), + (("xspim", "P1_IO4"), quote!(crate::xspi::D4Pin)), + (("xspim", "P1_IO5"), quote!(crate::xspi::D5Pin)), + (("xspim", "P1_IO6"), quote!(crate::xspi::D6Pin)), + (("xspim", "P1_IO7"), quote!(crate::xspi::D7Pin)), + (("xspim", "P1_IO8"), quote!(crate::xspi::D8Pin)), + (("xspim", "P1_IO9"), quote!(crate::xspi::D9Pin)), + (("xspim", "P1_IO10"), quote!(crate::xspi::D10Pin)), + (("xspim", "P1_IO11"), quote!(crate::xspi::D11Pin)), + (("xspim", "P1_IO12"), quote!(crate::xspi::D12Pin)), + (("xspim", "P1_IO13"), quote!(crate::xspi::D13Pin)), + (("xspim", "P1_IO14"), quote!(crate::xspi::D14Pin)), + (("xspim", "P1_IO15"), quote!(crate::xspi::D15Pin)), + (("xspim", "P1_DQS0"), quote!(crate::xspi::DQS0Pin)), + (("xspim", "P1_DQS1"), quote!(crate::xspi::DQS1Pin)), + (("xspim", "P1_NCS1"), quote!(crate::xspi::NCS1Pin)), + (("xspim", "P1_NCS2"), quote!(crate::xspi::NCS2Pin)), + (("xspim", "P1_CLK"), quote!(crate::xspi::CLKPin)), + (("xspim", "P1_NCLK"), quote!(crate::xspi::NCLKPin)), + (("xspim", "P2_IO0"), quote!(crate::xspi::D0Pin)), + (("xspim", "P2_IO1"), quote!(crate::xspi::D1Pin)), + (("xspim", "P2_IO2"), quote!(crate::xspi::D2Pin)), + (("xspim", "P2_IO3"), quote!(crate::xspi::D3Pin)), + (("xspim", "P2_IO4"), quote!(crate::xspi::D4Pin)), + (("xspim", "P2_IO5"), quote!(crate::xspi::D5Pin)), + (("xspim", "P2_IO6"), quote!(crate::xspi::D6Pin)), + (("xspim", "P2_IO7"), quote!(crate::xspi::D7Pin)), + (("xspim", "P2_IO8"), quote!(crate::xspi::D8Pin)), + (("xspim", "P2_IO9"), quote!(crate::xspi::D9Pin)), + (("xspim", "P2_IO10"), quote!(crate::xspi::D10Pin)), + (("xspim", "P2_IO11"), quote!(crate::xspi::D11Pin)), + (("xspim", "P2_IO12"), quote!(crate::xspi::D12Pin)), + (("xspim", "P2_IO13"), quote!(crate::xspi::D13Pin)), + (("xspim", "P2_IO14"), quote!(crate::xspi::D14Pin)), + (("xspim", "P2_IO15"), quote!(crate::xspi::D15Pin)), + (("xspim", "P2_DQS0"), quote!(crate::xspi::DQS0Pin)), + (("xspim", "P2_DQS1"), quote!(crate::xspi::DQS1Pin)), + (("xspim", "P2_NCS1"), quote!(crate::xspi::NCS1Pin)), + (("xspim", "P2_NCS2"), quote!(crate::xspi::NCS2Pin)), + (("xspim", "P2_CLK"), quote!(crate::xspi::CLKPin)), + (("xspim", "P2_NCLK"), quote!(crate::xspi::NCLKPin)), (("hspi", "IO0"), quote!(crate::hspi::D0Pin)), (("hspi", "IO1"), quote!(crate::hspi::D1Pin)), (("hspi", "IO2"), quote!(crate::hspi::D2Pin)), @@ -1190,6 +1257,17 @@ fn main() { peri = format_ident!("{}", "OCTOSPI1"); } + // XSPIM is special + if p.name == "XSPIM" { + if pin.signal.starts_with("P1") { + peri = format_ident!("{}", "XSPI1"); + } else if pin.signal.starts_with("P2") { + peri = format_ident!("{}", "XSPI2"); + } else { + panic! {"malformed XSPIM pin: {:?}", pin} + } + } + g.extend(quote! { pin_trait_impl!(#tr, #peri, #pin_name, #af); }) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 4d7aac81f..226293a9d 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -127,6 +127,8 @@ pub mod usart; pub mod usb; #[cfg(iwdg)] pub mod wdg; +#[cfg(xspi)] +pub mod xspi; // This must go last, so that it sees all the impl_foo! macros defined earlier. pub(crate) mod _generated { diff --git a/embassy-stm32/src/xspi/enums.rs b/embassy-stm32/src/xspi/enums.rs new file mode 100644 index 000000000..4214dde37 --- /dev/null +++ b/embassy-stm32/src/xspi/enums.rs @@ -0,0 +1,385 @@ +//! Enums used in Xspi configuration. +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub(crate) enum XspiMode { + IndirectWrite, + IndirectRead, + AutoPolling, + MemoryMapped, +} + +impl Into for XspiMode { + fn into(self) -> u8 { + match self { + XspiMode::IndirectWrite => 0b00, + XspiMode::IndirectRead => 0b01, + XspiMode::AutoPolling => 0b10, + XspiMode::MemoryMapped => 0b11, + } + } +} + +/// Xspi lane width +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum XspiWidth { + /// None + NONE, + /// Single lane + SING, + /// Dual lanes + DUAL, + /// Quad lanes + QUAD, + /// Eight lanes + OCTO, +} + +impl Into for XspiWidth { + fn into(self) -> u8 { + match self { + XspiWidth::NONE => 0b00, + XspiWidth::SING => 0b01, + XspiWidth::DUAL => 0b10, + XspiWidth::QUAD => 0b11, + XspiWidth::OCTO => 0b100, + } + } +} + +/// Flash bank selection +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum FlashSelection { + /// Bank 1 + Flash1, + /// Bank 2 + Flash2, +} + +impl Into for FlashSelection { + fn into(self) -> bool { + match self { + FlashSelection::Flash1 => false, + FlashSelection::Flash2 => true, + } + } +} + +/// Wrap Size +#[allow(dead_code)] +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum WrapSize { + None, + _16Bytes, + _32Bytes, + _64Bytes, + _128Bytes, +} + +impl Into for WrapSize { + fn into(self) -> u8 { + match self { + WrapSize::None => 0x00, + WrapSize::_16Bytes => 0x02, + WrapSize::_32Bytes => 0x03, + WrapSize::_64Bytes => 0x04, + WrapSize::_128Bytes => 0x05, + } + } +} + +/// Memory Type +#[allow(missing_docs)] +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum MemoryType { + Micron, + Macronix, + Standard, + MacronixRam, + HyperBusMemory, + HyperBusRegister, +} + +impl Into for MemoryType { + fn into(self) -> u8 { + match self { + MemoryType::Micron => 0x00, + MemoryType::Macronix => 0x01, + MemoryType::Standard => 0x02, + MemoryType::MacronixRam => 0x03, + MemoryType::HyperBusMemory => 0x04, + MemoryType::HyperBusRegister => 0x04, + } + } +} + +/// Xspi memory size. +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum MemorySize { + _1KiB, + _2KiB, + _4KiB, + _8KiB, + _16KiB, + _32KiB, + _64KiB, + _128KiB, + _256KiB, + _512KiB, + _1MiB, + _2MiB, + _4MiB, + _8MiB, + _16MiB, + _32MiB, + _64MiB, + _128MiB, + _256MiB, + _512MiB, + _1GiB, + _2GiB, + _4GiB, + Other(u8), +} + +impl Into for MemorySize { + fn into(self) -> u8 { + match self { + MemorySize::_1KiB => 9, + MemorySize::_2KiB => 10, + MemorySize::_4KiB => 11, + MemorySize::_8KiB => 12, + MemorySize::_16KiB => 13, + MemorySize::_32KiB => 14, + MemorySize::_64KiB => 15, + MemorySize::_128KiB => 16, + MemorySize::_256KiB => 17, + MemorySize::_512KiB => 18, + MemorySize::_1MiB => 19, + MemorySize::_2MiB => 20, + MemorySize::_4MiB => 21, + MemorySize::_8MiB => 22, + MemorySize::_16MiB => 23, + MemorySize::_32MiB => 24, + MemorySize::_64MiB => 25, + MemorySize::_128MiB => 26, + MemorySize::_256MiB => 27, + MemorySize::_512MiB => 28, + MemorySize::_1GiB => 29, + MemorySize::_2GiB => 30, + MemorySize::_4GiB => 31, + MemorySize::Other(val) => val, + } + } +} + +/// Xspi Address size +#[derive(Copy, Clone)] +pub enum AddressSize { + /// 8-bit address + _8Bit, + /// 16-bit address + _16Bit, + /// 24-bit address + _24bit, + /// 32-bit address + _32bit, +} + +impl Into for AddressSize { + fn into(self) -> u8 { + match self { + AddressSize::_8Bit => 0b00, + AddressSize::_16Bit => 0b01, + AddressSize::_24bit => 0b10, + AddressSize::_32bit => 0b11, + } + } +} + +/// Time the Chip Select line stays high. +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum ChipSelectHighTime { + _1Cycle, + _2Cycle, + _3Cycle, + _4Cycle, + _5Cycle, + _6Cycle, + _7Cycle, + _8Cycle, +} + +impl Into for ChipSelectHighTime { + fn into(self) -> u8 { + match self { + ChipSelectHighTime::_1Cycle => 0, + ChipSelectHighTime::_2Cycle => 1, + ChipSelectHighTime::_3Cycle => 2, + ChipSelectHighTime::_4Cycle => 3, + ChipSelectHighTime::_5Cycle => 4, + ChipSelectHighTime::_6Cycle => 5, + ChipSelectHighTime::_7Cycle => 6, + ChipSelectHighTime::_8Cycle => 7, + } + } +} + +/// FIFO threshold. +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum FIFOThresholdLevel { + _1Bytes, + _2Bytes, + _3Bytes, + _4Bytes, + _5Bytes, + _6Bytes, + _7Bytes, + _8Bytes, + _9Bytes, + _10Bytes, + _11Bytes, + _12Bytes, + _13Bytes, + _14Bytes, + _15Bytes, + _16Bytes, + _17Bytes, + _18Bytes, + _19Bytes, + _20Bytes, + _21Bytes, + _22Bytes, + _23Bytes, + _24Bytes, + _25Bytes, + _26Bytes, + _27Bytes, + _28Bytes, + _29Bytes, + _30Bytes, + _31Bytes, + _32Bytes, +} + +impl Into for FIFOThresholdLevel { + fn into(self) -> u8 { + match self { + FIFOThresholdLevel::_1Bytes => 0, + FIFOThresholdLevel::_2Bytes => 1, + FIFOThresholdLevel::_3Bytes => 2, + FIFOThresholdLevel::_4Bytes => 3, + FIFOThresholdLevel::_5Bytes => 4, + FIFOThresholdLevel::_6Bytes => 5, + FIFOThresholdLevel::_7Bytes => 6, + FIFOThresholdLevel::_8Bytes => 7, + FIFOThresholdLevel::_9Bytes => 8, + FIFOThresholdLevel::_10Bytes => 9, + FIFOThresholdLevel::_11Bytes => 10, + FIFOThresholdLevel::_12Bytes => 11, + FIFOThresholdLevel::_13Bytes => 12, + FIFOThresholdLevel::_14Bytes => 13, + FIFOThresholdLevel::_15Bytes => 14, + FIFOThresholdLevel::_16Bytes => 15, + FIFOThresholdLevel::_17Bytes => 16, + FIFOThresholdLevel::_18Bytes => 17, + FIFOThresholdLevel::_19Bytes => 18, + FIFOThresholdLevel::_20Bytes => 19, + FIFOThresholdLevel::_21Bytes => 20, + FIFOThresholdLevel::_22Bytes => 21, + FIFOThresholdLevel::_23Bytes => 22, + FIFOThresholdLevel::_24Bytes => 23, + FIFOThresholdLevel::_25Bytes => 24, + FIFOThresholdLevel::_26Bytes => 25, + FIFOThresholdLevel::_27Bytes => 26, + FIFOThresholdLevel::_28Bytes => 27, + FIFOThresholdLevel::_29Bytes => 28, + FIFOThresholdLevel::_30Bytes => 29, + FIFOThresholdLevel::_31Bytes => 30, + FIFOThresholdLevel::_32Bytes => 31, + } + } +} + +/// Dummy cycle count +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum DummyCycles { + _0, + _1, + _2, + _3, + _4, + _5, + _6, + _7, + _8, + _9, + _10, + _11, + _12, + _13, + _14, + _15, + _16, + _17, + _18, + _19, + _20, + _21, + _22, + _23, + _24, + _25, + _26, + _27, + _28, + _29, + _30, + _31, +} + +impl Into for DummyCycles { + fn into(self) -> u8 { + match self { + DummyCycles::_0 => 0, + DummyCycles::_1 => 1, + DummyCycles::_2 => 2, + DummyCycles::_3 => 3, + DummyCycles::_4 => 4, + DummyCycles::_5 => 5, + DummyCycles::_6 => 6, + DummyCycles::_7 => 7, + DummyCycles::_8 => 8, + DummyCycles::_9 => 9, + DummyCycles::_10 => 10, + DummyCycles::_11 => 11, + DummyCycles::_12 => 12, + DummyCycles::_13 => 13, + DummyCycles::_14 => 14, + DummyCycles::_15 => 15, + DummyCycles::_16 => 16, + DummyCycles::_17 => 17, + DummyCycles::_18 => 18, + DummyCycles::_19 => 19, + DummyCycles::_20 => 20, + DummyCycles::_21 => 21, + DummyCycles::_22 => 22, + DummyCycles::_23 => 23, + DummyCycles::_24 => 24, + DummyCycles::_25 => 25, + DummyCycles::_26 => 26, + DummyCycles::_27 => 27, + DummyCycles::_28 => 28, + DummyCycles::_29 => 29, + DummyCycles::_30 => 30, + DummyCycles::_31 => 31, + } + } +} diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs new file mode 100644 index 000000000..b2fdebe75 --- /dev/null +++ b/embassy-stm32/src/xspi/mod.rs @@ -0,0 +1,1427 @@ +//! XSPI Serial Peripheral Interface +//! + +#![macro_use] + +pub mod enums; + +use core::marker::PhantomData; + +use embassy_embedded_hal::{GetConfig, SetConfig}; +use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use enums::*; + +use crate::dma::{word, ChannelAndRequest}; +use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; +use crate::mode::{Async, Blocking, Mode as PeriMode}; +use crate::pac::xspi::{vals::*, Xspi as Regs}; +#[cfg(xspim_v2_1)] +use crate::pac::xspim::Xspim; +use crate::rcc::{self, RccPeripheral}; +use crate::{peripherals, Peripheral}; + +/// XPSI driver config. +#[derive(Clone, Copy)] +pub struct Config { + /// Fifo threshold used by the peripheral to generate the interrupt indicating data + /// or space is available in the FIFO + pub fifo_threshold: FIFOThresholdLevel, + /// Indicates the type of external device connected + pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface + /// Defines the size of the external device connected to the XSPI corresponding + /// to the number of address bits required to access the device + pub device_size: MemorySize, + /// Sets the minimum number of clock cycles that the chip select signal must be held high + /// between commands + pub chip_select_high_time: ChipSelectHighTime, + /// Enables the free running clock + pub free_running_clock: bool, + /// Sets the clock level when the device is not selected + pub clock_mode: bool, + /// Indicates the wrap size corresponding to the external device configuration + pub wrap_size: WrapSize, + /// Specified the prescaler factor used for generating the external clock based + /// on the AHB clock + pub clock_prescaler: u8, + /// Allows the delay of 1/2 cycle the data sampling to account for external + /// signal delays + pub sample_shifting: bool, + /// Allows hold to 1/4 cycle the data + pub delay_hold_quarter_cycle: bool, + /// Enables the transaction boundary feature and defines the boundary to release + /// the chip select + pub chip_select_boundary: u8, + /// Enables communication regulation feature. Chip select is released when the other + /// XSpi requests access to the bus + pub max_transfer: u8, + /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles + pub refresh: u32, +} + +impl Default for Config { + fn default() -> Self { + Self { + fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity + memory_type: MemoryType::Micron, + device_size: MemorySize::Other(0), + chip_select_high_time: ChipSelectHighTime::_5Cycle, + free_running_clock: false, + clock_mode: false, + wrap_size: WrapSize::None, + clock_prescaler: 0, + sample_shifting: false, + delay_hold_quarter_cycle: false, + chip_select_boundary: 0, // Acceptable range 0 to 31 + max_transfer: 0, + refresh: 0, + } + } +} + +/// XSPI transfer configuration. +pub struct TransferConfig { + /// Instruction width (IMODE) + pub iwidth: XspiWidth, + /// Instruction Id + pub instruction: Option, + /// Number of Instruction Bytes + pub isize: AddressSize, + /// Instruction Double Transfer rate enable + pub idtr: bool, + + /// Address width (ADMODE) + pub adwidth: XspiWidth, + /// Device memory address + pub address: Option, + /// Number of Address Bytes + pub adsize: AddressSize, + /// Address Double Transfer rate enable + pub addtr: bool, + + /// Alternate bytes width (ABMODE) + pub abwidth: XspiWidth, + /// Alternate Bytes + pub alternate_bytes: Option, + /// Number of Alternate Bytes + pub absize: AddressSize, + /// Alternate Bytes Double Transfer rate enable + pub abdtr: bool, + + /// Data width (DMODE) + pub dwidth: XspiWidth, + /// Data buffer + pub ddtr: bool, + + /// Number of dummy cycles (DCYC) + pub dummy: DummyCycles, +} + +impl Default for TransferConfig { + fn default() -> Self { + Self { + iwidth: XspiWidth::NONE, + instruction: None, + isize: AddressSize::_8Bit, + idtr: false, + + adwidth: XspiWidth::NONE, + address: None, + adsize: AddressSize::_8Bit, + addtr: false, + + abwidth: XspiWidth::NONE, + alternate_bytes: None, + absize: AddressSize::_8Bit, + abdtr: false, + + dwidth: XspiWidth::NONE, + ddtr: false, + + dummy: DummyCycles::_0, + } + } +} + +/// Error used for Xspi implementation +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum XspiError { + /// Peripheral configuration is invalid + InvalidConfiguration, + /// Operation configuration is invalid + InvalidCommand, + /// Size zero buffer passed to instruction + EmptyBuffer, +} + +/// XSPI driver. +pub struct Xspi<'d, T: Instance, M: PeriMode> { + _peri: PeripheralRef<'d, T>, + clk: Option>, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + d4: Option>, + d5: Option>, + d6: Option>, + d7: Option>, + d8: Option>, + d9: Option>, + d10: Option>, + d11: Option>, + d12: Option>, + d13: Option>, + d14: Option>, + d15: Option>, + ncs1: Option>, + ncs2: Option>, + dqs0: Option>, + dqs1: Option>, + dma: Option>, + _phantom: PhantomData, + config: Config, + width: XspiWidth, +} + +impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { + /// Enter memory mode. + /// The Input `read_config` is used to configure the read operation in memory mode + pub fn enable_memory_mapped_mode( + &mut self, + read_config: TransferConfig, + write_config: TransferConfig, + ) -> Result<(), XspiError> { + // Use configure command to set read config + self.configure_command(&read_config, None)?; + + let reg = T::REGS; + while reg.xspi_sr().read().busy() {} + + reg.xspi_ccr().modify(|r| { + r.set_dqse(XspiCcrDqse::B_0X0); + }); + + // Set wrting configurations, there are separate registers for write configurations in memory mapped mode + reg.xspi_wccr().modify(|w| { + w.set_imode(XspiWccrImode::from_bits(write_config.iwidth.into())); + let idtr = match write_config.idtr { + true => XspiWccrIdtr::B_0X1, + false => XspiWccrIdtr::B_0X0, + }; + w.set_idtr(idtr); + w.set_isize(XspiWccrIsize::from_bits(write_config.isize.into())); + + w.set_admode(XspiWccrAdmode::from_bits(write_config.adwidth.into())); + let addtr = match write_config.idtr { + true => XspiWccrAddtr::B_0X1, + false => XspiWccrAddtr::B_0X0, + }; + w.set_addtr(addtr); + w.set_adsize(XspiWccrAdsize::from_bits(write_config.adsize.into())); + + w.set_dmode(XspiWccrDmode::from_bits(write_config.dwidth.into())); + let ddtr = match write_config.idtr { + true => XspiWccrDdtr::B_0X1, + false => XspiWccrDdtr::B_0X0, + }; + w.set_ddtr(ddtr); + + w.set_abmode(XspiWccrAbmode::from_bits(write_config.abwidth.into())); + w.set_dqse(XspiWccrDqse::B_0X1); + }); + + reg.xspi_wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); + + // Enable memory mapped mode + reg.xspi_cr().modify(|r| { + r.set_fmode(Fmode::B_0X3); + r.set_tcen(Tcen::B_0X0); + }); + Ok(()) + } + + /// Quit from memory mapped mode + pub fn disable_memory_mapped_mode(&mut self) { + let reg = T::REGS; + + reg.xspi_cr().modify(|r| { + r.set_fmode(Fmode::B_0X0); + r.set_abort(Abort::B_0X1); + r.set_dmaen(Dmaen::B_0X0); + r.set_en(En::B_0X0); + }); + + // Clear transfer complete flag + reg.xspi_fcr().write(|w| w.set_ctcf(true)); + + // Re-enable ospi + reg.xspi_cr().modify(|r| { + r.set_en(En::B_0X1); + }); + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + d4: Option>, + d5: Option>, + d6: Option>, + d7: Option>, + d8: Option>, + d9: Option>, + d10: Option>, + d11: Option>, + d12: Option>, + d13: Option>, + d14: Option>, + d15: Option>, + clk: Option>, + ncs1: Option>, + ncs2: Option>, + dqs0: Option>, + dqs1: Option>, + dma: Option>, + config: Config, + width: XspiWidth, + dual_quad: bool, + ) -> Self { + into_ref!(peri); + + #[cfg(xspim_v2_1)] + { + // RCC for xspim should be enabled before writing register + crate::pac::RCC.ahb5enr().modify(|w| w.set_iomngren(true)); + + // Disable XSPI peripheral first + T::REGS.xspi_cr().modify(|w| { + w.set_en(En::B_0X0); + }); + + // XSPI IO Manager has been enabled before + T::SPIM_REGS.xspim_cr().modify(|w| { + w.set_muxen(false); + w.set_req2ack_time(0xff); + }); + } + + // System configuration + rcc::enable_and_reset::(); + while T::REGS.xspi_sr().read().busy() {} + + // Device configuration + T::REGS.xspi_dcr1().modify(|w| { + w.set_devsize(config.device_size.into()); + w.set_mtyp(Mtyp::from_bits(config.memory_type.into())); + w.set_csht(Csht::from_bits(config.chip_select_high_time.into())); + w.set_frck(Frck::B_0X0); + w.set_ckmode(Ckmode::from_bits(config.clock_mode.into())); + }); + + T::REGS.xspi_dcr2().modify(|w| { + w.set_wrapsize(Wrapsize::from_bits(config.wrap_size.into())); + }); + + T::REGS.xspi_dcr3().modify(|w| { + w.set_csbound(Csbound::from_bits(config.chip_select_boundary.into())); + #[cfg(xspi_v2_1)] + { + w.set_maxtran(Maxtran::from_bits(config.max_transfer.into())); + } + }); + + T::REGS.xspi_dcr4().modify(|w| { + w.set_refresh(Refresh::from_bits(config.refresh.into())); + }); + + T::REGS.xspi_cr().modify(|w| { + w.set_fthres(Fthres::from_bits(config.fifo_threshold.into())); + }); + + // Wait for busy flag to clear + while T::REGS.xspi_sr().read().busy() {} + + T::REGS.xspi_dcr2().modify(|w| { + w.set_prescaler(Prescaler::from_bits(config.clock_prescaler.into())); + }); + + T::REGS.xspi_cr().modify(|w| { + w.set_dmm(match dual_quad { + true => Dmm::B_0X1, + false => Dmm::B_0X0, + }); + }); + + T::REGS.xspi_tcr().modify(|w| { + w.set_sshift(match config.sample_shifting { + true => XspiTcrSshift::B_0X1, + false => XspiTcrSshift::B_0X0, + }); + w.set_dhqc(match config.delay_hold_quarter_cycle { + true => XspiTcrDhqc::B_0X1, + false => XspiTcrDhqc::B_0X0, + }); + }); + + // Enable peripheral + T::REGS.xspi_cr().modify(|w| { + w.set_en(En::B_0X1); + }); + + // Free running clock needs to be set after peripheral enable + if config.free_running_clock { + T::REGS.xspi_dcr1().modify(|w| { + w.set_frck(match config.free_running_clock { + true => Frck::B_0X1, + false => Frck::B_0X0, + }); + }); + } + + Self { + _peri: peri, + clk, + d0, + d1, + d2, + d3, + d4, + d5, + d6, + d7, + d8, + d9, + d10, + d11, + d12, + d13, + d14, + d15, + ncs1, + ncs2, + dqs0, + dqs1, + dma, + _phantom: PhantomData, + config, + width, + } + } + + // Function to configure the peripheral for the requested command + fn configure_command(&mut self, command: &TransferConfig, data_len: Option) -> Result<(), XspiError> { + // Check that transaction doesn't use more than hardware initialized pins + if >::into(command.iwidth) > >::into(self.width) + || >::into(command.adwidth) > >::into(self.width) + || >::into(command.abwidth) > >::into(self.width) + || >::into(command.dwidth) > >::into(self.width) + { + return Err(XspiError::InvalidCommand); + } + + T::REGS.xspi_cr().modify(|w| { + w.set_fmode(0.into()); + }); + + // Configure alternate bytes + if let Some(ab) = command.alternate_bytes { + T::REGS.xspi_abr().write(|v| v.set_alternate(ab)); + T::REGS.xspi_ccr().modify(|w| { + w.set_abmode(XspiCcrAbmode::from_bits(command.abwidth.into())); + w.set_abdtr(XspiCcrAbdtr::from_bits(command.abdtr.into())); + w.set_absize(XspiCcrAbsize::from_bits(command.absize.into())); + }) + } + + // Configure dummy cycles + T::REGS.xspi_tcr().modify(|w| { + w.set_dcyc(command.dummy.into()); + }); + + // Configure data + if let Some(data_length) = data_len { + T::REGS.xspi_dlr().write(|v| { + v.set_dl((data_length - 1) as u32); + }) + } else { + T::REGS.xspi_dlr().write(|v| { + v.set_dl((0) as u32); + }) + } + + // Configure instruction/address/data modes + T::REGS.xspi_ccr().modify(|w| { + w.set_imode(XspiCcrImode::from_bits(command.iwidth.into())); + w.set_idtr(match command.idtr { + true => XspiCcrIdtr::B_0X1, + false => XspiCcrIdtr::B_0X0, + }); + w.set_isize(XspiCcrIsize::from_bits(command.isize.into())); + + w.set_admode(XspiCcrAdmode::from_bits(command.adwidth.into())); + w.set_addtr(match command.idtr { + true => XspiCcrAddtr::B_0X1, + false => XspiCcrAddtr::B_0X0, + }); + w.set_adsize(XspiCcrAdsize::from_bits(command.adsize.into())); + + w.set_dmode(XspiCcrDmode::from_bits(command.dwidth.into())); + w.set_ddtr(match command.ddtr { + true => XspiCcrDdtr::B_0X1, + false => XspiCcrDdtr::B_0X0, + }); + }); + + // Set information required to initiate transaction + if let Some(instruction) = command.instruction { + if let Some(address) = command.address { + T::REGS.xspi_ir().write(|v| { + v.set_instruction(instruction); + }); + + T::REGS.xspi_ar().write(|v| { + v.set_address(address); + }); + } else { + // Double check requirements for delay hold and sample shifting + // if let None = command.data_len { + // if self.config.delay_hold_quarter_cycle && command.idtr { + // T::REGS.xspi_ccr().modify(|w| { + // w.set_ddtr(true); + // }); + // } + // } + + warn!("instruction: {:#x}", instruction); + T::REGS.xspi_ir().write(|v| { + v.set_instruction(instruction); + }); + } + } else { + if let Some(address) = command.address { + T::REGS.xspi_ar().write(|v| { + v.set_address(address); + }); + } else { + // The only single phase transaction supported is instruction only + return Err(XspiError::InvalidCommand); + } + } + + Ok(()) + } + + /// Function used to control or configure the target device without data transfer + pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), XspiError> { + // Wait for peripheral to be free + while T::REGS.xspi_sr().read().busy() {} + + // Need additional validation that command configuration doesn't have data set + self.configure_command(command, None)?; + + // Transaction initiated by setting final configuration, i.e the instruction register + while !T::REGS.xspi_sr().read().tcf() {} + T::REGS.xspi_fcr().write(|w| { + w.set_ctcf(true); + }); + + Ok(()) + } + + /// Blocking read with byte by byte data transfer + pub fn blocking_read(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> { + if buf.is_empty() { + return Err(XspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.xspi_sr().read().busy() {} + + // Ensure DMA is not enabled for this transaction + T::REGS.xspi_cr().modify(|w| { + w.set_dmaen(Dmaen::B_0X0); + }); + + // self.configure_command(&transaction, Some(buf.len()))?; + self.configure_command(&transaction, Some(buf.len())).unwrap(); + + let current_address = T::REGS.xspi_ar().read().address(); + let current_instruction = T::REGS.xspi_ir().read().instruction(); + + // For a indirect read transaction, the transaction begins when the instruction/address is set + T::REGS + .xspi_cr() + .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into()))); + if T::REGS.xspi_ccr().read().admode() == XspiCcrAdmode::B_0X0 { + T::REGS.xspi_ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.xspi_ar().write(|v| v.set_address(current_address)); + } + + for idx in 0..buf.len() { + while !T::REGS.xspi_sr().read().tcf() && !T::REGS.xspi_sr().read().ftf() {} + buf[idx] = unsafe { (T::REGS.xspi_dr().as_ptr() as *mut W).read_volatile() }; + } + + while !T::REGS.xspi_sr().read().tcf() {} + T::REGS.xspi_fcr().write(|v| v.set_ctcf(true)); + + Ok(()) + } + + /// Blocking write with byte by byte data transfer + pub fn blocking_write(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> { + if buf.is_empty() { + return Err(XspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.xspi_sr().read().busy() {} + + T::REGS.xspi_cr().modify(|w| { + w.set_dmaen(Dmaen::B_0X0); + }); + + self.configure_command(&transaction, Some(buf.len()))?; + + T::REGS + .xspi_cr() + .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); + + for idx in 0..buf.len() { + while !T::REGS.xspi_sr().read().ftf() {} + unsafe { (T::REGS.xspi_dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; + } + + while !T::REGS.xspi_sr().read().tcf() {} + T::REGS.xspi_fcr().write(|v| v.set_ctcf(true)); + + Ok(()) + } + + /// Set new bus configuration + pub fn set_config(&mut self, config: &Config) { + // Wait for busy flag to clear + while T::REGS.xspi_sr().read().busy() {} + + // Disable DMA channel while configuring the peripheral + T::REGS.xspi_cr().modify(|w| { + w.set_dmaen(Dmaen::B_0X0); + }); + + // Device configuration + T::REGS.xspi_dcr1().modify(|w| { + w.set_devsize(config.device_size.into()); + w.set_mtyp(Mtyp::from_bits(config.memory_type.into())); + w.set_csht(Csht::from_bits(config.chip_select_high_time.into())); + w.set_frck(Frck::B_0X0); + w.set_ckmode(match config.clock_mode { + true => Ckmode::B_0X1, + false => Ckmode::B_0X0, + }); + }); + + T::REGS.xspi_dcr2().modify(|w| { + w.set_wrapsize(Wrapsize::from_bits(config.wrap_size.into())); + }); + + T::REGS.xspi_dcr3().modify(|w| { + w.set_csbound(Csbound::from_bits(config.chip_select_boundary.into())); + #[cfg(xspi_v2_1)] + { + w.set_maxtran(Maxtran::from_bits(config.max_transfer.into())); + } + }); + + T::REGS.xspi_dcr4().modify(|w| { + w.set_refresh(Refresh::from_bits(config.refresh.into())); + }); + + T::REGS.xspi_cr().modify(|w| { + w.set_fthres(Fthres::from_bits(config.fifo_threshold.into())); + }); + + // Wait for busy flag to clear + while T::REGS.xspi_sr().read().busy() {} + + T::REGS.xspi_dcr2().modify(|w| { + w.set_prescaler(Prescaler::from_bits(config.clock_prescaler.into())); + }); + + T::REGS.xspi_tcr().modify(|w| { + w.set_sshift(match config.sample_shifting { + true => XspiTcrSshift::B_0X1, + false => XspiTcrSshift::B_0X0, + }); + w.set_dhqc(match config.delay_hold_quarter_cycle { + true => XspiTcrDhqc::B_0X1, + false => XspiTcrDhqc::B_0X0, + }); + }); + + // Enable peripheral + T::REGS.xspi_cr().modify(|w| { + w.set_en(En::B_0X1); + }); + + // Free running clock needs to be set after peripheral enable + if config.free_running_clock { + T::REGS.xspi_dcr1().modify(|w| { + w.set_frck(match config.free_running_clock { + true => Frck::B_0X1, + false => Frck::B_0X0, + }); + }); + } + + self.config = *config; + } + + /// Get current configuration + pub fn get_config(&self) -> Config { + self.config + } +} + +impl<'d, T: Instance> Xspi<'d, T, Blocking> { + /// Create new blocking XSPI driver for a single spi external chip + pub fn new_blocking_singlespi( + peri: impl Peripheral

+ 'd, + clk: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + ncs: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::input(Pull::None)), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(ncs, AfType::output(OutputType::OpenDrain, Speed::VeryHigh)), + None, + None, + None, + None, + config, + XspiWidth::SING, + false, + ) + } + + /// Create new blocking XSPI driver for a dualspi external chip + pub fn new_blocking_dualspi( + peri: impl Peripheral

+ 'd, + clk: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + ncs: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + ncs, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), + None, + None, + None, + None, + config, + XspiWidth::DUAL, + false, + ) + } + + /// Create new blocking XSPI driver for a quadspi external chip + pub fn new_blocking_quadspi( + peri: impl Peripheral

+ 'd, + clk: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + ncs: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + ncs, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), + None, + None, + None, + None, + config, + XspiWidth::QUAD, + false, + ) + } + + /// Create new blocking XSPI driver for two quadspi external chips + pub fn new_blocking_dualquadspi( + peri: impl Peripheral

+ 'd, + clk: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + d4: impl Peripheral

> + 'd, + d5: impl Peripheral

> + 'd, + d6: impl Peripheral

> + 'd, + d7: impl Peripheral

> + 'd, + ncs: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + None, + None, + None, + None, + None, + None, + None, + new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + ncs, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), + None, + None, + None, + None, + config, + XspiWidth::QUAD, + true, + ) + } + + /// Create new blocking XSPI driver for xspi external chips + pub fn new_blocking_xspi( + peri: impl Peripheral

+ 'd, + clk: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + d4: impl Peripheral

> + 'd, + d5: impl Peripheral

> + 'd, + d6: impl Peripheral

> + 'd, + d7: impl Peripheral

> + 'd, + ncs: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + None, + None, + None, + None, + None, + None, + None, + new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + ncs, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), + None, + None, + None, + None, + config, + XspiWidth::OCTO, + false, + ) + } +} + +impl<'d, T: Instance> Xspi<'d, T, Async> { + /// Create new blocking XSPI driver for a single spi external chip + pub fn new_singlespi( + peri: impl Peripheral

+ 'd, + clk: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + ncs: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::input(Pull::None)), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(ncs, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + None, + None, + new_dma!(dma), + config, + XspiWidth::SING, + false, + ) + } + + /// Create new blocking XSPI driver for a dualspi external chip + pub fn new_dualspi( + peri: impl Peripheral

+ 'd, + clk: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + ncs: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + ncs, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), + None, + None, + None, + new_dma!(dma), + config, + XspiWidth::DUAL, + false, + ) + } + + /// Create new blocking XSPI driver for a quadspi external chip + pub fn new_quadspi( + peri: impl Peripheral

+ 'd, + clk: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + ncs: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + None, + new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + ncs, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), + None, + None, + None, + new_dma!(dma), + config, + XspiWidth::QUAD, + false, + ) + } + + /// Create new blocking XSPI driver for two quadspi external chips + pub fn new_dualquadspi( + peri: impl Peripheral

+ 'd, + clk: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + d4: impl Peripheral

> + 'd, + d5: impl Peripheral

> + 'd, + d6: impl Peripheral

> + 'd, + d7: impl Peripheral

> + 'd, + ncs: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + None, + None, + None, + None, + None, + None, + None, + new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + ncs, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), + None, + None, + None, + new_dma!(dma), + config, + XspiWidth::QUAD, + true, + ) + } + + /// Create new blocking XSPI driver for xspi external chips + pub fn new_xspi( + peri: impl Peripheral

+ 'd, + clk: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + d4: impl Peripheral

> + 'd, + d5: impl Peripheral

> + 'd, + d6: impl Peripheral

> + 'd, + d7: impl Peripheral

> + 'd, + ncs: impl Peripheral

> + 'd, + dma: impl Peripheral

> + 'd, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + None, + None, + None, + None, + None, + None, + None, + new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + ncs, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), + None, + None, + None, + new_dma!(dma), + config, + XspiWidth::OCTO, + false, + ) + } + + /// Blocking read with DMA transfer + pub fn blocking_read_dma(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> { + if buf.is_empty() { + return Err(XspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.xspi_sr().read().busy() {} + + self.configure_command(&transaction, Some(buf.len()))?; + + let current_address = T::REGS.xspi_ar().read().address(); + let current_instruction = T::REGS.xspi_ir().read().instruction(); + + // For a indirect read transaction, the transaction begins when the instruction/address is set + T::REGS + .xspi_cr() + .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into()))); + if T::REGS.xspi_ccr().read().admode() == XspiCcrAdmode::B_0X0 { + T::REGS.xspi_ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.xspi_ar().write(|v| v.set_address(current_address)); + } + + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.xspi_dr().as_ptr() as *mut W, buf, Default::default()) + }; + + T::REGS.xspi_cr().modify(|w| w.set_dmaen(Dmaen::B_0X1)); + + transfer.blocking_wait(); + + finish_dma(T::REGS); + + Ok(()) + } + + /// Blocking write with DMA transfer + pub fn blocking_write_dma(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> { + if buf.is_empty() { + return Err(XspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.xspi_sr().read().busy() {} + + self.configure_command(&transaction, Some(buf.len()))?; + T::REGS + .xspi_cr() + .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); + + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(buf, T::REGS.xspi_dr().as_ptr() as *mut W, Default::default()) + }; + + T::REGS.xspi_cr().modify(|w| w.set_dmaen(Dmaen::B_0X1)); + + transfer.blocking_wait(); + + finish_dma(T::REGS); + + Ok(()) + } + + /// Asynchronous read from external device + pub async fn read(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), XspiError> { + if buf.is_empty() { + return Err(XspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.xspi_sr().read().busy() {} + + self.configure_command(&transaction, Some(buf.len()))?; + + let current_address = T::REGS.xspi_ar().read().address(); + let current_instruction = T::REGS.xspi_ir().read().instruction(); + + // For a indirect read transaction, the transaction begins when the instruction/address is set + T::REGS + .xspi_cr() + .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into()))); + if T::REGS.xspi_ccr().read().admode() == XspiCcrAdmode::B_0X0 { + T::REGS.xspi_ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.xspi_ar().write(|v| v.set_address(current_address)); + } + + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.xspi_dr().as_ptr() as *mut W, buf, Default::default()) + }; + + T::REGS.xspi_cr().modify(|w| w.set_dmaen(Dmaen::B_0X1)); + + transfer.await; + + finish_dma(T::REGS); + + Ok(()) + } + + /// Asynchronous write to external device + pub async fn write(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), XspiError> { + if buf.is_empty() { + return Err(XspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.xspi_sr().read().busy() {} + + self.configure_command(&transaction, Some(buf.len()))?; + T::REGS + .xspi_cr() + .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); + + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(buf, T::REGS.xspi_dr().as_ptr() as *mut W, Default::default()) + }; + + T::REGS.xspi_cr().modify(|w| w.set_dmaen(Dmaen::B_0X1)); + + transfer.await; + + finish_dma(T::REGS); + + Ok(()) + } +} + +impl<'d, T: Instance, M: PeriMode> Drop for Xspi<'d, T, M> { + fn drop(&mut self) { + self.clk.as_ref().map(|x| x.set_as_disconnected()); + self.d0.as_ref().map(|x| x.set_as_disconnected()); + self.d1.as_ref().map(|x| x.set_as_disconnected()); + self.d2.as_ref().map(|x| x.set_as_disconnected()); + self.d3.as_ref().map(|x| x.set_as_disconnected()); + self.d4.as_ref().map(|x| x.set_as_disconnected()); + self.d5.as_ref().map(|x| x.set_as_disconnected()); + self.d6.as_ref().map(|x| x.set_as_disconnected()); + self.d7.as_ref().map(|x| x.set_as_disconnected()); + self.d8.as_ref().map(|x| x.set_as_disconnected()); + self.d9.as_ref().map(|x| x.set_as_disconnected()); + self.d10.as_ref().map(|x| x.set_as_disconnected()); + self.d11.as_ref().map(|x| x.set_as_disconnected()); + self.d12.as_ref().map(|x| x.set_as_disconnected()); + self.d13.as_ref().map(|x| x.set_as_disconnected()); + self.d14.as_ref().map(|x| x.set_as_disconnected()); + self.d15.as_ref().map(|x| x.set_as_disconnected()); + self.ncs1.as_ref().map(|x| x.set_as_disconnected()); + self.ncs2.as_ref().map(|x| x.set_as_disconnected()); + self.dqs0.as_ref().map(|x| x.set_as_disconnected()); + self.dqs1.as_ref().map(|x| x.set_as_disconnected()); + + rcc::disable::(); + } +} + +fn finish_dma(regs: Regs) { + while !regs.xspi_sr().read().tcf() {} + regs.xspi_fcr().write(|v| v.set_ctcf(true)); + + regs.xspi_cr().modify(|w| { + w.set_dmaen(Dmaen::B_0X0); + }); +} + +/// XSPI I/O manager instance trait. +#[cfg(xspim_v2_1)] +pub(crate) trait SealedXspimInstance { + const SPIM_REGS: Xspim; + const SPI_IDX: u8; +} + +/// XSPI instance trait. +pub(crate) trait SealedInstance { + const REGS: Regs; +} + +/// XSPI instance trait. +#[cfg(xspim_v2_1)] +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral + SealedXspimInstance {} + +/// XSPI instance trait. +#[cfg(not(xspim_v2_1))] +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} + +pin_trait!(D0Pin, Instance); +pin_trait!(D1Pin, Instance); +pin_trait!(D2Pin, Instance); +pin_trait!(D3Pin, Instance); +pin_trait!(D4Pin, Instance); +pin_trait!(D5Pin, Instance); +pin_trait!(D6Pin, Instance); +pin_trait!(D7Pin, Instance); +pin_trait!(D8Pin, Instance); +pin_trait!(D9Pin, Instance); +pin_trait!(D10Pin, Instance); +pin_trait!(D11Pin, Instance); +pin_trait!(D12Pin, Instance); +pin_trait!(D13Pin, Instance); +pin_trait!(D14Pin, Instance); +pin_trait!(D15Pin, Instance); +pin_trait!(DQS0Pin, Instance); +pin_trait!(DQS1Pin, Instance); +pin_trait!(NCS1Pin, Instance); +pin_trait!(NCS2Pin, Instance); +pin_trait!(CLKPin, Instance); +pin_trait!(NCLKPin, Instance); +dma_trait!(XDma, Instance); + +// Hard-coded the xspi index, for SPIM +#[cfg(xspim_v2_1)] +impl SealedXspimInstance for peripherals::XSPI1 { + const SPIM_REGS: Xspim = crate::pac::XSPIM; + const SPI_IDX: u8 = 1; +} + +// #[cfg(all(xspim_v2_1, peri_xspi2))] +#[cfg(xspim_v2_1)] +impl SealedXspimInstance for peripherals::XSPI2 { + const SPIM_REGS: Xspim = crate::pac::XSPIM; + const SPI_IDX: u8 = 2; +} + +#[cfg(xspim_v2_1)] +foreach_peripheral!( + (xspi, $inst:ident) => { + impl SealedInstance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); + +#[cfg(not(xspim_v2_1))] +foreach_peripheral!( + (xspi, $inst:ident) => { + impl SealedInstance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); + +impl<'d, T: Instance, M: PeriMode> SetConfig for Xspi<'d, T, M> { + type Config = Config; + type ConfigError = (); + fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { + self.set_config(config); + Ok(()) + } +} + +impl<'d, T: Instance, M: PeriMode> GetConfig for Xspi<'d, T, M> { + type Config = Config; + fn get_config(&self) -> Self::Config { + self.get_config() + } +} + +/// Word sizes usable for XSPI. +#[allow(private_bounds)] +pub trait Word: word::Word {} + +macro_rules! impl_word { + ($T:ty) => { + impl Word for $T {} + }; +} + +impl_word!(u8); +impl_word!(u16); +impl_word!(u32); From 36a5b02774a5254fd3a1b715515c84d04dee2d58 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 2 Apr 2025 11:16:31 +0800 Subject: [PATCH 3/8] stm32: Update xspi for stm32-metapac changes This is now closer to the original ospi, using more idiomatic naming. Some dead code is removed (previously was hidden by [allow(dead_code)]). --- embassy-stm32/build.rs | 8 +- embassy-stm32/src/xspi/enums.rs | 25 +-- embassy-stm32/src/xspi/mod.rs | 365 +++++++++++++++----------------- 3 files changed, 175 insertions(+), 223 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 13ef74f16..fa9e3f953 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -62,7 +62,13 @@ fn main() { // generate one singleton per peripheral (with many exceptions...) for p in METADATA.peripherals { if let Some(r) = &p.registers { - if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" || r.kind == "otg" || r.kind == "octospi" { + if r.kind == "adccommon" + || r.kind == "sai" + || r.kind == "ucpd" + || r.kind == "otg" + || r.kind == "octospi" + || r.kind == "xspi" + { // TODO: should we emit this for all peripherals? if so, we will need a list of all // possible peripherals across all chips, so that we can declare the configs // (replacing the hard-coded list of `peri_*` cfgs below) diff --git a/embassy-stm32/src/xspi/enums.rs b/embassy-stm32/src/xspi/enums.rs index 4214dde37..e02ec797e 100644 --- a/embassy-stm32/src/xspi/enums.rs +++ b/embassy-stm32/src/xspi/enums.rs @@ -1,10 +1,11 @@ //! Enums used in Xspi configuration. -#[allow(dead_code)] #[derive(Copy, Clone)] pub(crate) enum XspiMode { IndirectWrite, IndirectRead, + #[expect(dead_code)] AutoPolling, + #[expect(dead_code)] MemoryMapped, } @@ -20,7 +21,6 @@ impl Into for XspiMode { } /// Xspi lane width -#[allow(dead_code)] #[derive(Copy, Clone)] pub enum XspiWidth { /// None @@ -47,27 +47,7 @@ impl Into for XspiWidth { } } -/// Flash bank selection -#[allow(dead_code)] -#[derive(Copy, Clone)] -pub enum FlashSelection { - /// Bank 1 - Flash1, - /// Bank 2 - Flash2, -} - -impl Into for FlashSelection { - fn into(self) -> bool { - match self { - FlashSelection::Flash1 => false, - FlashSelection::Flash2 => true, - } - } -} - /// Wrap Size -#[allow(dead_code)] #[allow(missing_docs)] #[derive(Copy, Clone)] pub enum WrapSize { @@ -92,7 +72,6 @@ impl Into for WrapSize { /// Memory Type #[allow(missing_docs)] -#[allow(dead_code)] #[derive(Copy, Clone)] pub enum MemoryType { Micron, diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index b2fdebe75..3b5406a57 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -14,8 +14,9 @@ pub use enums::*; use crate::dma::{word, ChannelAndRequest}; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; -use crate::pac::xspi::{vals::*, Xspi as Regs}; -#[cfg(xspim_v2_1)] +use crate::pac::xspi::vals::*; +use crate::pac::xspi::Xspi as Regs; +#[cfg(xspim_v1)] use crate::pac::xspim::Xspim; use crate::rcc::{self, RccPeripheral}; use crate::{peripherals, Peripheral}; @@ -41,7 +42,7 @@ pub struct Config { /// Indicates the wrap size corresponding to the external device configuration pub wrap_size: WrapSize, /// Specified the prescaler factor used for generating the external clock based - /// on the AHB clock + /// on the AHB clock. 0 = Fkernel, 1 = Fkernel/2, 2 = Fkernel/3 etc. pub clock_prescaler: u8, /// Allows the delay of 1/2 cycle the data sampling to account for external /// signal delays @@ -196,47 +197,35 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { self.configure_command(&read_config, None)?; let reg = T::REGS; - while reg.xspi_sr().read().busy() {} + while reg.sr().read().busy() {} - reg.xspi_ccr().modify(|r| { - r.set_dqse(XspiCcrDqse::B_0X0); + reg.ccr().modify(|r| { + r.set_dqse(false); }); // Set wrting configurations, there are separate registers for write configurations in memory mapped mode - reg.xspi_wccr().modify(|w| { - w.set_imode(XspiWccrImode::from_bits(write_config.iwidth.into())); - let idtr = match write_config.idtr { - true => XspiWccrIdtr::B_0X1, - false => XspiWccrIdtr::B_0X0, - }; - w.set_idtr(idtr); - w.set_isize(XspiWccrIsize::from_bits(write_config.isize.into())); + reg.wccr().modify(|w| { + w.set_imode(WccrImode::from_bits(write_config.iwidth.into())); + w.set_idtr(write_config.idtr); + w.set_isize(WccrIsize::from_bits(write_config.isize.into())); - w.set_admode(XspiWccrAdmode::from_bits(write_config.adwidth.into())); - let addtr = match write_config.idtr { - true => XspiWccrAddtr::B_0X1, - false => XspiWccrAddtr::B_0X0, - }; - w.set_addtr(addtr); - w.set_adsize(XspiWccrAdsize::from_bits(write_config.adsize.into())); + w.set_admode(WccrAdmode::from_bits(write_config.adwidth.into())); + w.set_addtr(write_config.idtr); + w.set_adsize(WccrAdsize::from_bits(write_config.adsize.into())); - w.set_dmode(XspiWccrDmode::from_bits(write_config.dwidth.into())); - let ddtr = match write_config.idtr { - true => XspiWccrDdtr::B_0X1, - false => XspiWccrDdtr::B_0X0, - }; - w.set_ddtr(ddtr); + w.set_dmode(WccrDmode::from_bits(write_config.dwidth.into())); + w.set_ddtr(write_config.idtr); - w.set_abmode(XspiWccrAbmode::from_bits(write_config.abwidth.into())); - w.set_dqse(XspiWccrDqse::B_0X1); + w.set_abmode(WccrAbmode::from_bits(write_config.abwidth.into())); + w.set_dqse(true); }); - reg.xspi_wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); + reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); // Enable memory mapped mode - reg.xspi_cr().modify(|r| { + reg.cr().modify(|r| { r.set_fmode(Fmode::B_0X3); - r.set_tcen(Tcen::B_0X0); + r.set_tcen(false); }); Ok(()) } @@ -245,19 +234,19 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { pub fn disable_memory_mapped_mode(&mut self) { let reg = T::REGS; - reg.xspi_cr().modify(|r| { + reg.cr().modify(|r| { r.set_fmode(Fmode::B_0X0); - r.set_abort(Abort::B_0X1); - r.set_dmaen(Dmaen::B_0X0); - r.set_en(En::B_0X0); + r.set_abort(true); + r.set_dmaen(false); + r.set_en(false); }); // Clear transfer complete flag - reg.xspi_fcr().write(|w| w.set_ctcf(true)); + reg.fcr().write(|w| w.set_ctcf(true)); // Re-enable ospi - reg.xspi_cr().modify(|r| { - r.set_en(En::B_0X1); + reg.cr().modify(|r| { + r.set_en(true); }); } @@ -291,18 +280,18 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { ) -> Self { into_ref!(peri); - #[cfg(xspim_v2_1)] + #[cfg(xspim_v1)] { // RCC for xspim should be enabled before writing register crate::pac::RCC.ahb5enr().modify(|w| w.set_iomngren(true)); // Disable XSPI peripheral first - T::REGS.xspi_cr().modify(|w| { - w.set_en(En::B_0X0); + T::REGS.cr().modify(|w| { + w.set_en(false); }); // XSPI IO Manager has been enabled before - T::SPIM_REGS.xspim_cr().modify(|w| { + T::SPIM_REGS.cr().modify(|w| { w.set_muxen(false); w.set_req2ack_time(0xff); }); @@ -310,74 +299,70 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { // System configuration rcc::enable_and_reset::(); - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} // Device configuration - T::REGS.xspi_dcr1().modify(|w| { + T::REGS.dcr1().modify(|w| { w.set_devsize(config.device_size.into()); w.set_mtyp(Mtyp::from_bits(config.memory_type.into())); w.set_csht(Csht::from_bits(config.chip_select_high_time.into())); - w.set_frck(Frck::B_0X0); + w.set_frck(false); w.set_ckmode(Ckmode::from_bits(config.clock_mode.into())); }); - T::REGS.xspi_dcr2().modify(|w| { + T::REGS.dcr2().modify(|w| { w.set_wrapsize(Wrapsize::from_bits(config.wrap_size.into())); }); - T::REGS.xspi_dcr3().modify(|w| { + T::REGS.dcr3().modify(|w| { w.set_csbound(Csbound::from_bits(config.chip_select_boundary.into())); - #[cfg(xspi_v2_1)] + #[cfg(xspi_v1)] { w.set_maxtran(Maxtran::from_bits(config.max_transfer.into())); } }); - T::REGS.xspi_dcr4().modify(|w| { + T::REGS.dcr4().modify(|w| { w.set_refresh(Refresh::from_bits(config.refresh.into())); }); - T::REGS.xspi_cr().modify(|w| { + T::REGS.cr().modify(|w| { w.set_fthres(Fthres::from_bits(config.fifo_threshold.into())); }); // Wait for busy flag to clear - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} - T::REGS.xspi_dcr2().modify(|w| { - w.set_prescaler(Prescaler::from_bits(config.clock_prescaler.into())); + T::REGS.dcr2().modify(|w| { + w.set_prescaler(config.clock_prescaler); }); - T::REGS.xspi_cr().modify(|w| { - w.set_dmm(match dual_quad { - true => Dmm::B_0X1, - false => Dmm::B_0X0, - }); + T::REGS.cr().modify(|w| { + w.set_dmm(dual_quad); }); - T::REGS.xspi_tcr().modify(|w| { - w.set_sshift(match config.sample_shifting { - true => XspiTcrSshift::B_0X1, - false => XspiTcrSshift::B_0X0, - }); - w.set_dhqc(match config.delay_hold_quarter_cycle { - true => XspiTcrDhqc::B_0X1, - false => XspiTcrDhqc::B_0X0, - }); + T::REGS.tcr().modify(|w| { + w.set_sshift(config.sample_shifting); + w.set_dhqc(config.delay_hold_quarter_cycle); + }); + + // TODO: at the moment only ncs1 seems to get passed in? + // Only one must be selected + assert!(!(ncs1.is_some() && ncs2.is_some())); + assert!(!(ncs1.is_none() && ncs2.is_none())); + T::REGS.cr().modify(|w| { + w.set_cssel(if ncs1.is_some() { Cssel::B_0X0 } else { Cssel::B_0X1 }); }); // Enable peripheral - T::REGS.xspi_cr().modify(|w| { - w.set_en(En::B_0X1); + T::REGS.cr().modify(|w| { + w.set_en(true); }); // Free running clock needs to be set after peripheral enable if config.free_running_clock { - T::REGS.xspi_dcr1().modify(|w| { - w.set_frck(match config.free_running_clock { - true => Frck::B_0X1, - false => Frck::B_0X0, - }); + T::REGS.dcr1().modify(|w| { + w.set_frck(config.free_running_clock); }); } @@ -422,87 +407,78 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { return Err(XspiError::InvalidCommand); } - T::REGS.xspi_cr().modify(|w| { + T::REGS.cr().modify(|w| { w.set_fmode(0.into()); }); // Configure alternate bytes if let Some(ab) = command.alternate_bytes { - T::REGS.xspi_abr().write(|v| v.set_alternate(ab)); - T::REGS.xspi_ccr().modify(|w| { - w.set_abmode(XspiCcrAbmode::from_bits(command.abwidth.into())); - w.set_abdtr(XspiCcrAbdtr::from_bits(command.abdtr.into())); - w.set_absize(XspiCcrAbsize::from_bits(command.absize.into())); + T::REGS.abr().write(|v| v.set_alternate(ab)); + T::REGS.ccr().modify(|w| { + w.set_abmode(CcrAbmode::from_bits(command.abwidth.into())); + w.set_abdtr(command.abdtr); + w.set_absize(CcrAbsize::from_bits(command.absize.into())); }) } // Configure dummy cycles - T::REGS.xspi_tcr().modify(|w| { + T::REGS.tcr().modify(|w| { w.set_dcyc(command.dummy.into()); }); // Configure data if let Some(data_length) = data_len { - T::REGS.xspi_dlr().write(|v| { + T::REGS.dlr().write(|v| { v.set_dl((data_length - 1) as u32); }) } else { - T::REGS.xspi_dlr().write(|v| { + T::REGS.dlr().write(|v| { v.set_dl((0) as u32); }) } // Configure instruction/address/data modes - T::REGS.xspi_ccr().modify(|w| { - w.set_imode(XspiCcrImode::from_bits(command.iwidth.into())); - w.set_idtr(match command.idtr { - true => XspiCcrIdtr::B_0X1, - false => XspiCcrIdtr::B_0X0, - }); - w.set_isize(XspiCcrIsize::from_bits(command.isize.into())); + T::REGS.ccr().modify(|w| { + w.set_imode(CcrImode::from_bits(command.iwidth.into())); + w.set_idtr(command.idtr); + w.set_isize(CcrIsize::from_bits(command.isize.into())); - w.set_admode(XspiCcrAdmode::from_bits(command.adwidth.into())); - w.set_addtr(match command.idtr { - true => XspiCcrAddtr::B_0X1, - false => XspiCcrAddtr::B_0X0, - }); - w.set_adsize(XspiCcrAdsize::from_bits(command.adsize.into())); + w.set_admode(CcrAdmode::from_bits(command.adwidth.into())); + w.set_addtr(command.idtr); + w.set_adsize(CcrAdsize::from_bits(command.adsize.into())); - w.set_dmode(XspiCcrDmode::from_bits(command.dwidth.into())); - w.set_ddtr(match command.ddtr { - true => XspiCcrDdtr::B_0X1, - false => XspiCcrDdtr::B_0X0, - }); + w.set_dmode(CcrDmode::from_bits(command.dwidth.into())); + w.set_ddtr(command.ddtr); }); // Set information required to initiate transaction if let Some(instruction) = command.instruction { if let Some(address) = command.address { - T::REGS.xspi_ir().write(|v| { + T::REGS.ir().write(|v| { v.set_instruction(instruction); }); - T::REGS.xspi_ar().write(|v| { + T::REGS.ar().write(|v| { v.set_address(address); }); } else { // Double check requirements for delay hold and sample shifting // if let None = command.data_len { // if self.config.delay_hold_quarter_cycle && command.idtr { - // T::REGS.xspi_ccr().modify(|w| { + // T::REGS.ccr().modify(|w| { // w.set_ddtr(true); // }); // } // } - warn!("instruction: {:#x}", instruction); - T::REGS.xspi_ir().write(|v| { + // warn!("instruction: {:#x}", instruction); + T::REGS.ir().write(|v| { v.set_instruction(instruction); }); } } else { if let Some(address) = command.address { - T::REGS.xspi_ar().write(|v| { + T::REGS.ar().write(|v| { v.set_address(address); }); } else { @@ -517,14 +493,14 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { /// Function used to control or configure the target device without data transfer pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), XspiError> { // Wait for peripheral to be free - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} // Need additional validation that command configuration doesn't have data set self.configure_command(command, None)?; // Transaction initiated by setting final configuration, i.e the instruction register - while !T::REGS.xspi_sr().read().tcf() {} - T::REGS.xspi_fcr().write(|w| { + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().write(|w| { w.set_ctcf(true); }); @@ -538,36 +514,36 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { } // Wait for peripheral to be free - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} // Ensure DMA is not enabled for this transaction - T::REGS.xspi_cr().modify(|w| { - w.set_dmaen(Dmaen::B_0X0); + T::REGS.cr().modify(|w| { + w.set_dmaen(false); }); // self.configure_command(&transaction, Some(buf.len()))?; self.configure_command(&transaction, Some(buf.len())).unwrap(); - let current_address = T::REGS.xspi_ar().read().address(); - let current_instruction = T::REGS.xspi_ir().read().instruction(); + let current_address = T::REGS.ar().read().address(); + let current_instruction = T::REGS.ir().read().instruction(); // For a indirect read transaction, the transaction begins when the instruction/address is set T::REGS - .xspi_cr() + .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into()))); - if T::REGS.xspi_ccr().read().admode() == XspiCcrAdmode::B_0X0 { - T::REGS.xspi_ir().write(|v| v.set_instruction(current_instruction)); + if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); } else { - T::REGS.xspi_ar().write(|v| v.set_address(current_address)); + T::REGS.ar().write(|v| v.set_address(current_address)); } for idx in 0..buf.len() { - while !T::REGS.xspi_sr().read().tcf() && !T::REGS.xspi_sr().read().ftf() {} - buf[idx] = unsafe { (T::REGS.xspi_dr().as_ptr() as *mut W).read_volatile() }; + while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() }; } - while !T::REGS.xspi_sr().read().tcf() {} - T::REGS.xspi_fcr().write(|v| v.set_ctcf(true)); + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().write(|v| v.set_ctcf(true)); Ok(()) } @@ -579,25 +555,25 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { } // Wait for peripheral to be free - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} - T::REGS.xspi_cr().modify(|w| { - w.set_dmaen(Dmaen::B_0X0); + T::REGS.cr().modify(|w| { + w.set_dmaen(false); }); self.configure_command(&transaction, Some(buf.len()))?; T::REGS - .xspi_cr() + .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); for idx in 0..buf.len() { - while !T::REGS.xspi_sr().read().ftf() {} - unsafe { (T::REGS.xspi_dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; + while !T::REGS.sr().read().ftf() {} + unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; } - while !T::REGS.xspi_sr().read().tcf() {} - T::REGS.xspi_fcr().write(|v| v.set_ctcf(true)); + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().write(|v| v.set_ctcf(true)); Ok(()) } @@ -605,75 +581,66 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { /// Set new bus configuration pub fn set_config(&mut self, config: &Config) { // Wait for busy flag to clear - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} // Disable DMA channel while configuring the peripheral - T::REGS.xspi_cr().modify(|w| { - w.set_dmaen(Dmaen::B_0X0); + T::REGS.cr().modify(|w| { + w.set_dmaen(false); }); // Device configuration - T::REGS.xspi_dcr1().modify(|w| { + T::REGS.dcr1().modify(|w| { w.set_devsize(config.device_size.into()); w.set_mtyp(Mtyp::from_bits(config.memory_type.into())); w.set_csht(Csht::from_bits(config.chip_select_high_time.into())); - w.set_frck(Frck::B_0X0); + w.set_frck(false); w.set_ckmode(match config.clock_mode { true => Ckmode::B_0X1, false => Ckmode::B_0X0, }); }); - T::REGS.xspi_dcr2().modify(|w| { + T::REGS.dcr2().modify(|w| { w.set_wrapsize(Wrapsize::from_bits(config.wrap_size.into())); }); - T::REGS.xspi_dcr3().modify(|w| { + T::REGS.dcr3().modify(|w| { w.set_csbound(Csbound::from_bits(config.chip_select_boundary.into())); - #[cfg(xspi_v2_1)] + #[cfg(xspi_v1)] { w.set_maxtran(Maxtran::from_bits(config.max_transfer.into())); } }); - T::REGS.xspi_dcr4().modify(|w| { + T::REGS.dcr4().modify(|w| { w.set_refresh(Refresh::from_bits(config.refresh.into())); }); - T::REGS.xspi_cr().modify(|w| { + T::REGS.cr().modify(|w| { w.set_fthres(Fthres::from_bits(config.fifo_threshold.into())); }); // Wait for busy flag to clear - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} - T::REGS.xspi_dcr2().modify(|w| { - w.set_prescaler(Prescaler::from_bits(config.clock_prescaler.into())); + T::REGS.dcr2().modify(|w| { + w.set_prescaler(config.clock_prescaler); }); - T::REGS.xspi_tcr().modify(|w| { - w.set_sshift(match config.sample_shifting { - true => XspiTcrSshift::B_0X1, - false => XspiTcrSshift::B_0X0, - }); - w.set_dhqc(match config.delay_hold_quarter_cycle { - true => XspiTcrDhqc::B_0X1, - false => XspiTcrDhqc::B_0X0, - }); + T::REGS.tcr().modify(|w| { + w.set_sshift(config.sample_shifting); + w.set_dhqc(config.delay_hold_quarter_cycle); }); // Enable peripheral - T::REGS.xspi_cr().modify(|w| { - w.set_en(En::B_0X1); + T::REGS.cr().modify(|w| { + w.set_en(true); }); // Free running clock needs to be set after peripheral enable if config.free_running_clock { - T::REGS.xspi_dcr1().modify(|w| { - w.set_frck(match config.free_running_clock { - true => Frck::B_0X1, - false => Frck::B_0X0, - }); + T::REGS.dcr1().modify(|w| { + w.set_frck(config.free_running_clock); }); } @@ -1143,31 +1110,31 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { } // Wait for peripheral to be free - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} self.configure_command(&transaction, Some(buf.len()))?; - let current_address = T::REGS.xspi_ar().read().address(); - let current_instruction = T::REGS.xspi_ir().read().instruction(); + let current_address = T::REGS.ar().read().address(); + let current_instruction = T::REGS.ir().read().instruction(); // For a indirect read transaction, the transaction begins when the instruction/address is set T::REGS - .xspi_cr() + .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into()))); - if T::REGS.xspi_ccr().read().admode() == XspiCcrAdmode::B_0X0 { - T::REGS.xspi_ir().write(|v| v.set_instruction(current_instruction)); + if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); } else { - T::REGS.xspi_ar().write(|v| v.set_address(current_address)); + T::REGS.ar().write(|v| v.set_address(current_address)); } let transfer = unsafe { self.dma .as_mut() .unwrap() - .read(T::REGS.xspi_dr().as_ptr() as *mut W, buf, Default::default()) + .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) }; - T::REGS.xspi_cr().modify(|w| w.set_dmaen(Dmaen::B_0X1)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); transfer.blocking_wait(); @@ -1183,21 +1150,21 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { } // Wait for peripheral to be free - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} self.configure_command(&transaction, Some(buf.len()))?; T::REGS - .xspi_cr() + .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); let transfer = unsafe { self.dma .as_mut() .unwrap() - .write(buf, T::REGS.xspi_dr().as_ptr() as *mut W, Default::default()) + .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) }; - T::REGS.xspi_cr().modify(|w| w.set_dmaen(Dmaen::B_0X1)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); transfer.blocking_wait(); @@ -1213,31 +1180,31 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { } // Wait for peripheral to be free - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} self.configure_command(&transaction, Some(buf.len()))?; - let current_address = T::REGS.xspi_ar().read().address(); - let current_instruction = T::REGS.xspi_ir().read().instruction(); + let current_address = T::REGS.ar().read().address(); + let current_instruction = T::REGS.ir().read().instruction(); // For a indirect read transaction, the transaction begins when the instruction/address is set T::REGS - .xspi_cr() + .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectRead.into()))); - if T::REGS.xspi_ccr().read().admode() == XspiCcrAdmode::B_0X0 { - T::REGS.xspi_ir().write(|v| v.set_instruction(current_instruction)); + if T::REGS.ccr().read().admode() == CcrAdmode::B_0X0 { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); } else { - T::REGS.xspi_ar().write(|v| v.set_address(current_address)); + T::REGS.ar().write(|v| v.set_address(current_address)); } let transfer = unsafe { self.dma .as_mut() .unwrap() - .read(T::REGS.xspi_dr().as_ptr() as *mut W, buf, Default::default()) + .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) }; - T::REGS.xspi_cr().modify(|w| w.set_dmaen(Dmaen::B_0X1)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); transfer.await; @@ -1253,21 +1220,21 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { } // Wait for peripheral to be free - while T::REGS.xspi_sr().read().busy() {} + while T::REGS.sr().read().busy() {} self.configure_command(&transaction, Some(buf.len()))?; T::REGS - .xspi_cr() + .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); let transfer = unsafe { self.dma .as_mut() .unwrap() - .write(buf, T::REGS.xspi_dr().as_ptr() as *mut W, Default::default()) + .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) }; - T::REGS.xspi_cr().modify(|w| w.set_dmaen(Dmaen::B_0X1)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); transfer.await; @@ -1306,16 +1273,16 @@ impl<'d, T: Instance, M: PeriMode> Drop for Xspi<'d, T, M> { } fn finish_dma(regs: Regs) { - while !regs.xspi_sr().read().tcf() {} - regs.xspi_fcr().write(|v| v.set_ctcf(true)); + while !regs.sr().read().tcf() {} + regs.fcr().write(|v| v.set_ctcf(true)); - regs.xspi_cr().modify(|w| { - w.set_dmaen(Dmaen::B_0X0); + regs.cr().modify(|w| { + w.set_dmaen(false); }); } /// XSPI I/O manager instance trait. -#[cfg(xspim_v2_1)] +#[cfg(xspim_v1)] pub(crate) trait SealedXspimInstance { const SPIM_REGS: Xspim; const SPI_IDX: u8; @@ -1327,12 +1294,12 @@ pub(crate) trait SealedInstance { } /// XSPI instance trait. -#[cfg(xspim_v2_1)] +#[cfg(xspim_v1)] #[allow(private_bounds)] pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral + SealedXspimInstance {} /// XSPI instance trait. -#[cfg(not(xspim_v2_1))] +#[cfg(not(xspim_v1))] #[allow(private_bounds)] pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} @@ -1361,20 +1328,20 @@ pin_trait!(NCLKPin, Instance); dma_trait!(XDma, Instance); // Hard-coded the xspi index, for SPIM -#[cfg(xspim_v2_1)] +#[cfg(xspim_v1)] impl SealedXspimInstance for peripherals::XSPI1 { const SPIM_REGS: Xspim = crate::pac::XSPIM; const SPI_IDX: u8 = 1; } -// #[cfg(all(xspim_v2_1, peri_xspi2))] -#[cfg(xspim_v2_1)] +// Some cubedb files are missing XSPI2, for example STM32H7R3Z8 +#[cfg(all(xspim_v1, peri_xspi2))] impl SealedXspimInstance for peripherals::XSPI2 { const SPIM_REGS: Xspim = crate::pac::XSPIM; const SPI_IDX: u8 = 2; } -#[cfg(xspim_v2_1)] +#[cfg(xspim_v1)] foreach_peripheral!( (xspi, $inst:ident) => { impl SealedInstance for peripherals::$inst { @@ -1385,7 +1352,7 @@ foreach_peripheral!( }; ); -#[cfg(not(xspim_v2_1))] +#[cfg(not(xspim_v1))] foreach_peripheral!( (xspi, $inst:ident) => { impl SealedInstance for peripherals::$inst { From 65f849a589be78f8f5dce2311614982ee96bbae5 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 3 Apr 2025 10:48:29 +0800 Subject: [PATCH 4/8] stm32: xspi update for Peri --- embassy-stm32/src/xspi/mod.rs | 264 +++++++++++++++++----------------- 1 file changed, 131 insertions(+), 133 deletions(-) diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index 3b5406a57..c024b2ed6 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -8,7 +8,7 @@ pub mod enums; use core::marker::PhantomData; use embassy_embedded_hal::{GetConfig, SetConfig}; -use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_hal_internal::PeripheralType; pub use enums::*; use crate::dma::{word, ChannelAndRequest}; @@ -19,7 +19,7 @@ use crate::pac::xspi::Xspi as Regs; #[cfg(xspim_v1)] use crate::pac::xspim::Xspim; use crate::rcc::{self, RccPeripheral}; -use crate::{peripherals, Peripheral}; +use crate::{peripherals, Peri}; /// XPSI driver config. #[derive(Clone, Copy)] @@ -157,28 +157,28 @@ pub enum XspiError { /// XSPI driver. pub struct Xspi<'d, T: Instance, M: PeriMode> { - _peri: PeripheralRef<'d, T>, - clk: Option>, - d0: Option>, - d1: Option>, - d2: Option>, - d3: Option>, - d4: Option>, - d5: Option>, - d6: Option>, - d7: Option>, - d8: Option>, - d9: Option>, - d10: Option>, - d11: Option>, - d12: Option>, - d13: Option>, - d14: Option>, - d15: Option>, - ncs1: Option>, - ncs2: Option>, - dqs0: Option>, - dqs1: Option>, + _peri: Peri<'d, T>, + clk: Option>, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + d4: Option>, + d5: Option>, + d6: Option>, + d7: Option>, + d8: Option>, + d9: Option>, + d10: Option>, + d11: Option>, + d12: Option>, + d13: Option>, + d14: Option>, + d15: Option>, + ncs1: Option>, + ncs2: Option>, + dqs0: Option>, + dqs1: Option>, dma: Option>, _phantom: PhantomData, config: Config, @@ -251,35 +251,33 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { } fn new_inner( - peri: impl Peripheral

+ 'd, - d0: Option>, - d1: Option>, - d2: Option>, - d3: Option>, - d4: Option>, - d5: Option>, - d6: Option>, - d7: Option>, - d8: Option>, - d9: Option>, - d10: Option>, - d11: Option>, - d12: Option>, - d13: Option>, - d14: Option>, - d15: Option>, - clk: Option>, - ncs1: Option>, - ncs2: Option>, - dqs0: Option>, - dqs1: Option>, + peri: Peri<'d, T>, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + d4: Option>, + d5: Option>, + d6: Option>, + d7: Option>, + d8: Option>, + d9: Option>, + d10: Option>, + d11: Option>, + d12: Option>, + d13: Option>, + d14: Option>, + d15: Option>, + clk: Option>, + ncs1: Option>, + ncs2: Option>, + dqs0: Option>, + dqs1: Option>, dma: Option>, config: Config, width: XspiWidth, dual_quad: bool, ) -> Self { - into_ref!(peri); - #[cfg(xspim_v1)] { // RCC for xspim should be enabled before writing register @@ -656,11 +654,11 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { impl<'d, T: Instance> Xspi<'d, T, Blocking> { /// Create new blocking XSPI driver for a single spi external chip pub fn new_blocking_singlespi( - peri: impl Peripheral

+ 'd, - clk: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - ncs: impl Peripheral

> + 'd, + peri: Peri<'d, T>, + clk: Peri<'d, impl CLKPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + ncs: Peri<'d, impl NCS1Pin>, config: Config, ) -> Self { Self::new_inner( @@ -695,11 +693,11 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { /// Create new blocking XSPI driver for a dualspi external chip pub fn new_blocking_dualspi( - peri: impl Peripheral

+ 'd, - clk: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - ncs: impl Peripheral

> + 'd, + peri: Peri<'d, T>, + clk: Peri<'d, impl CLKPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + ncs: Peri<'d, impl NCS1Pin>, config: Config, ) -> Self { Self::new_inner( @@ -737,13 +735,13 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { /// Create new blocking XSPI driver for a quadspi external chip pub fn new_blocking_quadspi( - peri: impl Peripheral

+ 'd, - clk: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - ncs: impl Peripheral

> + 'd, + peri: Peri<'d, T>, + clk: Peri<'d, impl CLKPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + d2: Peri<'d, impl D2Pin>, + d3: Peri<'d, impl D3Pin>, + ncs: Peri<'d, impl NCS1Pin>, config: Config, ) -> Self { Self::new_inner( @@ -781,17 +779,17 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { /// Create new blocking XSPI driver for two quadspi external chips pub fn new_blocking_dualquadspi( - peri: impl Peripheral

+ 'd, - clk: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - d4: impl Peripheral

> + 'd, - d5: impl Peripheral

> + 'd, - d6: impl Peripheral

> + 'd, - d7: impl Peripheral

> + 'd, - ncs: impl Peripheral

> + 'd, + peri: Peri<'d, T>, + clk: Peri<'d, impl CLKPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + d2: Peri<'d, impl D2Pin>, + d3: Peri<'d, impl D3Pin>, + d4: Peri<'d, impl D4Pin>, + d5: Peri<'d, impl D5Pin>, + d6: Peri<'d, impl D6Pin>, + d7: Peri<'d, impl D7Pin>, + ncs: Peri<'d, impl NCS1Pin>, config: Config, ) -> Self { Self::new_inner( @@ -829,17 +827,17 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { /// Create new blocking XSPI driver for xspi external chips pub fn new_blocking_xspi( - peri: impl Peripheral

+ 'd, - clk: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - d4: impl Peripheral

> + 'd, - d5: impl Peripheral

> + 'd, - d6: impl Peripheral

> + 'd, - d7: impl Peripheral

> + 'd, - ncs: impl Peripheral

> + 'd, + peri: Peri<'d, T>, + clk: Peri<'d, impl CLKPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + d2: Peri<'d, impl D2Pin>, + d3: Peri<'d, impl D3Pin>, + d4: Peri<'d, impl D4Pin>, + d5: Peri<'d, impl D5Pin>, + d6: Peri<'d, impl D6Pin>, + d7: Peri<'d, impl D7Pin>, + ncs: Peri<'d, impl NCS1Pin>, config: Config, ) -> Self { Self::new_inner( @@ -879,12 +877,12 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { impl<'d, T: Instance> Xspi<'d, T, Async> { /// Create new blocking XSPI driver for a single spi external chip pub fn new_singlespi( - peri: impl Peripheral

+ 'd, - clk: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - ncs: impl Peripheral

> + 'd, - dma: impl Peripheral

> + 'd, + peri: Peri<'d, T>, + clk: Peri<'d, impl CLKPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + ncs: Peri<'d, impl NCS1Pin>, + dma: Peri<'d, impl XDma>, config: Config, ) -> Self { Self::new_inner( @@ -919,12 +917,12 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { /// Create new blocking XSPI driver for a dualspi external chip pub fn new_dualspi( - peri: impl Peripheral

+ 'd, - clk: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - ncs: impl Peripheral

> + 'd, - dma: impl Peripheral

> + 'd, + peri: Peri<'d, T>, + clk: Peri<'d, impl CLKPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + ncs: Peri<'d, impl NCS1Pin>, + dma: Peri<'d, impl XDma>, config: Config, ) -> Self { Self::new_inner( @@ -962,14 +960,14 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { /// Create new blocking XSPI driver for a quadspi external chip pub fn new_quadspi( - peri: impl Peripheral

+ 'd, - clk: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - ncs: impl Peripheral

> + 'd, - dma: impl Peripheral

> + 'd, + peri: Peri<'d, T>, + clk: Peri<'d, impl CLKPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + d2: Peri<'d, impl D2Pin>, + d3: Peri<'d, impl D3Pin>, + ncs: Peri<'d, impl NCS1Pin>, + dma: Peri<'d, impl XDma>, config: Config, ) -> Self { Self::new_inner( @@ -1007,18 +1005,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { /// Create new blocking XSPI driver for two quadspi external chips pub fn new_dualquadspi( - peri: impl Peripheral

+ 'd, - clk: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - d4: impl Peripheral

> + 'd, - d5: impl Peripheral

> + 'd, - d6: impl Peripheral

> + 'd, - d7: impl Peripheral

> + 'd, - ncs: impl Peripheral

> + 'd, - dma: impl Peripheral

> + 'd, + peri: Peri<'d, T>, + clk: Peri<'d, impl CLKPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + d2: Peri<'d, impl D2Pin>, + d3: Peri<'d, impl D3Pin>, + d4: Peri<'d, impl D4Pin>, + d5: Peri<'d, impl D5Pin>, + d6: Peri<'d, impl D6Pin>, + d7: Peri<'d, impl D7Pin>, + ncs: Peri<'d, impl NCS1Pin>, + dma: Peri<'d, impl XDma>, config: Config, ) -> Self { Self::new_inner( @@ -1056,18 +1054,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { /// Create new blocking XSPI driver for xspi external chips pub fn new_xspi( - peri: impl Peripheral

+ 'd, - clk: impl Peripheral

> + 'd, - d0: impl Peripheral

> + 'd, - d1: impl Peripheral

> + 'd, - d2: impl Peripheral

> + 'd, - d3: impl Peripheral

> + 'd, - d4: impl Peripheral

> + 'd, - d5: impl Peripheral

> + 'd, - d6: impl Peripheral

> + 'd, - d7: impl Peripheral

> + 'd, - ncs: impl Peripheral

> + 'd, - dma: impl Peripheral

> + 'd, + peri: Peri<'d, T>, + clk: Peri<'d, impl CLKPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + d2: Peri<'d, impl D2Pin>, + d3: Peri<'d, impl D3Pin>, + d4: Peri<'d, impl D4Pin>, + d5: Peri<'d, impl D5Pin>, + d6: Peri<'d, impl D6Pin>, + d7: Peri<'d, impl D7Pin>, + ncs: Peri<'d, impl NCS1Pin>, + dma: Peri<'d, impl XDma>, config: Config, ) -> Self { Self::new_inner( @@ -1296,12 +1294,12 @@ pub(crate) trait SealedInstance { /// XSPI instance trait. #[cfg(xspim_v1)] #[allow(private_bounds)] -pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral + SealedXspimInstance {} +pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + SealedXspimInstance {} /// XSPI instance trait. #[cfg(not(xspim_v1))] #[allow(private_bounds)] -pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} +pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {} pin_trait!(D0Pin, Instance); pin_trait!(D1Pin, Instance); From e22fe7cbcfbbb68e4ad6ef7914bf8ed952fca8f4 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Wed, 2 Apr 2025 21:00:08 +0800 Subject: [PATCH 5/8] stm32: xspi wait for prescaler change This is documented as being required (and is done by stm32cube), hasn't been observed as a problem though. --- embassy-stm32/src/xspi/mod.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index c024b2ed6..c315e2320 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -335,8 +335,17 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { w.set_prescaler(config.clock_prescaler); }); + // Wait for busy flag to clear after changing prescaler, during calibration + while T::REGS.sr().read().busy() {} + T::REGS.cr().modify(|w| { w.set_dmm(dual_quad); + + // TODO: at the moment only ncs1 seems to get passed in? + // Only one must be selected + assert!(!(ncs1.is_some() && ncs2.is_some())); + assert!(!(ncs1.is_none() && ncs2.is_none())); + w.set_cssel(if ncs1.is_some() { Cssel::B_0X0 } else { Cssel::B_0X1 }); }); T::REGS.tcr().modify(|w| { @@ -344,14 +353,6 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { w.set_dhqc(config.delay_hold_quarter_cycle); }); - // TODO: at the moment only ncs1 seems to get passed in? - // Only one must be selected - assert!(!(ncs1.is_some() && ncs2.is_some())); - assert!(!(ncs1.is_none() && ncs2.is_none())); - T::REGS.cr().modify(|w| { - w.set_cssel(if ncs1.is_some() { Cssel::B_0X0 } else { Cssel::B_0X1 }); - }); - // Enable peripheral T::REGS.cr().modify(|w| { w.set_en(true); From 6b80f3badaf722309f13accd0e8a7e0f04a837ce Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 4 Apr 2025 15:08:05 +0800 Subject: [PATCH 6/8] stm32: xspi: rework switching between ncs1 and ncs2 --- embassy-stm32/build.rs | 24 +++++++++--- embassy-stm32/src/macros.rs | 11 ++++++ embassy-stm32/src/xspi/mod.rs | 69 ++++++++++++++++++++++------------- 3 files changed, 73 insertions(+), 31 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index fa9e3f953..b4e61878c 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1123,8 +1123,8 @@ fn main() { (("xspi", "IO15"), quote!(crate::xspi::D15Pin)), (("xspi", "DQS0"), quote!(crate::xspi::DQS0Pin)), (("xspi", "DQS1"), quote!(crate::xspi::DQS1Pin)), - (("xspi", "NCS1"), quote!(crate::xspi::NCS1Pin)), - (("xspi", "NCS2"), quote!(crate::xspi::NCS2Pin)), + (("xspi", "NCS1"), quote!(crate::xspi::NCSPin)), + (("xspi", "NCS2"), quote!(crate::xspi::NCSPin)), (("xspi", "CLK"), quote!(crate::xspi::CLKPin)), (("xspi", "NCLK"), quote!(crate::xspi::NCLKPin)), (("xspim", "P1_IO0"), quote!(crate::xspi::D0Pin)), @@ -1145,8 +1145,8 @@ fn main() { (("xspim", "P1_IO15"), quote!(crate::xspi::D15Pin)), (("xspim", "P1_DQS0"), quote!(crate::xspi::DQS0Pin)), (("xspim", "P1_DQS1"), quote!(crate::xspi::DQS1Pin)), - (("xspim", "P1_NCS1"), quote!(crate::xspi::NCS1Pin)), - (("xspim", "P1_NCS2"), quote!(crate::xspi::NCS2Pin)), + (("xspim", "P1_NCS1"), quote!(crate::xspi::NCSPin)), + (("xspim", "P1_NCS2"), quote!(crate::xspi::NCSPin)), (("xspim", "P1_CLK"), quote!(crate::xspi::CLKPin)), (("xspim", "P1_NCLK"), quote!(crate::xspi::NCLKPin)), (("xspim", "P2_IO0"), quote!(crate::xspi::D0Pin)), @@ -1167,8 +1167,8 @@ fn main() { (("xspim", "P2_IO15"), quote!(crate::xspi::D15Pin)), (("xspim", "P2_DQS0"), quote!(crate::xspi::DQS0Pin)), (("xspim", "P2_DQS1"), quote!(crate::xspi::DQS1Pin)), - (("xspim", "P2_NCS1"), quote!(crate::xspi::NCS1Pin)), - (("xspim", "P2_NCS2"), quote!(crate::xspi::NCS2Pin)), + (("xspim", "P2_NCS1"), quote!(crate::xspi::NCSPin)), + (("xspim", "P2_NCS2"), quote!(crate::xspi::NCSPin)), (("xspim", "P2_CLK"), quote!(crate::xspi::CLKPin)), (("xspim", "P2_NCLK"), quote!(crate::xspi::NCLKPin)), (("hspi", "IO0"), quote!(crate::hspi::D0Pin)), @@ -1274,6 +1274,18 @@ fn main() { } } + // XSPI NCS pin to CSSEL mapping + if pin.signal.ends_with("NCS1") { + g.extend(quote! { + sel_trait_impl!(crate::xspi::NCSEither, #peri, #pin_name, 0); + }) + } + if pin.signal.ends_with("NCS2") { + g.extend(quote! { + sel_trait_impl!(crate::xspi::NCSEither, #peri, #pin_name, 1); + }) + } + g.extend(quote! { pin_trait_impl!(#tr, #peri, #pin_name, #af); }) diff --git a/embassy-stm32/src/macros.rs b/embassy-stm32/src/macros.rs index 2c181a254..7526bb180 100644 --- a/embassy-stm32/src/macros.rs +++ b/embassy-stm32/src/macros.rs @@ -60,6 +60,17 @@ macro_rules! pin_trait_impl { }; } +#[allow(unused_macros)] +macro_rules! sel_trait_impl { + (crate::$mod:ident::$trait:ident$(<$mode:ident>)?, $instance:ident, $pin:ident, $sel:expr) => { + impl crate::$mod::$trait for crate::peripherals::$pin { + fn sel(&self) -> u8 { + $sel + } + } + }; +} + // ==================== macro_rules! dma_trait { diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index c315e2320..bc3007fe8 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -175,8 +175,12 @@ pub struct Xspi<'d, T: Instance, M: PeriMode> { d13: Option>, d14: Option>, d15: Option>, - ncs1: Option>, - ncs2: Option>, + ncs: Option>, + // TODO: allow switching between multiple chips + ncs_alt: Option>, + // false if ncs == NCS1, true if ncs == NCS2 + // (ncs_alt will be the opposite to ncs). + _cssel_swap: bool, dqs0: Option>, dqs1: Option>, dma: Option>, @@ -269,8 +273,9 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { d14: Option>, d15: Option>, clk: Option>, - ncs1: Option>, - ncs2: Option>, + ncs_cssel: u8, + ncs: Option>, + ncs_alt: Option>, dqs0: Option>, dqs1: Option>, dma: Option>, @@ -341,11 +346,9 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { T::REGS.cr().modify(|w| { w.set_dmm(dual_quad); - // TODO: at the moment only ncs1 seems to get passed in? - // Only one must be selected - assert!(!(ncs1.is_some() && ncs2.is_some())); - assert!(!(ncs1.is_none() && ncs2.is_none())); - w.set_cssel(if ncs1.is_some() { Cssel::B_0X0 } else { Cssel::B_0X1 }); + assert!(ncs_alt.is_none(), "ncs_alt TODO"); + let cssel = if ncs_cssel == 0 { Cssel::B_0X0 } else { Cssel::B_0X1 }; + w.set_cssel(cssel); }); T::REGS.tcr().modify(|w| { @@ -384,8 +387,9 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { d13, d14, d15, - ncs1, - ncs2, + ncs, + ncs_alt, + _cssel_swap: ncs_cssel == 1, dqs0, dqs1, dma, @@ -659,7 +663,7 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { clk: Peri<'d, impl CLKPin>, d0: Peri<'d, impl D0Pin>, d1: Peri<'d, impl D1Pin>, - ncs: Peri<'d, impl NCS1Pin>, + ncs: Peri<'d, impl NCSEither>, config: Config, ) -> Self { Self::new_inner( @@ -681,6 +685,7 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { None, None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + ncs.sel(), new_pin!(ncs, AfType::output(OutputType::OpenDrain, Speed::VeryHigh)), None, None, @@ -698,7 +703,7 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { clk: Peri<'d, impl CLKPin>, d0: Peri<'d, impl D0Pin>, d1: Peri<'d, impl D1Pin>, - ncs: Peri<'d, impl NCS1Pin>, + ncs: Peri<'d, impl NCSEither>, config: Config, ) -> Self { Self::new_inner( @@ -720,6 +725,7 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { None, None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + ncs.sel(), new_pin!( ncs, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) @@ -742,7 +748,7 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { d1: Peri<'d, impl D1Pin>, d2: Peri<'d, impl D2Pin>, d3: Peri<'d, impl D3Pin>, - ncs: Peri<'d, impl NCS1Pin>, + ncs: Peri<'d, impl NCSEither>, config: Config, ) -> Self { Self::new_inner( @@ -764,6 +770,7 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { None, None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + ncs.sel(), new_pin!( ncs, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) @@ -790,7 +797,7 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { d5: Peri<'d, impl D5Pin>, d6: Peri<'d, impl D6Pin>, d7: Peri<'d, impl D7Pin>, - ncs: Peri<'d, impl NCS1Pin>, + ncs: Peri<'d, impl NCSEither>, config: Config, ) -> Self { Self::new_inner( @@ -812,6 +819,7 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { None, None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + ncs.sel(), new_pin!( ncs, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) @@ -838,7 +846,7 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { d5: Peri<'d, impl D5Pin>, d6: Peri<'d, impl D6Pin>, d7: Peri<'d, impl D7Pin>, - ncs: Peri<'d, impl NCS1Pin>, + ncs: Peri<'d, impl NCSEither>, config: Config, ) -> Self { Self::new_inner( @@ -860,6 +868,7 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { None, None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + ncs.sel(), new_pin!( ncs, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) @@ -882,7 +891,7 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { clk: Peri<'d, impl CLKPin>, d0: Peri<'d, impl D0Pin>, d1: Peri<'d, impl D1Pin>, - ncs: Peri<'d, impl NCS1Pin>, + ncs: Peri<'d, impl NCSEither>, dma: Peri<'d, impl XDma>, config: Config, ) -> Self { @@ -905,6 +914,7 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { None, None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + ncs.sel(), new_pin!(ncs, AfType::output(OutputType::PushPull, Speed::VeryHigh)), None, None, @@ -922,7 +932,7 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { clk: Peri<'d, impl CLKPin>, d0: Peri<'d, impl D0Pin>, d1: Peri<'d, impl D1Pin>, - ncs: Peri<'d, impl NCS1Pin>, + ncs: Peri<'d, impl NCSEither>, dma: Peri<'d, impl XDma>, config: Config, ) -> Self { @@ -945,6 +955,7 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { None, None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + ncs.sel(), new_pin!( ncs, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) @@ -967,7 +978,7 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { d1: Peri<'d, impl D1Pin>, d2: Peri<'d, impl D2Pin>, d3: Peri<'d, impl D3Pin>, - ncs: Peri<'d, impl NCS1Pin>, + ncs: Peri<'d, impl NCSEither>, dma: Peri<'d, impl XDma>, config: Config, ) -> Self { @@ -990,6 +1001,7 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { None, None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + ncs.sel(), new_pin!( ncs, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) @@ -1016,7 +1028,7 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { d5: Peri<'d, impl D5Pin>, d6: Peri<'d, impl D6Pin>, d7: Peri<'d, impl D7Pin>, - ncs: Peri<'d, impl NCS1Pin>, + ncs: Peri<'d, impl NCSEither>, dma: Peri<'d, impl XDma>, config: Config, ) -> Self { @@ -1039,6 +1051,7 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { None, None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + ncs.sel(), new_pin!( ncs, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) @@ -1065,7 +1078,7 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { d5: Peri<'d, impl D5Pin>, d6: Peri<'d, impl D6Pin>, d7: Peri<'d, impl D7Pin>, - ncs: Peri<'d, impl NCS1Pin>, + ncs: Peri<'d, impl NCSEither>, dma: Peri<'d, impl XDma>, config: Config, ) -> Self { @@ -1088,6 +1101,7 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { None, None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + ncs.sel(), new_pin!( ncs, AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) @@ -1262,8 +1276,8 @@ impl<'d, T: Instance, M: PeriMode> Drop for Xspi<'d, T, M> { self.d13.as_ref().map(|x| x.set_as_disconnected()); self.d14.as_ref().map(|x| x.set_as_disconnected()); self.d15.as_ref().map(|x| x.set_as_disconnected()); - self.ncs1.as_ref().map(|x| x.set_as_disconnected()); - self.ncs2.as_ref().map(|x| x.set_as_disconnected()); + self.ncs.as_ref().map(|x| x.set_as_disconnected()); + self.ncs_alt.as_ref().map(|x| x.set_as_disconnected()); self.dqs0.as_ref().map(|x| x.set_as_disconnected()); self.dqs1.as_ref().map(|x| x.set_as_disconnected()); @@ -1320,12 +1334,17 @@ pin_trait!(D14Pin, Instance); pin_trait!(D15Pin, Instance); pin_trait!(DQS0Pin, Instance); pin_trait!(DQS1Pin, Instance); -pin_trait!(NCS1Pin, Instance); -pin_trait!(NCS2Pin, Instance); +pin_trait!(NCSPin, Instance); pin_trait!(CLKPin, Instance); pin_trait!(NCLKPin, Instance); dma_trait!(XDma, Instance); +/// Trait for either NCS1 or NCS2 pins +pub trait NCSEither: NCSPin { + /// Get the CSSEL for this NCS pin + fn sel(&self) -> u8; +} + // Hard-coded the xspi index, for SPIM #[cfg(xspim_v1)] impl SealedXspimInstance for peripherals::XSPI1 { From 5f7da4cfc8e1028a9cffb0c539d860a73e830f03 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 4 Apr 2025 15:41:35 +0800 Subject: [PATCH 7/8] stm32: xspi fixes and consistency Fix some incorrect DTR flags, fix _bit vs _Bit inconsistency (copied from qspi and ospi). Use the same NCS pullup for all constructors. xspi is now enabled in PWR register --- embassy-stm32/src/xspi/enums.rs | 8 ++++---- embassy-stm32/src/xspi/mod.rs | 29 +++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/src/xspi/enums.rs b/embassy-stm32/src/xspi/enums.rs index e02ec797e..c96641180 100644 --- a/embassy-stm32/src/xspi/enums.rs +++ b/embassy-stm32/src/xspi/enums.rs @@ -160,9 +160,9 @@ impl Into for MemorySize { #[derive(Copy, Clone)] pub enum AddressSize { /// 8-bit address - _8Bit, + _8bit, /// 16-bit address - _16Bit, + _16bit, /// 24-bit address _24bit, /// 32-bit address @@ -172,8 +172,8 @@ pub enum AddressSize { impl Into for AddressSize { fn into(self) -> u8 { match self { - AddressSize::_8Bit => 0b00, - AddressSize::_16Bit => 0b01, + AddressSize::_8bit => 0b00, + AddressSize::_16bit => 0b01, AddressSize::_24bit => 0b10, AddressSize::_32bit => 0b11, } diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index bc3007fe8..44c10b961 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -122,17 +122,17 @@ impl Default for TransferConfig { Self { iwidth: XspiWidth::NONE, instruction: None, - isize: AddressSize::_8Bit, + isize: AddressSize::_8bit, idtr: false, adwidth: XspiWidth::NONE, address: None, - adsize: AddressSize::_8Bit, + adsize: AddressSize::_8bit, addtr: false, abwidth: XspiWidth::NONE, alternate_bytes: None, - absize: AddressSize::_8Bit, + absize: AddressSize::_8bit, abdtr: false, dwidth: XspiWidth::NONE, @@ -214,11 +214,11 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { w.set_isize(WccrIsize::from_bits(write_config.isize.into())); w.set_admode(WccrAdmode::from_bits(write_config.adwidth.into())); - w.set_addtr(write_config.idtr); + w.set_addtr(write_config.addtr); w.set_adsize(WccrAdsize::from_bits(write_config.adsize.into())); w.set_dmode(WccrDmode::from_bits(write_config.dwidth.into())); - w.set_ddtr(write_config.idtr); + w.set_ddtr(write_config.ddtr); w.set_abmode(WccrAbmode::from_bits(write_config.abwidth.into())); w.set_dqse(true); @@ -283,6 +283,13 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { width: XspiWidth, dual_quad: bool, ) -> Self { + // Enable the interface + match T::SPI_IDX { + 1 => crate::pac::PWR.csr2().modify(|r| r.set_en_xspim1(true)), + 2 => crate::pac::PWR.csr2().modify(|r| r.set_en_xspim2(true)), + _ => unreachable!(), + }; + #[cfg(xspim_v1)] { // RCC for xspim should be enabled before writing register @@ -447,7 +454,7 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { w.set_isize(CcrIsize::from_bits(command.isize.into())); w.set_admode(CcrAdmode::from_bits(command.adwidth.into())); - w.set_addtr(command.idtr); + w.set_addtr(command.addtr); w.set_adsize(CcrAdsize::from_bits(command.adsize.into())); w.set_dmode(CcrDmode::from_bits(command.dwidth.into())); @@ -686,7 +693,10 @@ impl<'d, T: Instance> Xspi<'d, T, Blocking> { None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), ncs.sel(), - new_pin!(ncs, AfType::output(OutputType::OpenDrain, Speed::VeryHigh)), + new_pin!( + ncs, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), None, None, None, @@ -915,7 +925,10 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { None, new_pin!(clk, AfType::output(OutputType::PushPull, Speed::VeryHigh)), ncs.sel(), - new_pin!(ncs, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + ncs, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), None, None, None, From 43ef76b1b6c49085e1af06e0b1dd09a2d6c664e7 Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Fri, 4 Apr 2025 15:47:32 +0800 Subject: [PATCH 8/8] Add stm32h7rs xpi_memory_mapped example Based on ospi_memory_mapped, targetting stm32h7s3 nucleo board. This works in single mode, no octo mode yet. --- .../stm32h7rs/src/bin/xspi_memory_mapped.rs | 448 ++++++++++++++++++ 1 file changed, 448 insertions(+) create mode 100644 examples/stm32h7rs/src/bin/xspi_memory_mapped.rs diff --git a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs new file mode 100644 index 000000000..88d914180 --- /dev/null +++ b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs @@ -0,0 +1,448 @@ +#![no_main] +#![no_std] + +//! For Nucleo STM32H7S3L8 MB1737, has MX25UW25645GXDI00 +//! +//! TODO: Currently this only uses single SPI, pending flash chip documentation for octo SPI. + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::mode::Blocking; +use embassy_stm32::time::Hertz; +use embassy_stm32::xspi::{ + AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, TransferConfig, + WrapSize, Xspi, XspiWidth, +}; +use embassy_stm32::Config; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // RCC config + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(24_000_000), + mode: HseMode::Oscillator, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV3, + mul: PllMul::MUL150, + divp: Some(PllDiv::DIV2), + divq: None, + divr: None, + }); + config.rcc.sys = Sysclk::PLL1_P; // 600 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV2; // 300 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 150 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 150 Mhz + config.rcc.apb4_pre = APBPrescaler::DIV2; // 150 Mhz + config.rcc.apb5_pre = APBPrescaler::DIV2; // 150 Mhz + config.rcc.voltage_scale = VoltageScale::HIGH; + } + + // Initialize peripherals + let p = embassy_stm32::init(config); + + let spi_config = embassy_stm32::xspi::Config { + fifo_threshold: FIFOThresholdLevel::_4Bytes, + memory_type: MemoryType::Macronix, + delay_hold_quarter_cycle: true, + // memory_type: MemoryType::Micron, + // delay_hold_quarter_cycle: false, + device_size: MemorySize::_32MiB, + chip_select_high_time: ChipSelectHighTime::_2Cycle, + free_running_clock: false, + clock_mode: false, + wrap_size: WrapSize::None, + // 300mhz / (4+1) = 60mhz. Unsure the limit, need to find a MX25UW25645GXDI00 datasheet. + clock_prescaler: 3, + sample_shifting: false, + chip_select_boundary: 0, + max_transfer: 0, + refresh: 0, + }; + + let mut cor = cortex_m::Peripherals::take().unwrap(); + + // Not necessary, but recommended if using XIP + cor.SCB.enable_icache(); + cor.SCB.enable_dcache(&mut cor.CPUID); + + let xspi = embassy_stm32::xspi::Xspi::new_blocking_xspi( + p.XSPI2, p.PN6, p.PN2, p.PN3, p.PN4, p.PN5, p.PN8, p.PN9, p.PN10, p.PN11, p.PN1, spi_config, + ); + + let mut flash = FlashMemory::new(xspi).await; + + let flash_id = flash.read_id(); + info!("FLASH ID: {=[u8]:x}", flash_id); + + let mut wr_buf = [0u8; 8]; + for i in 0..8 { + wr_buf[i] = 0x90 + i as u8; + } + let mut rd_buf = [0u8; 8]; + flash.erase_sector(0).await; + flash.write_memory(0, &wr_buf, true).await; + flash.read_memory(0, &mut rd_buf, true); + info!("WRITE BUF: {=[u8]:#X}", wr_buf); + info!("READ BUF: {=[u8]:#X}", rd_buf); + flash.enable_mm().await; + info!("Enabled memory mapped mode"); + + let first_u32 = unsafe { *(0x70000000 as *const u32) }; + assert_eq!(first_u32, 0x93929190); + info!("first_u32 {:08x}", first_u32); + + let second_u32 = unsafe { *(0x70000004 as *const u32) }; + assert_eq!(second_u32, 0x97969594); + info!("second_u32 {:08x}", first_u32); + + flash.disable_mm().await; + info!("Disabled memory mapped mode"); + + info!("DONE"); + // Output pin PE3 + let mut led = Output::new(p.PE3, Level::Low, Speed::Low); + + loop { + led.toggle(); + Timer::after_millis(1000).await; + } +} + +const MEMORY_PAGE_SIZE: usize = 8; + +const CMD_READ: u8 = 0x0B; +const _CMD_QUAD_READ: u8 = 0x6B; + +const CMD_WRITE_PG: u8 = 0x02; +const _CMD_QUAD_WRITE_PG: u8 = 0x32; + +const CMD_READ_ID: u8 = 0x9F; +const CMD_READ_ID_OCTO: u16 = 0x9F60; + +const CMD_ENABLE_RESET: u8 = 0x66; +const CMD_RESET: u8 = 0x99; + +const CMD_WRITE_ENABLE: u8 = 0x06; + +const CMD_CHIP_ERASE: u8 = 0xC7; +const CMD_SECTOR_ERASE: u8 = 0x20; +const CMD_BLOCK_ERASE_32K: u8 = 0x52; +const CMD_BLOCK_ERASE_64K: u8 = 0xD8; + +const CMD_READ_SR: u8 = 0x05; +const CMD_READ_CR: u8 = 0x35; + +const CMD_WRITE_SR: u8 = 0x01; +const CMD_WRITE_CR: u8 = 0x31; + +/// Implementation of access to flash chip. +/// +/// Chip commands are hardcoded as it depends on used chip. +/// This targets a MX25UW25645GXDI00. +pub struct FlashMemory { + xspi: Xspi<'static, I, Blocking>, +} + +impl FlashMemory { + pub async fn new(xspi: Xspi<'static, I, Blocking>) -> Self { + let mut memory = Self { xspi }; + + memory.reset_memory().await; + memory.enable_octo(); + memory + } + + async fn qpi_mode(&mut self) { + // Enter qpi mode + self.exec_command(0x38).await; + + // Set read param + let transaction = TransferConfig { + iwidth: XspiWidth::QUAD, + dwidth: XspiWidth::QUAD, + instruction: Some(0xC0), + ..Default::default() + }; + self.enable_write().await; + self.xspi.blocking_write(&[0x30_u8], transaction).unwrap(); + self.wait_write_finish(); + } + + pub async fn disable_mm(&mut self) { + self.xspi.disable_memory_mapped_mode(); + } + + pub async fn enable_mm(&mut self) { + self.qpi_mode().await; + + let read_config = TransferConfig { + iwidth: XspiWidth::SING, + isize: AddressSize::_8bit, + adwidth: XspiWidth::SING, + adsize: AddressSize::_24bit, + dwidth: XspiWidth::SING, + instruction: Some(CMD_READ as u32), + dummy: DummyCycles::_8, + ..Default::default() + }; + + let write_config = TransferConfig { + iwidth: XspiWidth::SING, + isize: AddressSize::_8bit, + adwidth: XspiWidth::SING, + adsize: AddressSize::_24bit, + dwidth: XspiWidth::SING, + instruction: Some(CMD_WRITE_PG as u32), + dummy: DummyCycles::_0, + ..Default::default() + }; + self.xspi.enable_memory_mapped_mode(read_config, write_config).unwrap(); + } + + fn enable_octo(&mut self) { + let cr = self.read_cr(); + // info!("Read cr: {:x}", cr); + self.write_cr(cr | 0x02); + // info!("Read cr after writing: {:x}", cr); + } + + pub fn disable_octo(&mut self) { + let cr = self.read_cr(); + self.write_cr(cr & (!(0x02))); + } + + async fn exec_command_4(&mut self, cmd: u8) { + let transaction = TransferConfig { + iwidth: XspiWidth::QUAD, + adwidth: XspiWidth::NONE, + // adsize: AddressSize::_24bit, + dwidth: XspiWidth::NONE, + instruction: Some(cmd as u32), + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + self.xspi.blocking_command(&transaction).unwrap(); + } + + async fn exec_command(&mut self, cmd: u8) { + let transaction = TransferConfig { + iwidth: XspiWidth::SING, + adwidth: XspiWidth::NONE, + // adsize: AddressSize::_24bit, + dwidth: XspiWidth::NONE, + instruction: Some(cmd as u32), + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + // info!("Excuting command: {:x}", transaction.instruction); + self.xspi.blocking_command(&transaction).unwrap(); + } + + pub async fn reset_memory(&mut self) { + self.exec_command_4(CMD_ENABLE_RESET).await; + self.exec_command_4(CMD_RESET).await; + self.exec_command(CMD_ENABLE_RESET).await; + self.exec_command(CMD_RESET).await; + self.wait_write_finish(); + } + + pub async fn enable_write(&mut self) { + self.exec_command(CMD_WRITE_ENABLE).await; + } + + pub fn read_id(&mut self) -> [u8; 3] { + let mut buffer = [0; 3]; + let transaction: TransferConfig = TransferConfig { + iwidth: XspiWidth::SING, + isize: AddressSize::_8bit, + adwidth: XspiWidth::NONE, + // adsize: AddressSize::_24bit, + dwidth: XspiWidth::SING, + instruction: Some(CMD_READ_ID as u32), + ..Default::default() + }; + // info!("Reading id: 0x{:X}", transaction.instruction); + self.xspi.blocking_read(&mut buffer, transaction).unwrap(); + buffer + } + + pub fn read_id_8(&mut self) -> [u8; 3] { + let mut buffer = [0; 3]; + let transaction: TransferConfig = TransferConfig { + iwidth: XspiWidth::OCTO, + isize: AddressSize::_16bit, + adwidth: XspiWidth::OCTO, + address: Some(0), + adsize: AddressSize::_32bit, + dwidth: XspiWidth::OCTO, + instruction: Some(CMD_READ_ID_OCTO as u32), + dummy: DummyCycles::_4, + ..Default::default() + }; + info!("Reading id: {:#X}", transaction.instruction); + self.xspi.blocking_read(&mut buffer, transaction).unwrap(); + buffer + } + + pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { + let transaction = TransferConfig { + iwidth: XspiWidth::SING, + adwidth: XspiWidth::SING, + adsize: AddressSize::_24bit, + dwidth: XspiWidth::SING, + instruction: Some(CMD_READ as u32), + dummy: DummyCycles::_8, + // dwidth: XspiWidth::QUAD, + // instruction: Some(CMD_QUAD_READ as u32), + // dummy: DummyCycles::_8, + address: Some(addr), + ..Default::default() + }; + if use_dma { + self.xspi.blocking_read(buffer, transaction).unwrap(); + } else { + self.xspi.blocking_read(buffer, transaction).unwrap(); + } + } + + fn wait_write_finish(&mut self) { + while (self.read_sr() & 0x01) != 0 {} + } + + async fn perform_erase(&mut self, addr: u32, cmd: u8) { + let transaction = TransferConfig { + iwidth: XspiWidth::SING, + adwidth: XspiWidth::SING, + adsize: AddressSize::_24bit, + dwidth: XspiWidth::NONE, + instruction: Some(cmd as u32), + address: Some(addr), + dummy: DummyCycles::_0, + ..Default::default() + }; + self.enable_write().await; + self.xspi.blocking_command(&transaction).unwrap(); + self.wait_write_finish(); + } + + pub async fn erase_sector(&mut self, addr: u32) { + self.perform_erase(addr, CMD_SECTOR_ERASE).await; + } + + pub async fn erase_block_32k(&mut self, addr: u32) { + self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await; + } + + pub async fn erase_block_64k(&mut self, addr: u32) { + self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await; + } + + pub async fn erase_chip(&mut self) { + self.exec_command(CMD_CHIP_ERASE).await; + } + + async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { + assert!( + (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, + "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", + len, + addr + ); + + let transaction = TransferConfig { + iwidth: XspiWidth::SING, + adsize: AddressSize::_24bit, + adwidth: XspiWidth::SING, + dwidth: XspiWidth::SING, + instruction: Some(CMD_WRITE_PG as u32), + // dwidth: XspiWidth::QUAD, + // instruction: Some(CMD_QUAD_WRITE_PG as u32), + address: Some(addr), + dummy: DummyCycles::_0, + ..Default::default() + }; + self.enable_write().await; + if use_dma { + self.xspi.blocking_write(buffer, transaction).unwrap(); + } else { + self.xspi.blocking_write(buffer, transaction).unwrap(); + } + self.wait_write_finish(); + } + + pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { + let mut left = buffer.len(); + let mut place = addr; + let mut chunk_start = 0; + + while left > 0 { + let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; + let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; + let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; + self.write_page(place, chunk, chunk_size, use_dma).await; + place += chunk_size as u32; + left -= chunk_size; + chunk_start += chunk_size; + } + } + + fn read_register(&mut self, cmd: u8) -> u8 { + let mut buffer = [0; 1]; + let transaction: TransferConfig = TransferConfig { + iwidth: XspiWidth::SING, + isize: AddressSize::_8bit, + adwidth: XspiWidth::NONE, + adsize: AddressSize::_24bit, + dwidth: XspiWidth::SING, + instruction: Some(cmd as u32), + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + self.xspi.blocking_read(&mut buffer, transaction).unwrap(); + // info!("Read w25q64 register: 0x{:x}", buffer[0]); + buffer[0] + } + + fn write_register(&mut self, cmd: u8, value: u8) { + let buffer = [value; 1]; + let transaction: TransferConfig = TransferConfig { + iwidth: XspiWidth::SING, + isize: AddressSize::_8bit, + instruction: Some(cmd as u32), + adsize: AddressSize::_24bit, + adwidth: XspiWidth::NONE, + dwidth: XspiWidth::SING, + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + self.xspi.blocking_write(&buffer, transaction).unwrap(); + } + + pub fn read_sr(&mut self) -> u8 { + self.read_register(CMD_READ_SR) + } + + pub fn read_cr(&mut self) -> u8 { + self.read_register(CMD_READ_CR) + } + + pub fn write_sr(&mut self, value: u8) { + self.write_register(CMD_WRITE_SR, value); + } + + pub fn write_cr(&mut self, value: u8) { + self.write_register(CMD_WRITE_CR, value); + } +}