diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ef0bf487..007ecb4d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,6 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add a `debug` feature to enable the PACs' `impl-register-debug` feature (#596) - Add initial support for `I2S` in ESP32-H2 (#597) - Fix rom::crc docs +- Add octal PSRAM support for ESP32-S3 (#610) ### Changed diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 40a85dfa9..5869b2884 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -82,6 +82,10 @@ psram_2m = [] psram_4m = [] psram_8m = [] +opsram_2m = [] +opsram_4m = [] +opsram_8m = [] + # Implement the `embedded-hal==1.0.0-alpha.x` traits eh1 = ["embedded-hal-1", "embedded-hal-nb", "embedded-can"] diff --git a/esp-hal-common/src/soc/esp32s3/psram.rs b/esp-hal-common/src/soc/esp32s3/psram.rs index c27db3e69..25305ad82 100644 --- a/esp-hal-common/src/soc/esp32s3/psram.rs +++ b/esp-hal-common/src/soc/esp32s3/psram.rs @@ -11,6 +11,12 @@ cfg_if::cfg_if! { 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 { const PSRAM_SIZE: u32 = 0; } @@ -21,7 +27,14 @@ pub const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; /// Initialize PSRAM to be used for data. /// /// Currently only QSPI is supported. -#[cfg(any(feature = "psram_2m", feature = "psram_4m", feature = "psram_8m"))] +#[cfg(any( + feature = "psram_2m", + feature = "psram_4m", + feature = "psram_8m", + feature = "opsram_2m", + feature = "opsram_4m", + feature = "opsram_8m" +))] pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral
) {
const CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE: u32 = 0x4000;
const CONFIG_ESP32S3_ICACHE_ASSOCIATED_WAYS: u8 = 8;
@@ -748,3 +761,1020 @@ pub(crate) mod utils {
}
}
}
+
+#[cfg(any(feature = "opsram_2m", feature = "opsram_4m", feature = "opsram_8m"))]
+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;
+ 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)]
+ 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_WRITE: u16 = 0x8080;
+ const OPI_PSRAM_REG_READ: u16 = 0x4040;
+ const OPI_PSRAM_REG_WRITE: u16 = 0xC0C0;
+ const OCT_PSRAM_RD_CMD_BITLEN: u8 = 16;
+ const OCT_PSRAM_WR_CMD_BITLEN: u8 = 16;
+ const OCT_PSRAM_ADDR_BITLEN: u8 = 32;
+ const OCT_PSRAM_RD_DUMMY_BITLEN: u8 = 2 * (10 - 1);
+ const OCT_PSRAM_WR_DUMMY_BITLEN: u8 = 2 * (5 - 1);
+ const OCT_PSRAM_CS1_IO: u8 = SPI_CS1_GPIO_NUM;
+ const OCT_PSRAM_VENDOR_ID: u8 = 0xD;
+
+ const OCT_PSRAM_CS_SETUP_TIME: u8 = 3;
+ const OCT_PSRAM_CS_HOLD_TIME: u8 = 3;
+ const OCT_PSRAM_CS_HOLD_DELAY: u8 = 2;
+
+ const PSRAM_SIZE_2MB: usize = 2 * 1024 * 1024;
+ const PSRAM_SIZE_4MB: usize = 4 * 1024 * 1024;
+ const PSRAM_SIZE_8MB: usize = 8 * 1024 * 1024;
+ const PSRAM_SIZE_16MB: usize = 16 * 1024 * 1024;
+ const PSRAM_SIZE_32MB: usize = 32 * 1024 * 1024;
+
+ const SPI_CS1_GPIO_NUM: u8 = 26;
+ const FUNC_SPICS1_SPICS1: u8 = 0;
+
+ const SPI1_MEM_DDR_REG: u32 = DR_REG_SPI1_BASE + 0xe0;
+ const SPI_MEM_SPI_FMEM_VAR_DUMMY: u32 = 1 << 1;
+
+ const DR_REG_SPI0_BASE: u32 = 0x60003000;
+ const SPI0_MEM_SPI_SMEM_AC_REG: u32 = DR_REG_SPI0_BASE + 0xDC;
+ const SPI_MEM_SPI_SMEM_CS_HOLD_TIME_V: u32 = 0x1F;
+ const SPI_MEM_SPI_SMEM_CS_HOLD_TIME_S: u32 = 7;
+ const SPI_MEM_SPI_SMEM_CS_SETUP_TIME_V: u32 = 0x1F;
+ const SPI_MEM_SPI_SMEM_CS_SETUP_TIME_S: u32 = 2;
+ const SPI_MEM_SPI_SMEM_CS_HOLD_M: u32 = 1 << 1;
+ const SPI_MEM_SPI_SMEM_CS_SETUP_M: u32 = 1 << 0;
+ const SPI0_MEM_CACHE_SCTRL_REG: u32 = DR_REG_SPI0_BASE + 0x40;
+ const SPI0_MEM_SRAM_DWR_CMD_REG: u32 = DR_REG_SPI0_BASE + 0x4C;
+ const SPI0_MEM_SRAM_DRD_CMD_REG: u32 = DR_REG_SPI0_BASE + 0x48;
+ const SPI_MEM_CACHE_SRAM_USR_RCMD_M: u32 = 1 << 5;
+ const SPI_MEM_CACHE_SRAM_USR_WCMD_M: u32 = 1 << 20;
+ const SPI_MEM_CACHE_SRAM_USR_WR_CMD_BITLEN: u32 = 0x0000000F;
+ const SPI_MEM_CACHE_SRAM_USR_WR_CMD_BITLEN_S: u32 = 28;
+ const SPI_MEM_CACHE_SRAM_USR_WR_CMD_VALUE: u32 = 0x0000FFFF;
+ const SPI_MEM_CACHE_SRAM_USR_WR_CMD_VALUE_S: u32 = 0;
+ const SPI_MEM_CACHE_SRAM_USR_RD_CMD_BITLEN_V: u32 = 0xF;
+ const SPI_MEM_CACHE_SRAM_USR_RD_CMD_BITLEN_S: u32 = 28;
+ const SPI_MEM_CACHE_SRAM_USR_RD_CMD_VALUE_V: u32 = 0xFFFF;
+ const SPI_MEM_CACHE_SRAM_USR_RD_CMD_VALUE_S: u32 = 0;
+ const SPI_MEM_SRAM_ADDR_BITLEN_V: u32 = 0x3F;
+ const SPI_MEM_SRAM_ADDR_BITLEN_S: u32 = 14;
+ const SPI_MEM_USR_RD_SRAM_DUMMY_M: u32 = 1 << 4;
+ const SPI_MEM_SRAM_RDUMMY_CYCLELEN_V: u32 = 0x3F;
+ const SPI_MEM_SRAM_RDUMMY_CYCLELEN_S: u32 = 6;
+ const SPI0_MEM_CORE_CLK_SEL_REG: u32 = DR_REG_SPI0_BASE + 0xEC;
+ const SPI_MEM_CORE_CLK_SEL: u32 = 0x00000003;
+ const DR_REG_SPI1_BASE: u32 = 0x60002000;
+ const SPI0_MEM_DATE_REG: u32 = DR_REG_SPI0_BASE + 0x3FC;
+ const SPI0_MEM_CLOCK_REG: u32 = DR_REG_SPI0_BASE + 0x14;
+ const SPI1_MEM_CLOCK_REG: u32 = DR_REG_SPI1_BASE + 0x14;
+ const SPI0_MEM_SRAM_CLK_REG: u32 = DR_REG_SPI0_BASE + 0x50;
+ const SPI_MEM_CLK_EQU_SYSCLK: u32 = 1 << 31;
+ const SPI_MEM_SCLK_EQU_SYSCLK: u32 = 1 << 31;
+ const SPI_MEM_CLKCNT_N_S: u32 = 16;
+ const SPI_MEM_SCLKCNT_N_S: u32 = 16;
+ const SPI_MEM_CLKCNT_H_S: u32 = 8;
+ const SPI_MEM_SCLKCNT_H_S: u32 = 8;
+ const SPI_MEM_CLKCNT_L_S: u32 = 0;
+ const SPI_MEM_SCLKCNT_L_S: u32 = 0;
+ const SPI_MEM_CACHE_USR_SCMD_4BYTE_M: u32 = 1 << 0;
+ const SPI_MEM_USR_WR_SRAM_DUMMY_M: u32 = 1 << 3;
+ const SPI0_MEM_SPI_SMEM_DDR_REG: u32 = DR_REG_SPI0_BASE + 0xE4;
+ const SPI0_MEM_SRAM_CMD_REG: u32 = DR_REG_SPI0_BASE + 0x44;
+ const SPI_MEM_SPI_SMEM_VAR_DUMMY_M: u32 = 1 << 1;
+ const SPI_MEM_SRAM_WDUMMY_CYCLELEN_V: u32 = 0x3F;
+ const SPI_MEM_SRAM_WDUMMY_CYCLELEN_S: u32 = 22;
+ const SPI_MEM_SPI_SMEM_DDR_WDAT_SWP_M: u32 = 1 << 3;
+ const SPI_MEM_SPI_SMEM_DDR_RDAT_SWP_M: u32 = 1 << 2;
+ const SPI_MEM_SPI_SMEM_DDR_EN_M: u32 = 1 << 0;
+ const SPI_MEM_SDUMMY_OUT_M: u32 = 1 << 22;
+ const SPI_MEM_SCMD_OCT_M: u32 = 1 << 21;
+ const SPI_MEM_SADDR_OCT_M: u32 = 1 << 20;
+ const SPI_MEM_SDOUT_OCT_M: u32 = 1 << 19;
+ const SPI_MEM_SDIN_OCT_M: u32 = 1 << 18;
+ const SPI_MEM_SRAM_OCT_M: u32 = 1 << 21;
+ const SPI_MEM_SPI_SMEM_CS_HOLD_DELAY_V: u32 = 0x3F;
+ const SPI_MEM_SPI_SMEM_CS_HOLD_DELAY_S: u32 = 25;
+ const SPI_MEM_SPI_SMEM_SPICLK_FUN_DRV: u32 = 0x00000003;
+ const SPI_MEM_SPI_SMEM_SPICLK_FUN_DRV_V: u32 = 0x3;
+ const SPI_MEM_SPI_SMEM_SPICLK_FUN_DRV_S: u32 = 0;
+ const ESP_ROM_SPIFLASH_OPI_DTR_MODE: u8 = 7;
+
+ extern "C" {
+ // @brief To execute a flash operation command
+ // @param spi_num spi port
+ // @param mode Flash Read Mode
+ // @param cmd data to send in command field
+ // @param cmd_bit_len bit length of command field
+ // @param addr data to send in address field
+ // @param addr_bit_len bit length of address field
+ // @param dummy_bits bit length of dummy field
+ // @param mosi_data data buffer to be sent in mosi field
+ // @param mosi_bit_len bit length of data buffer to be sent in mosi field
+ // @param miso_data data buffer to accept data in miso field
+ // @param miso_bit_len bit length of data buffer to accept data in miso field
+ // @param cs_mark decide which cs pin to use. 0: cs0, 1: cs1
+ // @param is_write_erase_operation to indicate whether this a write or erase
+ // flash operation
+ fn esp_rom_opiflash_exec_cmd(
+ spi_num: u32,
+ mode: u8,
+ cmd: u32,
+ cmd_bit_len: u32,
+ addr: u32,
+ addr_bit_len: u32,
+ dummy_bits: u32,
+ mosi_data: *const u8,
+ mosi_bit_len: u32,
+ miso_data: *mut u8,
+ miso_bit_len: u32,
+ cs_mask: u32,
+ is_write_erase_operation: bool,
+ );
+
+ // @brief Set data swap mode in DTR(DDR) mode
+ // @param spi_num spi port
+ // @param wr_swap to decide whether to swap fifo data in dtr write operation
+ // @param rd_swap to decide whether to swap fifo data in dtr read operation
+ fn esp_rom_spi_set_dtr_swap_mode(spi: u32, wr_swap: bool, rd_swap: bool);
+
+ fn esp_rom_opiflash_pin_config();
+ }
+
+ #[derive(Default)]
+ #[repr(C)]
+ struct OpiPsramModeReg {
+ pub mr0: u8,
+ pub mr1: u8,
+ pub mr2: u8,
+ pub mr3: u8,
+ pub mr4: u8,
+ pub mr8: u8,
+ }
+
+ #[allow(unused)]
+ impl OpiPsramModeReg {
+ fn drive_str(&self) -> u8 {
+ self.mr0 & 0b11
+ }
+
+ fn set_drive_str(&mut self, value: u8) {
+ self.mr0 &= !0b11;
+ self.mr0 |= value & 0b11;
+ }
+
+ fn read_latency(&self) -> u8 {
+ (self.mr0 >> 2) & 0b111
+ }
+
+ fn set_read_latency(&mut self, value: u8) {
+ self.mr0 &= !(0b111 << 2);
+ self.mr0 |= (value & 0b111) << 2;
+ }
+
+ fn lt(&self) -> u8 {
+ (self.mr0 >> 5) & 0b1
+ }
+
+ fn set_lt(&mut self, value: u8) {
+ self.mr0 &= !(0b1 << 5);
+ self.mr0 |= (value & 0b1) << 5;
+ }
+
+ fn rsvd0_1(&self) -> u8 {
+ (self.mr0 >> 6) & 0b11
+ }
+
+ fn set_rsvd0_1(&mut self, value: u8) {
+ self.mr0 &= !(0b11 << 6);
+ self.mr0 |= (value & 0b11) << 6;
+ }
+
+ fn vendor_id(&self) -> u8 {
+ self.mr1 & 0b11111
+ }
+
+ fn set_vendor_id(&mut self, value: u8) {
+ self.mr1 &= !0b11111;
+ self.mr1 |= value & 0b11111;
+ }
+
+ fn rsvd0_2(&self) -> u8 {
+ (self.mr1 >> 5) & 0b111
+ }
+
+ fn set_rsvd0_2(&mut self, value: u8) {
+ self.mr1 &= !(0b111 << 5);
+ self.mr1 |= (value & 0b111) << 5;
+ }
+
+ fn density(&self) -> u8 {
+ self.mr2 & 0b111
+ }
+
+ fn set_density(&mut self, value: u8) {
+ self.mr2 &= !0b111;
+ self.mr2 |= value & 0b111;
+ }
+
+ fn dev_id(&self) -> u8 {
+ (self.mr2 >> 3) & 0b11
+ }
+
+ fn set_dev_id(&mut self, value: u8) {
+ self.mr2 &= !(0b11 << 3);
+ self.mr2 |= (value & 0b11) << 3;
+ }
+
+ fn rsvd1_2(&self) -> u8 {
+ (self.mr2 >> 5) & 0b11
+ }
+
+ fn set_rsvd1_2(&mut self, value: u8) {
+ self.mr2 &= !(0b11 << 5);
+ self.mr2 |= (value & 0b11) << 5;
+ }
+
+ fn gb(&self) -> u8 {
+ (self.mr2 >> 7) & 0b1
+ }
+
+ fn set_gb(&mut self, value: u8) {
+ self.mr2 &= !(0b1 << 7);
+ self.mr2 |= (value & 0b1) << 7;
+ }
+
+ fn rsvd3_7(&self) -> u8 {
+ self.mr3 & 0b11111
+ }
+
+ fn set_rsvd3_7(&mut self, value: u8) {
+ self.mr3 &= !0b11111;
+ self.mr3 |= value & 0b11111;
+ }
+
+ fn srf(&self) -> u8 {
+ (self.mr3 >> 5) & 0b1
+ }
+
+ fn set_srf(&mut self, value: u8) {
+ self.mr3 &= !(0b1 << 5);
+ self.mr3 |= (value & 0b1) << 5;
+ }
+
+ fn vcc(&self) -> u8 {
+ (self.mr3 >> 6) & 0b1
+ }
+
+ fn set_vcc(&mut self, value: u8) {
+ self.mr3 &= !(0b1 << 6);
+ self.mr3 |= (value & 0b1) << 6;
+ }
+
+ fn rsvd0(&self) -> u8 {
+ (self.mr3 >> 7) & 0b1
+ }
+
+ fn set_rsvd0(&mut self, value: u8) {
+ self.mr3 &= !(0b1 << 7);
+ self.mr3 |= (value & 0b1) << 7;
+ }
+
+ fn pasr(&self) -> u8 {
+ self.mr4 & 0b111
+ }
+
+ fn set_pasr(&mut self, value: u8) {
+ self.mr4 &= !0b111;
+ self.mr4 |= value & 0b111;
+ }
+
+ fn rf(&self) -> u8 {
+ (self.mr4 >> 3) & 0b1
+ }
+
+ fn set_rf(&mut self, value: u8) {
+ self.mr4 &= !(0b1 << 3);
+ self.mr4 |= (value & 0b1) << 3;
+ }
+
+ fn rsvd3(&self) -> u8 {
+ (self.mr4 >> 4) & 0b1
+ }
+
+ fn set_rsvd3(&mut self, value: u8) {
+ self.mr4 &= !(0b1 << 4);
+ self.mr4 |= (value & 0b1) << 4;
+ }
+
+ fn wr_latency(&self) -> u8 {
+ (self.mr4 >> 5) & 0b111
+ }
+
+ fn set_wr_latency(&mut self, value: u8) {
+ self.mr4 &= !(0b111 << 5);
+ self.mr4 |= (value & 0b111) << 5;
+ }
+
+ fn bl(&self) -> u8 {
+ self.mr8 & 0b11
+ }
+
+ fn set_bl(&mut self, value: u8) {
+ self.mr8 &= !0b11;
+ self.mr8 |= value & 0b11;
+ }
+
+ fn bt(&self) -> u8 {
+ (self.mr8 >> 2) & 0b1
+ }
+
+ fn set_bt(&mut self, value: u8) {
+ self.mr8 &= !(0b1 << 2);
+ self.mr8 |= (value & 0b1) << 2;
+ }
+
+ fn rsvd0_4(&self) -> u8 {
+ (self.mr8 >> 3) & 0b11111
+ }
+
+ fn set_rsvd0_4(&mut self, value: u8) {
+ self.mr8 &= !(0b11111 << 3);
+ self.mr8 |= (value & 0b11111) << 3;
+ }
+ }
+
+ #[ram]
+ pub(crate) fn psram_init() {
+ mspi_pin_init();
+ init_psram_pins();
+ set_psram_cs_timing();
+
+ // for now we don't support ECC
+ // "s_configure_psram_ecc();"
+
+ // enter MSPI slow mode to init PSRAM device registers
+ spi_timing_enter_mspi_low_speed_mode(true);
+
+ // set to variable dummy mode
+ set_peri_reg_mask(SPI1_MEM_DDR_REG, SPI_MEM_SPI_FMEM_VAR_DUMMY);
+ unsafe {
+ esp_rom_spi_set_dtr_swap_mode(1, false, false);
+ }
+
+ // Set PSRAM read latency and drive strength
+ let mut mode_reg = OpiPsramModeReg::default();
+ mode_reg.set_lt(1);
+ mode_reg.set_read_latency(2);
+ mode_reg.set_drive_str(0);
+ mode_reg.set_bl(3);
+ mode_reg.set_bt(0);
+
+ init_psram_mode_reg(1, &mode_reg);
+ // Print PSRAM info
+ get_psram_mode_reg(1, &mut mode_reg);
+
+ print_psram_info(&mode_reg);
+
+ if mode_reg.vendor_id() != OCT_PSRAM_VENDOR_ID {
+ log::warn!("PSRAM ID read error: {:x}, PSRAM chip not found or not supported, or wrong PSRAM line mode", mode_reg.vendor_id());
+ return;
+ }
+
+ let psram_size = match mode_reg.density() {
+ 0x0 => PSRAM_SIZE_2MB,
+ 0x1 => PSRAM_SIZE_4MB,
+ 0x3 => PSRAM_SIZE_8MB,
+ 0x5 => PSRAM_SIZE_16MB,
+ 0x7 => PSRAM_SIZE_32MB,
+ _ => 0,
+ };
+ log::info!("{psram_size} bytes of PSRAM");
+
+ // 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
+ // spi_timing_psram_tuning();
+
+ // 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);
+
+ // Tuning may change SPI1 regs, whereas legacy spi_flash APIs rely on
+ // these regs. This function is to restore SPI1 init state.
+ spi_flash_set_rom_required_regs();
+
+ // Flash chip requires MSPI specifically, call this function to set them
+ // this is unsupported for now
+ // spi_flash_set_vendor_required_regs();
+
+ config_psram_spi_phases();
+ }
+
+ // Configure PSRAM SPI0 phase related registers here according to the PSRAM chip
+ // requirement
+ fn config_psram_spi_phases() {
+ // Config Write CMD phase for SPI0 to access PSRAM
+ set_peri_reg_mask(SPI0_MEM_CACHE_SCTRL_REG, SPI_MEM_CACHE_SRAM_USR_WCMD_M);
+ set_peri_reg_bits(
+ SPI0_MEM_SRAM_DWR_CMD_REG,
+ SPI_MEM_CACHE_SRAM_USR_WR_CMD_BITLEN,
+ (OCT_PSRAM_WR_CMD_BITLEN - 1) as u32,
+ SPI_MEM_CACHE_SRAM_USR_WR_CMD_BITLEN_S,
+ );
+ set_peri_reg_bits(
+ SPI0_MEM_SRAM_DWR_CMD_REG,
+ SPI_MEM_CACHE_SRAM_USR_WR_CMD_VALUE,
+ OPI_PSRAM_SYNC_WRITE as u32,
+ SPI_MEM_CACHE_SRAM_USR_WR_CMD_VALUE_S,
+ );
+
+ // Config Read CMD phase for SPI0 to access PSRAM
+ set_peri_reg_mask(SPI0_MEM_CACHE_SCTRL_REG, SPI_MEM_CACHE_SRAM_USR_RCMD_M);
+ set_peri_reg_bits(
+ SPI0_MEM_SRAM_DRD_CMD_REG,
+ SPI_MEM_CACHE_SRAM_USR_RD_CMD_BITLEN_V,
+ (OCT_PSRAM_RD_CMD_BITLEN - 1) as u32,
+ SPI_MEM_CACHE_SRAM_USR_RD_CMD_BITLEN_S,
+ );
+ set_peri_reg_bits(
+ SPI0_MEM_SRAM_DRD_CMD_REG,
+ SPI_MEM_CACHE_SRAM_USR_RD_CMD_VALUE_V,
+ OPI_PSRAM_SYNC_READ as u32,
+ SPI_MEM_CACHE_SRAM_USR_RD_CMD_VALUE_S,
+ );
+
+ // Config ADDR phase
+ set_peri_reg_bits(
+ SPI0_MEM_CACHE_SCTRL_REG,
+ SPI_MEM_SRAM_ADDR_BITLEN_V,
+ (OCT_PSRAM_ADDR_BITLEN - 1) as u32,
+ SPI_MEM_SRAM_ADDR_BITLEN_S,
+ );
+ set_peri_reg_mask(SPI0_MEM_CACHE_SCTRL_REG, SPI_MEM_CACHE_USR_SCMD_4BYTE_M);
+
+ // Config RD/WR Dummy phase
+ set_peri_reg_mask(
+ SPI0_MEM_CACHE_SCTRL_REG,
+ SPI_MEM_USR_RD_SRAM_DUMMY_M | SPI_MEM_USR_WR_SRAM_DUMMY_M,
+ );
+ set_peri_reg_bits(
+ SPI0_MEM_CACHE_SCTRL_REG,
+ SPI_MEM_SRAM_RDUMMY_CYCLELEN_V,
+ (OCT_PSRAM_RD_DUMMY_BITLEN - 1) as u32,
+ SPI_MEM_SRAM_RDUMMY_CYCLELEN_S,
+ );
+ set_peri_reg_mask(SPI0_MEM_SPI_SMEM_DDR_REG, SPI_MEM_SPI_SMEM_VAR_DUMMY_M);
+ set_peri_reg_bits(
+ SPI0_MEM_CACHE_SCTRL_REG,
+ SPI_MEM_SRAM_WDUMMY_CYCLELEN_V,
+ (OCT_PSRAM_WR_DUMMY_BITLEN - 1) as u32,
+ SPI_MEM_SRAM_WDUMMY_CYCLELEN_S,
+ );
+
+ clear_peri_reg_mask(
+ SPI0_MEM_SPI_SMEM_DDR_REG,
+ SPI_MEM_SPI_SMEM_DDR_WDAT_SWP_M | SPI_MEM_SPI_SMEM_DDR_RDAT_SWP_M,
+ );
+ set_peri_reg_mask(SPI0_MEM_SPI_SMEM_DDR_REG, SPI_MEM_SPI_SMEM_DDR_EN_M);
+
+ set_peri_reg_mask(
+ SPI0_MEM_SRAM_CMD_REG,
+ SPI_MEM_SDUMMY_OUT_M
+ | SPI_MEM_SCMD_OCT_M
+ | SPI_MEM_SADDR_OCT_M
+ | SPI_MEM_SDOUT_OCT_M
+ | SPI_MEM_SDIN_OCT_M,
+ );
+ set_peri_reg_mask(SPI0_MEM_CACHE_SCTRL_REG, SPI_MEM_SRAM_OCT_M);
+ }
+
+ #[ram]
+ fn spi_flash_set_rom_required_regs() {
+ // Disable the variable dummy mode when doing timing tuning
+ clear_peri_reg_mask(SPI1_MEM_DDR_REG, SPI_MEM_SPI_FMEM_VAR_DUMMY);
+ // STR /DTR mode setting is done every time when
+ // `esp_rom_opiflash_exec_cmd` is called
+ //
+ // Add any registers that are not set in ROM SPI flash functions here in
+ // the future
+ }
+
+ #[ram]
+ fn mspi_pin_init() {
+ unsafe {
+ esp_rom_opiflash_pin_config();
+ }
+ spi_timing_set_pin_drive_strength();
+ // Set F4R4 board pin drive strength. TODO: IDF-3663
+ }
+
+ #[ram]
+ fn spi_timing_set_pin_drive_strength() {
+ // For now, set them all to 3. Need to check after QVL test results are out.
+ // TODO: IDF-3663 Set default clk
+ set_peri_reg_mask(SPI0_MEM_DATE_REG, SPI_MEM_SPICLK_PAD_DRV_CTL_EN);
+ set_peri_reg_bits(
+ SPI0_MEM_DATE_REG,
+ SPI_MEM_SPI_SMEM_SPICLK_FUN_DRV,
+ 3,
+ SPI_MEM_SPI_SMEM_SPICLK_FUN_DRV_S,
+ );
+ set_peri_reg_bits(
+ SPI0_MEM_DATE_REG,
+ SPI_MEM_SPI_FMEM_SPICLK_FUN_DRV,
+ 3,
+ SPI_MEM_SPI_FMEM_SPICLK_FUN_DRV_S,
+ );
+ // Set default mspi d0 ~ d7, dqs pin drive strength
+ let pins = &[27usize, 28, 31, 32, 33, 34, 35, 36, 37];
+ for pin in pins {
+ unsafe {
+ let iomux = &*esp32s3::IO_MUX::PTR;
+ iomux.gpio[*pin].modify(|_, w| w.fun_drv().variant(3))
+ }
+ }
+ }
+
+ const SPI_MEM_SPICLK_PAD_DRV_CTL_EN: u32 = 1 << 4;
+ const SPI_MEM_SPI_FMEM_SPICLK_FUN_DRV: u32 = 0x00000003;
+ const SPI_MEM_SPI_FMEM_SPICLK_FUN_DRV_S: u32 = 2;
+
+ fn spi_timing_enter_mspi_low_speed_mode(control_spi1: bool) {
+ // Here we are going to slow the SPI1 frequency to 20Mhz, so we need to
+ // set SPI1 din_num and din_mode regs.
+ //
+ // Because SPI0 and SPI1 share the din_num and din_mode regs, so if we
+ // clear SPI1 din_num and din_mode to 0, if the SPI0 flash
+ // module clock is still in high freq, it may not work correctly.
+ //
+ // Therefore, here we need to slow both the SPI0 and SPI1 and related
+ // timing tuning regs to 20Mhz configuration.
+ // Switch SPI1 and SPI0 clock as 20MHz, set its SPIMEM core clock as 80M and set
+ // clock division as 4
+ spi0_timing_config_set_core_clock(SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m); // SPI0 and SPI1 share the register for core clock. So we only set SPI0 here.
+ spi0_timing_config_set_flash_clock(4);
+ if control_spi1 {
+ // After tuning, won't touch SPI1 again
+ spi1_timing_config_set_flash_clock(4);
+ }
+
+ // Set PSRAM module clock
+ spi0_timing_config_set_psram_clock(4);
+
+ // for now we don't support tuning the timing
+ // "clear_timing_tuning_regs(control_spi1);"
+ }
+
+ // Set SPI0 FLASH and PSRAM module clock, din_num, din_mode and extra dummy,
+ // according to the configuration got from timing tuning function
+ // (`calculate_best_flash_tuning_config`). iF control_spi1 == 1, will also
+ // update SPI1 timing registers. Should only be set to 1 when do tuning.
+ //
+ // 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) {
+ // 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();
+
+ // 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.
+ // Set FLASH module clock
+ spi0_timing_config_set_flash_clock(flash_div);
+ if control_spi1 {
+ spi1_timing_config_set_flash_clock(flash_div);
+ }
+ // Set PSRAM module clock
+ spi0_timing_config_set_psram_clock(psram_div);
+
+ // for now we don't support tuning the timing
+ // "set_timing_tuning_regs_as_required(true);"
+ }
+
+ fn set_psram_cs_timing() {
+ // SPI0/1 share the cs_hold / cs_setup, cd_hold_time / cd_setup_time,
+ // cs_hold_delay registers for PSRAM, so we only need to set SPI0 related
+ // registers here
+ set_peri_reg_mask(
+ SPI0_MEM_SPI_SMEM_AC_REG,
+ SPI_MEM_SPI_SMEM_CS_HOLD_M | SPI_MEM_SPI_SMEM_CS_SETUP_M,
+ );
+ set_peri_reg_bits(
+ SPI0_MEM_SPI_SMEM_AC_REG,
+ SPI_MEM_SPI_SMEM_CS_HOLD_TIME_V,
+ OCT_PSRAM_CS_HOLD_TIME as u32,
+ SPI_MEM_SPI_SMEM_CS_HOLD_TIME_S,
+ );
+ set_peri_reg_bits(
+ SPI0_MEM_SPI_SMEM_AC_REG,
+ SPI_MEM_SPI_SMEM_CS_SETUP_TIME_V,
+ OCT_PSRAM_CS_SETUP_TIME as u32,
+ SPI_MEM_SPI_SMEM_CS_SETUP_TIME_S,
+ );
+ // CONFIG_SPIRAM_ECC_ENABLE unsupported for now
+ // CS1 high time
+ set_peri_reg_bits(
+ SPI0_MEM_SPI_SMEM_AC_REG,
+ SPI_MEM_SPI_SMEM_CS_HOLD_DELAY_V,
+ OCT_PSRAM_CS_HOLD_DELAY as u32,
+ SPI_MEM_SPI_SMEM_CS_HOLD_DELAY_S,
+ );
+ }
+
+ fn init_psram_pins() {
+ // Set cs1 pin function
+ unsafe {
+ let iomux = &*esp32s3::IO_MUX::PTR;
+ iomux.gpio[OCT_PSRAM_CS1_IO as usize]
+ .modify(|_, w| w.mcu_sel().variant(FUNC_SPICS1_SPICS1))
+ }
+
+ // Set mspi cs1 drive strength
+ unsafe {
+ let iomux = &*esp32s3::IO_MUX::PTR;
+ iomux.gpio[OCT_PSRAM_CS1_IO as usize].modify(|_, w| w.fun_drv().variant(3))
+ }
+
+ // Set psram clock pin drive strength
+ set_peri_reg_bits(
+ SPI0_MEM_DATE_REG,
+ SPI_MEM_SPI_SMEM_SPICLK_FUN_DRV_V,
+ 3,
+ SPI_MEM_SPI_SMEM_SPICLK_FUN_DRV_S,
+ );
+ }
+
+ fn get_psram_mode_reg(spi_num: u32, out_reg: &mut OpiPsramModeReg) {
+ let mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE;
+ let cmd_len: u32 = 16;
+ let addr_bit_len: u32 = 32;
+ let dummy: u32 = OCT_PSRAM_RD_DUMMY_BITLEN as u32;
+ let mut data_bit_len: u32 = 16;
+
+ unsafe {
+ // Read MR0~1 register
+ esp_rom_opiflash_exec_cmd(
+ spi_num,
+ mode,
+ OPI_PSRAM_REG_READ as u32,
+ cmd_len,
+ 0x0,
+ addr_bit_len,
+ dummy,
+ core::ptr::null(),
+ 0,
+ &mut out_reg.mr0,
+ data_bit_len,
+ 1 << 1,
+ false,
+ );
+ // Read MR2~3 register
+ esp_rom_opiflash_exec_cmd(
+ spi_num,
+ mode,
+ OPI_PSRAM_REG_READ as u32,
+ cmd_len,
+ 0x2,
+ addr_bit_len,
+ dummy,
+ core::ptr::null(),
+ 0,
+ &mut out_reg.mr2,
+ data_bit_len,
+ 1 << 1,
+ false,
+ );
+ data_bit_len = 8;
+ // Read MR4 register
+ esp_rom_opiflash_exec_cmd(
+ spi_num,
+ mode,
+ OPI_PSRAM_REG_READ as u32,
+ cmd_len,
+ 0x4,
+ addr_bit_len,
+ dummy,
+ core::ptr::null(),
+ 0,
+ &mut out_reg.mr4,
+ data_bit_len,
+ 1 << 1,
+ false,
+ );
+ // Read MR8 register
+ esp_rom_opiflash_exec_cmd(
+ spi_num,
+ mode,
+ OPI_PSRAM_REG_READ as u32,
+ cmd_len,
+ 0x8,
+ addr_bit_len,
+ dummy,
+ core::ptr::null(),
+ 0,
+ &mut out_reg.mr8,
+ data_bit_len,
+ 1 << 1,
+ false,
+ );
+ }
+ }
+
+ // Initialise mode registers of the PSRAM
+ fn init_psram_mode_reg(spi_num: u32, mode_reg_config: &OpiPsramModeReg) {
+ let mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE;
+ let cmd_len: u32 = 16;
+ let addr: u32 = 0x0; // 0x0 is the MR0 register
+ let addr_bit_len: u32 = 32;
+ let dummy = OCT_PSRAM_RD_DUMMY_BITLEN as u32;
+ let mut mode_reg = OpiPsramModeReg::default();
+ let data_bit_len: u32 = 16;
+
+ // read
+ unsafe {
+ esp_rom_opiflash_exec_cmd(
+ spi_num,
+ mode,
+ OPI_PSRAM_REG_READ as u32,
+ cmd_len,
+ addr,
+ addr_bit_len,
+ dummy,
+ core::ptr::null(),
+ 0,
+ &mut mode_reg.mr0,
+ data_bit_len,
+ 1 << 1,
+ false,
+ );
+ }
+
+ // modify
+ mode_reg.set_lt(mode_reg_config.lt());
+ mode_reg.set_read_latency(mode_reg_config.read_latency());
+ mode_reg.set_drive_str(mode_reg_config.drive_str());
+
+ // write
+ unsafe {
+ esp_rom_opiflash_exec_cmd(
+ spi_num,
+ mode,
+ OPI_PSRAM_REG_WRITE as u32,
+ cmd_len,
+ addr,
+ addr_bit_len,
+ 0,
+ &mode_reg.mr0,
+ 16,
+ core::ptr::null_mut(),
+ 0,
+ 1 << 1,
+ false,
+ );
+ }
+
+ // CONFIG_SPIRAM_ECC_ENABLE not yet supported
+ }
+
+ fn print_psram_info(reg_val: &OpiPsramModeReg) {
+ log::info!(
+ "vendor id : {:02x} ({})",
+ reg_val.vendor_id(),
+ if reg_val.vendor_id() == 0x0d {
+ "AP"
+ } else {
+ "UNKNOWN"
+ }
+ );
+ log::info!(
+ "dev id : {:02x} (generation {})",
+ reg_val.dev_id(),
+ reg_val.dev_id() + 1
+ );
+ log::info!(
+ "density : {:02x} ({} Mbit)",
+ reg_val.density(),
+ if reg_val.density() == 0x1 {
+ 32
+ } else if reg_val.density() == 0x3 {
+ 64
+ } else if reg_val.density() == 0x5 {
+ 128
+ } else if reg_val.density() == 0x7 {
+ 256
+ } else {
+ 0
+ }
+ );
+ log::info!(
+ "good-die : {:02x} ({})",
+ reg_val.gb(),
+ if reg_val.gb() == 1 { "Pass" } else { "Fail" }
+ );
+ log::info!(
+ "Latency : {:02x} ({})",
+ reg_val.lt(),
+ if reg_val.lt() == 1 {
+ "Fixed"
+ } else {
+ "Variable"
+ }
+ );
+ log::info!(
+ "VCC : {:02x} ({})",
+ reg_val.vcc(),
+ if reg_val.vcc() == 1 { "3V" } else { "1.8V" }
+ );
+ log::info!(
+ "SRF : {:02x} ({} Refresh)",
+ reg_val.srf(),
+ if reg_val.srf() == 0x1 { "Fast" } else { "Slow" }
+ );
+ log::info!(
+ "BurstType : {:02x} ({} Wrap)",
+ reg_val.bt(),
+ if reg_val.bt() == 1 && reg_val.bl() != 3 {
+ "Hybrid"
+ } else {
+ ""
+ }
+ );
+ log::info!(
+ "BurstLen : {:02x} ({} Byte)",
+ reg_val.bl(),
+ if reg_val.bl() == 0x00 {
+ 16
+ } else if reg_val.bl() == 0x01 {
+ 32
+ } else if reg_val.bl() == 0x10 {
+ 64
+ } else {
+ 1024
+ }
+ );
+ log::info!(
+ "Readlatency : {:02x} ({} cycles@{})",
+ reg_val.read_latency(),
+ reg_val.read_latency() * 2 + 6,
+ if reg_val.lt() == 1 {
+ "Fixed"
+ } else {
+ "Variable"
+ }
+ );
+ log::info!(
+ "DriveStrength: {:02x} (1/{})",
+ reg_val.drive_str(),
+ if reg_val.drive_str() == 0x00 {
+ 1
+ } else if reg_val.drive_str() == 0x01 {
+ 2
+ } else if reg_val.drive_str() == 0x02 {
+ 4
+ } else {
+ 8
+ }
+ );
+ }
+
+ #[ram]
+ fn spi0_timing_config_set_core_clock(core_clock: SpiTimingConfigCoreClock) {
+ let reg_val = match core_clock {
+ SpiTimingConfigCoreClock::SpiTimingConfigCoreClock80m => 0,
+ SpiTimingConfigCoreClock::SpiTimingConfigCoreClock120m => 1,
+ SpiTimingConfigCoreClock::SpiTimingConfigCoreClock160m => 2,
+ SpiTimingConfigCoreClock::SpiTimingConfigCoreClock240m => 3,
+ };
+
+ set_peri_reg_bits(SPI0_MEM_CORE_CLK_SEL_REG, SPI_MEM_CORE_CLK_SEL, reg_val, 0);
+ }
+
+ #[ram]
+ fn spi0_timing_config_set_flash_clock(freqdiv: u32) {
+ if freqdiv == 1 {
+ write_peri_reg(SPI0_MEM_CLOCK_REG, SPI_MEM_CLK_EQU_SYSCLK);
+ } else {
+ let freqbits: u32 = ((freqdiv - 1) << SPI_MEM_CLKCNT_N_S)
+ | ((freqdiv / 2 - 1) << SPI_MEM_CLKCNT_H_S)
+ | ((freqdiv - 1) << SPI_MEM_CLKCNT_L_S);
+ write_peri_reg(SPI0_MEM_CLOCK_REG, freqbits);
+ }
+ }
+
+ #[ram]
+ fn spi1_timing_config_set_flash_clock(freqdiv: u32) {
+ if freqdiv == 1 {
+ write_peri_reg(SPI1_MEM_CLOCK_REG, SPI_MEM_CLK_EQU_SYSCLK);
+ } else {
+ let freqbits: u32 = ((freqdiv - 1) << SPI_MEM_CLKCNT_N_S)
+ | ((freqdiv / 2 - 1) << SPI_MEM_CLKCNT_H_S)
+ | ((freqdiv - 1) << SPI_MEM_CLKCNT_L_S);
+ write_peri_reg(SPI1_MEM_CLOCK_REG, freqbits);
+ }
+ }
+
+ #[ram]
+ fn spi0_timing_config_set_psram_clock(freqdiv: u32) {
+ if freqdiv == 1 {
+ write_peri_reg(SPI0_MEM_SRAM_CLK_REG, SPI_MEM_SCLK_EQU_SYSCLK);
+ } else {
+ let freqbits: u32 = ((freqdiv - 1) << SPI_MEM_SCLKCNT_N_S)
+ | ((freqdiv / 2 - 1) << SPI_MEM_SCLKCNT_H_S)
+ | ((freqdiv - 1) << SPI_MEM_SCLKCNT_L_S);
+ write_peri_reg(SPI0_MEM_SRAM_CLK_REG, freqbits);
+ }
+ }
+
+ #[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,
+ }
+ }
+
+ #[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,
+ }
+ }
+
+ #[inline(always)]
+ fn clear_peri_reg_mask(reg: u32, mask: u32) {
+ unsafe {
+ (reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !mask);
+ }
+ }
+
+ #[inline(always)]
+ fn set_peri_reg_mask(reg: u32, mask: u32) {
+ unsafe {
+ (reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() | mask);
+ }
+ }
+
+ #[inline(always)]
+ fn set_peri_reg_bits(reg: u32, bitmap: u32, value: u32, shift: u32) {
+ unsafe {
+ (reg as *mut u32).write_volatile(
+ ((reg as *mut u32).read_volatile() & !(bitmap << shift))
+ | ((value & bitmap) << shift),
+ );
+ }
+ }
+
+ #[inline(always)]
+ fn write_peri_reg(reg: u32, val: u32) {
+ unsafe {
+ (reg as *mut u32).write_volatile(val);
+ }
+ }
+}
diff --git a/esp32s3-hal/Cargo.toml b/esp32s3-hal/Cargo.toml
index a3760aa8b..9c07f7569 100644
--- a/esp32s3-hal/Cargo.toml
+++ b/esp32s3-hal/Cargo.toml
@@ -70,6 +70,9 @@ psram = []
psram_2m = ["esp-hal-common/psram_2m", "psram"]
psram_4m = ["esp-hal-common/psram_4m", "psram"]
psram_8m = ["esp-hal-common/psram_8m", "psram"]
+opsram_2m = ["esp-hal-common/opsram_2m", "psram"]
+opsram_4m = ["esp-hal-common/opsram_4m", "psram"]
+opsram_8m = ["esp-hal-common/opsram_8m", "psram"]
[[example]]
name = "spi_eh1_loopback"
@@ -95,6 +98,10 @@ required-features = ["embassy", "async"]
name = "psram"
required-features = ["psram_2m"]
+[[example]]
+name = "octal_psram"
+required-features = ["opsram_2m"]
+
[[example]]
name = "embassy_serial"
required-features = ["embassy", "async"]
diff --git a/esp32s3-hal/examples/octal_psram.rs b/esp32s3-hal/examples/octal_psram.rs
new file mode 100644
index 000000000..1d9d5ba7b
--- /dev/null
+++ b/esp32s3-hal/examples/octal_psram.rs
@@ -0,0 +1,80 @@
+//! This shows how to use PSRAM as heap-memory via esp-alloc
+//!
+//! You need an ESP32-S3 with at least 2 MB of PSRAM memory.
+
+#![no_std]
+#![no_main]
+
+use esp32s3_hal::{
+ clock::ClockControl,
+ peripherals::Peripherals,
+ prelude::*,
+ soc,
+ timer::TimerGroup,
+ Rtc,
+};
+use esp_backtrace as _;
+use esp_println::println;
+
+extern crate alloc;
+
+#[global_allocator]
+static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();
+
+fn init_psram_heap() {
+ unsafe {
+ ALLOCATOR.init(
+ soc::psram::PSRAM_VADDR_START as *mut u8,
+ soc::psram::PSRAM_BYTES,
+ );
+ }
+}
+
+#[entry]
+fn main() -> ! {
+ #[cfg(debug_assertions)]
+ compile_error!("PSRAM on ESP32-S3 needs to be built in release mode");
+
+ esp_println::logger::init_logger_from_env();
+
+ let peripherals = Peripherals::take();
+ soc::psram::init_psram(peripherals.PSRAM);
+ init_psram_heap();
+
+ let mut system = peripherals.SYSTEM.split();
+ let clocks = ClockControl::configure(
+ system.clock_control,
+ esp_hal_common::clock::CpuClock::Clock240MHz,
+ )
+ .freeze();
+
+ let timer_group0 = TimerGroup::new(
+ peripherals.TIMG0,
+ &clocks,
+ &mut system.peripheral_clock_control,
+ );
+ let mut wdt = timer_group0.wdt;
+ let mut rtc = Rtc::new(peripherals.RTC_CNTL);
+
+ // Disable MWDT and RWDT (Watchdog) flash boot protection
+ wdt.disable();
+ rtc.rwdt.disable();
+
+ println!("Going to access PSRAM");
+ let mut large_vec: alloc::vec::Vec