Remove most PSRAM features (#2178)

* Make most of PSRAM features into run-time configs

* Make CI green again

* Make CI green again

* Update esp-hal/MIGRATING-0.20.md

Co-authored-by: Scott Mabin <scott@mabez.dev>

* Use Range

* CI

* Rebase fixes

* Update esp-hal/src/lock.rs

Co-authored-by: Scott Mabin <scott@mabez.dev>

---------

Co-authored-by: Scott Mabin <scott@mabez.dev>
This commit is contained in:
Björn Quentin 2024-09-20 18:43:25 +02:00 committed by GitHub
parent d4e463b3ff
commit 37fa662fe7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 512 additions and 379 deletions

View File

@ -31,11 +31,11 @@ macro_rules! heap_allocator {
macro_rules! psram_allocator { macro_rules! psram_allocator {
($peripheral:expr,$psram_module:path) => {{ ($peripheral:expr,$psram_module:path) => {{
use $psram_module as _psram; use $psram_module as _psram;
_psram::init_psram($peripheral); let (start, size) = _psram::init_psram($peripheral, _psram::PsramConfig::default());
unsafe { unsafe {
$crate::HEAP.add_region($crate::HeapRegion::new( $crate::HEAP.add_region($crate::HeapRegion::new(
_psram::psram_vaddr_start() as *mut u8, start,
_psram::PSRAM_BYTES, size,
$crate::MemoryCapability::External.into(), $crate::MemoryCapability::External.into(),
)); ));
} }

View File

@ -50,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The `NO_PIN` constant has been removed. (#2133) - The `NO_PIN` constant has been removed. (#2133)
- MSRV bump to 1.79 (#2156) - MSRV bump to 1.79 (#2156)
- Allow handling interrupts while trying to lock critical section on multi-core chips. (#2197) - 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 ### Fixed

View File

@ -143,24 +143,11 @@ defmt = [
] ]
#! ### PSRAM Feature Flags #! ### PSRAM Feature Flags
## Use externally connected PSRAM (2MB). ## Use externally connected Quad PSRAM
psram-2m = [] quad-psram = []
## 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
## Use externally connected Octal RAM (2MB). octal-psram = []
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 = []
# This feature is intended for testing; you probably don't want to enable it: # This feature is intended for testing; you probably don't want to enable it:
ci = ["defmt", "bluetooth"] ci = ["defmt", "bluetooth"]

View File

@ -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 # key in .cargo/config.toml [env] section
+ ESP_HAL_PLACE_SPI_DRIVER_IN_RAM=true + 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);
...
```

View File

@ -58,12 +58,14 @@ fn main() -> Result<(), Box<dyn Error>> {
let config = Config::for_chip(&chip); let config = Config::for_chip(&chip);
// Check PSRAM features are only given if the target supports PSRAM: // Check PSRAM features are only given if the target supports PSRAM:
if !config.contains(&String::from("psram")) if !config.contains(&String::from("psram")) && cfg!(feature = "quad-psram") {
&& (cfg!(feature = "psram-2m") || cfg!(feature = "psram-4m") || cfg!(feature = "psram-8m"))
{
panic!("The target does not support 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: // Define all necessary configuration symbols for the configured device:
config.define_symbols(); config.define_symbols();
@ -226,11 +228,7 @@ fn generate_memory_extras() -> Vec<u8> {
#[cfg(feature = "esp32s2")] #[cfg(feature = "esp32s2")]
fn generate_memory_extras() -> Vec<u8> { fn generate_memory_extras() -> Vec<u8> {
let reserved_cache = if cfg!(any( let reserved_cache = if cfg!(feature = "quad-psram") {
feature = "psram-2m",
feature = "psram-4m",
feature = "psram-8m"
)) {
"0x4000" "0x4000"
} else { } else {
"0x2000" "0x2000"

View File

@ -155,7 +155,7 @@ pub use self::soc::efuse;
#[cfg(lp_core)] #[cfg(lp_core)]
pub use self::soc::lp_core; pub use self::soc::lp_core;
pub use self::soc::peripherals; pub use self::soc::peripherals;
#[cfg(psram)] #[cfg(any(feature = "quad-psram", feature = "octal-psram"))]
pub use self::soc::psram; pub use self::soc::psram;
#[cfg(ulp_riscv_core)] #[cfg(ulp_riscv_core)]
pub use self::soc::ulp_core; pub use self::soc::ulp_core;

View File

@ -1,3 +1,5 @@
use core::cell::UnsafeCell;
mod single_core { mod single_core {
pub unsafe fn disable_interrupts() -> critical_section::RawRestoreState { pub unsafe fn disable_interrupts() -> critical_section::RawRestoreState {
cfg_if::cfg_if! { cfg_if::cfg_if! {
@ -210,6 +212,31 @@ pub(crate) fn lock<T>(lock: &Lock, f: impl FnOnce() -> T) -> T {
f() f()
} }
/// Data protected by a [Lock]
#[allow(unused)]
pub(crate) struct Locked<T> {
lock_state: Lock,
data: UnsafeCell<T>,
}
#[allow(unused)]
impl<T> Locked<T> {
/// 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<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
lock(&self.lock_state, || f(unsafe { &mut *self.data.get() }))
}
}
unsafe impl<T> Sync for Locked<T> {}
struct CriticalSection; struct CriticalSection;
critical_section::set_impl!(CriticalSection); critical_section::set_impl!(CriticalSection);

View File

@ -13,7 +13,7 @@ pub mod cpu_control;
pub mod efuse; pub mod efuse;
pub mod gpio; pub mod gpio;
pub mod peripherals; pub mod peripherals;
#[cfg(psram)] #[cfg(feature = "quad-psram")]
pub mod psram; pub mod psram;
pub mod radio_clocks; pub mod radio_clocks;
pub mod trng; pub mod trng;

View File

@ -9,53 +9,98 @@
//! present on the `ESP32` chip. `PSRAM` provides additional external memory to //! present on the `ESP32` chip. `PSRAM` provides additional external memory to
//! supplement the internal memory of the `ESP32`, allowing for increased //! supplement the internal memory of the `ESP32`, allowing for increased
//! storage capacity and improved performance in certain applications. //! 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 use crate::soc::psram_common::*;
pub const PSRAM_VADDR_START: usize = 0x3F800000;
/// Retrieves the starting virtual address of the PSRAM (Pseudo-SRAM) region in const EXTMEM_ORIGIN: usize = 0x3F800000;
/// memory.
pub fn psram_vaddr_start() -> usize { /// Cache Speed
PSRAM_VADDR_START #[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,
} }
cfg_if::cfg_if! { /// PSRAM configuration
if #[cfg(feature = "psram-2m")] { #[derive(Copy, Clone, Debug, Default)]
const PSRAM_SIZE: u32 = 2; #[cfg_attr(feature = "defmt", derive(defmt::Format))]
} else if #[cfg(feature = "psram-4m")] { pub struct PsramConfig {
const PSRAM_SIZE: u32 = 4; /// PSRAM size
} else if #[cfg(feature = "psram-8m")] { pub size: PsramSize,
const PSRAM_SIZE: u32 = 8; /// Cache speed
} else { pub cache_speed: PsramCacheSpeed,
const PSRAM_SIZE: u32 = 0;
}
} }
/// The total size of the PSRAM (Pseudo-SRAM) in bytes.
pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024;
/// Initializes the PSRAM memory on supported devices. /// 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<P = crate::peripherals::PSRAM>) { /// Returns the start of the mapped memory and the size
utils::psram_init(); pub fn init_psram(_peripheral: crate::peripherals::PSRAM, config: PsramConfig) -> (*mut u8, usize) {
utils::s_mapping(PSRAM_VADDR_START as u32, PSRAM_BYTES as u32); 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 {
utils::s_mapping(EXTMEM_ORIGIN as u32, config.size.get() as u32);
}
crate::soc::MAPPED_PSRAM.with(|mapped_psram| {
mapped_psram.memory_range = EXTMEM_ORIGIN..EXTMEM_ORIGIN + config.size.get();
});
(EXTMEM_ORIGIN as *mut u8, config.size.get())
} }
#[cfg(any(feature = "psram-2m", feature = "psram-4m", feature = "psram-8m"))]
pub(crate) mod utils { pub(crate) mod utils {
use core::ptr::addr_of_mut; use core::ptr::addr_of_mut;
use procmacros::ram; use procmacros::ram;
use super::*;
#[ram]
pub(crate) fn s_mapping(v_start: u32, size: u32) { pub(crate) fn s_mapping(v_start: u32, size: u32) {
// Enable external RAM in MMU // Enable external RAM in MMU
cache_sram_mmu_set(0, 0, v_start, 0, 32, size / 1024 / 32); 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 // we can use the ROM version of this: it works well enough and keeps the size
// of the binary down. // of the binary down.
#[ram]
fn cache_sram_mmu_set( fn cache_sram_mmu_set(
cpu_no: u32, cpu_no: u32,
pid: u32, pid: u32,
@ -198,15 +244,6 @@ pub(crate) mod utils {
(((spi_config) >> shift) & mask) as u8 (((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)] #[derive(PartialEq, Eq, Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct PsramIo { struct PsramIo {
@ -228,13 +265,13 @@ pub(crate) mod utils {
} }
#[repr(C)] #[repr(C)]
struct EspRomSpiflashChip { pub(super) struct EspRomSpiflashChip {
device_id: u32, pub device_id: u32,
chip_size: u32, // chip size in bytes pub chip_size: u32, // chip size in bytes
block_size: u32, pub block_size: u32,
sector_size: u32, pub sector_size: u32,
page_size: u32, pub page_size: u32,
status_mask: u32, pub status_mask: u32,
} }
extern "C" { extern "C" {
@ -253,7 +290,7 @@ pub(crate) mod utils {
static mut g_rom_spiflash_dummy_len_plus: u8; 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( fn cache_sram_mmu_set_rom(
cpu_no: u32, cpu_no: u32,
@ -265,10 +302,11 @@ pub(crate) mod utils {
) -> i32; ) -> i32;
} }
pub(crate) fn psram_init() { #[ram]
pub(crate) fn psram_init(config: &PsramConfig) {
let chip = crate::efuse::Efuse::get_chip_type(); 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 mut psram_io = PsramIo::default();
let clk_mode; let clk_mode;
@ -416,6 +454,7 @@ pub(crate) mod utils {
} }
let extra_dummy = psram_gpio_config(&psram_io, mode); let extra_dummy = psram_gpio_config(&psram_io, mode);
info!("extra dummy = {}", extra_dummy);
// psram_is_32mbit_ver0 would need special handling here // psram_is_32mbit_ver0 would need special handling here
@ -615,6 +654,7 @@ pub(crate) mod utils {
} }
// spi param init for psram // spi param init for psram
#[ram]
fn psram_spi_init( fn psram_spi_init(
// psram_spi_num_t spi_num = PSRAM_SPI_1, // psram_spi_num_t spi_num = PSRAM_SPI_1,
mode: PsramCacheSpeed, mode: PsramCacheSpeed,
@ -748,7 +788,7 @@ pub(crate) mod utils {
ps_cmd.rx_data_bit_len = 0; ps_cmd.rx_data_bit_len = 0;
ps_cmd.dummy_bit_len = 0; ps_cmd.dummy_bit_len = 0;
let (backup_usr, backup_usr1, backup_usr2) = psram_cmd_config_spi1(&ps_cmd); 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); 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 // start sending cmd/addr and optionally, receiving data
#[ram] #[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 { unsafe {
let spi = &*crate::peripherals::SPI1::PTR; let spi = &*crate::peripherals::SPI1::PTR;
// get cs1 // get cs1
@ -942,8 +986,8 @@ pub(crate) mod utils {
if !p_rx_data.is_null() { if !p_rx_data.is_null() {
// Read data out // Read data out
loop { for i in 0..rx_data_len_words {
p_rx_data.write_volatile(spi.w(0).read().bits()); 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; let flash_id: u32 = g_rom_flashchip.device_id;
info!("Flash-ID = {}", flash_id); info!("Flash-ID = {}", flash_id);
info!("Flash size = {}", g_rom_flashchip.chip_size);
if flash_id == FLASH_ID_GD25LQ32C { if flash_id == FLASH_ID_GD25LQ32C {
// Set drive ability for 1.8v flash in 80Mhz. // Set drive ability for 1.8v flash in 80Mhz.

View File

@ -16,7 +16,7 @@ use crate::rtc_cntl::SocResetReason;
pub mod efuse; pub mod efuse;
pub mod gpio; pub mod gpio;
pub mod peripherals; pub mod peripherals;
#[cfg(psram)] #[cfg(feature = "quad-psram")]
pub mod psram; pub mod psram;
pub mod radio_clocks; pub mod radio_clocks;
pub mod trng; pub mod trng;

View File

@ -9,42 +9,42 @@
//! present on the `ESP32-S2` chip. `PSRAM` provides additional external memory //! present on the `ESP32-S2` chip. `PSRAM` provides additional external memory
//! to supplement the internal memory of the `ESP32-S2`, allowing for increased //! to supplement the internal memory of the `ESP32-S2`, allowing for increased
//! storage capacity and improved performance in certain applications. //! 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 use crate::soc::psram_common::*;
pub fn psram_vaddr_start() -> usize {
PSRAM_VADDR_START 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,
} }
cfg_if::cfg_if! { /// PSRAM configuration
if #[cfg(feature = "psram-2m")] { #[derive(Copy, Clone, Debug, Default)]
const PSRAM_SIZE: u32 = 2; #[cfg_attr(feature = "defmt", derive(defmt::Format))]
} else if #[cfg(feature = "psram-4m")] { pub struct PsramConfig {
const PSRAM_SIZE: u32 = 4; /// PSRAM size
} else if #[cfg(feature = "psram-8m")] { pub size: PsramSize,
const PSRAM_SIZE: u32 = 8; /// Cache Speed
} else { pub speed: PsramCacheSpeed,
const PSRAM_SIZE: u32 = 0;
}
} }
/// 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;
/// Initialize PSRAM to be used for data. /// 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] #[procmacros::ram]
pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral<P = crate::peripherals::PSRAM>) { pub fn init_psram(_peripheral: crate::peripherals::PSRAM, config: PsramConfig) -> (*mut u8, usize) {
let mut config = config;
utils::psram_init(&mut config);
#[allow(unused)] #[allow(unused)]
enum CacheLayout { enum CacheLayout {
Invalid = 0, Invalid = 0,
@ -120,10 +120,10 @@ pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral<P = crate::per
if cache_dbus_mmu_set( if cache_dbus_mmu_set(
MMU_ACCESS_SPIRAM, MMU_ACCESS_SPIRAM,
PSRAM_VADDR, EXTMEM_ORIGIN as u32,
START_PAGE << 16, START_PAGE << 16,
64, 64,
PSRAM_SIZE * 1024 / 64, // number of pages to map config.size.get() as u32 / 1024 / 64, // number of pages to map
0, 0,
) != 0 ) != 0
{ {
@ -139,31 +139,94 @@ pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral<P = crate::per
.pro_dcache_mask_bus2() .pro_dcache_mask_bus2()
.clear_bit() .clear_bit()
}); });
utils::psram_init();
} }
crate::soc::MAPPED_PSRAM.with(|mapped_psram| {
mapped_psram.memory_range = EXTMEM_ORIGIN..EXTMEM_ORIGIN + config.size.get();
});
(EXTMEM_ORIGIN as *mut u8, config.size.get())
} }
#[cfg(any(feature = "psram-2m", feature = "psram-4m", feature = "psram-8m"))]
pub(crate) mod utils { pub(crate) mod utils {
use super::*;
const PSRAM_RESET_EN: u16 = 0x66;
const PSRAM_RESET: u16 = 0x99;
const PSRAM_DEVICE_ID: u16 = 0x9F;
const CS_PSRAM_SEL: u8 = 1 << 1;
/// PS-RAM addressing mode
#[derive(PartialEq, Eq, Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(unused)]
pub enum PsramVaddrMode {
/// App and pro CPU use their own flash cache for external RAM access
#[default]
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,
}
// Function initializes the PSRAM by configuring GPIO pins, resetting the PSRAM, // Function initializes the PSRAM by configuring GPIO pins, resetting the PSRAM,
// and enabling Quad I/O (QIO) mode. It also calls the psram_cache_init // and enabling Quad I/O (QIO) mode. It also calls the psram_cache_init
// function to configure cache parameters and read/write commands. // function to configure cache parameters and read/write commands.
pub(crate) fn psram_init() { pub(crate) fn psram_init(config: &mut PsramConfig) {
psram_gpio_config(); psram_gpio_config();
if config.size.is_auto() {
// read chip id
let mut dev_id = 0u32;
psram_exec_cmd(
CommandMode::PsramCmdSpi,
PSRAM_DEVICE_ID,
8, // command and command bit len
0,
24, // address and address bit len
0, // dummy bit len
core::ptr::null(),
0, // tx data and tx bit len
&mut dev_id as *mut _ as *mut u8,
24, // rx data and rx bit len
CS_PSRAM_SEL, // cs bit mask
false,
);
info!("chip id = {:x}", dev_id);
const PSRAM_ID_EID_S: u32 = 16;
const PSRAM_ID_EID_M: u32 = 0xff;
const PSRAM_EID_SIZE_M: u32 = 0x07;
const PSRAM_EID_SIZE_S: u32 = 5;
let size_id = (((dev_id) >> 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_reset_mode();
psram_enable_qio_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 // send reset command to psram, in spi mode
fn psram_reset_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( psram_exec_cmd(
CommandMode::PsramCmdSpi, CommandMode::PsramCmdSpi,
PSRAM_RESET_EN, PSRAM_RESET_EN,
@ -224,6 +287,7 @@ pub(crate) mod utils {
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[inline(always)]
fn psram_exec_cmd( fn psram_exec_cmd(
mode: CommandMode, mode: CommandMode,
cmd: u16, cmd: u16,
@ -265,7 +329,7 @@ pub(crate) mod utils {
_psram_exec_cmd( _psram_exec_cmd(
cmd, cmd,
cmd_bit_len, cmd_bit_len,
addr, &addr,
addr_bit_len, addr_bit_len,
dummy_bits, dummy_bits,
mosi_data, mosi_data,
@ -289,10 +353,11 @@ pub(crate) mod utils {
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
#[inline(always)]
fn _psram_exec_cmd( fn _psram_exec_cmd(
cmd: u16, cmd: u16,
cmd_bit_len: u16, cmd_bit_len: u16,
addr: u32, addr: *const u32,
addr_bit_len: u32, addr_bit_len: u32,
dummy_bits: u32, dummy_bits: u32,
mosi_data: *const u8, mosi_data: *const u8,
@ -323,7 +388,7 @@ pub(crate) mod utils {
let conf = esp_rom_spi_cmd_t { let conf = esp_rom_spi_cmd_t {
cmd, cmd,
cmd_bit_len, cmd_bit_len,
addr: addr as *const u32, addr,
addr_bit_len, addr_bit_len,
tx_data: mosi_data as *const u32, tx_data: mosi_data as *const u32,
tx_data_bit_len: mosi_bit_len, 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_20M: u32 = 0;
const PSRAM_IO_MATRIX_DUMMY_40M: u32 = 0; const PSRAM_IO_MATRIX_DUMMY_40M: u32 = 0;
const PSRAM_IO_MATRIX_DUMMY_80M: u32 = 0; const PSRAM_IO_MATRIX_DUMMY_80M: u32 = 0;

View File

@ -17,7 +17,7 @@ pub mod cpu_control;
pub mod efuse; pub mod efuse;
pub mod gpio; pub mod gpio;
pub mod peripherals; pub mod peripherals;
#[cfg(psram)] #[cfg(any(feature = "quad-psram", feature = "octal-psram"))]
pub mod psram; pub mod psram;
pub mod radio_clocks; pub mod radio_clocks;
pub mod trng; pub mod trng;

View File

@ -10,55 +10,70 @@
//! to supplement the internal memory of the `ESP32-S3`, allowing for increased //! to supplement the internal memory of the `ESP32-S3`, allowing for increased
//! storage capacity and improved performance in certain applications. //! storage capacity and improved performance in certain applications.
//! //!
//! The `PSRAM` module is accessed through a virtual address, defined as //! The mapped start address for PSRAM depends on the amount of mapped flash
//! `PSRAM_VADDR`. The starting virtual address for the PSRAM module is //! memory.
//! 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; pub use crate::soc::psram_common::*;
/// Returns the start address of the PSRAM virtual address space. const EXTMEM_ORIGIN: u32 = 0x3C000000;
pub fn psram_vaddr_start() -> usize {
unsafe { PSRAM_VADDR as usize } /// 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,
} }
cfg_if::cfg_if! { /// Frequency of PSRAM memory
if #[cfg(feature = "psram-2m")] { #[derive(Copy, Clone, Debug, Default)]
const PSRAM_SIZE: u32 = 2; #[cfg_attr(feature = "defmt", derive(defmt::Format))]
} else if #[cfg(feature = "psram-4m")] { #[allow(missing_docs)]
const PSRAM_SIZE: u32 = 4; pub enum SpiRamFreq {
} else if #[cfg(feature = "psram-8m")] { #[default]
const PSRAM_SIZE: u32 = 8; Freq40m = 40,
} else if #[cfg(feature = "opsram-2m")] { Freq80m = 80,
const PSRAM_SIZE: u32 = 2; Freq120m = 120,
} 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;
}
} }
/// The total size of the PSRAM in bytes, calculated from the PSRAM size /// Core timing configuration
/// constant. #[derive(Copy, Clone, Debug, Default)]
pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[allow(missing_docs)]
pub enum SpiTimingConfigCoreClock {
#[default]
SpiTimingConfigCoreClock80m = 80,
SpiTimingConfigCoreClock120m = 120,
SpiTimingConfigCoreClock160m = 160,
SpiTimingConfigCoreClock240m = 240,
}
/// 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. /// Initialize PSRAM to be used for data.
#[cfg(any( ///
feature = "psram-2m", /// Returns the start of the mapped memory and the size
feature = "psram-4m",
feature = "psram-8m",
feature = "opsram-2m",
feature = "opsram-4m",
feature = "opsram-8m",
feature = "opsram-16m"
))]
#[procmacros::ram] #[procmacros::ram]
pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral<P = crate::peripherals::PSRAM>) { 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_INSTRUCTION_CACHE_SIZE: u32 = 0x4000;
const CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS: u8 = 8; const CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS: u8 = 8;
const CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE: u8 = 32; const CONFIG_ESP32S3_INSTRUCTION_CACHE_LINE_SIZE: u8 = 32;
@ -102,7 +117,8 @@ pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral<P = crate::per
fixed: u32, fixed: u32,
) -> i32; ) -> i32;
} }
unsafe {
let start = unsafe {
const MMU_PAGE_SIZE: u32 = 0x10000; const MMU_PAGE_SIZE: u32 = 0x10000;
const ICACHE_MMU_SIZE: usize = 0x800; const ICACHE_MMU_SIZE: usize = 0x800;
const FLASH_MMU_TABLE_SIZE: usize = ICACHE_MMU_SIZE / core::mem::size_of::<u32>(); const FLASH_MMU_TABLE_SIZE: usize = ICACHE_MMU_SIZE / core::mem::size_of::<u32>();
@ -110,7 +126,7 @@ pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral<P = crate::per
const DR_REG_MMU_TABLE: u32 = 0x600C5000; const DR_REG_MMU_TABLE: u32 = 0x600C5000;
// calculate the PSRAM start address to map // calculate the PSRAM start address to map
let mut start = PSRAM_VADDR; let mut start = EXTMEM_ORIGIN;
let mmu_table_ptr = DR_REG_MMU_TABLE as *const u32; let mmu_table_ptr = DR_REG_MMU_TABLE as *const u32;
for i in 0..FLASH_MMU_TABLE_SIZE { for i in 0..FLASH_MMU_TABLE_SIZE {
if mmu_table_ptr.add(i).read_volatile() != MMU_INVALID { if mmu_table_ptr.add(i).read_volatile() != MMU_INVALID {
@ -120,7 +136,6 @@ pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral<P = crate::per
} }
} }
debug!("PSRAM start address = {:x}", start); debug!("PSRAM start address = {:x}", start);
PSRAM_VADDR = start;
// Configure the mode of instruction cache : cache size, cache line size. // Configure the mode of instruction cache : cache size, cache line size.
rom_config_instruction_cache_mode( rom_config_instruction_cache_mode(
@ -141,10 +156,10 @@ pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral<P = crate::per
if cache_dbus_mmu_set( if cache_dbus_mmu_set(
MMU_ACCESS_SPIRAM, MMU_ACCESS_SPIRAM,
PSRAM_VADDR, start,
START_PAGE << 16, START_PAGE << 16,
64, 64,
PSRAM_SIZE * 1024 / 64, // number of pages to map config.size.get() as u32 / 1024 / 64, // number of pages to map
0, 0,
) != 0 ) != 0
{ {
@ -160,70 +175,74 @@ pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral<P = crate::per
}); });
Cache_Resume_DCache(0); Cache_Resume_DCache(0);
}
utils::psram_init(); start
};
crate::soc::MAPPED_PSRAM.with(|mapped_psram| {
mapped_psram.memory_range = start as usize..start as usize + config.size.get();
});
(start as *mut u8, config.size.get())
} }
#[cfg(any(feature = "psram-2m", feature = "psram-4m", feature = "psram-8m"))] #[cfg(feature = "quad-psram")]
pub(crate) mod utils { pub(crate) mod utils {
use procmacros::ram; use procmacros::ram;
// these should probably be configurable, relates to https://github.com/esp-rs/esp-hal/issues/42 use super::*;
// 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! { const PSRAM_RESET_EN: u16 = 0x66;
if #[cfg(feature = "psram-80mhz")] { const PSRAM_RESET: u16 = 0x99;
const SPIRAM_SPEED: SpiRamFreq = SpiRamFreq::Freq80m; const PSRAM_DEVICE_ID: u16 = 0x9F;
} else { const CS_PSRAM_SEL: u8 = 1 << 1;
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,
}
}
}
#[ram] #[ram]
pub(crate) fn psram_init() { pub(crate) fn psram_init(config: &mut super::PsramConfig) {
psram_gpio_config(); psram_gpio_config();
psram_set_cs_timing(); psram_set_cs_timing();
if config.size.is_auto() {
// read chip id
let mut dev_id = 0u32;
psram_exec_cmd(
CommandMode::PsramCmdSpi,
PSRAM_DEVICE_ID,
8, // command and command bit len
0,
24, // address and address bit len
0, // dummy bit len
core::ptr::null(),
0, // tx data and tx bit len
&mut dev_id as *mut _ as *mut u8,
24, // rx data and rx bit len
CS_PSRAM_SEL, // cs bit mask
false,
);
info!("chip id = {:x}", dev_id);
const PSRAM_ID_EID_S: u32 = 16;
const PSRAM_ID_EID_M: u32 = 0xff;
const PSRAM_EID_SIZE_M: u32 = 0x07;
const PSRAM_EID_SIZE_S: u32 = 5;
let size_id = (((dev_id) >> 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 => 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 // SPI1: send psram reset command
psram_reset_mode_spi1(); psram_reset_mode_spi1();
// SPI1: send QPI enable command // SPI1: send QPI enable command
@ -237,7 +256,7 @@ pub(crate) mod utils {
config_psram_spi_phases(); config_psram_spi_phases();
// Back to the high speed mode. Flash/PSRAM clocks are set to the clock that // Back to the high speed mode. Flash/PSRAM clocks are set to the clock that
// user selected. SPI0/1 registers are all set correctly // 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; 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` /// This function should always be called after `mspi_timing_flash_tuning`
/// or `calculate_best_flash_tuning_config` /// or `calculate_best_flash_tuning_config`
#[ram] #[ram]
fn mspi_timing_enter_high_speed_mode(control_spi1: bool) { fn mspi_timing_enter_high_speed_mode(control_spi1: bool, config: &PsramConfig) {
let core_clock: SpiTimingConfigCoreClock = get_mspi_core_clock(); let core_clock: SpiTimingConfigCoreClock = get_mspi_core_clock(config);
let flash_div: u32 = get_flash_clock_divider(); let flash_div: u32 = get_flash_clock_divider(config);
let psram_div: u32 = get_psram_clock_divider(); let psram_div: u32 = get_psram_clock_divider(config);
info!( info!(
"PSRAM core_clock {:?}, flash_div = {}, psram_div = {}", "PSRAM core_clock {:?}, flash_div = {}, psram_div = {}",
@ -447,36 +466,23 @@ pub(crate) mod utils {
} }
#[ram] #[ram]
fn get_mspi_core_clock() -> SpiTimingConfigCoreClock { fn get_mspi_core_clock(config: &PsramConfig) -> SpiTimingConfigCoreClock {
SPI_TIMING_CORE_CLOCK config.core_clock
} }
#[ram] #[ram]
fn get_flash_clock_divider() -> u32 { fn get_flash_clock_divider(config: &PsramConfig) -> u32 {
match FLASH_FREQ { config.core_clock as u32 / config.flash_frequency as u32
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,
}
} }
#[ram] #[ram]
fn get_psram_clock_divider() -> u32 { fn get_psram_clock_divider(config: &PsramConfig) -> u32 {
match SPIRAM_SPEED { config.core_clock as u32 / config.ram_frequency as u32
SpiRamFreq::Freq40m => SPI_TIMING_CORE_CLOCK.mhz() / 40,
SpiRamFreq::Freq80m => SPI_TIMING_CORE_CLOCK.mhz() / 80,
SpiRamFreq::Freq120m => SPI_TIMING_CORE_CLOCK.mhz() / 120,
}
} }
// send reset command to psram, in spi mode // send reset command to psram, in spi mode
#[ram] #[ram]
fn psram_reset_mode_spi1() { 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( psram_exec_cmd(
CommandMode::PsramCmdSpi, CommandMode::PsramCmdSpi,
PSRAM_RESET_EN, PSRAM_RESET_EN,
@ -617,7 +623,7 @@ pub(crate) mod utils {
let conf = esp_rom_spi_cmd_t { let conf = esp_rom_spi_cmd_t {
cmd, cmd,
cmd_bit_len, cmd_bit_len,
addr: addr as *const u32, addr: &addr,
addr_bit_len, addr_bit_len,
tx_data: mosi_data as *const u32, tx_data: mosi_data as *const u32,
tx_data_bit_len: mosi_bit_len, tx_data_bit_len: mosi_bit_len,
@ -733,64 +739,11 @@ pub(crate) mod utils {
} }
} }
#[cfg(any( #[cfg(feature = "octal-psram")]
feature = "opsram-2m",
feature = "opsram-4m",
feature = "opsram-8m",
feature = "opsram-16m"
))]
pub(crate) mod utils { pub(crate) mod utils {
use procmacros::ram; use procmacros::ram;
// these should probably be configurable, relates to https://github.com/esp-rs/esp-hal/issues/42 use super::*;
// 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,
}
}
}
const OPI_PSRAM_SYNC_READ: u16 = 0x0000; const OPI_PSRAM_SYNC_READ: u16 = 0x0000;
const OPI_PSRAM_SYNC_WRITE: u16 = 0x8080; const OPI_PSRAM_SYNC_WRITE: u16 = 0x8080;
@ -1077,7 +1030,7 @@ pub(crate) mod utils {
} }
#[ram] #[ram]
pub(crate) fn psram_init() { pub(crate) fn psram_init(config: &mut PsramConfig) {
mspi_pin_init(); mspi_pin_init();
init_psram_pins(); init_psram_pins();
set_psram_cs_timing(); set_psram_cs_timing();
@ -1124,6 +1077,10 @@ pub(crate) mod utils {
}; };
info!("{} bytes of PSRAM", psram_size); 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 // Do PSRAM timing tuning, we use SPI1 to do the tuning, and set the
// SPI0 PSRAM timing related registers accordingly // SPI0 PSRAM timing related registers accordingly
// this is unsupported for now // 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 // Back to the high speed mode. Flash/PSRAM clocks are set to the clock
// that user selected. SPI0/1 registers are all set correctly // 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 // Tuning may change SPI1 regs, whereas legacy spi_flash APIs rely on
// these regs. This function is to restore SPI1 init state. // 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 // This function should always be called after `spi_timing_flash_tuning` or
// `calculate_best_flash_tuning_config` // `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(); // spi_timing_config_core_clock_t core_clock = get_mspi_core_clock();
let core_clock = SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m; let core_clock = SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m;
let flash_div: u32 = get_flash_clock_divider(); let flash_div: u32 = get_flash_clock_divider(config);
let psram_div: u32 = get_psram_clock_divider(); let psram_div: u32 = get_psram_clock_divider(config);
// Set SPI01 core clock // 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. 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] #[ram]
fn get_flash_clock_divider() -> u32 { fn get_flash_clock_divider(config: &PsramConfig) -> u32 {
match FLASH_FREQ { config.core_clock as u32 / config.flash_frequency as u32
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,
}
} }
#[ram] #[ram]
fn get_psram_clock_divider() -> u32 { fn get_psram_clock_divider(config: &PsramConfig) -> u32 {
match SPIRAM_SPEED { config.core_clock as u32 / config.ram_frequency as u32
SpiRamFreq::Freq40m => SPI_TIMING_CORE_CLOCK.mhz() / 40,
SpiRamFreq::Freq80m => SPI_TIMING_CORE_CLOCK.mhz() / 80,
SpiRamFreq::Freq120m => SPI_TIMING_CORE_CLOCK.mhz() / 120,
}
} }
} }

View File

@ -1,6 +1,8 @@
use portable_atomic::{AtomicU8, Ordering}; use portable_atomic::{AtomicU8, Ordering};
pub use self::implementation::*; pub use self::implementation::*;
#[cfg(psram)]
use crate::lock::Locked;
#[cfg_attr(esp32, path = "esp32/mod.rs")] #[cfg_attr(esp32, path = "esp32/mod.rs")]
#[cfg_attr(esp32c2, path = "esp32c2/mod.rs")] #[cfg_attr(esp32c2, path = "esp32c2/mod.rs")]
@ -13,6 +15,17 @@ mod implementation;
mod efuse_field; mod efuse_field;
#[cfg(any(feature = "quad-psram", feature = "octal-psram"))]
mod psram_common;
#[cfg(psram)]
static MAPPED_PSRAM: Locked<MappedPsram> = Locked::new(MappedPsram { memory_range: 0..0 });
#[cfg(psram)]
pub struct MappedPsram {
memory_range: core::ops::Range<usize>,
}
// Indicates the state of setting the mac address // Indicates the state of setting the mac address
// 0 -- unset // 0 -- unset
// 1 -- in the process of being set // 1 -- in the process of being set
@ -59,7 +72,7 @@ impl self::efuse::Efuse {
/// Get base mac address /// Get base mac address
/// ///
/// By default this reads the base mac address from eFuse, but it can be /// 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] { pub fn get_mac_address() -> [u8; 6] {
if MAC_OVERRIDE_STATE.load(Ordering::Relaxed) == 2 { if MAC_OVERRIDE_STATE.load(Ordering::Relaxed) == 2 {
unsafe { MAC_OVERRIDE } unsafe { MAC_OVERRIDE }
@ -85,9 +98,8 @@ pub(crate) fn is_slice_in_dram<T>(slice: &[T]) -> bool {
pub(crate) fn is_valid_psram_address(address: u32) -> bool { pub(crate) fn is_valid_psram_address(address: u32) -> bool {
#[cfg(psram)] #[cfg(psram)]
{ {
let start = crate::psram::psram_vaddr_start() as u32; let memory_range = MAPPED_PSRAM.with(|mapped_psram| mapped_psram.memory_range.clone());
let end = start + crate::psram::PSRAM_BYTES as u32; memory_range.contains(&(address as usize))
(start..=end).contains(&address)
} }
#[cfg(not(psram))] #[cfg(not(psram))]
false false

View File

@ -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)
}
}

View File

@ -68,6 +68,7 @@ symbols = [
"bt", "bt",
"wifi", "wifi",
"psram", "psram",
"octal_psram",
"ulp_riscv_core", "ulp_riscv_core",
"timg_timer1", "timg_timer1",
"very_large_intr_status", "very_large_intr_status",

View File

@ -73,9 +73,6 @@ esp-wifi = ["dep:esp-wifi"]
embassy = ["dep:esp-hal-embassy"] embassy = ["dep:esp-hal-embassy"]
embassy-generic-timers = ["embassy-time/generic-queue-8"] embassy-generic-timers = ["embassy-time/generic-queue-8"]
opsram-2m = ["esp-hal/opsram-2m"]
psram-2m = ["esp-hal/psram-2m"]
[profile.release] [profile.release]
codegen-units = 1 codegen-units = 1
debug = 2 debug = 2

View File

@ -1,6 +1,6 @@
//! Uses DMA to copy psram to internal memory. //! 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 //% CHIPS: esp32s3
#![no_std] #![no_std]
@ -42,16 +42,13 @@ macro_rules! dma_alloc_buffer {
}}; }};
} }
fn init_heap(psram: impl esp_hal::peripheral::Peripheral<P = esp_hal::peripherals::PSRAM>) { fn init_heap(psram: esp_hal::peripherals::PSRAM) {
esp_hal::psram::init_psram(psram); let (start, size) = esp_hal::psram::init_psram(psram, esp_hal::psram::PsramConfig::default());
info!( info!("init_heap: start: {:p}", start);
"init_heap: start: 0x{:0x}",
esp_hal::psram::psram_vaddr_start()
);
unsafe { unsafe {
esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new(
esp_hal::psram::psram_vaddr_start() as *mut u8, start,
esp_hal::psram::PSRAM_BYTES, size,
esp_alloc::MemoryCapability::External.into(), esp_alloc::MemoryCapability::External.into(),
)); ));
} }

View File

@ -3,7 +3,7 @@
//! You need an ESP32-S3 with at least 2 MB of PSRAM memory. //! You need an ESP32-S3 with at least 2 MB of PSRAM memory.
//% CHIPS: esp32s3 //% CHIPS: esp32s3
//% FEATURES: opsram-2m //% FEATURES: esp-hal/octal-psram
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -17,11 +17,11 @@ use esp_backtrace as _;
use esp_hal::{prelude::*, psram}; use esp_hal::{prelude::*, psram};
use esp_println::println; use esp_println::println;
fn init_psram_heap() { fn init_psram_heap(start: *mut u8, size: usize) {
unsafe { unsafe {
esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new(
psram::psram_vaddr_start() as *mut u8, start,
psram::PSRAM_BYTES, size,
esp_alloc::MemoryCapability::External.into(), esp_alloc::MemoryCapability::External.into(),
)); ));
} }
@ -32,10 +32,11 @@ compile_error!("PSRAM example must be built in release mode!");
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
esp_println::logger::init_logger_from_env();
let peripherals = esp_hal::init(esp_hal::Config::default()); let peripherals = esp_hal::init(esp_hal::Config::default());
psram::init_psram(peripherals.PSRAM); let (start, size) = psram::init_psram(peripherals.PSRAM, psram::PsramConfig::default());
init_psram_heap(); init_psram_heap(start, size);
println!("Going to access PSRAM"); println!("Going to access PSRAM");
let mut large_vec: Vec<u32> = Vec::with_capacity(500 * 1024 / 4); let mut large_vec: Vec<u32> = Vec::with_capacity(500 * 1024 / 4);

View File

@ -3,7 +3,7 @@
//! You need an ESP32, ESP32-S2 or ESP32-S3 with at least 2 MB of PSRAM memory. //! You need an ESP32, ESP32-S2 or ESP32-S3 with at least 2 MB of PSRAM memory.
//% CHIPS: esp32 esp32s2 esp32s3 //% CHIPS: esp32 esp32s2 esp32s3
//% FEATURES: psram-2m //% FEATURES: esp-hal/quad-psram
#![no_std] #![no_std]
#![no_main] #![no_main]
@ -17,11 +17,11 @@ use esp_backtrace as _;
use esp_hal::{prelude::*, psram}; use esp_hal::{prelude::*, psram};
use esp_println::println; use esp_println::println;
fn init_psram_heap() { fn init_psram_heap(start: *mut u8, size: usize) {
unsafe { unsafe {
esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new( esp_alloc::HEAP.add_region(esp_alloc::HeapRegion::new(
psram::psram_vaddr_start() as *mut u8, start,
psram::PSRAM_BYTES, size,
esp_alloc::MemoryCapability::External.into(), esp_alloc::MemoryCapability::External.into(),
)); ));
} }
@ -32,10 +32,11 @@ compile_error!("PSRAM example must be built in release mode!");
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
esp_println::logger::init_logger_from_env();
let peripherals = esp_hal::init(esp_hal::Config::default()); let peripherals = esp_hal::init(esp_hal::Config::default());
psram::init_psram(peripherals.PSRAM); let (start, size) = psram::init_psram(peripherals.PSRAM, psram::PsramConfig::default());
init_psram_heap(); init_psram_heap(start, size);
println!("Going to access PSRAM"); println!("Going to access PSRAM");
let mut large_vec = Vec::<u32>::with_capacity(500 * 1024 / 4); let mut large_vec = Vec::<u32>::with_capacity(500 * 1024 / 4);

View File

@ -122,8 +122,13 @@ pub fn build_documentation(
let mut features = vec![chip.to_string()]; let mut features = vec![chip.to_string()];
// future enhancement: see https://github.com/esp-rs/esp-hal/issues/2195
if matches!(package, Package::EspHal) { 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`: // Build up an array of command-line arguments to pass to `cargo`:

View File

@ -556,7 +556,7 @@ fn lint_packages(workspace: &Path, args: LintPackagesArgs) -> Result<()> {
} }
if device.contains("psram") { if device.contains("psram") {
// TODO this doesn't test octal psram as it would require a separate build // 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) { if matches!(chip, Chip::Esp32c6 | Chip::Esp32h2) {
features.push_str(",flip-link") features.push_str(",flip-link")