diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36faa940ff1..73d7e10602d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -323,7 +323,7 @@ jobs: - name: clippy (esp32-hal) run: cargo +esp clippy --manifest-path=esp32-hal/Cargo.toml -- --no-deps - name: clippy (esp32s2-hal) - run: cargo +esp clippy --manifest-path=esp32s2-hal/Cargo.toml --target=xtensa-esp32s2-none-elf -Zbuild-std=core -- --no-deps + run: cargo +esp clippy --manifest-path=esp32s2-hal/Cargo.toml -- --no-deps - name: clippy (esp32s3-hal) run: cargo +esp clippy --manifest-path=esp32s3-hal/Cargo.toml -- --no-deps diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a642230284..3f6ce6c72b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add bare-bones PSRAM support for ESP32 (#506) + ### Fixed - DMA is supported for SPI3 on ESP32-S3 (#507) + ## [0.9.0] - 2023-05-02 ### Added diff --git a/esp-hal-common/Cargo.toml b/esp-hal-common/Cargo.toml index 493f4197e30..8aff954fa02 100644 --- a/esp-hal-common/Cargo.toml +++ b/esp-hal-common/Cargo.toml @@ -22,6 +22,7 @@ embedded-hal-1 = { version = "=1.0.0-alpha.10", optional = true, package = embedded-hal-nb = { version = "=1.0.0-alpha.2", optional = true } esp-synopsys-usb-otg = { version = "0.3.1", optional = true, features = ["fs", "esp32sx"] } fugit = "0.3.6" +log = "0.4.17" lock_api = { version = "0.4.9", optional = true } nb = "1.1.0" paste = "1.0.12" diff --git a/esp-hal-common/devices/esp32.toml b/esp-hal-common/devices/esp32.toml index 2ea0d05486c..03d4d4e600a 100644 --- a/esp-hal-common/devices/esp32.toml +++ b/esp-hal-common/devices/esp32.toml @@ -56,6 +56,7 @@ peripherals = [ "phy", "bt", "wifi", + "psram", # Wakeup SOC based on ESP-IDF: "pm_support_ext0_wakeup", diff --git a/esp-hal-common/src/interrupt/riscv.rs b/esp-hal-common/src/interrupt/riscv.rs index 864f63ad187..4db2854ebbe 100644 --- a/esp-hal-common/src/interrupt/riscv.rs +++ b/esp-hal-common/src/interrupt/riscv.rs @@ -527,8 +527,6 @@ pub fn _setup_interrupts() { unsafe { core::arch::asm!("csrw mie, {0}", in(reg) u32::MAX); } - - crate::common_init(); } /// Disable the given peripheral interrupt. diff --git a/esp-hal-common/src/lib.rs b/esp-hal-common/src/lib.rs index 7febf406a60..b122c7fae0f 100644 --- a/esp-hal-common/src/lib.rs +++ b/esp-hal-common/src/lib.rs @@ -152,12 +152,6 @@ pub fn disable_apm_filter() { } } -#[doc(hidden)] -pub fn common_init() { - #[cfg(psram)] - soc::psram::init_psram(); -} - /// Enumeration of CPU cores /// The actual number of available cores depends on the target. pub enum Cpu { diff --git a/esp-hal-common/src/soc/esp32/mod.rs b/esp-hal-common/src/soc/esp32/mod.rs index 108ee6ad3df..7019cd545fa 100644 --- a/esp-hal-common/src/soc/esp32/mod.rs +++ b/esp-hal-common/src/soc/esp32/mod.rs @@ -2,4 +2,6 @@ pub mod cpu_control; pub mod efuse; pub mod gpio; pub mod peripherals; +#[cfg(psram)] +pub mod psram; pub mod radio_clocks; diff --git a/esp-hal-common/src/soc/esp32/peripherals.rs b/esp-hal-common/src/soc/esp32/peripherals.rs index d711311ef77..68b76f1b7e8 100644 --- a/esp-hal-common/src/soc/esp32/peripherals.rs +++ b/esp-hal-common/src/soc/esp32/peripherals.rs @@ -49,5 +49,6 @@ crate::peripherals! { UART2 => true, UHCI0 => true, UHCI1 => true, - RADIO => false + RADIO => false, + PSRAM => false, } diff --git a/esp-hal-common/src/soc/esp32/psram.rs b/esp-hal-common/src/soc/esp32/psram.rs new file mode 100644 index 00000000000..d797c70cff2 --- /dev/null +++ b/esp-hal-common/src/soc/esp32/psram.rs @@ -0,0 +1,1327 @@ +const PSRAM_VADDR: u32 = 0x3F800000; + +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 const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; + +pub const PSRAM_VADDR_START: usize = PSRAM_VADDR as usize; + +#[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, PSRAM_BYTES as u32); +} + +#[cfg(any(feature = "psram_2m", feature = "psram_4m", feature = "psram_8m"))] +pub(crate) mod utils { + use procmacros::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); + // Flush and enable icache for APP CPU + unsafe { + let dport = &*esp32::DPORT::PTR; + dport + .app_cache_ctrl1 + .modify(|_, w| w.app_cache_mask_dram1().clear_bit()); + } + + cache_sram_mmu_set(1, 0, v_start, 0, 32, size / 1024 / 32); + } + + // we can use the ROM version of this: it works well enough and keeps the size + // of the binary down. + fn cache_sram_mmu_set( + cpu_no: u32, + pid: u32, + vaddr: u32, + paddr: u32, + psize: u32, + num: u32, + ) -> i32 { + unsafe { cache_sram_mmu_set_rom(cpu_no, pid, vaddr, paddr, psize, num) } + } + + // PSRAM clock and cs IO should be configured based on hardware design. + // For ESP32-WROVER or ESP32-WROVER-B module, the clock IO is IO17, the cs IO is + // IO16, they are the default value for these two configs. + const D0WD_PSRAM_CLK_IO: u8 = 17; + const D0WD_PSRAM_CS_IO: u8 = 16; + + const D2WD_PSRAM_CLK_IO: u8 = 9; // Default value is 9 + const D2WD_PSRAM_CS_IO: u8 = 10; // Default value is 10 + + // For ESP32-PICO chip, the psram share clock with flash. The flash clock pin is + // fixed, which is IO6. + const PICO_PSRAM_CLK_IO: u8 = 6; + const PICO_PSRAM_CS_IO: u8 = 10; // Default value is 10 + + const ESP_ROM_EFUSE_FLASH_DEFAULT_SPI: u32 = 0; + const ESP_ROM_EFUSE_FLASH_DEFAULT_HSPI: u32 = 1; + + const SPI_IOMUX_PIN_NUM_CLK: u8 = 6; + const SPI_IOMUX_PIN_NUM_CS: u8 = 11; + + // IO-pins for PSRAM. + // WARNING: PSRAM shares all but the CS and CLK pins with the flash, so these + // defines hardcode the flash pins as well, making this code incompatible + // with either a setup that has the flash on non-standard pins or ESP32s + // with built-in flash. + const PSRAM_SPIQ_SD0_IO: u8 = 7; + const PSRAM_SPID_SD1_IO: u8 = 8; + const PSRAM_SPIWP_SD3_IO: u8 = 10; + const PSRAM_SPIHD_SD2_IO: u8 = 9; + + const FLASH_HSPI_CLK_IO: u8 = 14; + const FLASH_HSPI_CS_IO: u8 = 15; + + const PSRAM_HSPI_SPIQ_SD0_IO: u8 = 12; + const PSRAM_HSPI_SPID_SD1_IO: u8 = 13; + const PSRAM_HSPI_SPIWP_SD3_IO: u8 = 2; + const PSRAM_HSPI_SPIHD_SD2_IO: u8 = 4; + + const DR_REG_SPI1_BASE: u32 = 0x3ff42000; + const SPI1_USER_REG: u32 = DR_REG_SPI1_BASE + 0x1C; + const SPI1_SLAVE_REG: u32 = DR_REG_SPI1_BASE + 0x038; + const SPI1_PIN_REG: u32 = DR_REG_SPI1_BASE + 0x34; + const SPI1_CTRL_REG: u32 = DR_REG_SPI1_BASE + 0x8; + const SPI1_USER1_REG: u32 = DR_REG_SPI1_BASE + 0x20; + const SPI1_W0_REG: u32 = DR_REG_SPI1_BASE + 0x80; + const SPI1_CTRL2_REG: u32 = DR_REG_SPI1_BASE + 0x14; + const SPI1_CMD_REG: u32 = DR_REG_SPI1_BASE + 0x0; + const SPI1_USER2_REG: u32 = DR_REG_SPI1_BASE + 0x24; + const SPI1_MOSI_DLEN_REG: u32 = DR_REG_SPI1_BASE + 0x28; + const SPI1_MISO_DLEN_REG: u32 = DR_REG_SPI1_BASE + 0x2C; + const SPI1_ADDR_REG: u32 = DR_REG_SPI1_BASE + 0x4; + + const DR_REG_SPI0_BASE: u32 = 0x3ff43000; + const SPI0_EXT2_REG: u32 = DR_REG_SPI0_BASE + 0xf8; + const SPI0_EXT3_REG: u32 = DR_REG_SPI0_BASE + 0xfc; + const SPI0_USER_REG: u32 = DR_REG_SPI0_BASE + 0x1C; + const SPI0_PIN_REG: u32 = DR_REG_SPI0_BASE + 0x34; + const SPI0_CTRL_REG: u32 = DR_REG_SPI0_BASE + 0x8; + const SPI0_USER1_REG: u32 = DR_REG_SPI0_BASE + 0x20; + const SPI0_CTRL2_REG: u32 = DR_REG_SPI0_BASE + 0x14; + const SPI0_DATE_REG: u32 = DR_REG_SPI0_BASE + 0x3FC; + const SPI0_CLOCK_REG: u32 = DR_REG_SPI0_BASE + 0x18; + const SPI0_CACHE_SCTRL_REG: u32 = DR_REG_SPI0_BASE + 0x54; + const SPI0_SRAM_DRD_CMD_REG: u32 = DR_REG_SPI0_BASE + 0x5C; + const SPI0_SRAM_DWR_CMD_REG: u32 = DR_REG_SPI0_BASE + 0x60; + + const SPI_USR_PREP_HOLD_M: u32 = 1 << 23; + const SPI_TRANS_DONE: u32 = 1 << 4; + const SPI_CK_IDLE_EDGE: u32 = 1 << 29; + const SPI_CK_OUT_EDGE: u32 = 1 << 7; + const SPI_WR_BIT_ORDER: u32 = 1 << 26; + const SPI_RD_BIT_ORDER: u32 = 1 << 25; + const SPI_DOUTDIN: u32 = 1 << 0; + const SPI_SLAVE_MODE: u32 = 1 << 30; + const SPI_CS_HOLD_M: u32 = 1 << 4; + const SPI_CS_SETUP_M: u32 = 1 << 5; + const SPI_HOLD_TIME_V: u32 = 0xF; + + const fn psram_cs_hold_time_from_psram_speed(speed: PsramCacheSpeed) -> u32 { + match speed { + PsramCacheSpeed::PsramCacheF80mS40m => 0, + PsramCacheSpeed::PsramCacheF40mS40m => 0, + PsramCacheSpeed::PsramCacheF80mS80m => 1, + } + } + + const SPI_HOLD_TIME_S: u32 = 4; + const SPI_SETUP_TIME_V: u32 = 0xF; + const SPI_SETUP_TIME_S: u32 = 0; + const SPI_USR_DUMMY: u32 = 1 << 29; + + const PSRAM_INTERNAL_IO_28: u32 = 28; + const PSRAM_INTERNAL_IO_29: u32 = 29; + const SIG_GPIO_OUT_IDX: u32 = 256; + const SPICLK_OUT_IDX: u32 = 0; + const SIG_IN_FUNC224_IDX: u32 = 224; + const SIG_IN_FUNC225_IDX: u32 = 225; + const SPICS0_OUT_IDX: u32 = 5; + const SPICS1_OUT_IDX: u32 = 6; + const SPIQ_OUT_IDX: u32 = 1; + const SPIQ_IN_IDX: u32 = 1; + const SPID_OUT_IDX: u32 = 2; + const SPID_IN_IDX: u32 = 2; + const SPIWP_OUT_IDX: u32 = 4; + const SPIWP_IN_IDX: u32 = 4; + const SPIHD_OUT_IDX: u32 = 3; + const SPIHD_IN_IDX: u32 = 3; + const FUNC_SD_CLK_SPICLK: u32 = 1; + const PIN_FUNC_GPIO: u32 = 2; + + const FUN_DRV_V: u32 = 0x3; + const FUN_DRV_S: u32 = 10; + const FUN_DRV: u32 = 0x3; + + const SPI_CLK_EQU_SYSCLK_M: u32 = 1 << 31; + const SPI_CLKDIV_PRE_V: u32 = 0x1FFF; + const SPI_CLKDIV_PRE_S: u32 = 18; + const SPI_CLKCNT_N: u32 = 0x0000003F; + const SPI_CLKCNT_N_S: u32 = 12; + const SPI_CLKCNT_H: u32 = 0x0000003F; + const SPI_CLKCNT_H_S: u32 = 6; + const SPI_CLKCNT_L: u32 = 0x0000003F; + const SPI_CLKCNT_L_S: u32 = 0; + const SPI_USR_SRAM_DIO_M: u32 = 1 << 1; + const SPI_USR_SRAM_QIO_M: u32 = 1 << 2; + const SPI_CACHE_SRAM_USR_RCMD_M: u32 = 1 << 5; + const SPI_CACHE_SRAM_USR_WCMD_M: u32 = 1 << 28; + const SPI_SRAM_ADDR_BITLEN_V: u32 = 0x3F; + const SPI_USR_RD_SRAM_DUMMY_M: u32 = 1 << 4; + const SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V: u32 = 0xF; + const SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S: u32 = 28; + const SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V: u32 = 0xFFFF; + const SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S: u32 = 0; + const SPI_CACHE_SRAM_USR_WR_CMD_BITLEN: u32 = 0x0000000F; + const SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S: u32 = 28; + const SPI_CACHE_SRAM_USR_WR_CMD_VALUE: u32 = 0x0000FFFF; + const PSRAM_QUAD_WRITE: u32 = 0x38; + const SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S: u32 = 0; + const SPI_SRAM_DUMMY_CYCLELEN_V: u32 = 0xFF; + const PSRAM_FAST_READ_QUAD_DUMMY: u32 = 0x5; + const SPI_SRAM_DUMMY_CYCLELEN_S: u32 = 14; + const SPI_SRAM_ADDR_BITLEN_S: u32 = 22; + const PSRAM_FAST_READ_QUAD: u32 = 0xEB; + + const SPI_USR: u32 = 1 << 18; + const SPI_USR_COMMAND_BITLEN: u32 = 0x0000000F; + const SPI_USR_COMMAND_BITLEN_S: u32 = 28; + const SPI_USR_COMMAND: u32 = 1 << 31; + const SPI_USR_COMMAND_VALUE: u32 = 0x0000FFFF; + const SPI_USR_COMMAND_VALUE_S: u32 = 0; + const SPI_USR_ADDR_BITLEN: u32 = 0x0000003F; + const SPI_USR_ADDR: u32 = 1 << 30; + const SPI_USR_MOSI: u32 = 1 << 27; + const SPI_USR_MISO_DBITLEN: u32 = 0x00FFFFFF; + const SPI_USR_MOSI_DBITLEN: u32 = 0x00FFFFFF; + const SPI_USR_MOSI_DBITLEN_S: u32 = 0; + const SPI_USR_MISO_DBITLEN_S: u32 = 0; + const SPI_USR_MISO: u32 = 1 << 28; + const SPI_FWRITE_DUAL_S: u32 = 12; + const SPI_FWRITE_DUAL_M: u32 = 1 << 12; + + const SPI_CS1_DIS_M: u32 = 1 << 1; + const SPI_CS0_DIS_M: u32 = 1 << 0; + const SPI_FWRITE_QIO: u32 = 1 << 15; + const SPI_FWRITE_DIO: u32 = 1 << 14; + const SPI_FWRITE_QUAD: u32 = 1 << 13; + const SPI_FWRITE_DUAL: u32 = 1 << 12; + const SPI_FREAD_QIO: u32 = 1 << 24; + const SPI_FREAD_QUAD: u32 = 1 << 20; + const SPI_FREAD_DUAL: u32 = 1 << 14; + const SPI_FREAD_DIO: u32 = 1 << 23; + + const SPI_FREAD_QIO_M: u32 = 1 << 24; + const SPI0_R_QIO_DUMMY_CYCLELEN: u32 = 3; + const SPI_FREAD_DIO_M: u32 = 1 << 23; + const SPI0_R_DIO_DUMMY_CYCLELEN: u32 = 1; + const SPI_USR_ADDR_BITLEN_V: u32 = 0x3F; + const SPI0_R_DIO_ADDR_BITSLEN: u32 = 27; + const SPI_USR_ADDR_BITLEN_S: u32 = 26; + const SPI_FREAD_QUAD_M: u32 = 1 << 20; + const SPI_FREAD_DUAL_M: u32 = 1 << 14; + const SPI0_R_FAST_DUMMY_CYCLELEN: u32 = 7; + const PSRAM_IO_MATRIX_DUMMY_40M: u8 = 1; + const PSRAM_IO_MATRIX_DUMMY_80M: u8 = 2; + + const _SPI_CACHE_PORT: u8 = 0; + const _SPI_FLASH_PORT: u8 = 1; + const SPI_USR_DUMMY_CYCLELEN_V: u32 = 0xFF; + const SPI_USR_DUMMY_CYCLELEN_S: u32 = 0; + const _SPI_80M_CLK_DIV: u8 = 1; + const _SPI_40M_CLK_DIV: u8 = 2; + + const FLASH_ID_GD25LQ32C: u32 = 0xC86016; + + #[derive(PartialEq, Eq, Clone, Copy, Debug)] + #[allow(unused)] + enum PsramCacheSpeed { + PsramCacheF80mS40m = 0, + PsramCacheF40mS40m, + PsramCacheF80mS80m, + } + + #[derive(PartialEq, Eq, Debug, Default)] + struct PsramIo { + flash_clk_io: u8, + flash_cs_io: u8, + psram_clk_io: u8, + psram_cs_io: u8, + psram_spiq_sd0_io: u8, + psram_spid_sd1_io: u8, + psram_spiwp_sd3_io: u8, + psram_spihd_sd2_io: u8, + } + + #[derive(PartialEq, Eq, Copy, Clone, Debug)] + enum PsramClkMode { + PsramClkModeNorm = 0, // Normal SPI mode + PsramClkModeDclk = 1, // Two extra clock cycles after CS is set high level + } + + #[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, + } + + extern "C" { + fn esp_rom_efuse_get_flash_gpio_info() -> u32; + + fn esp_rom_gpio_connect_out_signal( + gpio_num: u32, + signal_idx: u32, + out_inv: bool, + oen_inv: bool, + ); + + fn esp_rom_gpio_connect_in_signal(gpio_num: u32, signal_idx: u32, inv: bool); + + fn esp_rom_spiflash_config_clk(freqdiv: u8, spi: u8) -> i32; + + static g_rom_spiflash_dummy_len_plus: u8; + + static g_rom_flashchip: EspRomSpiflashChip; + + fn cache_sram_mmu_set_rom( + cpu_no: u32, + pid: u32, + vaddr: u32, + paddr: u32, + psize: u32, + num: u32, + ) -> i32; + } + + pub(crate) fn psram_init() { + let chip = crate::efuse::Efuse::get_chip_type(); + + let mode = PsramCacheSpeed::PsramCacheF40mS40m; // How to make this configurable + let mut psram_io = PsramIo::default(); + let clk_mode; + + match chip { + crate::efuse::ChipType::Esp32D0wdq6 | crate::efuse::ChipType::Esp32D0wdq5 => { + clk_mode = PsramClkMode::PsramClkModeNorm; + psram_io.psram_clk_io = D0WD_PSRAM_CLK_IO; + psram_io.psram_cs_io = D0WD_PSRAM_CS_IO; + } + crate::efuse::ChipType::Esp32D2wdq5 => { + clk_mode = PsramClkMode::PsramClkModeDclk; + psram_io.psram_clk_io = D2WD_PSRAM_CLK_IO; + psram_io.psram_cs_io = D2WD_PSRAM_CS_IO; + } + crate::efuse::ChipType::Esp32Picod2 => { + clk_mode = PsramClkMode::PsramClkModeNorm; + psram_io.psram_clk_io = PICO_PSRAM_CLK_IO; + psram_io.psram_cs_io = PICO_PSRAM_CS_IO; + } + crate::efuse::ChipType::Esp32Picod4 => { + panic!("PSRAM is unsupported on this chip"); + } + crate::efuse::ChipType::Unknown => { + panic!("Unknown chip type. PSRAM is not supported"); + } + } + + let spiconfig = unsafe { esp_rom_efuse_get_flash_gpio_info() }; + if spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_SPI { + psram_io.flash_clk_io = SPI_IOMUX_PIN_NUM_CLK; + psram_io.flash_cs_io = SPI_IOMUX_PIN_NUM_CS; + psram_io.psram_spiq_sd0_io = PSRAM_SPIQ_SD0_IO; + psram_io.psram_spid_sd1_io = PSRAM_SPID_SD1_IO; + psram_io.psram_spiwp_sd3_io = PSRAM_SPIWP_SD3_IO; + psram_io.psram_spihd_sd2_io = PSRAM_SPIHD_SD2_IO; + } else if spiconfig == ESP_ROM_EFUSE_FLASH_DEFAULT_HSPI { + psram_io.flash_clk_io = FLASH_HSPI_CLK_IO; + psram_io.flash_cs_io = FLASH_HSPI_CS_IO; + psram_io.psram_spiq_sd0_io = PSRAM_HSPI_SPIQ_SD0_IO; + psram_io.psram_spid_sd1_io = PSRAM_HSPI_SPID_SD1_IO; + psram_io.psram_spiwp_sd3_io = PSRAM_HSPI_SPIWP_SD3_IO; + psram_io.psram_spihd_sd2_io = PSRAM_HSPI_SPIHD_SD2_IO; + } else { + panic!("Getting Flash/PSRAM pins from efuse is not supported"); + // psram_io.flash_clk_io = EFUSE_SPICONFIG_RET_SPICLK(spiconfig); + // psram_io.flash_cs_io = EFUSE_SPICONFIG_RET_SPICS0(spiconfig); + // psram_io.psram_spiq_sd0_io = EFUSE_SPICONFIG_RET_SPIQ(spiconfig); + // psram_io.psram_spid_sd1_io = EFUSE_SPICONFIG_RET_SPID(spiconfig); + // psram_io.psram_spihd_sd2_io = + // EFUSE_SPICONFIG_RET_SPIHD(spiconfig); + // psram_io.psram_spiwp_sd3_io = bootloader_flash_get_wp_pin(); + } + log::info!("PS-RAM pins {:?}", &psram_io); + + write_peri_reg(SPI0_EXT3_REG, 0x1); + clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_PREP_HOLD_M); + + psram_spi_init(mode, clk_mode); + + match mode { + PsramCacheSpeed::PsramCacheF80mS80m => unsafe { + esp_rom_gpio_connect_out_signal( + psram_io.psram_clk_io as u32, + SPICLK_OUT_IDX, + false, + false, + ); + }, + _ => unsafe { + if clk_mode == PsramClkMode::PsramClkModeDclk { + // We need to delay CLK to the PSRAM with respect to the clock signal as output + // by the SPI peripheral. We do this by routing it signal to + // signal 224/225, which are used as a loopback; the extra run through + // the GPIO matrix causes the delay. We use GPIO20 (which is not in any package + // but has pad logic in silicon) as a temporary pad for + // this. So the signal path is: SPI CLK --> GPIO28 --> + // signal224(in then out) --> internal GPIO29 --> signal225(in then out) --> + // GPIO17(PSRAM CLK) + esp_rom_gpio_connect_out_signal( + PSRAM_INTERNAL_IO_28, + SPICLK_OUT_IDX, + false, + false, + ); + esp_rom_gpio_connect_in_signal(PSRAM_INTERNAL_IO_28, SIG_IN_FUNC224_IDX, false); + esp_rom_gpio_connect_out_signal( + PSRAM_INTERNAL_IO_29, + SIG_IN_FUNC224_IDX, + false, + false, + ); + esp_rom_gpio_connect_in_signal(PSRAM_INTERNAL_IO_29, SIG_IN_FUNC225_IDX, false); + esp_rom_gpio_connect_out_signal( + psram_io.psram_clk_io as u32, + SIG_IN_FUNC225_IDX, + false, + false, + ); + } else { + esp_rom_gpio_connect_out_signal( + psram_io.psram_clk_io as u32, + SPICLK_OUT_IDX, + false, + false, + ); + } + }, + } + + let extra_dummy = psram_gpio_config(&psram_io, mode); + + // psram_is_32mbit_ver0 would need special handling here + + unsafe { + esp_rom_gpio_connect_out_signal(PSRAM_INTERNAL_IO_28, SIG_GPIO_OUT_IDX, false, false); + esp_rom_gpio_connect_out_signal(PSRAM_INTERNAL_IO_29, SIG_GPIO_OUT_IDX, false, false); + esp_rom_gpio_connect_out_signal( + psram_io.psram_clk_io as u32, + SPICLK_OUT_IDX, + false, + false, + ); + } + + // Update cs timing according to psram driving method. + psram_set_cs_timing_spi1(mode, clk_mode); + psram_set_cs_timing_spi0(mode, clk_mode); // SPI_CACHE_PORT + psram_enable_qio_mode_spi1(clk_mode, mode); + + log::info!( + "PS-RAM vaddrmode = {:?}", + PsramVaddrMode::PsramVaddrModeLowhigh + ); + + psram_cache_init( + mode, + PsramVaddrMode::PsramVaddrModeLowhigh, + clk_mode, + extra_dummy, + ); + } + + #[allow(unused)] + #[derive(Debug, Clone, Copy, PartialEq)] + enum PsramVaddrMode { + /// App and pro CPU use their own flash cache for external RAM access + PsramVaddrModeNormal = 0, + /// App and pro CPU share external RAM caches: pro CPU has low * 2M, app + /// CPU has high 2M + PsramVaddrModeLowhigh, + /// App and pro CPU share external RAM caches: pro CPU does even 32yte + /// ranges, app does odd ones. + PsramVaddrModeEvenodd, + } + + // register initialization for sram cache params and r/w commands + fn psram_cache_init( + psram_cache_mode: PsramCacheSpeed, + vaddrmode: PsramVaddrMode, + clk_mode: PsramClkMode, + extra_dummy: u32, + ) { + log::info!( + "PS-RAM cache_init, psram_cache_mode={:?}, extra_dummy={}, clk_mode={:?}", + psram_cache_mode, + extra_dummy, + clk_mode + ); + match psram_cache_mode { + PsramCacheSpeed::PsramCacheF80mS80m => { + // flash 1 div clk,80+40; + clear_peri_reg_mask(SPI0_DATE_REG, 1 << 31); + + // pre clk div , ONLY IF SPI/ SRAM@ DIFFERENT SPEED,JUST FOR SPI0. FLASH DIV + // 2+SRAM DIV4 + clear_peri_reg_mask(SPI0_DATE_REG, 1 << 30); + } + PsramCacheSpeed::PsramCacheF80mS40m => { + clear_peri_reg_mask(SPI0_CLOCK_REG, SPI_CLK_EQU_SYSCLK_M); + set_peri_reg_bits(SPI0_CLOCK_REG, SPI_CLKDIV_PRE_V, 0, SPI_CLKDIV_PRE_S); + set_peri_reg_bits(SPI0_CLOCK_REG, SPI_CLKCNT_N, 1, SPI_CLKCNT_N_S); + set_peri_reg_bits(SPI0_CLOCK_REG, SPI_CLKCNT_H, 0, SPI_CLKCNT_H_S); + set_peri_reg_bits(SPI0_CLOCK_REG, SPI_CLKCNT_L, 1, SPI_CLKCNT_L_S); + + // flash 1 div clk + set_peri_reg_mask(SPI0_DATE_REG, 1 << 31); + + // pre clk div ONLY IF SPI/SRAM@ DIFFERENT SPEED,JUST FOR SPI0. + clear_peri_reg_mask(SPI0_DATE_REG, 1 << 30); + } + _ => { + clear_peri_reg_mask(SPI0_DATE_REG, 1 << 31); // flash 1 div clk + clear_peri_reg_mask(SPI0_DATE_REG, 1 << 30); // pre clk div + } + } + + clear_peri_reg_mask(SPI0_CACHE_SCTRL_REG, SPI_USR_SRAM_DIO_M); // disable dio mode for cache command + set_peri_reg_mask(SPI0_CACHE_SCTRL_REG, SPI_USR_SRAM_QIO_M); // enable qio mode for cache command + set_peri_reg_mask(SPI0_CACHE_SCTRL_REG, SPI_CACHE_SRAM_USR_RCMD_M); // enable cache read command + set_peri_reg_mask(SPI0_CACHE_SCTRL_REG, SPI_CACHE_SRAM_USR_WCMD_M); // enable cache write command + set_peri_reg_bits( + SPI0_CACHE_SCTRL_REG, + SPI_SRAM_ADDR_BITLEN_V, + 23, + SPI_SRAM_ADDR_BITLEN_S, + ); // write address for cache command. + set_peri_reg_mask(SPI0_CACHE_SCTRL_REG, SPI_USR_RD_SRAM_DUMMY_M); // enable cache read dummy + + // config sram cache r/w command + set_peri_reg_bits( + SPI0_SRAM_DRD_CMD_REG, + SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V, + 7, + SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S, + ); + set_peri_reg_bits( + SPI0_SRAM_DRD_CMD_REG, + SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V, + PSRAM_FAST_READ_QUAD, + SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S, + ); // 0xEB + set_peri_reg_bits( + SPI0_SRAM_DWR_CMD_REG, + SPI_CACHE_SRAM_USR_WR_CMD_BITLEN, + 7, + SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S, + ); + set_peri_reg_bits( + SPI0_SRAM_DWR_CMD_REG, + SPI_CACHE_SRAM_USR_WR_CMD_VALUE, + PSRAM_QUAD_WRITE, + SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S, + ); // 0x38 + set_peri_reg_bits( + SPI0_CACHE_SCTRL_REG, + SPI_SRAM_DUMMY_CYCLELEN_V, + PSRAM_FAST_READ_QUAD_DUMMY + extra_dummy, + SPI_SRAM_DUMMY_CYCLELEN_S, + ); // dummy, psram cache : 40m--+1dummy; 80m--+2dummy + + match psram_cache_mode { + PsramCacheSpeed::PsramCacheF80mS80m => (), // in this mode , no delay is needed + _ => { + if clk_mode == PsramClkMode::PsramClkModeDclk { + set_peri_reg_bits( + SPI0_SRAM_DRD_CMD_REG, + SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V, + 15, + SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S, + ); // read command length, 2 bytes(1byte for delay),sending in qio mode in cache + set_peri_reg_bits( + SPI0_SRAM_DRD_CMD_REG, + SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V, + (PSRAM_FAST_READ_QUAD) << 8, + SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S, + ); // 0xEB, read command value,(0x00 for delay,0xeb for cmd) + set_peri_reg_bits( + SPI0_SRAM_DWR_CMD_REG, + SPI_CACHE_SRAM_USR_WR_CMD_BITLEN, + 15, + SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S, + ); // write command length,2 bytes(1byte for delay,send in qio mode in cache) + set_peri_reg_bits( + SPI0_SRAM_DWR_CMD_REG, + SPI_CACHE_SRAM_USR_WR_CMD_VALUE, + (PSRAM_QUAD_WRITE) << 8, + SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S, + ); // 0x38, write command value,(0x00 for delay) + set_peri_reg_bits( + SPI0_CACHE_SCTRL_REG, + SPI_SRAM_DUMMY_CYCLELEN_V, + PSRAM_FAST_READ_QUAD_DUMMY + extra_dummy, + SPI_SRAM_DUMMY_CYCLELEN_S, + ); // dummy, psram cache : 40m--+1dummy; 80m--+2dummy + } + } + } + + unsafe { + let dport = &*esp32::DPORT::PTR; + + dport + .pro_cache_ctrl + .modify(|_, w| w.pro_dram_hl().clear_bit().pro_dram_split().clear_bit()); + dport + .app_cache_ctrl + .modify(|_, w| w.app_dram_hl().clear_bit().app_dram_split().clear_bit()); + if vaddrmode == PsramVaddrMode::PsramVaddrModeLowhigh { + dport + .pro_cache_ctrl + .modify(|_, w| w.pro_dram_hl().set_bit()); + dport + .app_cache_ctrl + .modify(|_, w| w.app_dram_hl().set_bit()); + } else if vaddrmode == PsramVaddrMode::PsramVaddrModeEvenodd { + dport + .pro_cache_ctrl + .modify(|_, w| w.pro_dram_split().set_bit()); + dport + .app_cache_ctrl + .modify(|_, w| w.app_dram_split().set_bit()); + } + + // use Dram1 to visit ext sram. cache page mode : 1 -->16k 4 -->2k + // 0-->32k,(accord with the settings in cache_sram_mmu_set) + dport.pro_cache_ctrl1.modify(|_, w| { + w.pro_cache_mask_dram1() + .clear_bit() + .pro_cache_mask_opsdram() + .clear_bit() + }); + dport + .pro_cache_ctrl1 + .modify(|_, w| w.pro_cmmu_sram_page_mode().variant(0)); + + // use Dram1 to visit ext sram. cache page mode : 1 -->16k 4 -->2k + // 0-->32k,(accord with the settings in cache_sram_mmu_set) + dport.app_cache_ctrl1.modify(|_, w| { + w.app_cache_mask_dram1() + .clear_bit() + .app_cache_mask_opsdram() + .clear_bit() + }); + dport + .app_cache_ctrl1 + .modify(|_, w| w.app_cmmu_sram_page_mode().variant(0)); + } + + // ENABLE SPI0 CS1 TO PSRAM(CS0--FLASH; CS1--SRAM) + clear_peri_reg_mask(SPI0_PIN_REG, SPI_CS1_DIS_M); + } + + // spi param init for psram + fn psram_spi_init( + // psram_spi_num_t spi_num = PSRAM_SPI_1, + mode: PsramCacheSpeed, + clk_mode: PsramClkMode, + ) { + clear_peri_reg_mask(SPI1_SLAVE_REG, SPI_TRANS_DONE << 5); + // SPI_CPOL & SPI_CPHA + clear_peri_reg_mask(SPI1_PIN_REG, SPI_CK_IDLE_EDGE); + clear_peri_reg_mask(SPI1_USER_REG, SPI_CK_OUT_EDGE); + // SPI bit order + clear_peri_reg_mask(SPI1_CTRL_REG, SPI_WR_BIT_ORDER); + clear_peri_reg_mask(SPI1_CTRL_REG, SPI_RD_BIT_ORDER); + // SPI bit order + clear_peri_reg_mask(SPI1_USER_REG, SPI_DOUTDIN); + // May be not must to do. + write_peri_reg(SPI1_USER1_REG, 0); + // SPI mode type + clear_peri_reg_mask(SPI1_SLAVE_REG, SPI_SLAVE_MODE); + unsafe { + let ptr = SPI1_W0_REG as *mut u32; + for i in 0..16 { + ptr.offset(i).write_volatile(0); + } + } + psram_set_cs_timing_spi1(mode, clk_mode); + } + + fn psram_set_cs_timing_spi1(psram_cache_mode: PsramCacheSpeed, clk_mode: PsramClkMode) { + if clk_mode == PsramClkMode::PsramClkModeNorm { + set_peri_reg_mask(SPI1_USER_REG, SPI_CS_HOLD_M | SPI_CS_SETUP_M); + // Set cs time. + set_peri_reg_bits( + SPI1_CTRL2_REG, + SPI_HOLD_TIME_V, + psram_cs_hold_time_from_psram_speed(psram_cache_mode), + SPI_HOLD_TIME_S, + ); + set_peri_reg_bits(SPI1_CTRL2_REG, SPI_SETUP_TIME_V, 0, SPI_SETUP_TIME_S); + } else { + clear_peri_reg_mask(SPI1_USER_REG, SPI_CS_HOLD_M | SPI_CS_SETUP_M); + } + } + + fn psram_set_cs_timing_spi0(psram_cache_mode: PsramCacheSpeed, clk_mode: PsramClkMode) { + if clk_mode == PsramClkMode::PsramClkModeNorm { + set_peri_reg_mask(SPI0_USER_REG, SPI_CS_HOLD_M | SPI_CS_SETUP_M); + // Set cs time. + set_peri_reg_bits( + SPI0_CTRL2_REG, + SPI_HOLD_TIME_V, + psram_cs_hold_time_from_psram_speed(psram_cache_mode), + SPI_HOLD_TIME_S, + ); + set_peri_reg_bits(SPI0_CTRL2_REG, SPI_SETUP_TIME_V, 0, SPI_SETUP_TIME_S); + } else { + clear_peri_reg_mask(SPI0_USER_REG, SPI_CS_HOLD_M | SPI_CS_SETUP_M); + } + } + + #[derive(Debug, Copy, Clone, PartialEq)] + struct PsramCmd { + cmd: u16, // Command value + cmd_bit_len: u16, // Command byte length + addr: *const u32, // Point to address value + addr_bit_len: u16, // Address byte length + tx_data: *const u32, // Point to send data buffer + tx_data_bit_len: u16, // Send data byte length. + rx_data: *mut u32, // Point to recevie data buffer + rx_data_bit_len: u16, // Recevie Data byte length. + dummy_bit_len: u32, + } + + impl Default for PsramCmd { + fn default() -> Self { + Self { + cmd: Default::default(), + cmd_bit_len: Default::default(), + addr: core::ptr::null(), + addr_bit_len: Default::default(), + tx_data: core::ptr::null(), + tx_data_bit_len: Default::default(), + rx_data: core::ptr::null_mut(), + rx_data_bit_len: Default::default(), + dummy_bit_len: Default::default(), + } + } + } + + const PSRAM_ENTER_QMODE: u32 = 0x35; + + // enter QPI mode + #[ram] + fn psram_enable_qio_mode_spi1(clk_mode: PsramClkMode, psram_mode: PsramCacheSpeed) { + let mut ps_cmd: PsramCmd = PsramCmd::default(); + let addr: u32 = (PSRAM_ENTER_QMODE << 24) | 0; + + ps_cmd.cmd_bit_len = 0; + if clk_mode == PsramClkMode::PsramClkModeDclk { + match psram_mode { + PsramCacheSpeed::PsramCacheF80mS80m => (), + _ => { + ps_cmd.cmd_bit_len = 2; + } + } + } + ps_cmd.cmd = 0; + ps_cmd.addr = &addr; + ps_cmd.addr_bit_len = 8; + ps_cmd.tx_data = core::ptr::null(); + ps_cmd.tx_data_bit_len = 0; + ps_cmd.rx_data = core::ptr::null_mut(); + 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(), 0, PsramCmdMode::PsramCmdSpi); + psram_cmd_end_spi1(backup_usr, backup_usr1, backup_usr2); + } + + #[ram] + fn psram_cmd_end_spi1(backup_usr: u32, backup_usr1: u32, backup_usr2: u32) { + loop { + if read_peri_reg(SPI1_CMD_REG) & SPI_USR == 0 { + break; + } + } + + write_peri_reg(SPI1_USER_REG, backup_usr); + write_peri_reg(SPI1_USER1_REG, backup_usr1); + write_peri_reg(SPI1_USER2_REG, backup_usr2); + } + + // setup spi command/addr/data/dummy in user mode + #[ram] + fn psram_cmd_config_spi1(p_in_data: &PsramCmd) -> (u32, u32, u32) { + loop { + if read_peri_reg(SPI1_CMD_REG) & SPI_USR == 0 { + break; + } + } + + let backup_usr = read_peri_reg(SPI1_USER_REG); + let backup_usr1 = read_peri_reg(SPI1_USER1_REG); + let backup_usr2 = read_peri_reg(SPI1_USER2_REG); + + // Set command by user. + if p_in_data.cmd_bit_len != 0 { + // Max command length 16 bits. + set_peri_reg_bits( + SPI1_USER2_REG, + SPI_USR_COMMAND_BITLEN, + (p_in_data.cmd_bit_len - 1) as u32, + SPI_USR_COMMAND_BITLEN_S, + ); + // Enable command + set_peri_reg_mask(SPI1_USER_REG, SPI_USR_COMMAND); + // Load command,bit15-0 is cmd value. + set_peri_reg_bits( + SPI1_USER2_REG, + SPI_USR_COMMAND_VALUE, + p_in_data.cmd as u32, + SPI_USR_COMMAND_VALUE_S, + ); + } else { + clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_COMMAND); + set_peri_reg_bits( + SPI1_USER2_REG, + SPI_USR_COMMAND_BITLEN, + 0, + SPI_USR_COMMAND_BITLEN_S, + ); + } + // Set Address by user. + if p_in_data.addr_bit_len != 0 { + set_peri_reg_bits( + SPI1_USER1_REG, + SPI_USR_ADDR_BITLEN, + (p_in_data.addr_bit_len - 1) as u32, + SPI_USR_ADDR_BITLEN_S, + ); + // Enable address + set_peri_reg_mask(SPI1_USER_REG, SPI_USR_ADDR); + // Set address + write_peri_reg(SPI1_ADDR_REG, unsafe { p_in_data.addr.read_volatile() }); + } else { + clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_ADDR); + set_peri_reg_bits( + SPI1_USER1_REG, + SPI_USR_ADDR_BITLEN, + 0, + SPI_USR_ADDR_BITLEN_S, + ); + } + // Set data by user. + let p_tx_val = p_in_data.tx_data; + if p_in_data.tx_data_bit_len != 0 { + // Enable MOSI + clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_MOSI); + // Load send buffer + let len = (p_in_data.tx_data_bit_len + 31) / 32; + if !p_tx_val.is_null() { + for i in 0..len { + write_peri_reg(SPI1_W0_REG, unsafe { + p_tx_val.offset(i as isize).read_volatile() + }); + } + } + // Set data send buffer length.Max data length 64 bytes. + set_peri_reg_bits( + SPI1_MOSI_DLEN_REG, + SPI_USR_MOSI_DBITLEN, + (p_in_data.tx_data_bit_len - 1) as u32, + SPI_USR_MOSI_DBITLEN_S, + ); + } else { + clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_MOSI); + set_peri_reg_bits( + SPI1_MOSI_DLEN_REG, + SPI_USR_MOSI_DBITLEN, + 0, + SPI_USR_MOSI_DBITLEN_S, + ); + } + // Set rx data by user. + if p_in_data.rx_data_bit_len != 0 { + // Enable MOSI + set_peri_reg_mask(SPI1_USER_REG, SPI_USR_MISO); + // Set data send buffer length.Max data length 64 bytes. + set_peri_reg_bits( + SPI1_MISO_DLEN_REG, + SPI_USR_MISO_DBITLEN, + (p_in_data.rx_data_bit_len - 1) as u32, + SPI_USR_MISO_DBITLEN_S, + ); + } else { + clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_MISO); + set_peri_reg_bits( + SPI1_MISO_DLEN_REG, + SPI_USR_MISO_DBITLEN, + 0, + SPI_USR_MISO_DBITLEN_S, + ); + } + if p_in_data.dummy_bit_len != 0 { + set_peri_reg_mask(SPI1_USER_REG, SPI_USR_DUMMY); // dummy en + set_peri_reg_bits( + SPI1_USER1_REG, + SPI_USR_DUMMY_CYCLELEN_V, + p_in_data.dummy_bit_len - 1, + SPI_USR_DUMMY_CYCLELEN_S, + ); // DUMMY + } else { + clear_peri_reg_mask(SPI1_USER_REG, SPI_USR_DUMMY); // dummy en + set_peri_reg_bits( + SPI1_USER1_REG, + SPI_USR_DUMMY_CYCLELEN_V, + 0, + SPI_USR_DUMMY_CYCLELEN_S, + ); // DUMMY + } + + (backup_usr, backup_usr1, backup_usr2) + } + + #[derive(Debug, Clone, Copy, PartialEq)] + enum PsramCmdMode { + PsramCmdQpi, + PsramCmdSpi, + } + + // start sending cmd/addr and optionally, receiving data + #[ram] + fn psram_cmd_recv_start_spi1(p_rx_data: *mut u32, rx_byte_len: u16, cmd_mode: PsramCmdMode) { + // get cs1 + clear_peri_reg_mask(SPI1_PIN_REG, SPI_CS1_DIS_M); + set_peri_reg_mask(SPI1_PIN_REG, SPI_CS0_DIS_M); + + let mode_backup: u32 = (read_peri_reg(SPI1_USER_REG) >> SPI_FWRITE_DUAL_S) & 0xf; + let rd_mode_backup: u32 = read_peri_reg(SPI1_CTRL_REG) + & (SPI_FREAD_DIO_M | SPI_FREAD_DUAL_M | SPI_FREAD_QUAD_M | SPI_FREAD_QIO_M); + if cmd_mode == PsramCmdMode::PsramCmdSpi { + psram_set_basic_write_mode_spi1(); + psram_set_basic_read_mode_spi1(); + } else if cmd_mode == PsramCmdMode::PsramCmdQpi { + psram_set_qio_write_mode_spi1(); + psram_set_qio_read_mode_spi1(); + } + + // Wait for SPI0 to idle + loop { + if read_peri_reg(SPI0_EXT2_REG) == 0 { + break; + } + } + unsafe { + // DPORT_SET_PERI_REG_MASK(DPORT_HOST_INF_SEL_REG, 1 << 14); + let dport = &*esp32::DPORT::PTR; + dport.host_inf_sel.modify(|r, w| w.bits(r.bits() | 1 << 14)); + } + + // Start send data + set_peri_reg_mask(SPI1_CMD_REG, SPI_USR); + loop { + if read_peri_reg(SPI1_CMD_REG) & SPI_USR == 0 { + break; + } + } + unsafe { + // DPORT_CLEAR_PERI_REG_MASK(DPORT_HOST_INF_SEL_REG, 1 << 14); + let dport = &*esp32::DPORT::PTR; + dport + .host_inf_sel + .modify(|r, w| w.bits(r.bits() & !(1 << 14))); + } + + // recover spi mode + set_peri_reg_bits( + SPI1_USER_REG, + if !p_rx_data.is_null() { + SPI_FWRITE_DUAL_M + } else { + 0xf + }, + mode_backup, + SPI_FWRITE_DUAL_S, + ); + clear_peri_reg_mask( + SPI1_CTRL_REG, + SPI_FREAD_DIO_M | SPI_FREAD_DUAL_M | SPI_FREAD_QUAD_M | SPI_FREAD_QIO_M, + ); + set_peri_reg_mask(SPI1_CTRL_REG, rd_mode_backup); + + // return cs to cs0 + set_peri_reg_mask(SPI1_PIN_REG, SPI_CS1_DIS_M); + clear_peri_reg_mask(SPI1_PIN_REG, SPI_CS0_DIS_M); + + if !p_rx_data.is_null() { + let mut idx = 0; + // Read data out + loop { + unsafe { + p_rx_data + .offset(idx) + .write_volatile(read_peri_reg(SPI1_W0_REG + ((idx as u32) << 2))); + } + + idx += 1; + if idx > ((rx_byte_len / 4) + if (rx_byte_len % 4) != 0 { 1 } else { 0 }) as isize { + break; + } + } + } + } + + // set basic SPI write mode + fn psram_set_basic_write_mode_spi1() { + clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_QIO); + clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_DIO); + clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_QUAD); + clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_DUAL); + } + // set QPI write mode + fn psram_set_qio_write_mode_spi1() { + set_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_QIO); + clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_DIO); + clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_QUAD); + clear_peri_reg_mask(SPI1_USER_REG, SPI_FWRITE_DUAL); + } + // set QPI read mode + fn psram_set_qio_read_mode_spi1() { + set_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_QIO); + clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_QUAD); + clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_DUAL); + clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_DIO); + } + // set SPI read mode + fn psram_set_basic_read_mode_spi1() { + clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_QIO); + clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_QUAD); + clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_DUAL); + clear_peri_reg_mask(SPI1_CTRL_REG, SPI_FREAD_DIO); + } + + // psram gpio init , different working frequency we have different solutions + fn psram_gpio_config(psram_io: &PsramIo, mode: PsramCacheSpeed) -> u32 { + let g_rom_spiflash_dummy_len_plus_ptr: *mut u8 = + unsafe { core::mem::transmute(&g_rom_spiflash_dummy_len_plus) }; + + fn gpio_pin_mux_reg(gpio: u8) -> u32 { + crate::gpio::get_io_mux_reg(gpio).as_ptr() as u32 + } + + fn gpio_hal_iomux_func_sel(reg: u32, function: u32) { + unsafe { + let ptr = reg as *mut u32; + let old = ptr.read_volatile(); + ptr.write_volatile((old & !(0b111 << 12)) | (function << 12)); + } + } + + let spi_cache_dummy; + let rd_mode_reg = read_peri_reg(SPI0_CTRL_REG); + if (rd_mode_reg & SPI_FREAD_QIO_M) != 0 { + spi_cache_dummy = SPI0_R_QIO_DUMMY_CYCLELEN; + } else if (rd_mode_reg & SPI_FREAD_DIO_M) != 0 { + spi_cache_dummy = SPI0_R_DIO_DUMMY_CYCLELEN; + set_peri_reg_bits( + SPI0_USER1_REG, + SPI_USR_ADDR_BITLEN_V, + SPI0_R_DIO_ADDR_BITSLEN, + SPI_USR_ADDR_BITLEN_S, + ); + } else if (rd_mode_reg & (SPI_FREAD_QUAD_M | SPI_FREAD_DUAL_M)) != 0 { + spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; + } else { + spi_cache_dummy = SPI0_R_FAST_DUMMY_CYCLELEN; + } + + let extra_dummy; + + match mode { + PsramCacheSpeed::PsramCacheF80mS40m => { + extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M; + unsafe { + g_rom_spiflash_dummy_len_plus_ptr + .offset(_SPI_CACHE_PORT as isize) + .write_volatile(PSRAM_IO_MATRIX_DUMMY_80M); + g_rom_spiflash_dummy_len_plus_ptr + .offset(_SPI_FLASH_PORT as isize) + .write_volatile(PSRAM_IO_MATRIX_DUMMY_40M); + } + set_peri_reg_bits( + SPI0_USER1_REG, + SPI_USR_DUMMY_CYCLELEN_V, + spi_cache_dummy + PSRAM_IO_MATRIX_DUMMY_80M as u32, + SPI_USR_DUMMY_CYCLELEN_S, + ); // DUMMY + unsafe { + esp_rom_spiflash_config_clk(_SPI_80M_CLK_DIV, _SPI_CACHE_PORT); + esp_rom_spiflash_config_clk(_SPI_40M_CLK_DIV, _SPI_FLASH_PORT); + } + // set drive ability for clock + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.flash_clk_io), + FUN_DRV, + 3, + FUN_DRV_S, + ); + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.psram_clk_io), + FUN_DRV, + 2, + FUN_DRV_S, + ); + } + PsramCacheSpeed::PsramCacheF80mS80m => { + extra_dummy = PSRAM_IO_MATRIX_DUMMY_80M; + unsafe { + g_rom_spiflash_dummy_len_plus_ptr + .offset(_SPI_CACHE_PORT as isize) + .write_volatile(PSRAM_IO_MATRIX_DUMMY_80M); + g_rom_spiflash_dummy_len_plus_ptr + .offset(_SPI_FLASH_PORT as isize) + .write_volatile(PSRAM_IO_MATRIX_DUMMY_80M); + } + set_peri_reg_bits( + SPI0_USER1_REG, + SPI_USR_DUMMY_CYCLELEN_V, + spi_cache_dummy + PSRAM_IO_MATRIX_DUMMY_80M as u32, + SPI_USR_DUMMY_CYCLELEN_S, + ); // DUMMY + unsafe { + esp_rom_spiflash_config_clk(_SPI_80M_CLK_DIV, _SPI_CACHE_PORT); + esp_rom_spiflash_config_clk(_SPI_80M_CLK_DIV, _SPI_FLASH_PORT); + } + // set drive ability for clock + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.flash_clk_io), + FUN_DRV, + 3, + FUN_DRV_S, + ); + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.psram_clk_io), + FUN_DRV, + 3, + FUN_DRV_S, + ); + } + PsramCacheSpeed::PsramCacheF40mS40m => { + extra_dummy = PSRAM_IO_MATRIX_DUMMY_40M; + unsafe { + g_rom_spiflash_dummy_len_plus_ptr + .offset(_SPI_CACHE_PORT as isize) + .write_volatile(PSRAM_IO_MATRIX_DUMMY_40M); + g_rom_spiflash_dummy_len_plus_ptr + .offset(_SPI_FLASH_PORT as isize) + .write_volatile(PSRAM_IO_MATRIX_DUMMY_40M); + } + set_peri_reg_bits( + SPI0_USER1_REG, + SPI_USR_DUMMY_CYCLELEN_V, + spi_cache_dummy + PSRAM_IO_MATRIX_DUMMY_40M as u32, + SPI_USR_DUMMY_CYCLELEN_S, + ); // DUMMY + unsafe { + esp_rom_spiflash_config_clk(_SPI_40M_CLK_DIV, _SPI_CACHE_PORT); + esp_rom_spiflash_config_clk(_SPI_40M_CLK_DIV, _SPI_FLASH_PORT); + } + // set drive ability for clock + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.flash_clk_io), + FUN_DRV, + 2, + FUN_DRV_S, + ); + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.psram_clk_io), + FUN_DRV, + 2, + FUN_DRV_S, + ); + } + } + set_peri_reg_mask(SPI0_USER_REG, SPI_USR_DUMMY); // dummy enable + + // In bootloader, all the signals are already configured, + // We keep the following code in case the bootloader is some older version. + unsafe { + esp_rom_gpio_connect_out_signal( + psram_io.flash_cs_io as u32, + SPICS0_OUT_IDX, + false, + false, + ); + esp_rom_gpio_connect_out_signal( + psram_io.psram_cs_io as u32, + SPICS1_OUT_IDX, + false, + false, + ); + esp_rom_gpio_connect_out_signal( + psram_io.psram_spiq_sd0_io as u32, + SPIQ_OUT_IDX, + false, + false, + ); + esp_rom_gpio_connect_in_signal(psram_io.psram_spiq_sd0_io as u32, SPIQ_IN_IDX, false); + esp_rom_gpio_connect_out_signal( + psram_io.psram_spid_sd1_io as u32, + SPID_OUT_IDX, + false, + false, + ); + esp_rom_gpio_connect_in_signal(psram_io.psram_spid_sd1_io as u32, SPID_IN_IDX, false); + esp_rom_gpio_connect_out_signal( + psram_io.psram_spiwp_sd3_io as u32, + SPIWP_OUT_IDX, + false, + false, + ); + esp_rom_gpio_connect_in_signal(psram_io.psram_spiwp_sd3_io as u32, SPIWP_IN_IDX, false); + esp_rom_gpio_connect_out_signal( + psram_io.psram_spihd_sd2_io as u32, + SPIHD_OUT_IDX, + false, + false, + ); + esp_rom_gpio_connect_in_signal(psram_io.psram_spihd_sd2_io as u32, SPIHD_IN_IDX, false); + } + + // select pin function gpio + if (psram_io.flash_clk_io == SPI_IOMUX_PIN_NUM_CLK) + && (psram_io.flash_clk_io != psram_io.psram_clk_io) + { + // flash clock signal should come from IO MUX. + gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.flash_clk_io), FUNC_SD_CLK_SPICLK); + } else { + // flash clock signal should come from GPIO matrix. + gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.flash_clk_io), PIN_FUNC_GPIO); + } + gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.flash_cs_io), PIN_FUNC_GPIO); + gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_cs_io), PIN_FUNC_GPIO); + gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_clk_io), PIN_FUNC_GPIO); + gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_spiq_sd0_io), PIN_FUNC_GPIO); + gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_spid_sd1_io), PIN_FUNC_GPIO); + gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_spihd_sd2_io), PIN_FUNC_GPIO); + gpio_hal_iomux_func_sel(gpio_pin_mux_reg(psram_io.psram_spiwp_sd3_io), PIN_FUNC_GPIO); + + let flash_id: u32 = unsafe { g_rom_flashchip.device_id }; + log::info!("Flash-ID = {}", flash_id); + + if flash_id == FLASH_ID_GD25LQ32C { + // Set drive ability for 1.8v flash in 80Mhz. + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.flash_cs_io), + FUN_DRV_V, + 3, + FUN_DRV_S, + ); + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.flash_clk_io), + FUN_DRV_V, + 3, + FUN_DRV_S, + ); + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.psram_cs_io), + FUN_DRV_V, + 3, + FUN_DRV_S, + ); + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.psram_clk_io), + FUN_DRV_V, + 3, + FUN_DRV_S, + ); + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.psram_spiq_sd0_io), + FUN_DRV_V, + 3, + FUN_DRV_S, + ); + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.psram_spid_sd1_io), + FUN_DRV_V, + 3, + FUN_DRV_S, + ); + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.psram_spihd_sd2_io), + FUN_DRV_V, + 3, + FUN_DRV_S, + ); + set_peri_reg_bits( + gpio_pin_mux_reg(psram_io.psram_spiwp_sd3_io), + FUN_DRV_V, + 3, + FUN_DRV_S, + ); + } + + extra_dummy as u32 + } + + fn clear_peri_reg_mask(reg: u32, mask: u32) { + unsafe { + (reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() & !mask); + } + } + + fn set_peri_reg_mask(reg: u32, mask: u32) { + unsafe { + (reg as *mut u32).write_volatile((reg as *mut u32).read_volatile() | mask); + } + } + + 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), + ); + } + } + + fn write_peri_reg(reg: u32, val: u32) { + unsafe { + (reg as *mut u32).write_volatile(val); + } + } + + fn read_peri_reg(reg: u32) -> u32 { + unsafe { (reg as *mut u32).read_volatile() } + } +} diff --git a/esp-hal-common/src/soc/esp32s2/peripherals.rs b/esp-hal-common/src/soc/esp32s2/peripherals.rs index 56da54c5e65..5b284039e1b 100644 --- a/esp-hal-common/src/soc/esp32s2/peripherals.rs +++ b/esp-hal-common/src/soc/esp32s2/peripherals.rs @@ -47,5 +47,6 @@ crate::peripherals! { USB0 => true, USB_WRAP => true, XTS_AES => true, - RADIO => false + RADIO => false, + PSRAM => false, } diff --git a/esp-hal-common/src/soc/esp32s2/psram.rs b/esp-hal-common/src/soc/esp32s2/psram.rs index 09c8e9846de..dde48bdbd44 100644 --- a/esp-hal-common/src/soc/esp32s2/psram.rs +++ b/esp-hal-common/src/soc/esp32s2/psram.rs @@ -1,10 +1,23 @@ -#[cfg(not(any(feature = "psram_2m", feature = "psram_4m", feature = "psram_8m")))] -pub(crate) fn init_psram() { - // nothing to do +const PSRAM_VADDR: u32 = 0x3f500000; + +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 const PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024; + +pub const PSRAM_VADDR_START: usize = PSRAM_VADDR as usize; + #[cfg(any(feature = "psram_2m", feature = "psram_4m", feature = "psram_8m"))] -pub(crate) fn init_psram() { +pub fn init_psram(_peripheral: impl crate::peripheral::Peripheral
) {
#[allow(unused)]
enum CacheLayout {
CacheMemoryInvalid = 0,
@@ -80,24 +93,6 @@ pub(crate) fn init_psram() {
const START_PAGE: u32 = 0;
- 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;
- }
- };
-
- #[no_mangle]
- static PSRAM_BYTES: usize = PSRAM_SIZE as usize * 1024 * 1024;
-
- #[no_mangle]
- static PSRAM_VADDR_START: usize = PSRAM_VADDR as usize;
-
if cache_dbus_mmu_set(
MMU_ACCESS_SPIRAM,
PSRAM_VADDR,
diff --git a/esp32-hal/.cargo/config.toml b/esp32-hal/.cargo/config.toml
index aef483f9749..97f257ba667 100644
--- a/esp32-hal/.cargo/config.toml
+++ b/esp32-hal/.cargo/config.toml
@@ -9,4 +9,4 @@ rustflags = [
target = "xtensa-esp32-none-elf"
[unstable]
-build-std = ["core"]
+build-std = ["core", "alloc"]
diff --git a/esp32-hal/Cargo.toml b/esp32-hal/Cargo.toml
index df3f6fe66d4..243ecb7d177 100644
--- a/esp32-hal/Cargo.toml
+++ b/esp32-hal/Cargo.toml
@@ -38,9 +38,10 @@ critical-section = "1.1.1"
crypto-bigint = { version = "0.5.2", default-features = false}
embassy-executor = { version = "0.2.0", features = ["nightly", "integrated-timers", "arch-xtensa", "executor-thread"] }
embedded-graphics = "0.7.1"
+esp-alloc = "0.3.0"
esp-backtrace = { version = "0.7.0", features = ["esp32", "panic-handler", "exception-handler", "print-uart"] }
esp-hal-smartled = { version = "0.2.0", features = ["esp32"], path = "../esp-hal-smartled" }
-esp-println = { version = "0.5.0", features = ["esp32"] }
+esp-println = { version = "0.5.0", features = ["esp32", "log"] }
sha2 = { version = "0.10.6", default-features = false}
smart-leds = "0.3.0"
ssd1306 = "0.7.1"
@@ -59,6 +60,11 @@ embassy-time-timg0 = ["esp-hal-common/embassy-time-timg0", "embassy-time/tick-hz
xtal40mhz = ["esp-hal-common/esp32_40mhz"]
xtal26mhz = ["esp-hal-common/esp32_26mhz"]
+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"]
+
[[example]]
name = "spi_eh1_loopback"
required-features = ["eh1"]
@@ -78,3 +84,7 @@ required-features = ["embassy", "async"]
[[example]]
name = "embassy_spi"
required-features = ["embassy", "async"]
+
+[[example]]
+name = "psram"
+required-features = ["psram_2m"]
diff --git a/esp32-hal/examples/psram.rs b/esp32-hal/examples/psram.rs
new file mode 100644
index 00000000000..b55670c674f
--- /dev/null
+++ b/esp32-hal/examples/psram.rs
@@ -0,0 +1,77 @@
+//! This shows how to use PSRAM as heap-memory via esp-alloc
+//!
+//! You need an ESP32 with at least 2 MB of PSRAM memory.
+
+#![no_std]
+#![no_main]
+
+use esp32_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)]
+ panic!("PSRAM on ESP32 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.DPORT.split();
+ let clocks = ClockControl::boot_defaults(system.clock_control).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