diff --git a/esp-alloc/src/macros.rs b/esp-alloc/src/macros.rs index 47d9c454fa4..2454ec2faaa 100644 --- a/esp-alloc/src/macros.rs +++ b/esp-alloc/src/macros.rs @@ -31,11 +31,11 @@ macro_rules! heap_allocator { macro_rules! psram_allocator { ($peripheral:expr,$psram_module:path) => {{ use $psram_module as _psram; - _psram::init_psram($peripheral); + let (start, size) = _psram::init_psram($peripheral, _psram::PsramConfig::default()); unsafe { $crate::HEAP.add_region($crate::HeapRegion::new( - _psram::psram_vaddr_start() as *mut u8, - _psram::PSRAM_BYTES, + start, + size, $crate::MemoryCapability::External.into(), )); } diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index e35e202fa3e..66505dd814e 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -49,6 +49,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The `NO_PIN` constant has been removed. (#2133) - MSRV bump to 1.79 (#2156) - Allow handling interrupts while trying to lock critical section on multi-core chips. (#2197) +- Removed the PS-RAM related features, replaced by `quad-psram`/`octal-psram`, `init_psram` takes a configuration parameter, it's now possible to auto-detect PS-RAM size (#2178) ### Fixed diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index cbde3477652..f5a7fc4fe14 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -143,24 +143,11 @@ defmt = [ ] #! ### PSRAM Feature Flags -## Use externally connected PSRAM (2MB). -psram-2m = [] -## Use externally connected PSRAM (4MB). -psram-4m = [] -## Use externally connected PSRAM (8MB). -psram-8m = [] -## PSRAM 80Mhz frequency support -psram-80mhz = [] - -#! ### Octal RAM Feature Flags -## Use externally connected Octal RAM (2MB). -opsram-2m = [] -## Use externally connected Octal RAM (4MB). -opsram-4m = [] -## Use externally connected Octal RAM (8MB). -opsram-8m = [] -## Use externally connected Octal RAM (16MB). -opsram-16m = [] +## Use externally connected Quad PSRAM +quad-psram = [] + +## Use externally connected Octal RAM +octal-psram = [] # This feature is intended for testing; you probably don't want to enable it: ci = ["defmt", "bluetooth"] diff --git a/esp-hal/MIGRATING-0.20.md b/esp-hal/MIGRATING-0.20.md index 4f19c3f4815..09e82dacb7f 100644 --- a/esp-hal/MIGRATING-0.20.md +++ b/esp-hal/MIGRATING-0.20.md @@ -209,3 +209,49 @@ We've replaced some usage of features with [esp-config](https://docs.rs/esp-conf # key in .cargo/config.toml [env] section + ESP_HAL_PLACE_SPI_DRIVER_IN_RAM=true ``` + +## PS-RAM + +Initializing PS-RAM now takes a chip specific config and returns start of the mapped memory and the size. + +Example +```rust +let (start, size) = psram::init_psram(peripherals.PSRAM, psram::PsramConfig::default()); +``` + +If you don't specify the size of PS-RAM via `PsramConfig::size` the size of PS-RAM is derived from the RAM-chip id (or via probing in case of ESP32). + +`psram::psram_vaddr_start()` and `psram::PSRAM_BYTES` are removed. + +The features `psram-Xm` and `opsram-Xm` are removed and replaced by `quad-psram`/`octal-psram`. +The feature `psram-80mhz` is removed and replaced by `PsramConfig` + +Diff of the `psram_quad.rs` example +```diff +-//% FEATURES: psram-2m ++//% FEATURES: esp-hal/quad-psram + +... + +-fn init_psram_heap() { ++fn init_psram_heap(start: *mut u8, size: usize) { + unsafe { + esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( +- psram::psram_vaddr_start() as *mut u8, +- psram::PSRAM_BYTES, ++ start, ++ size, + esp_alloc::MemoryCapability::External.into(), + )); + } + +... + +- psram::init_psram(peripherals.PSRAM); +- init_psram_heap(); ++ let (start, size) = psram::init_psram(peripherals.PSRAM, psram::PsramConfig::default()); ++ init_psram_heap(start, size); + +... + +``` diff --git a/esp-hal/build.rs b/esp-hal/build.rs index 644b32c1f49..a5411fe9743 100644 --- a/esp-hal/build.rs +++ b/esp-hal/build.rs @@ -58,12 +58,14 @@ fn main() -> Result<(), Box> { let config = Config::for_chip(&chip); // Check PSRAM features are only given if the target supports PSRAM: - if !config.contains(&String::from("psram")) - && (cfg!(feature = "psram-2m") || cfg!(feature = "psram-4m") || cfg!(feature = "psram-8m")) - { + if !config.contains(&String::from("psram")) && cfg!(feature = "quad-psram") { panic!("The target does not support PSRAM"); } + if !config.contains(&String::from("octal_psram")) && cfg!(feature = "octal-psram") { + panic!("The target does not support Octal PSRAM"); + } + // Define all necessary configuration symbols for the configured device: config.define_symbols(); @@ -226,11 +228,7 @@ fn generate_memory_extras() -> Vec { #[cfg(feature = "esp32s2")] fn generate_memory_extras() -> Vec { - let reserved_cache = if cfg!(any( - feature = "psram-2m", - feature = "psram-4m", - feature = "psram-8m" - )) { + let reserved_cache = if cfg!(feature = "quad-psram") { "0x4000" } else { "0x2000" diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index cf86a3b4758..478e0fe7b9d 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -155,7 +155,7 @@ pub use self::soc::efuse; #[cfg(lp_core)] pub use self::soc::lp_core; pub use self::soc::peripherals; -#[cfg(psram)] +#[cfg(any(feature = "quad-psram", feature = "octal-psram"))] pub use self::soc::psram; #[cfg(ulp_riscv_core)] pub use self::soc::ulp_core; diff --git a/esp-hal/src/lock.rs b/esp-hal/src/lock.rs index bf76bc60b57..932dee32b8a 100644 --- a/esp-hal/src/lock.rs +++ b/esp-hal/src/lock.rs @@ -1,3 +1,5 @@ +use core::cell::UnsafeCell; + mod single_core { pub unsafe fn disable_interrupts() -> critical_section::RawRestoreState { cfg_if::cfg_if! { @@ -210,6 +212,31 @@ pub(crate) fn lock(lock: &Lock, f: impl FnOnce() -> T) -> T { f() } +/// Data protected by a [Lock] +#[allow(unused)] +pub(crate) struct Locked { + lock_state: Lock, + data: UnsafeCell, +} + +#[allow(unused)] +impl Locked { + /// Create a new instance + pub(crate) const fn new(data: T) -> Self { + Self { + lock_state: Lock::new(), + data: UnsafeCell::new(data), + } + } + + /// Provide exclusive access to the protected data to the given closure + pub(crate) fn with(&self, f: impl FnOnce(&mut T) -> R) -> R { + lock(&self.lock_state, || f(unsafe { &mut *self.data.get() })) + } +} + +unsafe impl Sync for Locked {} + struct CriticalSection; critical_section::set_impl!(CriticalSection); diff --git a/esp-hal/src/soc/esp32/mod.rs b/esp-hal/src/soc/esp32/mod.rs index 6196075d2fa..5f7e9311ec3 100644 --- a/esp-hal/src/soc/esp32/mod.rs +++ b/esp-hal/src/soc/esp32/mod.rs @@ -13,7 +13,7 @@ pub mod cpu_control; pub mod efuse; pub mod gpio; pub mod peripherals; -#[cfg(psram)] +#[cfg(feature = "quad-psram")] pub mod psram; pub mod radio_clocks; pub mod trng; diff --git a/esp-hal/src/soc/esp32/psram.rs b/esp-hal/src/soc/esp32/psram.rs index 4f1631ad011..3e46c79f91a 100644 --- a/esp-hal/src/soc/esp32/psram.rs +++ b/esp-hal/src/soc/esp32/psram.rs @@ -9,53 +9,98 @@ //! present on the `ESP32` chip. `PSRAM` provides additional external memory to //! supplement the internal memory of the `ESP32`, allowing for increased //! storage capacity and improved performance in certain applications. -//! -//! The `PSRAM` module is accessed through a virtual address, defined as -//! `PSRAM_VADDR`. The starting virtual address for the PSRAM module is -//! 0x3F800000. The `PSRAM` module size depends on the configuration specified -//! during the compilation process. The available `PSRAM` sizes are `2MB`, -//! `4MB`, and `8MB`. -//! -//! NOTE: If you want to use `PSRAM` on `ESP32` or `ESP32-S3`, it'll work only -//! in `release` mode. -/// The starting virtual address of the PSRAM (Pseudo-SRAM) region in memory. -pub const PSRAM_VADDR_START: usize = 0x3F800000; +pub use crate::soc::psram_common::*; + +const EXTMEM_ORIGIN: usize = 0x3F800000; + +/// Cache Speed +#[derive(PartialEq, Eq, Clone, Copy, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(missing_docs)] +pub enum PsramCacheSpeed { + #[default] + PsramCacheF80mS40m = 0, + PsramCacheF40mS40m, + PsramCacheF80mS80m, +} -/// Retrieves the starting virtual address of the PSRAM (Pseudo-SRAM) region in -/// memory. -pub fn psram_vaddr_start() -> usize { - PSRAM_VADDR_START +/// PSRAM configuration +#[derive(Copy, Clone, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PsramConfig { + /// PSRAM size + pub size: PsramSize, + /// Cache speed + pub cache_speed: PsramCacheSpeed, } -cfg_if::cfg_if! { - if #[cfg(feature = "psram-2m")] { - const PSRAM_SIZE: u32 = 2; - } else if #[cfg(feature = "psram-4m")] { - const PSRAM_SIZE: u32 = 4; - } else if #[cfg(feature = "psram-8m")] { - const PSRAM_SIZE: u32 = 8; +/// Initializes the PSRAM memory on supported devices. +/// +/// Returns the start of the mapped memory and the size +pub fn init_psram(_peripheral: crate::peripherals::PSRAM, config: PsramConfig) -> (*mut u8, usize) { + let mut config = config; + + utils::psram_init(&config); + + if config.size.is_auto() { + // Reading the device-id turned out to not work as expected (some bits flipped + // for unknown reason) + // + // As a workaround we just map 4m (maximum we can do) and + // probe if we can access top of PSRAM - if not we assume it's 2m + // + // This currently doesn't work as expected because of https://github.com/esp-rs/esp-hal/issues/2182 + utils::s_mapping(EXTMEM_ORIGIN as u32, 4 * 1024 * 1024); + + let guessed_size = unsafe { + let ptr = (EXTMEM_ORIGIN + 4 * 1024 * 1024 - 36 * 1024) as *mut u8; + for i in 0..(36 * 1024) { + ptr.add(i).write_volatile(0x7f); + } + + let ptr = EXTMEM_ORIGIN as *mut u8; + for i in 0..(36 * 1024) { + ptr.add(i).write_volatile(0x7f); + } + + let mut success = true; + let ptr = (EXTMEM_ORIGIN + 4 * 1024 * 1024 - 36 * 1024) as *mut u8; + for i in 0..(36 * 1024) { + if ptr.add(i).read_volatile() != 0x7f { + success = false; + break; + } + } + + if success { + 4 * 1024 * 1024 + } else { + 2 * 1024 * 1024 + } + }; + + info!("Assuming {} bytes of PSRAM", guessed_size); + config.size = PsramSize::Size(guessed_size); } else { - const PSRAM_SIZE: u32 = 0; + utils::s_mapping(EXTMEM_ORIGIN as u32, config.size.get() as u32); } -} -/// The total size of the PSRAM (Pseudo-SRAM) in bytes. -pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; + crate::soc::MAPPED_PSRAM.with(|mapped_psram| { + mapped_psram.memory_range = EXTMEM_ORIGIN..EXTMEM_ORIGIN + config.size.get(); + }); -/// Initializes the PSRAM memory on supported devices. -#[cfg(any(feature = "psram-2m", feature = "psram-4m", feature = "psram-8m"))] -pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral

) { - utils::psram_init(); - utils::s_mapping(PSRAM_VADDR_START as u32, PSRAM_BYTES as u32); + (EXTMEM_ORIGIN as *mut u8, config.size.get()) } -#[cfg(any(feature = "psram-2m", feature = "psram-4m", feature = "psram-8m"))] pub(crate) mod utils { use core::ptr::addr_of_mut; use procmacros::ram; + use super::*; + + #[ram] pub(crate) fn s_mapping(v_start: u32, size: u32) { // Enable external RAM in MMU cache_sram_mmu_set(0, 0, v_start, 0, 32, size / 1024 / 32); @@ -72,6 +117,7 @@ pub(crate) mod utils { // we can use the ROM version of this: it works well enough and keeps the size // of the binary down. + #[ram] fn cache_sram_mmu_set( cpu_no: u32, pid: u32, @@ -198,15 +244,6 @@ pub(crate) mod utils { (((spi_config) >> shift) & mask) as u8 } - #[derive(PartialEq, Eq, Clone, Copy, Debug)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - #[allow(unused)] - enum PsramCacheSpeed { - PsramCacheF80mS40m = 0, - PsramCacheF40mS40m, - PsramCacheF80mS80m, - } - #[derive(PartialEq, Eq, Debug, Default)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] struct PsramIo { @@ -228,13 +265,13 @@ pub(crate) mod utils { } #[repr(C)] - struct EspRomSpiflashChip { - device_id: u32, - chip_size: u32, // chip size in bytes - block_size: u32, - sector_size: u32, - page_size: u32, - status_mask: u32, + pub(super) struct EspRomSpiflashChip { + pub device_id: u32, + pub chip_size: u32, // chip size in bytes + pub block_size: u32, + pub sector_size: u32, + pub page_size: u32, + pub status_mask: u32, } extern "C" { @@ -253,7 +290,7 @@ pub(crate) mod utils { static mut g_rom_spiflash_dummy_len_plus: u8; - static g_rom_flashchip: EspRomSpiflashChip; + pub(super) static g_rom_flashchip: EspRomSpiflashChip; fn cache_sram_mmu_set_rom( cpu_no: u32, @@ -265,10 +302,11 @@ pub(crate) mod utils { ) -> i32; } - pub(crate) fn psram_init() { + #[ram] + pub(crate) fn psram_init(config: &PsramConfig) { let chip = crate::efuse::Efuse::get_chip_type(); - let mode = PsramCacheSpeed::PsramCacheF40mS40m; // How to make this configurable + let mode = config.cache_speed; let mut psram_io = PsramIo::default(); let clk_mode; @@ -416,6 +454,7 @@ pub(crate) mod utils { } let extra_dummy = psram_gpio_config(&psram_io, mode); + info!("extra dummy = {}", extra_dummy); // psram_is_32mbit_ver0 would need special handling here @@ -615,6 +654,7 @@ pub(crate) mod utils { } // spi param init for psram + #[ram] fn psram_spi_init( // psram_spi_num_t spi_num = PSRAM_SPI_1, mode: PsramCacheSpeed, @@ -748,7 +788,7 @@ pub(crate) mod utils { ps_cmd.rx_data_bit_len = 0; ps_cmd.dummy_bit_len = 0; let (backup_usr, backup_usr1, backup_usr2) = psram_cmd_config_spi1(&ps_cmd); - psram_cmd_recv_start_spi1(core::ptr::null_mut(), PsramCmdMode::PsramCmdSpi); + psram_cmd_recv_start_spi1(core::ptr::null_mut(), 0, PsramCmdMode::PsramCmdSpi); psram_cmd_end_spi1(backup_usr, backup_usr1, backup_usr2); } @@ -870,7 +910,11 @@ pub(crate) mod utils { // start sending cmd/addr and optionally, receiving data #[ram] - fn psram_cmd_recv_start_spi1(p_rx_data: *mut u32, cmd_mode: PsramCmdMode) { + fn psram_cmd_recv_start_spi1( + p_rx_data: *mut u32, + rx_data_len_words: usize, + cmd_mode: PsramCmdMode, + ) { unsafe { let spi = &*crate::peripherals::SPI1::PTR; // get cs1 @@ -942,8 +986,8 @@ pub(crate) mod utils { if !p_rx_data.is_null() { // Read data out - loop { - p_rx_data.write_volatile(spi.w(0).read().bits()); + for i in 0..rx_data_len_words { + p_rx_data.add(i).write_volatile(spi.w(i).read().bits()); } } } @@ -1191,6 +1235,7 @@ pub(crate) mod utils { let flash_id: u32 = g_rom_flashchip.device_id; info!("Flash-ID = {}", flash_id); + info!("Flash size = {}", g_rom_flashchip.chip_size); if flash_id == FLASH_ID_GD25LQ32C { // Set drive ability for 1.8v flash in 80Mhz. diff --git a/esp-hal/src/soc/esp32s2/mod.rs b/esp-hal/src/soc/esp32s2/mod.rs index ffae3496d1a..5406fcdc595 100644 --- a/esp-hal/src/soc/esp32s2/mod.rs +++ b/esp-hal/src/soc/esp32s2/mod.rs @@ -16,7 +16,7 @@ use crate::rtc_cntl::SocResetReason; pub mod efuse; pub mod gpio; pub mod peripherals; -#[cfg(psram)] +#[cfg(feature = "quad-psram")] pub mod psram; pub mod radio_clocks; pub mod trng; diff --git a/esp-hal/src/soc/esp32s2/psram.rs b/esp-hal/src/soc/esp32s2/psram.rs index a02b059f6e5..f8326296586 100644 --- a/esp-hal/src/soc/esp32s2/psram.rs +++ b/esp-hal/src/soc/esp32s2/psram.rs @@ -9,42 +9,42 @@ //! present on the `ESP32-S2` chip. `PSRAM` provides additional external memory //! to supplement the internal memory of the `ESP32-S2`, allowing for increased //! storage capacity and improved performance in certain applications. -//! -//! The `PSRAM` module is accessed through a virtual address, defined as -//! `PSRAM_VADDR`. The starting virtual address for the PSRAM module is -//! 0x3f500000. The `PSRAM` module size depends on the configuration specified -//! during the compilation process. The available `PSRAM` sizes are `2MB`, -//! `4MB`, and `8MB`. -const PSRAM_VADDR: u32 = 0x3f500000; - -/// Returns the start address of the PSRAM virtual address space. -pub fn psram_vaddr_start() -> usize { - PSRAM_VADDR_START -} -cfg_if::cfg_if! { - if #[cfg(feature = "psram-2m")] { - const PSRAM_SIZE: u32 = 2; - } else if #[cfg(feature = "psram-4m")] { - const PSRAM_SIZE: u32 = 4; - } else if #[cfg(feature = "psram-8m")] { - const PSRAM_SIZE: u32 = 8; - } else { - const PSRAM_SIZE: u32 = 0; - } +pub use crate::soc::psram_common::*; + +const EXTMEM_ORIGIN: usize = 0x3f500000; + +// Cache Speed +#[derive(PartialEq, Eq, Debug, Copy, Clone, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(missing_docs)] +pub enum PsramCacheSpeed { + PsramCacheS80m = 1, + PsramCacheS40m, + PsramCacheS26m, + PsramCacheS20m, + #[default] + PsramCacheMax, } -/// The total size of the PSRAM in bytes, calculated from the PSRAM size -/// constant. -pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; - -/// The start address of the PSRAM virtual address space. -pub const PSRAM_VADDR_START: usize = PSRAM_VADDR as usize; +/// PSRAM configuration +#[derive(Copy, Clone, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PsramConfig { + /// PSRAM size + pub size: PsramSize, + /// Cache Speed + pub speed: PsramCacheSpeed, +} /// Initialize PSRAM to be used for data. -#[cfg(any(feature = "psram-2m", feature = "psram-4m", feature = "psram-8m"))] +/// +/// Returns the start of the mapped memory and the size #[procmacros::ram] -pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral

) { +pub fn init_psram(_peripheral: crate::peripherals::PSRAM, config: PsramConfig) -> (*mut u8, usize) { + let mut config = config; + utils::psram_init(&mut config); + #[allow(unused)] enum CacheLayout { Invalid = 0, @@ -120,10 +120,10 @@ pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral

> PSRAM_ID_EID_S) & PSRAM_ID_EID_M) >> PSRAM_EID_SIZE_S + & PSRAM_EID_SIZE_M; + + const PSRAM_EID_SIZE_32MBITS: u32 = 1; + const PSRAM_EID_SIZE_64MBITS: u32 = 2; + + let size = match size_id { + PSRAM_EID_SIZE_64MBITS => 16 / 8 * 1024 * 1024, + PSRAM_EID_SIZE_32MBITS => 16 / 8 * 1024 * 1024, + _ => 16 / 8 * 1024 * 1024, + }; + + info!("size is {}", size); + + config.size = PsramSize::Size(size); + } + psram_reset_mode(); psram_enable_qio_mode(); - psram_cache_init(PsramCacheSpeed::PsramCacheMax, PsramVaddrMode::Normal); + psram_cache_init(config.speed, PsramVaddrMode::Normal); } // send reset command to psram, in spi mode fn psram_reset_mode() { - const PSRAM_RESET_EN: u16 = 0x66; - const PSRAM_RESET: u16 = 0x99; - const CS_PSRAM_SEL: u8 = 1 << 1; - psram_exec_cmd( CommandMode::PsramCmdSpi, PSRAM_RESET_EN, @@ -224,6 +287,7 @@ pub(crate) mod utils { } #[allow(clippy::too_many_arguments)] + #[inline(always)] fn psram_exec_cmd( mode: CommandMode, cmd: u16, @@ -265,7 +329,7 @@ pub(crate) mod utils { _psram_exec_cmd( cmd, cmd_bit_len, - addr, + &addr, addr_bit_len, dummy_bits, mosi_data, @@ -289,10 +353,11 @@ pub(crate) mod utils { } #[allow(clippy::too_many_arguments)] + #[inline(always)] fn _psram_exec_cmd( cmd: u16, cmd_bit_len: u16, - addr: u32, + addr: *const u32, addr_bit_len: u32, dummy_bits: u32, mosi_data: *const u8, @@ -323,7 +388,7 @@ pub(crate) mod utils { let conf = esp_rom_spi_cmd_t { cmd, cmd_bit_len, - addr: addr as *const u32, + addr, addr_bit_len, tx_data: mosi_data as *const u32, tx_data_bit_len: mosi_bit_len, @@ -434,29 +499,6 @@ pub(crate) mod utils { } } - #[derive(PartialEq, Eq, Debug)] - #[allow(unused)] - enum PsramCacheSpeed { - PsramCacheS80m = 1, - PsramCacheS40m, - PsramCacheS26m, - PsramCacheS20m, - PsramCacheMax, - } - - #[derive(PartialEq, Eq, Debug)] - #[allow(unused)] - enum PsramVaddrMode { - /// App and pro CPU use their own flash cache for external RAM access - Normal = 0, - /// App and pro CPU share external RAM caches: pro CPU has low2M, app - /// CPU has high 2M - Lowhigh, - /// App and pro CPU share external RAM caches: pro CPU does even 32yte - /// ranges, app does odd ones. - Evenodd, - } - const PSRAM_IO_MATRIX_DUMMY_20M: u32 = 0; const PSRAM_IO_MATRIX_DUMMY_40M: u32 = 0; const PSRAM_IO_MATRIX_DUMMY_80M: u32 = 0; diff --git a/esp-hal/src/soc/esp32s3/mod.rs b/esp-hal/src/soc/esp32s3/mod.rs index 43f62fa64a5..dde09d9df8d 100644 --- a/esp-hal/src/soc/esp32s3/mod.rs +++ b/esp-hal/src/soc/esp32s3/mod.rs @@ -17,7 +17,7 @@ pub mod cpu_control; pub mod efuse; pub mod gpio; pub mod peripherals; -#[cfg(psram)] +#[cfg(any(feature = "quad-psram", feature = "octal-psram"))] pub mod psram; pub mod radio_clocks; pub mod trng; diff --git a/esp-hal/src/soc/esp32s3/psram.rs b/esp-hal/src/soc/esp32s3/psram.rs index c910895e941..27067c93724 100644 --- a/esp-hal/src/soc/esp32s3/psram.rs +++ b/esp-hal/src/soc/esp32s3/psram.rs @@ -10,55 +10,70 @@ //! to supplement the internal memory of the `ESP32-S3`, allowing for increased //! storage capacity and improved performance in certain applications. //! -//! The `PSRAM` module is accessed through a virtual address, defined as -//! `PSRAM_VADDR`. The starting virtual address for the PSRAM module is -//! 0x3c000000. The `PSRAM` module size depends on the configuration specified -//! during the compilation process. The available `PSRAM` sizes are `2MB`, -//! `4MB`, and `8MB`. - -static mut PSRAM_VADDR: u32 = 0x3C000000; +//! The mapped start address for PSRAM depends on the amount of mapped flash +//! memory. + +pub use crate::soc::psram_common::*; + +const EXTMEM_ORIGIN: u32 = 0x3C000000; + +/// Frequency of flash memory +#[derive(Copy, Clone, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(missing_docs)] +pub enum FlashFreq { + FlashFreq20m = 20, + FlashFreq40m = 40, + #[default] + FlashFreq80m = 80, + FlashFreq120m = 120, +} -/// Returns the start address of the PSRAM virtual address space. -pub fn psram_vaddr_start() -> usize { - unsafe { PSRAM_VADDR as usize } +/// Frequency of PSRAM memory +#[derive(Copy, Clone, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(missing_docs)] +pub enum SpiRamFreq { + #[default] + Freq40m = 40, + Freq80m = 80, + Freq120m = 120, } -cfg_if::cfg_if! { - if #[cfg(feature = "psram-2m")] { - const PSRAM_SIZE: u32 = 2; - } else if #[cfg(feature = "psram-4m")] { - const PSRAM_SIZE: u32 = 4; - } else if #[cfg(feature = "psram-8m")] { - const PSRAM_SIZE: u32 = 8; - } else if #[cfg(feature = "opsram-2m")] { - const PSRAM_SIZE: u32 = 2; - } else if #[cfg(feature = "opsram-4m")] { - const PSRAM_SIZE: u32 = 4; - } else if #[cfg(feature = "opsram-8m")] { - const PSRAM_SIZE: u32 = 8; - } else if #[cfg(feature = "opsram-16m")] { - const PSRAM_SIZE: u32 = 16; - }else { - const PSRAM_SIZE: u32 = 0; - } +/// Core timing configuration +#[derive(Copy, Clone, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(missing_docs)] +pub enum SpiTimingConfigCoreClock { + #[default] + SpiTimingConfigCoreClock80m = 80, + SpiTimingConfigCoreClock120m = 120, + SpiTimingConfigCoreClock160m = 160, + SpiTimingConfigCoreClock240m = 240, } -/// The total size of the PSRAM in bytes, calculated from the PSRAM size -/// constant. -pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; +/// PSRAM configuration +#[derive(Copy, Clone, Debug, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PsramConfig { + /// PSRAM size + pub size: PsramSize, + /// Core timing configuration + pub core_clock: SpiTimingConfigCoreClock, + /// Frequency of flash memory + pub flash_frequency: FlashFreq, + /// Frequency of PSRAM memory + pub ram_frequency: SpiRamFreq, +} /// Initialize PSRAM to be used for data. -#[cfg(any( - feature = "psram-2m", - feature = "psram-4m", - feature = "psram-8m", - feature = "opsram-2m", - feature = "opsram-4m", - feature = "opsram-8m", - feature = "opsram-16m" -))] +/// +/// Returns the start of the mapped memory and the size #[procmacros::ram] -pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral

) { +pub fn init_psram(_peripheral: crate::peripherals::PSRAM, config: PsramConfig) -> (*mut u8, usize) { + let mut config = config; + utils::psram_init(&mut config); + const CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE: u32 = 0x4000; const CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS: u8 = 8; const CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE: u8 = 32; @@ -102,7 +117,8 @@ pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral

i32; } - unsafe { + + let start = unsafe { const MMU_PAGE_SIZE: u32 = 0x10000; const ICACHE_MMU_SIZE: usize = 0x800; const FLASH_MMU_TABLE_SIZE: usize = ICACHE_MMU_SIZE / core::mem::size_of::(); @@ -110,7 +126,7 @@ pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral

u32 { - match self { - SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m => 80, - SpiTimingConfigCoreClock::SpiTimingConfigCoreClock120m => 120, - SpiTimingConfigCoreClock::SpiTimingConfigCoreClock160m => 160, - SpiTimingConfigCoreClock::SpiTimingConfigCoreClock240m => 240, - } - } - } + let size_id = (((dev_id) >> PSRAM_ID_EID_S) & PSRAM_ID_EID_M) >> PSRAM_EID_SIZE_S + & PSRAM_EID_SIZE_M; - #[ram] - pub(crate) fn psram_init() { - psram_gpio_config(); - psram_set_cs_timing(); + const PSRAM_EID_SIZE_32MBITS: u32 = 1; + const PSRAM_EID_SIZE_64MBITS: u32 = 2; + + let size = match size_id { + PSRAM_EID_SIZE_64MBITS => 64 / 8 * 1024 * 1024, + PSRAM_EID_SIZE_32MBITS => 32 / 8 * 1024 * 1024, + _ => 16 / 8 * 1024 * 1024, + }; + + info!("size is {}", size); + + config.size = PsramSize::Size(size); + } // SPI1: send psram reset command psram_reset_mode_spi1(); @@ -237,7 +256,7 @@ pub(crate) mod utils { config_psram_spi_phases(); // Back to the high speed mode. Flash/PSRAM clocks are set to the clock that // user selected. SPI0/1 registers are all set correctly - mspi_timing_enter_high_speed_mode(true); + mspi_timing_enter_high_speed_mode(true, config); } const PSRAM_CS_IO: u8 = 26; @@ -359,10 +378,10 @@ pub(crate) mod utils { /// This function should always be called after `mspi_timing_flash_tuning` /// or `calculate_best_flash_tuning_config` #[ram] - fn mspi_timing_enter_high_speed_mode(control_spi1: bool) { - let core_clock: SpiTimingConfigCoreClock = get_mspi_core_clock(); - let flash_div: u32 = get_flash_clock_divider(); - let psram_div: u32 = get_psram_clock_divider(); + fn mspi_timing_enter_high_speed_mode(control_spi1: bool, config: &PsramConfig) { + let core_clock: SpiTimingConfigCoreClock = get_mspi_core_clock(config); + let flash_div: u32 = get_flash_clock_divider(config); + let psram_div: u32 = get_psram_clock_divider(config); info!( "PSRAM core_clock {:?}, flash_div = {}, psram_div = {}", @@ -447,36 +466,23 @@ pub(crate) mod utils { } #[ram] - fn get_mspi_core_clock() -> SpiTimingConfigCoreClock { - SPI_TIMING_CORE_CLOCK + fn get_mspi_core_clock(config: &PsramConfig) -> SpiTimingConfigCoreClock { + config.core_clock } #[ram] - fn get_flash_clock_divider() -> u32 { - match FLASH_FREQ { - FlashFreq::FlashFreq20m => SPI_TIMING_CORE_CLOCK.mhz() / 20, - FlashFreq::FlashFreq40m => SPI_TIMING_CORE_CLOCK.mhz() / 40, - FlashFreq::FlashFreq80m => SPI_TIMING_CORE_CLOCK.mhz() / 80, - FlashFreq::FlashFreq120m => SPI_TIMING_CORE_CLOCK.mhz() / 120, - } + fn get_flash_clock_divider(config: &PsramConfig) -> u32 { + config.core_clock as u32 / config.flash_frequency as u32 } #[ram] - fn get_psram_clock_divider() -> u32 { - match SPIRAM_SPEED { - SpiRamFreq::Freq40m => SPI_TIMING_CORE_CLOCK.mhz() / 40, - SpiRamFreq::Freq80m => SPI_TIMING_CORE_CLOCK.mhz() / 80, - SpiRamFreq::Freq120m => SPI_TIMING_CORE_CLOCK.mhz() / 120, - } + fn get_psram_clock_divider(config: &PsramConfig) -> u32 { + config.core_clock as u32 / config.ram_frequency as u32 } // send reset command to psram, in spi mode #[ram] fn psram_reset_mode_spi1() { - const PSRAM_RESET_EN: u16 = 0x66; - const PSRAM_RESET: u16 = 0x99; - const CS_PSRAM_SEL: u8 = 1 << 1; - psram_exec_cmd( CommandMode::PsramCmdSpi, PSRAM_RESET_EN, @@ -617,7 +623,7 @@ pub(crate) mod utils { let conf = esp_rom_spi_cmd_t { cmd, cmd_bit_len, - addr: addr as *const u32, + addr: &addr, addr_bit_len, tx_data: mosi_data as *const u32, tx_data_bit_len: mosi_bit_len, @@ -733,64 +739,11 @@ pub(crate) mod utils { } } -#[cfg(any( - feature = "opsram-2m", - feature = "opsram-4m", - feature = "opsram-8m", - feature = "opsram-16m" -))] +#[cfg(feature = "octal-psram")] pub(crate) mod utils { use procmacros::ram; - // these should probably be configurable, relates to https://github.com/esp-rs/esp-hal/issues/42 - // probably also the PSRAM size shouldn't get configured via features - const SPI_TIMING_CORE_CLOCK: SpiTimingConfigCoreClock = - SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m; - const FLASH_FREQ: FlashFreq = FlashFreq::FlashFreq80m; - - cfg_if::cfg_if! { - if #[cfg(feature = "psram-80mhz")] { - const SPIRAM_SPEED: SpiRamFreq = SpiRamFreq::Freq80m; - } else { - const SPIRAM_SPEED: SpiRamFreq = SpiRamFreq::Freq40m; - } - } - - #[allow(unused)] - enum FlashFreq { - FlashFreq20m, - FlashFreq40m, - FlashFreq80m, - FlashFreq120m, - } - - #[allow(unused)] - enum SpiRamFreq { - Freq40m, - Freq80m, - Freq120m, - } - - #[allow(unused)] - #[derive(Debug)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - enum SpiTimingConfigCoreClock { - SpiTimingConfigCoreClock80m, - SpiTimingConfigCoreClock120m, - SpiTimingConfigCoreClock160m, - SpiTimingConfigCoreClock240m, - } - - impl SpiTimingConfigCoreClock { - fn mhz(&self) -> u32 { - match self { - SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m => 80, - SpiTimingConfigCoreClock::SpiTimingConfigCoreClock120m => 120, - SpiTimingConfigCoreClock::SpiTimingConfigCoreClock160m => 160, - SpiTimingConfigCoreClock::SpiTimingConfigCoreClock240m => 240, - } - } - } + use super::*; const OPI_PSRAM_SYNC_READ: u16 = 0x0000; const OPI_PSRAM_SYNC_WRITE: u16 = 0x8080; @@ -1077,7 +1030,7 @@ pub(crate) mod utils { } #[ram] - pub(crate) fn psram_init() { + pub(crate) fn psram_init(config: &mut PsramConfig) { mspi_pin_init(); init_psram_pins(); set_psram_cs_timing(); @@ -1124,6 +1077,10 @@ pub(crate) mod utils { }; info!("{} bytes of PSRAM", psram_size); + if config.size.is_auto() { + config.size = PsramSize::Size(psram_size); + } + // Do PSRAM timing tuning, we use SPI1 to do the tuning, and set the // SPI0 PSRAM timing related registers accordingly // this is unsupported for now @@ -1131,7 +1088,7 @@ pub(crate) mod utils { // Back to the high speed mode. Flash/PSRAM clocks are set to the clock // that user selected. SPI0/1 registers are all set correctly - spi_timing_enter_mspi_high_speed_mode(true); + spi_timing_enter_mspi_high_speed_mode(true, config); // Tuning may change SPI1 regs, whereas legacy spi_flash APIs rely on // these regs. This function is to restore SPI1 init state. @@ -1280,12 +1237,12 @@ pub(crate) mod utils { // // This function should always be called after `spi_timing_flash_tuning` or // `calculate_best_flash_tuning_config` - fn spi_timing_enter_mspi_high_speed_mode(control_spi1: bool) { + fn spi_timing_enter_mspi_high_speed_mode(control_spi1: bool, config: &PsramConfig) { // spi_timing_config_core_clock_t core_clock = get_mspi_core_clock(); let core_clock = SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m; - let flash_div: u32 = get_flash_clock_divider(); - let psram_div: u32 = get_psram_clock_divider(); + let flash_div: u32 = get_flash_clock_divider(config); + let psram_div: u32 = get_psram_clock_divider(config); // Set SPI01 core clock spi0_timing_config_set_core_clock(core_clock); // SPI0 and SPI1 share the register for core clock. So we only set SPI0 here. @@ -1644,21 +1601,12 @@ pub(crate) mod utils { } #[ram] - fn get_flash_clock_divider() -> u32 { - match FLASH_FREQ { - FlashFreq::FlashFreq20m => SPI_TIMING_CORE_CLOCK.mhz() / 20, - FlashFreq::FlashFreq40m => SPI_TIMING_CORE_CLOCK.mhz() / 40, - FlashFreq::FlashFreq80m => SPI_TIMING_CORE_CLOCK.mhz() / 80, - FlashFreq::FlashFreq120m => SPI_TIMING_CORE_CLOCK.mhz() / 120, - } + fn get_flash_clock_divider(config: &PsramConfig) -> u32 { + config.core_clock as u32 / config.flash_frequency as u32 } #[ram] - fn get_psram_clock_divider() -> u32 { - match SPIRAM_SPEED { - SpiRamFreq::Freq40m => SPI_TIMING_CORE_CLOCK.mhz() / 40, - SpiRamFreq::Freq80m => SPI_TIMING_CORE_CLOCK.mhz() / 80, - SpiRamFreq::Freq120m => SPI_TIMING_CORE_CLOCK.mhz() / 120, - } + fn get_psram_clock_divider(config: &PsramConfig) -> u32 { + config.core_clock as u32 / config.ram_frequency as u32 } } diff --git a/esp-hal/src/soc/mod.rs b/esp-hal/src/soc/mod.rs index d364895a771..2338237f515 100644 --- a/esp-hal/src/soc/mod.rs +++ b/esp-hal/src/soc/mod.rs @@ -1,6 +1,8 @@ use portable_atomic::{AtomicU8, Ordering}; pub use self::implementation::*; +#[cfg(psram)] +use crate::lock::Locked; #[cfg_attr(esp32, path = "esp32/mod.rs")] #[cfg_attr(esp32c2, path = "esp32c2/mod.rs")] @@ -13,6 +15,17 @@ mod implementation; mod efuse_field; +#[cfg(any(feature = "quad-psram", feature = "octal-psram"))] +mod psram_common; + +#[cfg(psram)] +static MAPPED_PSRAM: Locked = Locked::new(MappedPsram { memory_range: 0..0 }); + +#[cfg(psram)] +pub struct MappedPsram { + memory_range: core::ops::Range, +} + // Indicates the state of setting the mac address // 0 -- unset // 1 -- in the process of being set @@ -59,7 +72,7 @@ impl self::efuse::Efuse { /// Get base mac address /// /// By default this reads the base mac address from eFuse, but it can be - /// overriden by `set_mac_address`. + /// overridden by `set_mac_address`. pub fn get_mac_address() -> [u8; 6] { if MAC_OVERRIDE_STATE.load(Ordering::Relaxed) == 2 { unsafe { MAC_OVERRIDE } @@ -85,9 +98,8 @@ pub(crate) fn is_slice_in_dram(slice: &[T]) -> bool { pub(crate) fn is_valid_psram_address(address: u32) -> bool { #[cfg(psram)] { - let start = crate::psram::psram_vaddr_start() as u32; - let end = start + crate::psram::PSRAM_BYTES as u32; - (start..=end).contains(&address) + let memory_range = MAPPED_PSRAM.with(|mapped_psram| mapped_psram.memory_range.clone()); + memory_range.contains(&(address as usize)) } #[cfg(not(psram))] false diff --git a/esp-hal/src/soc/psram_common.rs b/esp-hal/src/soc/psram_common.rs new file mode 100644 index 00000000000..a598f86b2ea --- /dev/null +++ b/esp-hal/src/soc/psram_common.rs @@ -0,0 +1,25 @@ +/// Size of PSRAM +/// +/// [PsramSize::AutoDetect] will try to detect the size of PSRAM +#[derive(Copy, Clone, Debug, Default, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PsramSize { + /// Detect PSRAM size + #[default] + AutoDetect, + /// A fixed PSRAM size + Size(usize), +} + +impl PsramSize { + pub(crate) fn get(&self) -> usize { + match self { + PsramSize::AutoDetect => 0, + PsramSize::Size(size) => *size, + } + } + + pub(crate) fn is_auto(&self) -> bool { + matches!(self, PsramSize::AutoDetect) + } +} diff --git a/esp-metadata/devices/esp32s3.toml b/esp-metadata/devices/esp32s3.toml index 7b592e239e5..96fbe57278a 100644 --- a/esp-metadata/devices/esp32s3.toml +++ b/esp-metadata/devices/esp32s3.toml @@ -68,6 +68,7 @@ symbols = [ "bt", "wifi", "psram", + "octal_psram", "ulp_riscv_core", "timg_timer1", "very_large_intr_status", diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 91f38d85eec..a5e923be4e2 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -73,9 +73,6 @@ esp-wifi = ["dep:esp-wifi"] embassy = ["dep:esp-hal-embassy"] embassy-generic-timers = ["embassy-time/generic-queue-8"] -opsram-2m = ["esp-hal/opsram-2m"] -psram-2m = ["esp-hal/psram-2m"] - [profile.release] codegen-units = 1 debug = 2 diff --git a/examples/src/bin/dma_extmem2mem.rs b/examples/src/bin/dma_extmem2mem.rs index 1fb0081f631..063197a7f6e 100644 --- a/examples/src/bin/dma_extmem2mem.rs +++ b/examples/src/bin/dma_extmem2mem.rs @@ -1,6 +1,6 @@ //! Uses DMA to copy psram to internal memory. -//% FEATURES: esp-hal/log opsram-2m aligned +//% FEATURES: esp-hal/log esp-hal/octal-psram aligned //% CHIPS: esp32s3 #![no_std] @@ -42,16 +42,13 @@ macro_rules! dma_alloc_buffer { }}; } -fn init_heap(psram: impl esp_hal::peripheral::Peripheral

) { - esp_hal::psram::init_psram(psram); - info!( - "init_heap: start: 0x{:0x}", - esp_hal::psram::psram_vaddr_start() - ); +fn init_heap(psram: esp_hal::peripherals::PSRAM) { + let (start, size) = esp_hal::psram::init_psram(psram, esp_hal::psram::PsramConfig::default()); + info!("init_heap: start: {:p}", start); unsafe { esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( - esp_hal::psram::psram_vaddr_start() as *mut u8, - esp_hal::psram::PSRAM_BYTES, + start, + size, esp_alloc::MemoryCapability::External.into(), )); } diff --git a/examples/src/bin/psram_octal.rs b/examples/src/bin/psram_octal.rs index c8cf9567853..3f2bc7e2588 100644 --- a/examples/src/bin/psram_octal.rs +++ b/examples/src/bin/psram_octal.rs @@ -3,7 +3,7 @@ //! You need an ESP32-S3 with at least 2 MB of PSRAM memory. //% CHIPS: esp32s3 -//% FEATURES: opsram-2m +//% FEATURES: esp-hal/octal-psram #![no_std] #![no_main] @@ -17,11 +17,11 @@ use esp_backtrace as _; use esp_hal::{prelude::*, psram}; use esp_println::println; -fn init_psram_heap() { +fn init_psram_heap(start: *mut u8, size: usize) { unsafe { esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( - psram::psram_vaddr_start() as *mut u8, - psram::PSRAM_BYTES, + start, + size, esp_alloc::MemoryCapability::External.into(), )); } @@ -32,10 +32,11 @@ compile_error!("PSRAM example must be built in release mode!"); #[entry] fn main() -> ! { + esp_println::logger::init_logger_from_env(); let peripherals = esp_hal::init(esp_hal::Config::default()); - psram::init_psram(peripherals.PSRAM); - init_psram_heap(); + let (start, size) = psram::init_psram(peripherals.PSRAM, psram::PsramConfig::default()); + init_psram_heap(start, size); println!("Going to access PSRAM"); let mut large_vec: Vec = Vec::with_capacity(500 * 1024 / 4); diff --git a/examples/src/bin/psram_quad.rs b/examples/src/bin/psram_quad.rs index 28718265771..75222b7ef2e 100644 --- a/examples/src/bin/psram_quad.rs +++ b/examples/src/bin/psram_quad.rs @@ -3,7 +3,7 @@ //! You need an ESP32, ESP32-S2 or ESP32-S3 with at least 2 MB of PSRAM memory. //% CHIPS: esp32 esp32s2 esp32s3 -//% FEATURES: psram-2m +//% FEATURES: esp-hal/quad-psram #![no_std] #![no_main] @@ -17,11 +17,11 @@ use esp_backtrace as _; use esp_hal::{prelude::*, psram}; use esp_println::println; -fn init_psram_heap() { +fn init_psram_heap(start: *mut u8, size: usize) { unsafe { esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( - psram::psram_vaddr_start() as *mut u8, - psram::PSRAM_BYTES, + start, + size, esp_alloc::MemoryCapability::External.into(), )); } @@ -32,10 +32,11 @@ compile_error!("PSRAM example must be built in release mode!"); #[entry] fn main() -> ! { + esp_println::logger::init_logger_from_env(); let peripherals = esp_hal::init(esp_hal::Config::default()); - psram::init_psram(peripherals.PSRAM); - init_psram_heap(); + let (start, size) = psram::init_psram(peripherals.PSRAM, psram::PsramConfig::default()); + init_psram_heap(start, size); println!("Going to access PSRAM"); let mut large_vec = Vec::::with_capacity(500 * 1024 / 4); diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 1378b70c684..b278ac717cf 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -122,8 +122,13 @@ pub fn build_documentation( let mut features = vec![chip.to_string()]; + // future enhancement: see https://github.com/esp-rs/esp-hal/issues/2195 if matches!(package, Package::EspHal) { - features.push("ci".to_owned()) + features.push("ci".to_owned()); + + if [Chip::Esp32, Chip::Esp32s2, Chip::Esp32s3].contains(&chip) { + features.push("quad-psram".to_owned()); + } } // Build up an array of command-line arguments to pass to `cargo`: diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 16ed23842c7..262bdd2102d 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -556,7 +556,7 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> { } if device.contains("psram") { // TODO this doesn't test octal psram as it would require a separate build - features.push_str(",psram-4m,psram-80mhz") + features.push_str(",quad-psram") } if matches!(chip, Chip::Esp32c6 | Chip::Esp32h2) { features.push_str(",flip-link")