From 2ab1b2ac9a13ae0436f1a2c32f984c01a67ebde7 Mon Sep 17 00:00:00 2001 From: Karun Date: Fri, 26 Jan 2024 20:40:05 -0500 Subject: [PATCH 01/20] Update stm-32 build script to include ospi traits --- embassy-stm32/build.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 948ce3aff..f1ead6d46 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -891,6 +891,18 @@ fn main() { (("quadspi", "BK2_IO3"), quote!(crate::qspi::BK2D3Pin)), (("quadspi", "BK2_NCS"), quote!(crate::qspi::BK2NSSPin)), (("quadspi", "CLK"), quote!(crate::qspi::SckPin)), + (("octospi", "IO0"), quote!(crate::ospi::D0Pin)), + (("octospi", "IO1"), quote!(crate::ospi::D1Pin)), + (("octospi", "IO2"), quote!(crate::ospi::D2Pin)), + (("octospi", "IO3"), quote!(crate::ospi::D3Pin)), + (("octospi", "IO4"), quote!(crate::ospi::D4Pin)), + (("octospi", "IO5"), quote!(crate::ospi::D5Pin)), + (("octospi", "IO6"), quote!(crate::ospi::D6Pin)), + (("octospi", "IO7"), quote!(crate::ospi::D7Pin)), + (("octospi", "DQS"), quote!(crate::ospi::DQSPin)), + (("octospi", "NCS"), quote!(crate::ospi::NSSPin)), + (("octospi", "CLK"), quote!(crate::ospi::SckPin)), + (("octospi", "NCLK"), quote!(crate::ospi::NckPin)), ].into(); for p in METADATA.peripherals { @@ -1012,6 +1024,7 @@ fn main() { // SDMMCv1 uses the same channel for both directions, so just implement for RX (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), + (("octospi", "OCTOSPI1"), quote!(crate::ospi::OctoDma)), (("dac", "CH1"), quote!(crate::dac::DacDma1)), (("dac", "CH2"), quote!(crate::dac::DacDma2)), (("timer", "UP"), quote!(crate::timer::UpDma)), From 9905bbe9f7ed195932bb7d14dd363a9624aabdc7 Mon Sep 17 00:00:00 2001 From: Karun Date: Fri, 26 Jan 2024 20:40:32 -0500 Subject: [PATCH 02/20] Update peripheral crate to updated octospi pac --- embassy-stm32/Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 865970dfb..b80509098 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -67,8 +67,8 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" -stm32-metapac = { version = "15" } -#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-656ecf6714fa34fdfb3b3e2f2cd034bffed3f303" } +# stm32-metapac = { version = "15" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2d51fbe7363a376606cb670cc2cec0f634251022" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -86,8 +86,8 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -#stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-656ecf6714fa34fdfb3b3e2f2cd034bffed3f303", default-features = false, features = ["metadata"]} +# stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2d51fbe7363a376606cb670cc2cec0f634251022", default-features = false, features = ["metadata"]} [features] From f3609f2842d95bf1408e3bef108e0bd30f87f9d5 Mon Sep 17 00:00:00 2001 From: Karun Date: Fri, 26 Jan 2024 20:40:48 -0500 Subject: [PATCH 03/20] Add initial octopsi module --- embassy-stm32/src/lib.rs | 2 + embassy-stm32/src/ospi/mod.rs | 178 ++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 embassy-stm32/src/ospi/mod.rs diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index a465fccd8..e18b16935 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -57,6 +57,8 @@ pub mod ipcc; pub mod low_power; #[cfg(opamp)] pub mod opamp; +#[cfg(octospi)] +pub mod ospi; #[cfg(quadspi)] pub mod qspi; #[cfg(rng)] diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs new file mode 100644 index 000000000..44824e42a --- /dev/null +++ b/embassy-stm32/src/ospi/mod.rs @@ -0,0 +1,178 @@ +//! OCTOSPI Serial Peripheral Interface +//! + +#![macro_use] + +use core::ptr; + +use embassy_embedded_hal::SetConfig; +use embassy_futures::join::join; +use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; + +use crate::dma::{slice_ptr_parts, word, Transfer}; +use crate::gpio::sealed::{AFType, Pin as _}; +use crate::gpio::{AnyPin, Pull}; +use crate::pac::octospi::{regs, vals, Octospi as Regs}; +use crate::rcc::RccPeripheral; +use crate::time::Hertz; +use crate::{peripherals, Peripheral}; + +pub struct Config; + +pub struct Ospi<'d, T: Instance, Dma> { + _peri: PeripheralRef<'d, T>, + sck: Option>, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + d4: Option>, + d5: Option>, + d6: Option>, + d7: Option>, + nss: Option>, + dqs: Option>, + dma: PeripheralRef<'d, Dma>, + config: Config, +} + +impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { + /// Create new OSPI driver for a dualspi external chip + pub fn new_dualspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, sck, d0, d1, nss); + + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + None, + None, + None, + None, + None, + None, + Some(sck.map_into()), + Some(nss.map_into()), + None, + dma, + config, + ) + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + d0: Option>, + d1: Option>, + d2: Option>, + d3: Option>, + d4: Option>, + d5: Option>, + d6: Option>, + d7: Option>, + sck: Option>, + nss: Option>, + dqs: Option>, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, dma); + + T::enable_and_reset(); + T::REGS.sr().read().busy(); + + T::REGS.cr().modify(|w| { + w.set_en(true); + }); + + #[cfg(octospi_v1)] + { + T::REGS.ccr().modify(|w| { + w.set_imode(vals::PhaseMode::TWOLINES); + w.set_admode(vals::PhaseMode::TWOLINES); + w.set_abmode(vals::PhaseMode::TWOLINES); + w.set_dmode(vals::PhaseMode::TWOLINES); + }); + T::REGS.wccr().modify(|w| { + w.set_imode(vals::PhaseMode::TWOLINES); + w.set_admode(vals::PhaseMode::TWOLINES); + w.set_abmode(vals::PhaseMode::TWOLINES); + w.set_dmode(vals::PhaseMode::TWOLINES); + }); + } + + // + + // while T::REGS::sr().read().busy() {} + + Self { + _peri: peri, + sck, + d0, + d1, + d2, + d3, + d4, + d5, + d6, + d7, + nss, + dqs, + dma, + config, + } + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance { + const REGS: Regs; + } +} + +/// OSPI instance trait. +pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} + +pin_trait!(SckPin, Instance); +pin_trait!(NckPin, Instance); +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!(DQSPin, Instance); +pin_trait!(NSSPin, Instance); + +dma_trait!(OctoDma, Instance); + +foreach_peripheral!( + (octospi, $inst:ident) => { + impl sealed::Instance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); From 9ed8d01b1102c574e23ffb4994a0b377fb762af2 Mon Sep 17 00:00:00 2001 From: Karun Date: Mon, 5 Feb 2024 21:23:03 -0500 Subject: [PATCH 04/20] Add transfer config, trait, functional initial configuration and read from memory --- embassy-stm32/src/ospi/mod.rs | 218 ++++++++++++++++++++++++++++++---- 1 file changed, 198 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 44824e42a..547de65d9 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -3,12 +3,17 @@ #![macro_use] +pub mod enums; + +use core::ops::Add; use core::ptr; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embedded_hal_02::blocking::i2c::Operation; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; +use enums::*; use crate::dma::{slice_ptr_parts, word, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; @@ -18,8 +23,109 @@ use crate::rcc::RccPeripheral; use crate::time::Hertz; use crate::{peripherals, Peripheral}; +/// OPSI driver config. pub struct Config; +/// OSPI transfer configuration. +pub struct TransferConfig { + /// Instruction width (IMODE) + pub iwidth: OspiWidth, + /// 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: OspiWidth, + /// 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: OspiWidth, + /// 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: OspiWidth, + /// Length of data + pub data_len: Option, + /// Data buffer + pub ddtr: bool, + + /// Number of dummy cycles (DCYC) + pub dummy: DummyCycles, +} + +impl Default for TransferConfig { + fn default() -> Self { + Self { + iwidth: OspiWidth::NONE, + instruction: None, + isize: AddressSize::_8Bit, + idtr: false, + + adwidth: OspiWidth::NONE, + address: None, + adsize: AddressSize::_8Bit, + addtr: false, + + abwidth: OspiWidth::NONE, + alternate_bytes: None, + absize: AddressSize::_8Bit, + abdtr: false, + + dwidth: OspiWidth::NONE, + data_len: None, + ddtr: false, + + dummy: DummyCycles::_0, + } + } +} + +pub enum OspiError { + Test, +} + +pub trait Error {} + +pub trait ErrorType { + type Error: Error; +} + +impl ErrorType for &mut T { + type Error = T::Error; +} + +/// MultiSpi interface trait +pub trait MultiSpi: ErrorType { + /// Transaction configuration for specific multispi implementation + type Config; + + /// Command function used for a configuration operation, when no user data is + /// supplied to or read from the target device. + async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error>; + + /// Read function used to read data from the target device following the supplied transaction + /// configuration. + async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error>; + + /// Write function used to send data to the target device following the supplied transaction + /// configuration. + async fn write(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error>; +} + +/// OSPI driver. pub struct Ospi<'d, T: Instance, Dma> { _peri: PeripheralRef<'d, T>, sck: Option>, @@ -37,6 +143,28 @@ pub struct Ospi<'d, T: Instance, Dma> { config: Config, } +impl Error for OspiError {} + +impl<'d, T: Instance, Dma> ErrorType for Ospi<'d, T, Dma> { + type Error = OspiError; +} + +impl<'d, T: Instance, Dma: OctoDma> MultiSpi for Ospi<'d, T, Dma> { + type Config = TransferConfig; + + async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { + Ok(()) + } + + async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> { + Ok(()) + } + + async fn write(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> { + Ok(()) + } +} + impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { /// Create new OSPI driver for a dualspi external chip pub fn new_dualspi( @@ -54,11 +182,29 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { sck.set_speed(crate::gpio::Speed::VeryHigh); nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); nss.set_speed(crate::gpio::Speed::VeryHigh); + // nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Down); + // nss.set_speed(crate::gpio::Speed::VeryHigh); d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); d0.set_speed(crate::gpio::Speed::VeryHigh); d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); + #[cfg(octospi_v1)] + { + T::REGS.ccr().modify(|w| { + w.set_imode(vals::PhaseMode::TWOLINES); + w.set_admode(vals::PhaseMode::TWOLINES); + w.set_abmode(vals::PhaseMode::TWOLINES); + w.set_dmode(vals::PhaseMode::TWOLINES); + }); + T::REGS.wccr().modify(|w| { + w.set_imode(vals::PhaseMode::TWOLINES); + w.set_admode(vals::PhaseMode::TWOLINES); + w.set_abmode(vals::PhaseMode::TWOLINES); + w.set_dmode(vals::PhaseMode::TWOLINES); + }); + } + Self::new_inner( peri, Some(d0.map_into()), @@ -96,31 +242,18 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { into_ref!(peri, dma); T::enable_and_reset(); - T::REGS.sr().read().busy(); + while T::REGS.sr().read().busy() {} T::REGS.cr().modify(|w| { w.set_en(true); }); - #[cfg(octospi_v1)] - { - T::REGS.ccr().modify(|w| { - w.set_imode(vals::PhaseMode::TWOLINES); - w.set_admode(vals::PhaseMode::TWOLINES); - w.set_abmode(vals::PhaseMode::TWOLINES); - w.set_dmode(vals::PhaseMode::TWOLINES); - }); - T::REGS.wccr().modify(|w| { - w.set_imode(vals::PhaseMode::TWOLINES); - w.set_admode(vals::PhaseMode::TWOLINES); - w.set_abmode(vals::PhaseMode::TWOLINES); - w.set_dmode(vals::PhaseMode::TWOLINES); - }); - } - - // - - // while T::REGS::sr().read().busy() {} + T::REGS.dcr1().modify(|w| { + w.set_devsize(23); + w.set_mtyp(vals::MemType::MACRONIX); + w.set_ckmode(false); + // w.se + }); Self { _peri: peri, @@ -139,6 +272,51 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { config, } } + + pub fn blocking_read(&mut self, transaction: TransferConfig) -> Result<(), ()> { + Ok(()) + } + + fn configure_command(&mut self, command: &TransferConfig) -> Result<(), ()> { + Ok(()) + } + + /// Poor attempt to read data from memory + pub fn receive(&mut self, buf: &mut [u8], intruction: u8, data_len: usize) -> Result<(), ()> { + T::REGS.cr().modify(|w| { + w.set_fmode(vals::FunctionalMode::INDIRECTREAD); + }); + + T::REGS.ccr().modify(|w| { + w.set_imode(vals::PhaseMode::ONELINE); + w.set_admode(vals::PhaseMode::NONE); + w.set_abmode(vals::PhaseMode::NONE); + + w.set_dmode(vals::PhaseMode::ONELINE); + }); + + T::REGS.dlr().modify(|w| { + w.set_dl((data_len - 1) as u32); + }); + + // set instruction + T::REGS.ir().modify(|w| w.set_instruction(intruction as u32)); + + // read bytes + // for idx in 0..data_len { + // while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + // buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + // } + // wait for finish + while !T::REGS.sr().read().tcf() {} + + let fifo_count = T::REGS.sr().read().flevel(); + for idx in 0..(fifo_count as usize) { + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + } + + Ok(()) + } } pub(crate) mod sealed { From a0b70672054784276d6c370b87fd4635314656ae Mon Sep 17 00:00:00 2001 From: Karun Date: Mon, 5 Feb 2024 21:24:33 -0500 Subject: [PATCH 05/20] Add user enums for transaction configuration --- embassy-stm32/src/ospi/enums.rs | 336 ++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 embassy-stm32/src/ospi/enums.rs diff --git a/embassy-stm32/src/ospi/enums.rs b/embassy-stm32/src/ospi/enums.rs new file mode 100644 index 000000000..2209d7b94 --- /dev/null +++ b/embassy-stm32/src/ospi/enums.rs @@ -0,0 +1,336 @@ +//! Enums used in Ospi configuration. + +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub(crate) enum OspiMode { + IndirectWrite, + IndirectRead, + AutoPolling, + MemoryMapped, +} + +impl Into for OspiMode { + fn into(self) -> u8 { + match self { + OspiMode::IndirectWrite => 0b00, + OspiMode::IndirectRead => 0b01, + OspiMode::AutoPolling => 0b10, + OspiMode::MemoryMapped => 0b11, + } + } +} + +/// Ospi lane width +#[allow(dead_code)] +#[derive(Copy, Clone)] +pub enum OspiWidth { + /// None + NONE, + /// Single lane + SING, + /// Dual lanes + DUAL, + /// Quad lanes + QUAD, + /// Eight lanes + OCTO, +} + +impl Into for OspiWidth { + fn into(self) -> u8 { + match self { + OspiWidth::NONE => 0b00, + OspiWidth::SING => 0b01, + OspiWidth::DUAL => 0b10, + OspiWidth::QUAD => 0b11, + OspiWidth::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, + } + } +} + +/// Ospi 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, + } + } +} + +/// Ospi 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, + } + } +} From b86a1f07009dfac8849cdc1beeb621fea348ccd5 Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 21 Feb 2024 10:43:49 -0500 Subject: [PATCH 06/20] Add constructors Add transfer configuration Update command configuration Add peripheral width consideration Add drop impl --- embassy-stm32/src/ospi/enums.rs | 23 + embassy-stm32/src/ospi/mod.rs | 718 +++++++++++++++++++++++++++++--- 2 files changed, 673 insertions(+), 68 deletions(-) diff --git a/embassy-stm32/src/ospi/enums.rs b/embassy-stm32/src/ospi/enums.rs index 2209d7b94..e52f1f54d 100644 --- a/embassy-stm32/src/ospi/enums.rs +++ b/embassy-stm32/src/ospi/enums.rs @@ -67,6 +67,29 @@ impl Into for FlashSelection { } } +/// Wrap Size +#[allow(dead_code)] +#[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, + } + } +} + /// Ospi memory size. #[allow(missing_docs)] #[derive(Copy, Clone)] diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 547de65d9..48b1ef789 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -11,20 +11,79 @@ use core::ptr; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; -use embedded_hal_02::blocking::i2c::Operation; -pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; -use enums::*; +// use embedded_hal_02::spi; +pub use enums::*; +use stm32_metapac::octospi::vals::{MemType, PhaseMode, SizeInBits}; use crate::dma::{slice_ptr_parts, word, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Pull}; use crate::pac::octospi::{regs, vals, Octospi as Regs}; use crate::rcc::RccPeripheral; -use crate::time::Hertz; use crate::{peripherals, Peripheral}; /// OPSI driver config. -pub struct Config; +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, + /// Enables dual-quad mode which allows access to two devices simultaneously to + /// increase throughput + pub dual_quad: bool, + /// Indicates the type of external device connected + pub memory_type: MemType, // Need to add an additional enum to provide this public interface + /// Defines the size of the external device connected to the OSPI 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, + /// Enbales the delay block bypass so the sampling is not affected by the delay block + pub delay_block_bypass: bool, + /// Enables communication regulation feature. Chip select is released when the other + /// OctoSpi 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 + dual_quad: false, + memory_type: MemType::B_STANDARD, + 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 + delay_block_bypass: true, + max_transfer: 0, + refresh: 0, + } + } +} /// OSPI transfer configuration. pub struct TransferConfig { @@ -94,7 +153,8 @@ impl Default for TransferConfig { } pub enum OspiError { - Test, + InvalidConfiguration, + InvalidCommand, } pub trait Error {} @@ -122,7 +182,7 @@ pub trait MultiSpi: ErrorType { /// Write function used to send data to the target device following the supplied transaction /// configuration. - async fn write(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error>; + async fn write(&mut self, data: &[u8], config: Self::Config) -> Result<(), Self::Error>; } /// OSPI driver. @@ -141,6 +201,7 @@ pub struct Ospi<'d, T: Instance, Dma> { dqs: Option>, dma: PeripheralRef<'d, Dma>, config: Config, + width: OspiWidth, } impl Error for OspiError {} @@ -153,21 +214,21 @@ impl<'d, T: Instance, Dma: OctoDma> MultiSpi for Ospi<'d, T, Dma> { type Config = TransferConfig; async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { - Ok(()) + self.command(&config).await } async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> { - Ok(()) + self.read(data, config).await } - async fn write(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> { - Ok(()) + async fn write(&mut self, data: &[u8], config: Self::Config) -> Result<(), Self::Error> { + self.write(data, config).await } } impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { /// Create new OSPI driver for a dualspi external chip - pub fn new_dualspi( + pub fn new_spi( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, d0: impl Peripheral

> + 'd, @@ -182,29 +243,11 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { sck.set_speed(crate::gpio::Speed::VeryHigh); nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); nss.set_speed(crate::gpio::Speed::VeryHigh); - // nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Down); - // nss.set_speed(crate::gpio::Speed::VeryHigh); d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); d0.set_speed(crate::gpio::Speed::VeryHigh); - d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_as_af_pull(d1.af_num(), AFType::Input, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); - #[cfg(octospi_v1)] - { - T::REGS.ccr().modify(|w| { - w.set_imode(vals::PhaseMode::TWOLINES); - w.set_admode(vals::PhaseMode::TWOLINES); - w.set_abmode(vals::PhaseMode::TWOLINES); - w.set_dmode(vals::PhaseMode::TWOLINES); - }); - T::REGS.wccr().modify(|w| { - w.set_imode(vals::PhaseMode::TWOLINES); - w.set_admode(vals::PhaseMode::TWOLINES); - w.set_abmode(vals::PhaseMode::TWOLINES); - w.set_dmode(vals::PhaseMode::TWOLINES); - }); - } - Self::new_inner( peri, Some(d0.map_into()), @@ -220,6 +263,209 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { None, dma, config, + OspiWidth::SING, + ) + } + + /// Create new OSPI driver for a dualspi external chip + pub fn new_dualspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, sck, d0, d1, nss); + + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + None, + None, + None, + None, + None, + None, + Some(sck.map_into()), + Some(nss.map_into()), + None, + dma, + config, + OspiWidth::DUAL, + ) + } + + /// Create new OSPI driver for a quadspi external chip + pub fn new_quadspi( + peri: impl Peripheral

+ 'd, + sck: impl Peripheral

> + 'd, + d0: impl Peripheral

> + 'd, + d1: impl Peripheral

> + 'd, + d2: impl Peripheral

> + 'd, + d3: impl Peripheral

> + 'd, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, sck, d0, d1, d2, d3, nss); + + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_speed(crate::gpio::Speed::VeryHigh); + d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); + d2.set_speed(crate::gpio::Speed::VeryHigh); + d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); + d3.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + Some(d2.map_into()), + Some(d3.map_into()), + None, + None, + None, + None, + Some(sck.map_into()), + Some(nss.map_into()), + None, + dma, + config, + OspiWidth::QUAD, + ) + } + + /// Create new OSPI driver for two quadspi external chips + pub fn new_dualquadspi( + peri: impl Peripheral

+ 'd, + sck: 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, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, sck, d0, d1, d2, d3, d4, d5, d6, d7, nss); + + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_speed(crate::gpio::Speed::VeryHigh); + d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); + d2.set_speed(crate::gpio::Speed::VeryHigh); + d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); + d3.set_speed(crate::gpio::Speed::VeryHigh); + d4.set_as_af_pull(d4.af_num(), AFType::OutputPushPull, Pull::None); + d4.set_speed(crate::gpio::Speed::VeryHigh); + d5.set_as_af_pull(d5.af_num(), AFType::OutputPushPull, Pull::None); + d5.set_speed(crate::gpio::Speed::VeryHigh); + d6.set_as_af_pull(d6.af_num(), AFType::OutputPushPull, Pull::None); + d6.set_speed(crate::gpio::Speed::VeryHigh); + d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); + d7.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + Some(d2.map_into()), + Some(d3.map_into()), + Some(d4.map_into()), + Some(d5.map_into()), + Some(d6.map_into()), + Some(d7.map_into()), + Some(sck.map_into()), + Some(nss.map_into()), + None, + dma, + config, + OspiWidth::QUAD, + ) + } + + /// Create new OSPI driver for two quadspi external chips + pub fn new_octospi( + peri: impl Peripheral

+ 'd, + sck: 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, + nss: impl Peripheral

> + 'd, + dma: impl Peripheral

+ 'd, + config: Config, + ) -> Self { + into_ref!(peri, sck, d0, d1, d2, d3, d4, d5, d6, d7, nss); + + sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None); + sck.set_speed(crate::gpio::Speed::VeryHigh); + nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up); + nss.set_speed(crate::gpio::Speed::VeryHigh); + d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None); + d0.set_speed(crate::gpio::Speed::VeryHigh); + d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); + d1.set_speed(crate::gpio::Speed::VeryHigh); + d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None); + d2.set_speed(crate::gpio::Speed::VeryHigh); + d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); + d3.set_speed(crate::gpio::Speed::VeryHigh); + d4.set_as_af_pull(d4.af_num(), AFType::OutputPushPull, Pull::None); + d4.set_speed(crate::gpio::Speed::VeryHigh); + d5.set_as_af_pull(d5.af_num(), AFType::OutputPushPull, Pull::None); + d5.set_speed(crate::gpio::Speed::VeryHigh); + d6.set_as_af_pull(d6.af_num(), AFType::OutputPushPull, Pull::None); + d6.set_speed(crate::gpio::Speed::VeryHigh); + d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); + d7.set_speed(crate::gpio::Speed::VeryHigh); + + Self::new_inner( + peri, + Some(d0.map_into()), + Some(d1.map_into()), + Some(d2.map_into()), + Some(d3.map_into()), + Some(d4.map_into()), + Some(d5.map_into()), + Some(d6.map_into()), + Some(d7.map_into()), + Some(sck.map_into()), + Some(nss.map_into()), + None, + dma, + config, + OspiWidth::OCTO, ) } @@ -238,22 +484,71 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dqs: Option>, dma: impl Peripheral

+ 'd, config: Config, + width: OspiWidth, ) -> Self { into_ref!(peri, dma); + // System configuration T::enable_and_reset(); while T::REGS.sr().read().busy() {} + // Device configuration + T::REGS.dcr1().modify(|w| { + w.set_devsize(config.device_size.into()); + w.set_mtyp(config.memory_type); + w.set_csht(config.chip_select_high_time.into()); + w.set_dlybyp(config.delay_block_bypass); + w.set_frck(false); + w.set_ckmode(config.clock_mode); + }); + + T::REGS.dcr2().modify(|w| { + w.set_wrapsize(config.wrap_size.into()); + }); + + T::REGS.dcr3().modify(|w| { + w.set_csbound(config.chip_select_boundary); + w.set_maxtran(config.max_transfer); + }); + + T::REGS.dcr4().modify(|w| { + w.set_refresh(config.refresh); + }); + + T::REGS.cr().modify(|w| { + w.set_fthres(vals::Threshold(config.fifo_threshold.into())); + }); + + // Wait for busy flag to clear + while T::REGS.sr().read().busy() {} + + T::REGS.dcr2().modify(|w| { + w.set_prescaler(config.clock_prescaler); + }); + + T::REGS.cr().modify(|w| { + w.set_dmm(config.dual_quad); + }); + + T::REGS.tcr().modify(|w| { + w.set_sshift(match config.sample_shifting { + true => vals::SampleShift::HALFCYCLE, + false => vals::SampleShift::NONE, + }); + w.set_dhqc(config.delay_hold_quarter_cycle); + }); + + // Enable peripheral T::REGS.cr().modify(|w| { w.set_en(true); }); - T::REGS.dcr1().modify(|w| { - w.set_devsize(23); - w.set_mtyp(vals::MemType::MACRONIX); - w.set_ckmode(false); - // w.se - }); + // Free running clock needs to be set after peripheral enable + if config.free_running_clock { + T::REGS.dcr1().modify(|w| { + w.set_frck(config.free_running_clock); + }); + } Self { _peri: peri, @@ -270,53 +565,341 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dqs, dma, config, + width, } } - pub fn blocking_read(&mut self, transaction: TransferConfig) -> Result<(), ()> { - Ok(()) - } + // Function to configure the peripheral for the requested command + fn configure_command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { + // 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(OspiError::InvalidCommand); + } - fn configure_command(&mut self, command: &TransferConfig) -> Result<(), ()> { - Ok(()) - } - - /// Poor attempt to read data from memory - pub fn receive(&mut self, buf: &mut [u8], intruction: u8, data_len: usize) -> Result<(), ()> { T::REGS.cr().modify(|w| { - w.set_fmode(vals::FunctionalMode::INDIRECTREAD); + w.set_fmode(0.into()); }); + // Configure alternate bytes + if let Some(ab) = command.alternate_bytes { + T::REGS.abr().write(|v| v.set_alternate(ab)); + T::REGS.ccr().modify(|w| { + w.set_abmode(PhaseMode::from_bits(command.abwidth.into())); + w.set_abdtr(command.abdtr); + w.set_absize(SizeInBits::from_bits(command.absize.into())); + }) + } + + // Configure dummy cycles + T::REGS.tcr().modify(|w| { + w.set_dcyc(command.dummy.into()); + }); + + // Configure data + if let Some(data_length) = command.data_len { + T::REGS.dlr().write(|v| { + v.set_dl((data_length - 1) as u32); + }) + } + + // Configure instruction/address/data modes T::REGS.ccr().modify(|w| { - w.set_imode(vals::PhaseMode::ONELINE); - w.set_admode(vals::PhaseMode::NONE); - w.set_abmode(vals::PhaseMode::NONE); + w.set_imode(PhaseMode::from_bits(command.iwidth.into())); + w.set_idtr(command.idtr); + w.set_isize(SizeInBits::from_bits(command.isize.into())); - w.set_dmode(vals::PhaseMode::ONELINE); + w.set_admode(PhaseMode::from_bits(command.adwidth.into())); + w.set_addtr(command.idtr); + w.set_adsize(SizeInBits::from_bits(command.adsize.into())); + + w.set_dmode(PhaseMode::from_bits(command.dwidth.into())); + w.set_ddtr(command.ddtr); }); - T::REGS.dlr().modify(|w| { - w.set_dl((data_len - 1) as u32); - }); + // Set informationrequired to initiate transaction + if let Some(instruction) = command.instruction { + if let Some(address) = command.address { + T::REGS.ir().write(|v| { + v.set_instruction(instruction); + }); - // set instruction - T::REGS.ir().modify(|w| w.set_instruction(intruction as u32)); + 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.ccr().modify(|w| { + // w.set_ddtr(true); + // }); + // } + // } - // read bytes - // for idx in 0..data_len { - // while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - // buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; - // } - // wait for finish - while !T::REGS.sr().read().tcf() {} - - let fifo_count = T::REGS.sr().read().flevel(); - for idx in 0..(fifo_count as usize) { - buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + T::REGS.ir().write(|v| { + v.set_instruction(instruction); + }); + } + } else { + if let Some(address) = command.address { + T::REGS.ar().write(|v| { + v.set_address(address); + }); + } else { + // The only single phase transaction supported is instruction only + return Err(OspiError::InvalidCommand); + } } Ok(()) } + + /// Function used to control or configure the target device without data transfer + pub async fn command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { + // Prevent a transaction from being set with expected data transmission or reception + if let Some(_) = command.data_len { + return Err(OspiError::InvalidCommand); + }; + while T::REGS.sr().read().busy() {} + + // Need additional validation that command configuration doesn't have data set + self.configure_command(command)?; + + // Transaction initiated by setting final configuration, i.e the instruction register + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().write(|w| { + w.set_ctcf(true); + }); + + Ok(()) + } + + /// Blocking read with byte by byte data transfer + pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> { + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + + // Ensure DMA is not enabled for this transaction + T::REGS.cr().modify(|w| { + w.set_dmaen(false); + }); + + self.configure_command(&transaction)?; + + if let Some(len) = transaction.data_len { + 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.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); + if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.ar().write(|v| v.set_address(current_address)); + } + + for idx in 0..len { + while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().write(|v| v.set_ctcf(true)); + + Ok(()) + } + + /// Blocking write with byte by byte data transfer + pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> { + T::REGS.cr().modify(|w| { + w.set_dmaen(false); + }); + self.configure_command(&transaction)?; + + if let Some(len) = transaction.data_len { + T::REGS + .cr() + .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); + + for idx in 0..len { + while !T::REGS.sr().read().ftf() {} + unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; + } + } + + while !T::REGS.sr().read().tcf() {} + T::REGS.fcr().write(|v| v.set_ctcf(true)); + + Ok(()) + } + + /// Blocking read with DMA transfer + pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> + where + Dma: OctoDma, + { + self.configure_command(&transaction)?; + + 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.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); + if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.ar().write(|v| v.set_address(current_address)); + } + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_read( + &mut self.dma, + request, + T::REGS.dr().as_ptr() as *mut u8, + buf, + Default::default(), + ) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.blocking_wait(); + + finish_dma(T::REGS); + + Ok(()) + } + + /// Blocking write with DMA transfer + pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> + where + Dma: OctoDma, + { + self.configure_command(&transaction)?; + T::REGS + .cr() + .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_write( + &mut self.dma, + request, + buf, + T::REGS.dr().as_ptr() as *mut u8, + Default::default(), + ) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.blocking_wait(); + + finish_dma(T::REGS); + + Ok(()) + } + + /// Asynchronous read from external device + pub async fn read(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> + where + Dma: OctoDma, + { + self.configure_command(&transaction)?; + + 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.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); + if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.ar().write(|v| v.set_address(current_address)); + } + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_read( + &mut self.dma, + request, + T::REGS.dr().as_ptr() as *mut u8, + buf, + Default::default(), + ) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.await; + + finish_dma(T::REGS); + + Ok(()) + } + + /// Asynchronous write to external device + pub async fn write(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> + where + Dma: OctoDma, + { + self.configure_command(&transaction)?; + T::REGS + .cr() + .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); + + let request = self.dma.request(); + let transfer = unsafe { + Transfer::new_write( + &mut self.dma, + request, + buf, + T::REGS.dr().as_ptr() as *mut u8, + Default::default(), + ) + }; + + T::REGS.cr().modify(|w| w.set_dmaen(true)); + + transfer.await; + + finish_dma(T::REGS); + + Ok(()) + } +} + +impl<'d, T: Instance, Dma> Drop for Ospi<'d, T, Dma> { + fn drop(&mut self) { + self.sck.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.nss.as_ref().map(|x| x.set_as_disconnected()); + self.dqs.as_ref().map(|x| x.set_as_disconnected()); + + T::disable(); + } +} + +fn finish_dma(regs: Regs) { + while !regs.sr().read().tcf() {} + regs.fcr().write(|v| v.set_ctcf(true)); + + regs.cr().modify(|w| { + w.set_dmaen(false); + }); } pub(crate) mod sealed { @@ -342,7 +925,6 @@ pin_trait!(D6Pin, Instance); pin_trait!(D7Pin, Instance); pin_trait!(DQSPin, Instance); pin_trait!(NSSPin, Instance); - dma_trait!(OctoDma, Instance); foreach_peripheral!( From e163572bec221f3367f96ec720d45bc22b985efe Mon Sep 17 00:00:00 2001 From: Karun Date: Thu, 7 Mar 2024 12:04:26 -0500 Subject: [PATCH 07/20] Add get and set config trait implementations --- embassy-stm32/src/ospi/mod.rs | 107 +++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 48b1ef789..8fb5f06e4 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -8,7 +8,7 @@ pub mod enums; use core::ops::Add; use core::ptr; -use embassy_embedded_hal::SetConfig; +use embassy_embedded_hal::{GetConfig, SetConfig}; use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; // use embedded_hal_02::spi; @@ -23,6 +23,7 @@ use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; /// OPSI 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 @@ -63,6 +64,7 @@ pub struct Config { /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles pub refresh: u32, } + impl Default for Config { fn default() -> Self { Self { @@ -873,6 +875,83 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { Ok(()) } + + /// Set new bus configuration + pub fn set_config(&mut self, config: &Config) -> Result<(), ()> { + // Wait for busy flag to clear + while T::REGS.sr().read().busy() {} + + // Disable DMA channel while configuring the peripheral + T::REGS.cr().modify(|w| { + w.set_dmaen(false); + }); + + // Device configuration + T::REGS.dcr1().modify(|w| { + w.set_devsize(config.device_size.into()); + w.set_mtyp(config.memory_type); + w.set_csht(config.chip_select_high_time.into()); + w.set_dlybyp(config.delay_block_bypass); + w.set_frck(false); + w.set_ckmode(config.clock_mode); + }); + + T::REGS.dcr2().modify(|w| { + w.set_wrapsize(config.wrap_size.into()); + }); + + T::REGS.dcr3().modify(|w| { + w.set_csbound(config.chip_select_boundary); + w.set_maxtran(config.max_transfer); + }); + + T::REGS.dcr4().modify(|w| { + w.set_refresh(config.refresh); + }); + + T::REGS.cr().modify(|w| { + w.set_fthres(vals::Threshold(config.fifo_threshold.into())); + }); + + // Wait for busy flag to clear + while T::REGS.sr().read().busy() {} + + T::REGS.dcr2().modify(|w| { + w.set_prescaler(config.clock_prescaler); + }); + + T::REGS.cr().modify(|w| { + w.set_dmm(config.dual_quad); + }); + + T::REGS.tcr().modify(|w| { + w.set_sshift(match config.sample_shifting { + true => vals::SampleShift::HALFCYCLE, + false => vals::SampleShift::NONE, + }); + w.set_dhqc(config.delay_hold_quarter_cycle); + }); + + // Enable peripheral + 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.dcr1().modify(|w| { + w.set_frck(config.free_running_clock); + }); + } + + self.config = *config; + Ok(()) + } + + /// Get current configuration + pub fn get_config(&self) -> Config { + self.config + } } impl<'d, T: Instance, Dma> Drop for Ospi<'d, T, Dma> { @@ -902,6 +981,17 @@ fn finish_dma(regs: Regs) { }); } +trait RegsExt { + fn dr_ptr(&self) -> *mut W; +} + +impl RegsExt for Regs { + fn dr_ptr(&self) -> *mut W { + let dr = self.dr(); + dr.as_ptr() as *mut W + } +} + pub(crate) mod sealed { use super::*; @@ -936,3 +1026,18 @@ foreach_peripheral!( impl Instance for peripherals::$inst {} }; ); + +impl<'d, T: Instance, Dma> SetConfig for Ospi<'d, T, Dma> { + type Config = Config; + type ConfigError = (); + fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { + self.set_config(config) + } +} + +impl<'d, T: Instance, Dma> GetConfig for Ospi<'d, T, Dma> { + type Config = Config; + fn get_config(&self) -> Self::Config { + self.get_config() + } +} From 3b1d87050e2a30b598e92979b6f202b67664a29c Mon Sep 17 00:00:00 2001 From: Karun Date: Thu, 7 Mar 2024 12:46:19 -0500 Subject: [PATCH 08/20] Update trait definitions Make operations generic against valid data widths --- embassy-stm32/src/ospi/enums.rs | 27 ++++++++ embassy-stm32/src/ospi/mod.rs | 114 +++++++++++++++++++++----------- 2 files changed, 102 insertions(+), 39 deletions(-) diff --git a/embassy-stm32/src/ospi/enums.rs b/embassy-stm32/src/ospi/enums.rs index e52f1f54d..4021f7ce3 100644 --- a/embassy-stm32/src/ospi/enums.rs +++ b/embassy-stm32/src/ospi/enums.rs @@ -69,6 +69,7 @@ impl Into for FlashSelection { /// Wrap Size #[allow(dead_code)] +#[allow(missing_docs)] #[derive(Copy, Clone)] pub enum WrapSize { None, @@ -90,6 +91,32 @@ impl Into for WrapSize { } } +/// 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, + } + } +} + /// Ospi memory size. #[allow(missing_docs)] #[derive(Copy, Clone)] diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 8fb5f06e4..3d458ba79 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -5,13 +5,9 @@ pub mod enums; -use core::ops::Add; -use core::ptr; - use embassy_embedded_hal::{GetConfig, SetConfig}; -use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; -// use embedded_hal_02::spi; +use embedded_hal_1::spi::ErrorKind; pub use enums::*; use stm32_metapac::octospi::vals::{MemType, PhaseMode, SizeInBits}; @@ -32,7 +28,7 @@ pub struct Config { /// increase throughput pub dual_quad: bool, /// Indicates the type of external device connected - pub memory_type: MemType, // Need to add an additional enum to provide this public interface + 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 OSPI corresponding /// to the number of address bits required to access the device pub device_size: MemorySize, @@ -70,7 +66,7 @@ impl Default for Config { Self { fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity dual_quad: false, - memory_type: MemType::B_STANDARD, + memory_type: MemoryType::Micron, device_size: MemorySize::Other(0), chip_select_high_time: ChipSelectHighTime::_5Cycle, free_running_clock: false, @@ -154,23 +150,17 @@ impl Default for TransferConfig { } } +/// Error used for Octospi implementation +#[derive(Debug)] pub enum OspiError { + /// Peripheral configuration is invalid InvalidConfiguration, + /// Operation configuration is invalid InvalidCommand, } -pub trait Error {} - -pub trait ErrorType { - type Error: Error; -} - -impl ErrorType for &mut T { - type Error = T::Error; -} - /// MultiSpi interface trait -pub trait MultiSpi: ErrorType { +pub trait MultiSpiBus: embedded_hal_1::spi::ErrorType { /// Transaction configuration for specific multispi implementation type Config; @@ -180,11 +170,27 @@ pub trait MultiSpi: ErrorType { /// Read function used to read data from the target device following the supplied transaction /// configuration. - async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error>; + async fn read(&mut self, data: &mut [Word], config: Self::Config) -> Result<(), Self::Error>; /// Write function used to send data to the target device following the supplied transaction /// configuration. - async fn write(&mut self, data: &[u8], config: Self::Config) -> Result<(), Self::Error>; + async fn write(&mut self, data: &[Word], config: Self::Config) -> Result<(), Self::Error>; +} + +impl + ?Sized, Word: Copy + 'static> MultiSpiBus for &mut T { + type Config = T::Config; + #[inline] + async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { + T::command(self, config).await + } + + async fn read(&mut self, data: &mut [Word], config: Self::Config) -> Result<(), Self::Error> { + T::read(self, data, config).await + } + + async fn write(&mut self, data: &[Word], config: Self::Config) -> Result<(), Self::Error> { + T::write(self, data, config).await + } } /// OSPI driver. @@ -206,24 +212,28 @@ pub struct Ospi<'d, T: Instance, Dma> { width: OspiWidth, } -impl Error for OspiError {} +impl embedded_hal_1::spi::Error for OspiError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other + } +} -impl<'d, T: Instance, Dma> ErrorType for Ospi<'d, T, Dma> { +impl<'d, T: Instance, Dma> embedded_hal_1::spi::ErrorType for Ospi<'d, T, Dma> { type Error = OspiError; } -impl<'d, T: Instance, Dma: OctoDma> MultiSpi for Ospi<'d, T, Dma> { +impl<'d, T: Instance, Dma: OctoDma, W: Word> MultiSpiBus for Ospi<'d, T, Dma> { type Config = TransferConfig; async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { self.command(&config).await } - async fn read(&mut self, data: &mut [u8], config: Self::Config) -> Result<(), Self::Error> { + async fn read(&mut self, data: &mut [W], config: Self::Config) -> Result<(), Self::Error> { self.read(data, config).await } - async fn write(&mut self, data: &[u8], config: Self::Config) -> Result<(), Self::Error> { + async fn write(&mut self, data: &[W], config: Self::Config) -> Result<(), Self::Error> { self.write(data, config).await } } @@ -497,7 +507,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { // Device configuration T::REGS.dcr1().modify(|w| { w.set_devsize(config.device_size.into()); - w.set_mtyp(config.memory_type); + w.set_mtyp(vals::MemType::from_bits(config.memory_type.into())); w.set_csht(config.chip_select_high_time.into()); w.set_dlybyp(config.delay_block_bypass); w.set_frck(false); @@ -681,7 +691,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Blocking read with byte by byte data transfer - pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> { + pub fn blocking_read(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} @@ -706,7 +716,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { for idx in 0..len { while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() }; + buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() }; } } @@ -717,7 +727,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Blocking write with byte by byte data transfer - pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> { + pub fn blocking_write(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> { T::REGS.cr().modify(|w| { w.set_dmaen(false); }); @@ -730,7 +740,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { for idx in 0..len { while !T::REGS.sr().read().ftf() {} - unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(buf[idx]) }; + unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; } } @@ -741,7 +751,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Blocking read with DMA transfer - pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> + pub fn blocking_read_dma(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> where Dma: OctoDma, { @@ -763,7 +773,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { Transfer::new_read( &mut self.dma, request, - T::REGS.dr().as_ptr() as *mut u8, + T::REGS.dr().as_ptr() as *mut W, buf, Default::default(), ) @@ -779,7 +789,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Blocking write with DMA transfer - pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> + pub fn blocking_write_dma(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> where Dma: OctoDma, { @@ -794,7 +804,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { &mut self.dma, request, buf, - T::REGS.dr().as_ptr() as *mut u8, + T::REGS.dr().as_ptr() as *mut W, Default::default(), ) }; @@ -809,7 +819,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Asynchronous read from external device - pub async fn read(&mut self, buf: &mut [u8], transaction: TransferConfig) -> Result<(), OspiError> + pub async fn read(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> where Dma: OctoDma, { @@ -831,7 +841,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { Transfer::new_read( &mut self.dma, request, - T::REGS.dr().as_ptr() as *mut u8, + T::REGS.dr().as_ptr() as *mut W, buf, Default::default(), ) @@ -847,7 +857,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Asynchronous write to external device - pub async fn write(&mut self, buf: &[u8], transaction: TransferConfig) -> Result<(), OspiError> + pub async fn write(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> where Dma: OctoDma, { @@ -862,7 +872,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { &mut self.dma, request, buf, - T::REGS.dr().as_ptr() as *mut u8, + T::REGS.dr().as_ptr() as *mut W, Default::default(), ) }; @@ -889,7 +899,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { // Device configuration T::REGS.dcr1().modify(|w| { w.set_devsize(config.device_size.into()); - w.set_mtyp(config.memory_type); + w.set_mtyp(vals::MemType::from_bits(config.memory_type.into())); w.set_csht(config.chip_select_high_time.into()); w.set_dlybyp(config.delay_block_bypass); w.set_frck(false); @@ -998,6 +1008,10 @@ pub(crate) mod sealed { pub trait Instance { const REGS: Regs; } + + pub trait Word { + const CONFIG: word_impl::Config; + } } /// OSPI instance trait. @@ -1041,3 +1055,25 @@ impl<'d, T: Instance, Dma> GetConfig for Ospi<'d, T, Dma> { self.get_config() } } + +/// Word sizes usable for OSPI. +pub trait Word: word::Word + sealed::Word {} + +macro_rules! impl_word { + ($T:ty, $config:expr) => { + impl sealed::Word for $T { + const CONFIG: Config = $config; + } + impl Word for $T {} + }; +} + +mod word_impl { + use super::*; + + pub type Config = u8; + + impl_word!(u8, 8); + impl_word!(u16, 16); + impl_word!(u32, 32); +} From fda6e3fb8ccae9b92d619fefa01d334177edb12a Mon Sep 17 00:00:00 2001 From: Karun Date: Thu, 7 Mar 2024 15:23:45 -0500 Subject: [PATCH 09/20] Resolve rustfmt issue and unused import errors --- embassy-stm32/build.rs | 1 - embassy-stm32/src/ospi/mod.rs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 3a30ba2fd..6217a3309 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1010,7 +1010,6 @@ fn main() { (("octospi", "NCLK"), quote!(crate::ospi::NckPin)), ].into(); - for p in METADATA.peripherals { if let Some(regs) = &p.registers { for pin in p.pins { diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 3d458ba79..794bdd127 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -9,12 +9,12 @@ use embassy_embedded_hal::{GetConfig, SetConfig}; use embassy_hal_internal::{into_ref, PeripheralRef}; use embedded_hal_1::spi::ErrorKind; pub use enums::*; -use stm32_metapac::octospi::vals::{MemType, PhaseMode, SizeInBits}; +use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; -use crate::dma::{slice_ptr_parts, word, Transfer}; +use crate::dma::{word, Transfer}; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::{AnyPin, Pull}; -use crate::pac::octospi::{regs, vals, Octospi as Regs}; +use crate::pac::octospi::{vals, Octospi as Regs}; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; From 47b492ba055910e4f5f1d32193d07a7689d3398e Mon Sep 17 00:00:00 2001 From: Karun Date: Tue, 2 Apr 2024 12:01:33 -0400 Subject: [PATCH 10/20] retrigger checks From d62615b5360a92fc3c039dfd9669b3e7771f1c79 Mon Sep 17 00:00:00 2001 From: Karun Date: Tue, 2 Apr 2024 15:48:39 -0400 Subject: [PATCH 11/20] Update metapac to use PR #442 with octospi rcc updates --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 89c17cb3d..6d1c2d25c 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -70,7 +70,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e7f91751fbbf856e0cb30e50ae6db79f0409b085" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ac187e40aa97da86f7d3cf22abad918f42f01739" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" From 2caea89b6ab9859470ccf6c7d7414c01251bbecd Mon Sep 17 00:00:00 2001 From: Karun Date: Tue, 2 Apr 2024 15:50:57 -0400 Subject: [PATCH 12/20] Update build dependency as well --- embassy-stm32/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6d1c2d25c..158c630b9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -95,7 +95,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-e7f91751fbbf856e0cb30e50ae6db79f0409b085", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ac187e40aa97da86f7d3cf22abad918f42f01739", default-features = false, features = ["metadata"]} [features] From 166c95be6c2917e5133fa361ee88d845a6f4ef73 Mon Sep 17 00:00:00 2001 From: Karun Date: Tue, 2 Apr 2024 16:14:10 -0400 Subject: [PATCH 13/20] Update to use private supertrait, following PR#2730 --- embassy-stm32/src/ospi/mod.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 794bdd127..f85f07fc4 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -12,8 +12,7 @@ pub use enums::*; use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; use crate::dma::{word, Transfer}; -use crate::gpio::sealed::{AFType, Pin as _}; -use crate::gpio::{AnyPin, Pull}; +use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _}; use crate::pac::octospi::{vals, Octospi as Regs}; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -1002,20 +1001,17 @@ impl RegsExt for Regs { } } -pub(crate) mod sealed { - use super::*; +pub(crate) trait SealedInstance { + const REGS: Regs; +} - pub trait Instance { - const REGS: Regs; - } - - pub trait Word { - const CONFIG: word_impl::Config; - } +trait SealedWord { + const CONFIG: word_impl::Config; } /// OSPI instance trait. -pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} pin_trait!(SckPin, Instance); pin_trait!(NckPin, Instance); @@ -1033,7 +1029,7 @@ dma_trait!(OctoDma, Instance); foreach_peripheral!( (octospi, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { const REGS: Regs = crate::pac::$inst; } @@ -1057,11 +1053,12 @@ impl<'d, T: Instance, Dma> GetConfig for Ospi<'d, T, Dma> { } /// Word sizes usable for OSPI. -pub trait Word: word::Word + sealed::Word {} +#[allow(private_bounds)] +pub trait Word: word::Word + SealedWord {} macro_rules! impl_word { ($T:ty, $config:expr) => { - impl sealed::Word for $T { + impl SealedWord for $T { const CONFIG: Config = $config; } impl Word for $T {} From 66a7b629097cca89895e8557cf31aba50f5a1982 Mon Sep 17 00:00:00 2001 From: Karun Date: Tue, 2 Apr 2024 16:24:31 -0400 Subject: [PATCH 14/20] Add octospi version dependency for max transfer support --- embassy-stm32/src/ospi/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index f85f07fc4..84d1ac284 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -519,7 +519,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { T::REGS.dcr3().modify(|w| { w.set_csbound(config.chip_select_boundary); - w.set_maxtran(config.max_transfer); + #[cfg(octospi_v1)] + { + w.set_maxtran(config.max_transfer); + } }); T::REGS.dcr4().modify(|w| { @@ -911,7 +914,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { T::REGS.dcr3().modify(|w| { w.set_csbound(config.chip_select_boundary); - w.set_maxtran(config.max_transfer); + #[cfg(octospi_v1)] + { + w.set_maxtran(config.max_transfer); + } }); T::REGS.dcr4().modify(|w| { From a031b3b79ec5ebf8f0ec1f7df605944acf7d5720 Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 13:42:38 -0400 Subject: [PATCH 15/20] Update metapac --- 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 d00e7aa55..89b24f0eb 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -70,7 +70,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ac187e40aa97da86f7d3cf22abad918f42f01739" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9" } vcell = "0.1.3" nb = "1.0.0" @@ -96,7 +96,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ac187e40aa97da86f7d3cf22abad918f42f01739", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9", default-features = false, features = ["metadata"]} [features] default = ["rt"] From 630fd90d26207521d39f1bf76d1f19b862e4393d Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 14:01:40 -0400 Subject: [PATCH 16/20] Address PR comments --- embassy-stm32/src/ospi/mod.rs | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 84d1ac284..59c73c24d 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -889,7 +889,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } /// Set new bus configuration - pub fn set_config(&mut self, config: &Config) -> Result<(), ()> { + pub fn set_config(&mut self, config: &Config) { // Wait for busy flag to clear while T::REGS.sr().read().busy() {} @@ -960,7 +960,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } self.config = *config; - Ok(()) } /// Get current configuration @@ -1012,7 +1011,7 @@ pub(crate) trait SealedInstance { } trait SealedWord { - const CONFIG: word_impl::Config; + const CONFIG: u8; } /// OSPI instance trait. @@ -1047,7 +1046,8 @@ impl<'d, T: Instance, Dma> SetConfig for Ospi<'d, T, Dma> { type Config = Config; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { - self.set_config(config) + self.set_config(config); + Ok(()) } } @@ -1065,18 +1065,12 @@ pub trait Word: word::Word + SealedWord {} macro_rules! impl_word { ($T:ty, $config:expr) => { impl SealedWord for $T { - const CONFIG: Config = $config; + const CONFIG: u8 = $config; } impl Word for $T {} }; } -mod word_impl { - use super::*; - - pub type Config = u8; - - impl_word!(u8, 8); - impl_word!(u16, 16); - impl_word!(u32, 32); -} +impl_word!(u8, 8); +impl_word!(u16, 16); +impl_word!(u32, 32); From b3bbf42b8bf4d31dab66bf75d80389279b0502f2 Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 15:58:20 -0400 Subject: [PATCH 17/20] Remove data length from transfer config Remove non hal traits Fix function comments --- embassy-stm32/src/ospi/mod.rs | 91 ++++++----------------------------- 1 file changed, 16 insertions(+), 75 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 59c73c24d..bcdffc266 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -113,8 +113,6 @@ pub struct TransferConfig { /// Data width (DMODE) pub dwidth: OspiWidth, - /// Length of data - pub data_len: Option, /// Data buffer pub ddtr: bool, @@ -141,7 +139,6 @@ impl Default for TransferConfig { abdtr: false, dwidth: OspiWidth::NONE, - data_len: None, ddtr: false, dummy: DummyCycles::_0, @@ -158,40 +155,6 @@ pub enum OspiError { InvalidCommand, } -/// MultiSpi interface trait -pub trait MultiSpiBus: embedded_hal_1::spi::ErrorType { - /// Transaction configuration for specific multispi implementation - type Config; - - /// Command function used for a configuration operation, when no user data is - /// supplied to or read from the target device. - async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error>; - - /// Read function used to read data from the target device following the supplied transaction - /// configuration. - async fn read(&mut self, data: &mut [Word], config: Self::Config) -> Result<(), Self::Error>; - - /// Write function used to send data to the target device following the supplied transaction - /// configuration. - async fn write(&mut self, data: &[Word], config: Self::Config) -> Result<(), Self::Error>; -} - -impl + ?Sized, Word: Copy + 'static> MultiSpiBus for &mut T { - type Config = T::Config; - #[inline] - async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { - T::command(self, config).await - } - - async fn read(&mut self, data: &mut [Word], config: Self::Config) -> Result<(), Self::Error> { - T::read(self, data, config).await - } - - async fn write(&mut self, data: &[Word], config: Self::Config) -> Result<(), Self::Error> { - T::write(self, data, config).await - } -} - /// OSPI driver. pub struct Ospi<'d, T: Instance, Dma> { _peri: PeripheralRef<'d, T>, @@ -211,35 +174,9 @@ pub struct Ospi<'d, T: Instance, Dma> { width: OspiWidth, } -impl embedded_hal_1::spi::Error for OspiError { - fn kind(&self) -> ErrorKind { - ErrorKind::Other - } -} - -impl<'d, T: Instance, Dma> embedded_hal_1::spi::ErrorType for Ospi<'d, T, Dma> { - type Error = OspiError; -} - -impl<'d, T: Instance, Dma: OctoDma, W: Word> MultiSpiBus for Ospi<'d, T, Dma> { - type Config = TransferConfig; - - async fn command(&mut self, config: Self::Config) -> Result<(), Self::Error> { - self.command(&config).await - } - - async fn read(&mut self, data: &mut [W], config: Self::Config) -> Result<(), Self::Error> { - self.read(data, config).await - } - - async fn write(&mut self, data: &[W], config: Self::Config) -> Result<(), Self::Error> { - self.write(data, config).await - } -} - impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { - /// Create new OSPI driver for a dualspi external chip - pub fn new_spi( + /// Create new OSPI driver for a single spi external chip + pub fn new_singlespi( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, d0: impl Peripheral

> + 'd, @@ -422,7 +359,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { ) } - /// Create new OSPI driver for two quadspi external chips + /// Create new OSPI driver for octospi external chips pub fn new_octospi( peri: impl Peripheral

+ 'd, sck: impl Peripheral

> + 'd, @@ -584,7 +521,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { } // Function to configure the peripheral for the requested command - fn configure_command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { + fn configure_command(&mut self, command: &TransferConfig, data_len: Option) -> Result<(), OspiError> { // Check that transaction doesn't use more than hardware initialized pins if >::into(command.iwidth) > >::into(self.width) || >::into(command.adwidth) > >::into(self.width) @@ -614,10 +551,14 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { }); // Configure data - if let Some(data_length) = command.data_len { + if let Some(data_length) = data_len { T::REGS.dlr().write(|v| { v.set_dl((data_length - 1) as u32); }) + } else { + T::REGS.dlr().write(|v| { + v.set_dl((0) as u32); + }) } // Configure instruction/address/data modes @@ -681,7 +622,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { while T::REGS.sr().read().busy() {} // Need additional validation that command configuration doesn't have data set - self.configure_command(command)?; + self.configure_command(command, None)?; // Transaction initiated by setting final configuration, i.e the instruction register while !T::REGS.sr().read().tcf() {} @@ -702,7 +643,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { w.set_dmaen(false); }); - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; if let Some(len) = transaction.data_len { let current_address = T::REGS.ar().read().address(); @@ -733,7 +674,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { T::REGS.cr().modify(|w| { w.set_dmaen(false); }); - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; if let Some(len) = transaction.data_len { T::REGS @@ -757,7 +698,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -795,7 +736,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; T::REGS .cr() .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); @@ -825,7 +766,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -863,7 +804,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { - self.configure_command(&transaction)?; + self.configure_command(&transaction, Some(buf.len()))?; T::REGS .cr() .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); From 80aeea93fd9d34b95441b6900e658e86b5b18b03 Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 16:05:23 -0400 Subject: [PATCH 18/20] Configure dual-quad setting by constructor --- embassy-stm32/src/ospi/mod.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index bcdffc266..7020cf9ad 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -23,9 +23,6 @@ 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, - /// Enables dual-quad mode which allows access to two devices simultaneously to - /// increase throughput - pub dual_quad: bool, /// 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 OSPI corresponding @@ -64,7 +61,6 @@ impl Default for Config { fn default() -> Self { Self { fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity - dual_quad: false, memory_type: MemoryType::Micron, device_size: MemorySize::Other(0), chip_select_high_time: ChipSelectHighTime::_5Cycle, @@ -196,6 +192,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d1.set_as_af_pull(d1.af_num(), AFType::Input, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); + T::REGS.cr().modify(|w| { + w.set_dmm(false); + }); + Self::new_inner( peri, Some(d0.map_into()), @@ -236,6 +236,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); + T::REGS.cr().modify(|w| { + w.set_dmm(false); + }); + Self::new_inner( peri, Some(d0.map_into()), @@ -282,6 +286,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); d3.set_speed(crate::gpio::Speed::VeryHigh); + T::REGS.cr().modify(|w| { + w.set_dmm(false); + }); + Self::new_inner( peri, Some(d0.map_into()), @@ -340,6 +348,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); d7.set_speed(crate::gpio::Speed::VeryHigh); + T::REGS.cr().modify(|w| { + w.set_dmm(true); + }); + Self::new_inner( peri, Some(d0.map_into()), @@ -398,6 +410,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); d7.set_speed(crate::gpio::Speed::VeryHigh); + T::REGS.cr().modify(|w| { + w.set_dmm(false); + }); + Self::new_inner( peri, Some(d0.map_into()), @@ -477,10 +493,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { w.set_prescaler(config.clock_prescaler); }); - T::REGS.cr().modify(|w| { - w.set_dmm(config.dual_quad); - }); - T::REGS.tcr().modify(|w| { w.set_sshift(match config.sample_shifting { true => vals::SampleShift::HALFCYCLE, From 4ea7dfce176cf588fe0bc37efbca44e229bd6e0e Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 16:36:02 -0400 Subject: [PATCH 19/20] Fix build errors Add empty checks/peripheral busy waits --- embassy-stm32/src/ospi/mod.rs | 93 +++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 31 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 7020cf9ad..4a89cd810 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -7,7 +7,6 @@ pub mod enums; use embassy_embedded_hal::{GetConfig, SetConfig}; use embassy_hal_internal::{into_ref, PeripheralRef}; -use embedded_hal_1::spi::ErrorKind; pub use enums::*; use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; @@ -144,11 +143,14 @@ impl Default for TransferConfig { /// Error used for Octospi implementation #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum OspiError { /// Peripheral configuration is invalid InvalidConfiguration, /// Operation configuration is invalid InvalidCommand, + /// Size zero buffer passed to instruction + EmptyBuffer, } /// OSPI driver. @@ -627,10 +629,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { /// Function used to control or configure the target device without data transfer pub async fn command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { - // Prevent a transaction from being set with expected data transmission or reception - if let Some(_) = command.data_len { - return Err(OspiError::InvalidCommand); - }; + // Wait for peripheral to be free while T::REGS.sr().read().busy() {} // Need additional validation that command configuration doesn't have data set @@ -647,6 +646,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { /// Blocking read with byte by byte data transfer pub fn blocking_read(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + // Wait for peripheral to be free while T::REGS.sr().read().busy() {} @@ -657,22 +660,20 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { self.configure_command(&transaction, Some(buf.len()))?; - if let Some(len) = transaction.data_len { - let current_address = T::REGS.ar().read().address(); - let current_instruction = T::REGS.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.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); - if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { - T::REGS.ir().write(|v| v.set_instruction(current_instruction)); - } else { - T::REGS.ar().write(|v| v.set_address(current_address)); - } + // For a indirect read transaction, the transaction begins when the instruction/address is set + T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD)); + if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE { + T::REGS.ir().write(|v| v.set_instruction(current_instruction)); + } else { + T::REGS.ar().write(|v| v.set_address(current_address)); + } - for idx in 0..len { - while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {} - buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() }; - } + for idx in 0..buf.len() { + 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.sr().read().tcf() {} @@ -683,20 +684,26 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { /// Blocking write with byte by byte data transfer pub fn blocking_write(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + T::REGS.cr().modify(|w| { w.set_dmaen(false); }); + self.configure_command(&transaction, Some(buf.len()))?; - if let Some(len) = transaction.data_len { - T::REGS - .cr() - .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); + T::REGS + .cr() + .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE)); - for idx in 0..len { - while !T::REGS.sr().read().ftf() {} - unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; - } + for idx in 0..buf.len() { + while !T::REGS.sr().read().ftf() {} + unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) }; } while !T::REGS.sr().read().tcf() {} @@ -710,6 +717,13 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + self.configure_command(&transaction, Some(buf.len()))?; let current_address = T::REGS.ar().read().address(); @@ -748,6 +762,13 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + self.configure_command(&transaction, Some(buf.len()))?; T::REGS .cr() @@ -778,6 +799,13 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + self.configure_command(&transaction, Some(buf.len()))?; let current_address = T::REGS.ar().read().address(); @@ -816,6 +844,13 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { where Dma: OctoDma, { + if buf.is_empty() { + return Err(OspiError::EmptyBuffer); + } + + // Wait for peripheral to be free + while T::REGS.sr().read().busy() {} + self.configure_command(&transaction, Some(buf.len()))?; T::REGS .cr() @@ -888,10 +923,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { w.set_prescaler(config.clock_prescaler); }); - T::REGS.cr().modify(|w| { - w.set_dmm(config.dual_quad); - }); - T::REGS.tcr().modify(|w| { w.set_sshift(match config.sample_shifting { true => vals::SampleShift::HALFCYCLE, From 330a3b04882a072406b9c3b4ef35d4c91c994e01 Mon Sep 17 00:00:00 2001 From: Karun Date: Wed, 3 Apr 2024 16:42:16 -0400 Subject: [PATCH 20/20] Fix passing of dual quad param to inner constructor --- embassy-stm32/src/ospi/mod.rs | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 4a89cd810..398c3298f 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -194,10 +194,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d1.set_as_af_pull(d1.af_num(), AFType::Input, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); - T::REGS.cr().modify(|w| { - w.set_dmm(false); - }); - Self::new_inner( peri, Some(d0.map_into()), @@ -214,6 +210,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma, config, OspiWidth::SING, + false, ) } @@ -238,10 +235,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None); d1.set_speed(crate::gpio::Speed::VeryHigh); - T::REGS.cr().modify(|w| { - w.set_dmm(false); - }); - Self::new_inner( peri, Some(d0.map_into()), @@ -258,6 +251,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma, config, OspiWidth::DUAL, + false, ) } @@ -288,10 +282,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None); d3.set_speed(crate::gpio::Speed::VeryHigh); - T::REGS.cr().modify(|w| { - w.set_dmm(false); - }); - Self::new_inner( peri, Some(d0.map_into()), @@ -308,6 +298,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma, config, OspiWidth::QUAD, + false, ) } @@ -350,10 +341,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); d7.set_speed(crate::gpio::Speed::VeryHigh); - T::REGS.cr().modify(|w| { - w.set_dmm(true); - }); - Self::new_inner( peri, Some(d0.map_into()), @@ -370,6 +357,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma, config, OspiWidth::QUAD, + true, ) } @@ -412,10 +400,6 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None); d7.set_speed(crate::gpio::Speed::VeryHigh); - T::REGS.cr().modify(|w| { - w.set_dmm(false); - }); - Self::new_inner( peri, Some(d0.map_into()), @@ -432,6 +416,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma, config, OspiWidth::OCTO, + false, ) } @@ -451,6 +436,7 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { dma: impl Peripheral

+ 'd, config: Config, width: OspiWidth, + dual_quad: bool, ) -> Self { into_ref!(peri, dma); @@ -495,6 +481,10 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { w.set_prescaler(config.clock_prescaler); }); + T::REGS.cr().modify(|w| { + w.set_dmm(dual_quad); + }); + T::REGS.tcr().modify(|w| { w.set_sshift(match config.sample_shifting { true => vals::SampleShift::HALFCYCLE,