Improve PSRAM detection (#3554)

* Improve PSRAM detection

* CHANGELOG.md

* Just probe ESP32 PSRAM

* Read device ID workaround

* Exit QIO mode before trying to read the SPI RAM device id

* Last try

* Fix

* Workaround faulty SVD
This commit is contained in:
Björn Quentin 2025-06-02 15:44:47 +02:00 committed by GitHub
parent 6ab240d7ff
commit 00565c39ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 101 additions and 42 deletions

View File

@ -100,6 +100,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed I2C `Timeout` errors experienced during high CPU load (#3458, #3555)
- Fix a problem where reading/writing flash didn't work when using PSRAM on ESP32 (#3524)
- Fixed `esp_hal::time::Instant::duration_since_epoch` (#3582)
- Improve PSRAM size detection for the case when no PSRAM is present or unusable (#3554)
### Removed

View File

@ -85,6 +85,8 @@ pub(crate) fn init_psram(config: PsramConfig) {
utils::psram_init(&config);
if config.size.is_auto() {
const MAX_MEM_SIZE: usize = 4 * 1024 * 1024;
// Reading the device-id turned out to not work as expected (some bits flipped
// for unknown reason)
//
@ -92,33 +94,24 @@ pub(crate) fn init_psram(config: PsramConfig) {
// 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);
utils::s_mapping(EXTMEM_ORIGIN as u32, MAX_MEM_SIZE as u32);
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) {
for i in (1023..MAX_MEM_SIZE).step_by(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;
let mut last_correctly_read = 0;
for i in (1023..MAX_MEM_SIZE).step_by(1024) {
if ptr.add(i).read_volatile() == 0x7f {
last_correctly_read = i;
} else {
break;
}
}
if success {
4 * 1024 * 1024
} else {
2 * 1024 * 1024
}
last_correctly_read + 1
};
info!("Assuming {} bytes of PSRAM", guessed_size);

View File

@ -215,6 +215,8 @@ pub(crate) mod utils {
psram_gpio_config();
if config.size.is_auto() {
psram_disable_qio_mode();
// read chip id
let mut dev_id = 0u32;
psram_exec_cmd(
@ -233,21 +235,25 @@ pub(crate) mod utils {
);
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 = if dev_id != 0xffffff {
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;
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;
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,
match size_id {
PSRAM_EID_SIZE_64MBITS => 8 * 1024 * 1024,
PSRAM_EID_SIZE_32MBITS => 4 * 1024 * 1024,
_ => 2 * 1024 * 1024,
}
} else {
0
};
info!("size is {}", size);
@ -315,6 +321,27 @@ pub(crate) mod utils {
);
}
/// Exit QPI mode
fn psram_disable_qio_mode() {
const PSRAM_EXIT_QMODE: u16 = 0xF5;
const CS_PSRAM_SEL: u8 = 1 << 1;
psram_exec_cmd(
CommandMode::PsramCmdQpi,
PSRAM_EXIT_QMODE,
8, // command and command bit len
0,
0, // address and address bit len
0, // dummy bit len
core::ptr::null(),
0, // tx data and tx bit len
core::ptr::null_mut(),
0, // rx data and rx bit len
CS_PSRAM_SEL, // cs bit mask
false, // whether is program/erase operation
);
}
#[derive(PartialEq)]
#[allow(unused)]
enum CommandMode {
@ -451,7 +478,12 @@ pub(crate) mod utils {
match mode {
CommandMode::PsramCmdQpi => {
esp_rom_spi_set_op_mode(1, ESP_ROM_SPIFLASH_QIO_MODE);
SPI1::regs().ctrl().modify(|_, w| w.fcmd_quad().set_bit());
// this should be `fcmd_quad` - apparently our ESP32-S2 SVD isn't correct for
// SPI1 and SPI0. i.e. GP-SPI vs MEM-SPI
//
// TODO fix the SVD and change this back!
// (i.e. in our SVD FCMD_QUAD is bit 9 while it should be bit 8)
SPI1::regs().ctrl().modify(|_, w| w.fcmd_dual().set_bit());
}
CommandMode::PsramCmdSpi => {
esp_rom_spi_set_op_mode(1, ESP_ROM_SPIFLASH_SLOWRD_MODE);

View File

@ -251,6 +251,8 @@ pub(crate) mod utils {
psram_set_cs_timing();
if config.size.is_auto() {
psram_disable_qio_mode_spi1();
// read chip id
let mut dev_id = 0u32;
psram_exec_cmd(
@ -269,21 +271,25 @@ pub(crate) mod utils {
);
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 = if dev_id != 0xffffff {
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;
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;
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,
match size_id {
PSRAM_EID_SIZE_64MBITS => 64 / 8 * 1024 * 1024,
PSRAM_EID_SIZE_32MBITS => 32 / 8 * 1024 * 1024,
_ => 16 / 8 * 1024 * 1024,
}
} else {
0
};
info!("size is {}", size);
@ -709,6 +715,28 @@ pub(crate) mod utils {
}
}
/// Exit QPI mode
#[ram]
fn psram_disable_qio_mode_spi1() {
const PSRAM_EXIT_QMODE: u16 = 0xF5;
const CS_PSRAM_SEL: u8 = 1 << 1;
psram_exec_cmd(
CommandMode::PsramCmdQpi,
PSRAM_EXIT_QMODE,
8, // command and command bit len
0,
0, // address and address bit len
0, // dummy bit len
core::ptr::null(),
0, // tx data and tx bit len
core::ptr::null_mut(),
0, // rx data and rx bit len
CS_PSRAM_SEL, // cs bit mask
false, // whether is program/erase operation
);
}
/// Enter QPI mode
#[ram]
fn psram_enable_qio_mode_spi1() {

View File

@ -41,6 +41,11 @@ fn main() -> ! {
let peripherals = esp_hal::init(esp_hal::Config::default());
let (start, size) = psram::psram_raw_parts(&peripherals.PSRAM);
if size == 0 {
panic!("No PSRAM detected");
}
init_psram_heap(start, size);
println!("Going to access PSRAM");