From 9a6f894046ce743f52f78d6a357204aacb676d57 Mon Sep 17 00:00:00 2001 From: Armando Date: Thu, 18 Nov 2021 15:34:22 +0800 Subject: [PATCH] psram: add octal psram ECC feature --- components/esp32s3/Kconfig | 11 +- .../esp_hw_support/Kconfig.spiram.common | 9 + .../include/soc/esp32s3/spiram.h | 6 +- .../port/esp32s3/opiram_psram.c | 129 +++++++++++--- .../esp_hw_support/port/esp32s3/spiram.c | 157 ++++++++---------- .../port/esp32s3/spiram_psram.c | 54 +++--- .../port/esp32s3/spiram_psram.h | 44 +++-- .../sdkconfig.ci.f8r8_80ddr_80ddr_ecc | 11 ++ 8 files changed, 273 insertions(+), 148 deletions(-) create mode 100644 tools/test_apps/system/flash_psram/sdkconfig.ci.f8r8_80ddr_80ddr_ecc diff --git a/components/esp32s3/Kconfig b/components/esp32s3/Kconfig index 5034d652cc..38a2909a15 100644 --- a/components/esp32s3/Kconfig +++ b/components/esp32s3/Kconfig @@ -85,6 +85,7 @@ menu "ESP32S3-Specific" config ESP32S3_INSTRUCTION_CACHE_WRAP bool ## TODO IDF-4307 default "n" + depends on !SPIRAM_ECC_ENABLE help If enabled, instruction cache will use wrap mode to read spi flash or spi ram. The wrap length equals to ESP32S3_INSTRUCTION_CACHE_LINE_SIZE. @@ -154,6 +155,7 @@ menu "ESP32S3-Specific" config ESP32S3_DATA_CACHE_WRAP bool ## TODO IDF-4307 default "n" + depends on !SPIRAM_ECC_ENABLE help If enabled, data cache will use wrap mode to read spi flash or spi ram. The wrap length equals to ESP32S3_DATA_CACHE_LINE_SIZE. @@ -259,8 +261,13 @@ menu "ESP32S3-Specific" bool "40Mhz clock speed" endchoice - # insert non-chip-specific items here - source "$IDF_PATH/components/esp_hw_support/Kconfig.spiram.common" + config SPIRAM_SPEED + int + default 120 if SPIRAM_SPEED_120M + default 80 if SPIRAM_SPEED_80M + default 40 if SPIRAM_SPEED_40M + + source "$IDF_PATH/components/esp_hw_support/Kconfig.spiram.common" # insert non-chip-specific items here endmenu diff --git a/components/esp_hw_support/Kconfig.spiram.common b/components/esp_hw_support/Kconfig.spiram.common index ae9b298b4a..7dbf539af9 100644 --- a/components/esp_hw_support/Kconfig.spiram.common +++ b/components/esp_hw_support/Kconfig.spiram.common @@ -111,3 +111,12 @@ config SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY Note the values placed into this section will not be initialized at startup and should keep its value after software restart. + +config SPIRAM_ECC_ENABLE + bool "Enable SPI RAM ECC" + default n + depends on SPIRAM_MODE_OCT && IDF_TARGET_ESP32S3 + help + Enable MSPI Error-Correcting Code function when accessing SPIRAM. + + If enabled, 1/16 of the SPI RAM total size will be reserved for error-correcting code. diff --git a/components/esp_hw_support/include/soc/esp32s3/spiram.h b/components/esp_hw_support/include/soc/esp32s3/spiram.h index 067c3d27bf..e25a15d692 100644 --- a/components/esp_hw_support/include/soc/esp32s3/spiram.h +++ b/components/esp_hw_support/include/soc/esp32s3/spiram.h @@ -52,15 +52,15 @@ bool esp_spiram_test(void); */ esp_err_t esp_spiram_add_to_heapalloc(void); - /** - * @brief Get the size of the attached SPI RAM chip selected in menuconfig + * @brief Get the available physical size of the attached SPI RAM chip + * + * @note If ECC is enabled, the available physical size would be smaller than the physical size. See `CONFIG_SPIRAM_ECC_ENABLE` * * @return Size in bytes, or 0 if no external RAM chip support compiled in. */ size_t esp_spiram_get_size(void); - /** * @brief Force a writeback of the data in the SPI RAM cache. This is to be called whenever * cache is disabled, because disabling cache on the ESP32 discards the data in the SPI diff --git a/components/esp_hw_support/port/esp32s3/opiram_psram.c b/components/esp_hw_support/port/esp32s3/opiram_psram.c index 927479e808..efcecdbc6b 100644 --- a/components/esp_hw_support/port/esp32s3/opiram_psram.c +++ b/components/esp_hw_support/port/esp32s3/opiram_psram.c @@ -42,8 +42,13 @@ #define OCT_PSRAM_CS_SETUP_TIME 3 #define OCT_PSRAM_CS_HOLD_TIME 3 +#define OCT_PSRAM_CS_ECC_HOLD_TIME 3 #define OCT_PSRAM_CS_HOLD_DELAY 2 +#define OCT_PSRAM_PAGE_SIZE 2 //2 for 1024B +#define OCT_PSRAM_ECC_ENABLE_MASK BIT(8) + + typedef struct { union { struct { @@ -99,7 +104,7 @@ typedef struct { } opi_psram_mode_reg_t; static const char* TAG = "opi psram"; -static psram_size_t s_psram_size; +static uint32_t s_psram_size; //this stands for physical psram size in bytes static void s_config_psram_spi_phases(void); uint8_t psram_get_cs_io(void) @@ -114,7 +119,7 @@ static void s_init_psram_mode_reg(int spi_num, opi_psram_mode_reg_t *mode_reg_co { esp_rom_spiflash_read_mode_t mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE; int cmd_len = 16; - uint32_t addr = 0x0; + uint32_t addr = 0x0; //0x0 is the MR0 register int addr_bit_len = 32; int dummy = OCT_PSRAM_RD_DUMMY_BITLEN; opi_psram_mode_reg_t mode_reg = {0}; @@ -129,6 +134,7 @@ static void s_init_psram_mode_reg(int spi_num, opi_psram_mode_reg_t *mode_reg_co &mode_reg.mr0.val, data_bit_len, BIT(1), false); + //modify mode_reg.mr0.lt = mode_reg_config->mr0.lt; mode_reg.mr0.read_latency = mode_reg_config->mr0.read_latency; @@ -143,6 +149,34 @@ static void s_init_psram_mode_reg(int spi_num, opi_psram_mode_reg_t *mode_reg_co NULL, 0, BIT(1), false); + +#if CONFIG_SPIRAM_ECC_ENABLE + addr = 0x8; //0x8 is the MR8 register + data_bit_len = 8; + //read + esp_rom_opiflash_exec_cmd(spi_num, mode, + OPI_PSRAM_REG_READ, cmd_len, + addr, addr_bit_len, + dummy, + NULL, 0, + &mode_reg.mr8.val, data_bit_len, + BIT(1), + false); + + //modify + mode_reg.mr8.bt = mode_reg_config->mr8.bt; + mode_reg.mr8.bl = mode_reg_config->mr8.bl; + + //write + esp_rom_opiflash_exec_cmd(spi_num, mode, + OPI_PSRAM_REG_WRITE, cmd_len, + addr, addr_bit_len, + 0, + &mode_reg.mr8.val, 16, + NULL, 0, + BIT(1), + false); +#endif } static void s_get_psram_mode_reg(int spi_num, opi_psram_mode_reg_t *out_reg) @@ -194,18 +228,18 @@ static void s_get_psram_mode_reg(int spi_num, opi_psram_mode_reg_t *out_reg) static void s_print_psram_info(opi_psram_mode_reg_t *reg_val) { - ESP_EARLY_LOGI(TAG, "vendor id : 0x%02x (%s)", reg_val->mr1.vendor_id, reg_val->mr1.vendor_id == 0x0d ? "AP" : "UNKNOWN"); - ESP_EARLY_LOGI(TAG, "dev id : 0x%02x (generation %d)", reg_val->mr2.dev_id, reg_val->mr2.dev_id + 1); - ESP_EARLY_LOGI(TAG, "density : 0x%02x (%d Mbit)", reg_val->mr2.density, reg_val->mr2.density == 0x1 ? 32 : - reg_val->mr2.density == 0X3 ? 64 : - reg_val->mr2.density == 0x5 ? 128 : - reg_val->mr2.density == 0x7 ? 256 : 0); - ESP_EARLY_LOGI(TAG, "good-die : 0x%02x (%s)", reg_val->mr2.gb, reg_val->mr2.gb == 1 ? "Pass" : "Fail"); - ESP_EARLY_LOGI(TAG, "Latency : 0x%02x (%s)", reg_val->mr0.lt, reg_val->mr0.lt == 1 ? "Fixed" : "Variable"); - ESP_EARLY_LOGI(TAG, "VCC : 0x%02x (%s)", reg_val->mr3.vcc, reg_val->mr3.vcc == 1 ? "3V" : "1.8V"); - ESP_EARLY_LOGI(TAG, "SRF : 0x%02x (%s Refresh)", reg_val->mr3.srf, reg_val->mr3.srf == 0x1 ? "Fast" : "Slow"); - ESP_EARLY_LOGI(TAG, "BurstType : 0x%02x (%s Wrap)", reg_val->mr8.bt, reg_val->mr8.bt == 1 && reg_val->mr8.bl != 3 ? "Hybrid" : ""); - ESP_EARLY_LOGI(TAG, "BurstLen : 0x%02x (%d Byte)", reg_val->mr8.bl, reg_val->mr8.bl == 0x00 ? 16 : + ESP_EARLY_LOGI(TAG, "vendor id : 0x%02x (%s)", reg_val->mr1.vendor_id, reg_val->mr1.vendor_id == 0x0d ? "AP" : "UNKNOWN"); + ESP_EARLY_LOGI(TAG, "dev id : 0x%02x (generation %d)", reg_val->mr2.dev_id, reg_val->mr2.dev_id + 1); + ESP_EARLY_LOGI(TAG, "density : 0x%02x (%d Mbit)", reg_val->mr2.density, reg_val->mr2.density == 0x1 ? 32 : + reg_val->mr2.density == 0X3 ? 64 : + reg_val->mr2.density == 0x5 ? 128 : + reg_val->mr2.density == 0x7 ? 256 : 0); + ESP_EARLY_LOGI(TAG, "good-die : 0x%02x (%s)", reg_val->mr2.gb, reg_val->mr2.gb == 1 ? "Pass" : "Fail"); + ESP_EARLY_LOGI(TAG, "Latency : 0x%02x (%s)", reg_val->mr0.lt, reg_val->mr0.lt == 1 ? "Fixed" : "Variable"); + ESP_EARLY_LOGI(TAG, "VCC : 0x%02x (%s)", reg_val->mr3.vcc, reg_val->mr3.vcc == 1 ? "3V" : "1.8V"); + ESP_EARLY_LOGI(TAG, "SRF : 0x%02x (%s Refresh)", reg_val->mr3.srf, reg_val->mr3.srf == 0x1 ? "Fast" : "Slow"); + ESP_EARLY_LOGI(TAG, "BurstType : 0x%02x (%s Wrap)", reg_val->mr8.bt, reg_val->mr8.bt == 1 && reg_val->mr8.bl != 3 ? "Hybrid" : ""); + ESP_EARLY_LOGI(TAG, "BurstLen : 0x%02x (%d Byte)", reg_val->mr8.bl, reg_val->mr8.bl == 0x00 ? 16 : reg_val->mr8.bl == 0x01 ? 32 : reg_val->mr8.bl == 0x10 ? 64 : 1024); ESP_EARLY_LOGI(TAG, "Readlatency : 0x%02x (%d cycles@%s)", reg_val->mr0.read_latency, reg_val->mr0.read_latency * 2 + 6, @@ -215,12 +249,15 @@ static void s_print_psram_info(opi_psram_mode_reg_t *reg_val) reg_val->mr0.drive_str == 0x02 ? 4 : 8); } -static void psram_set_cs_timing(void) +static void s_set_psram_cs_timing(void) { //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(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_CS_HOLD_M | SPI_MEM_SPI_SMEM_CS_SETUP_M); SET_PERI_REG_BITS(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_CS_HOLD_TIME_V, OCT_PSRAM_CS_HOLD_TIME, SPI_MEM_SPI_SMEM_CS_HOLD_TIME_S); SET_PERI_REG_BITS(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_CS_SETUP_TIME_V, OCT_PSRAM_CS_SETUP_TIME, SPI_MEM_SPI_SMEM_CS_SETUP_TIME_S); +#if CONFIG_SPIRAM_ECC_ENABLE + SET_PERI_REG_BITS(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_ECC_CS_HOLD_TIME_V, OCT_PSRAM_CS_ECC_HOLD_TIME, SPI_MEM_SPI_SMEM_ECC_CS_HOLD_TIME_S); +#endif //CS1 high time SET_PERI_REG_BITS(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_CS_HOLD_DELAY_V, OCT_PSRAM_CS_HOLD_DELAY, SPI_MEM_SPI_SMEM_CS_HOLD_DELAY_S); } @@ -235,10 +272,35 @@ static void s_init_psram_pins(void) REG_SET_FIELD(SPI_MEM_DATE_REG(0), SPI_MEM_SPI_SMEM_SPICLK_FUN_DRV, 3); } +/** + * Enable error correcting code feature + * + * Can add an input parameter for selecting ECC mode if needed + */ +static void s_configure_psram_ecc(void) +{ +#if CONFIG_SPIRAM_ECC_ENABLE + //Clear this bit to use ECC 16to17 mode + CLEAR_PERI_REG_MASK(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_ECC_16TO18_BYTE_EN_M); + SET_PERI_REG_BITS(SYSCON_SPI_MEM_ECC_CTRL_REG, SYSCON_SRAM_PAGE_SIZE_V, OCT_PSRAM_PAGE_SIZE, SYSCON_SRAM_PAGE_SIZE_S); + SET_PERI_REG_MASK(SPI_MEM_SPI_SMEM_AC_REG(0), SPI_MEM_SPI_SMEM_ECC_SKIP_PAGE_CORNER_M); + /** + * Enable ECC region 0 (ACE0) + * Default: ACE0 range: 0 ~ 256MB + * Current Octal PSRAM is 8MB, ACE0 is enough + */ + SET_PERI_REG_MASK(SYSCON_SRAM_ACE0_ATTR_REG, OCT_PSRAM_ECC_ENABLE_MASK); + ESP_EARLY_LOGI(TAG, "ECC is enabled"); +#else + CLEAR_PERI_REG_MASK(SYSCON_SRAM_ACE0_ATTR_REG, OCT_PSRAM_ECC_ENABLE_MASK); +#endif +} + esp_err_t psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vaddrmode) { s_init_psram_pins(); - psram_set_cs_timing(); + s_set_psram_cs_timing(); + s_configure_psram_ecc(); //enter MSPI slow mode to init PSRAM device registers spi_timing_enter_mspi_low_speed_mode(true); @@ -252,14 +314,16 @@ esp_err_t psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vaddrmode) mode_reg.mr0.lt = 1; mode_reg.mr0.read_latency = 2; mode_reg.mr0.drive_str = 0; + mode_reg.mr8.bl = 3; + mode_reg.mr8.bt = 0; s_init_psram_mode_reg(1, &mode_reg); //Print PSRAM info s_get_psram_mode_reg(1, &mode_reg); s_print_psram_info(&mode_reg); - s_psram_size = mode_reg.mr2.density == 0x1 ? PSRAM_SIZE_32MBITS : - mode_reg.mr2.density == 0X3 ? PSRAM_SIZE_64MBITS : - mode_reg.mr2.density == 0x5 ? PSRAM_SIZE_128MBITS : - mode_reg.mr2.density == 0x7 ? PSRAM_SIZE_256MBITS : 0; + s_psram_size = mode_reg.mr2.density == 0x1 ? PSRAM_SIZE_4MB : + mode_reg.mr2.density == 0X3 ? PSRAM_SIZE_8MB : + mode_reg.mr2.density == 0x5 ? PSRAM_SIZE_16MB : + mode_reg.mr2.density == 0x7 ? PSRAM_SIZE_32MB : 0; //Do PSRAM timing tuning, we use SPI1 to do the tuning, and set the SPI0 PSRAM timing related registers accordingly spi_timing_psram_tuning(); @@ -310,9 +374,30 @@ static void s_config_psram_spi_phases(void) Cache_Resume_DCache(0); } -psram_size_t psram_get_size() + +/*--------------------------------------------------------------------------------- + * Following APIs are not required to be IRAM-Safe + * + * Consider moving these to another file if this kind of APIs grows dramatically + *-------------------------------------------------------------------------------*/ +esp_err_t psram_get_physical_size(uint32_t *out_size_bytes) { - return s_psram_size; + *out_size_bytes = s_psram_size; + return (s_psram_size ? ESP_OK : ESP_ERR_INVALID_STATE); +} + +/** + * This function is to get the available physical psram size in bytes. + * If ECC is enabled, available PSRAM size will be 15/16 times its physical size. + */ +esp_err_t psram_get_available_size(uint32_t *out_size_bytes) +{ +#if CONFIG_SPIRAM_ECC_ENABLE + *out_size_bytes = s_psram_size * 15 / 16; +#else + *out_size_bytes = s_psram_size; +#endif + return (s_psram_size ? ESP_OK : ESP_ERR_INVALID_STATE); } #endif //#if CONFIG_SPIRAM_MODE_OCT diff --git a/components/esp_hw_support/port/esp32s3/spiram.c b/components/esp_hw_support/port/esp32s3/spiram.c index d0fa5f09af..8191986300 100644 --- a/components/esp_hw_support/port/esp32s3/spiram.c +++ b/components/esp_hw_support/port/esp32s3/spiram.c @@ -28,6 +28,11 @@ we add more types of external RAM memory, this can be made into a more intellige #include "soc/cache_memory.h" #include "soc/extmem_reg.h" +/** + * @note consider abstract these cache register operations, so as to make `spiram.c` not needed to be IRAM-SAFE. + * This file only contains abstract operations. + */ + #define PSRAM_MODE PSRAM_VADDR_MODE_NORMAL #if CONFIG_SPIRAM @@ -42,6 +47,41 @@ static const char *TAG = "spiram"; static bool s_spiram_inited = false; +//These variables are in bytes +static uint32_t s_allocable_vaddr_start; +static uint32_t s_allocable_vaddr_end; +static DRAM_ATTR uint32_t s_mapped_vaddr_start; +static DRAM_ATTR uint32_t s_mapped_size; + +/** + * Initially map all psram physical address to virtual address. + * If psram physical size is larger than virtual address range, then only map the virtual address range. + */ +void IRAM_ATTR esp_spiram_init_cache(void) +{ + esp_err_t ret = psram_get_available_size(&s_mapped_size); + if (ret != ESP_OK) { + abort(); + } + if ((SOC_EXTRAM_DATA_HIGH - SOC_EXTRAM_DATA_LOW) < s_mapped_size) { + //Decide these logics when there's a real PSRAM with larger size + ESP_EARLY_LOGE(TAG, "Virtual address not enough for PSRAM!"); + abort(); + } + s_mapped_vaddr_start = SOC_EXTRAM_DATA_HIGH - s_mapped_size; + + Cache_Suspend_DCache(); + Cache_Dbus_MMU_Set(MMU_ACCESS_SPIRAM, s_mapped_vaddr_start, 0, 64, s_mapped_size >> 16, 0); + REG_CLR_BIT(EXTMEM_DCACHE_CTRL1_REG, EXTMEM_DCACHE_SHUT_CORE0_BUS); +#if !CONFIG_FREERTOS_UNICORE + REG_CLR_BIT(EXTMEM_DCACHE_CTRL1_REG, EXTMEM_DCACHE_SHUT_CORE1_BUS); +#endif + Cache_Resume_DCache(0); + + //Currently no non-heap stuff on ESP32S3 + s_allocable_vaddr_start = s_mapped_vaddr_start; + s_allocable_vaddr_end = SOC_EXTRAM_DATA_HIGH; +} /* Simple RAM test. Writes a word every 32 bytes. Takes about a second to complete for 4MiB. Returns @@ -50,18 +90,13 @@ static bool s_spiram_inited = false; */ bool esp_spiram_test(void) { - size_t spiram_size = esp_spiram_get_size(); - volatile int *spiram = (volatile int *)(SOC_EXTRAM_DATA_HIGH - spiram_size); + volatile int *spiram = (volatile int *)s_mapped_vaddr_start; + + size_t s = s_mapped_size; size_t p; - size_t s = spiram_size; int errct = 0; int initial_err = -1; - if (SOC_EXTRAM_DATA_SIZE < spiram_size) { - ESP_EARLY_LOGW(TAG, "Only test spiram from %08x to %08x\n", SOC_EXTRAM_DATA_LOW, SOC_EXTRAM_DATA_HIGH); - spiram = (volatile int *)SOC_EXTRAM_DATA_LOW; - s = SOC_EXTRAM_DATA_HIGH - SOC_EXTRAM_DATA_LOW; - } for (p = 0; p < (s / sizeof(int)); p += 8) { spiram[p] = p ^ 0xAAAAAAAA; } @@ -85,23 +120,8 @@ bool esp_spiram_test(void) } } -void IRAM_ATTR esp_spiram_init_cache(void) -{ - size_t spiram_size = esp_spiram_get_size(); - Cache_Suspend_DCache(); - if ((SOC_EXTRAM_DATA_HIGH - SOC_EXTRAM_DATA_LOW) >= spiram_size) { - Cache_Dbus_MMU_Set(MMU_ACCESS_SPIRAM, SOC_EXTRAM_DATA_HIGH - spiram_size, 0, 64, spiram_size >> 16, 0); - } else { - Cache_Dbus_MMU_Set(MMU_ACCESS_SPIRAM, SOC_EXTRAM_DATA_HIGH - spiram_size, 0, 64, (SOC_EXTRAM_DATA_HIGH - SOC_EXTRAM_DATA_LOW) >> 16, 0); - } - REG_CLR_BIT(EXTMEM_DCACHE_CTRL1_REG, EXTMEM_DCACHE_SHUT_CORE0_BUS); -#if !CONFIG_FREERTOS_UNICORE - REG_CLR_BIT(EXTMEM_DCACHE_CTRL1_REG, EXTMEM_DCACHE_SHUT_CORE1_BUS); -#endif - Cache_Resume_DCache(0); -} - -static uint32_t pages_for_flash = 0; +//TODO IDF-4318 +// static uint32_t pages_for_flash = 0; static uint32_t instruction_in_spiram = 0; static uint32_t rodata_in_spiram = 0; @@ -135,41 +155,16 @@ uint32_t esp_spiram_rodata_access_enabled(void) #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS esp_err_t esp_spiram_enable_instruction_access(void) { - size_t spiram_size = esp_spiram_get_size(); - uint32_t pages_in_flash = 0; - pages_in_flash += Cache_Count_Flash_Pages(CACHE_IBUS, &page0_mapped); - if ((pages_in_flash + pages_for_flash) > (spiram_size >> 16)) { - ESP_EARLY_LOGE(TAG, "SPI RAM space not enough for the instructions, has %d pages, need %d pages.", (spiram_size >> 16), (pages_in_flash + pages_for_flash)); - return ESP_FAIL; - } - ESP_EARLY_LOGI(TAG, "Instructions copied and mapped to SPIRAM"); - uint32_t mmu_value = *(volatile uint32_t *)(DR_REG_MMU_TABLE + CACHE_IROM_MMU_START); - instr_flash2spiram_offs = mmu_value - pages_for_flash; - ESP_EARLY_LOGV(TAG, "Instructions from flash page%d copy to SPIRAM page%d, Offset: %d", mmu_value, pages_for_flash, instr_flash2spiram_offs); - pages_for_flash = Cache_Flash_To_SPIRAM_Copy(CACHE_IBUS, IRAM0_CACHE_ADDRESS_LOW, pages_for_flash, &page0_page); - instruction_in_spiram = 1; - return ESP_OK; + //TODO IDF-4318, `pages_for_flash` will be overwritten, however it influences the psram size to be added to the heap allocator. + abort(); } #endif #if CONFIG_SPIRAM_RODATA esp_err_t esp_spiram_enable_rodata_access(void) { - size_t spiram_size = esp_spiram_get_size(); - uint32_t pages_in_flash = 0; - pages_in_flash += Cache_Count_Flash_Pages(CACHE_DBUS, &page0_mapped); - - if ((pages_in_flash + pages_for_flash) > (spiram_size >> 16)) { - ESP_EARLY_LOGE(TAG, "SPI RAM space not enough for the read only data."); - return ESP_FAIL; - } - ESP_EARLY_LOGI(TAG, "Read only data copied and mapped to SPIRAM"); - uint32_t mmu_value = *(volatile uint32_t *)(DR_REG_MMU_TABLE + CACHE_DROM_MMU_START); - rodata_flash2spiram_offs = mmu_value - pages_for_flash; - ESP_EARLY_LOGV(TAG, "Rodata from flash page%d copy to SPIRAM page%d, Offset: %d", mmu_value, pages_for_flash, rodata_flash2spiram_offs); - pages_for_flash = Cache_Flash_To_SPIRAM_Copy(CACHE_DBUS, DRAM0_CACHE_ADDRESS_LOW, pages_for_flash, &page0_page); - rodata_in_spiram = 1; - return ESP_OK; + //TODO IDF-4318, `pages_for_flash` will be overwritten, however it influences the psram size to be added to the heap allocator. + abort(); } #endif @@ -228,6 +223,7 @@ int IRAM_ATTR rodata_flash2spiram_offset(void) esp_err_t esp_spiram_init(void) { esp_err_t r; + uint32_t psram_physical_size = 0; r = psram_enable(PSRAM_SPEED, PSRAM_MODE); if (r != ESP_OK) { #if CONFIG_SPIRAM_IGNORE_NOTFOUND @@ -236,33 +232,34 @@ esp_err_t esp_spiram_init(void) return r; } s_spiram_inited = true; + + r = psram_get_physical_size(&psram_physical_size); + if (r != ESP_OK) { + abort(); + } #if (CONFIG_SPIRAM_SIZE != -1) - if (esp_spiram_get_size() != CONFIG_SPIRAM_SIZE) { - ESP_EARLY_LOGE(TAG, "Expected %dKiB chip but found %dKiB chip. Bailing out..", CONFIG_SPIRAM_SIZE / 1024, esp_spiram_get_size() / 1024); + if (psram_physical_size != CONFIG_SPIRAM_SIZE) { + ESP_EARLY_LOGE(TAG, "Expected %dMB chip but found %dMB chip. Bailing out..", (CONFIG_SPIRAM_SIZE / 1024 / 1024), (psram_physical_size / 1024 / 1024)); return ESP_ERR_INVALID_SIZE; } #endif - - ESP_EARLY_LOGI(TAG, "Found %dMBit SPI RAM device", - (esp_spiram_get_size() * 8) / (1024 * 1024)); - ESP_EARLY_LOGI(TAG, "SPI RAM mode: %s", PSRAM_SPEED == PSRAM_CACHE_S40M ? "sram 40m" : "sram 80m"); - ESP_EARLY_LOGI(TAG, "PSRAM initialized, cache is in %s mode.", \ + ESP_EARLY_LOGI(TAG, "Found %dMB SPI RAM device", psram_physical_size / (1024 * 1024)); + ESP_EARLY_LOGI(TAG, "Speed: %dMHz", CONFIG_SPIRAM_SPEED); + ESP_EARLY_LOGI(TAG, "Initialized, cache is in %s mode.", \ (PSRAM_MODE == PSRAM_VADDR_MODE_EVENODD) ? "even/odd (2-core)" : \ (PSRAM_MODE == PSRAM_VADDR_MODE_LOWHIGH) ? "low/high (2-core)" : \ (PSRAM_MODE == PSRAM_VADDR_MODE_NORMAL) ? "normal (1-core)" : "ERROR"); return ESP_OK; } - +/** + * Add entire external RAM region to heap allocator. Heap allocator knows the capabilities of this type of memory, + * so there's no need to explicitly specify them. + */ esp_err_t esp_spiram_add_to_heapalloc(void) { - size_t spiram_size = esp_spiram_get_size(); - uint32_t size_for_flash = (pages_for_flash << 16); - ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", (spiram_size - (pages_for_flash << 16)) / 1024); - //Add entire external RAM region to heap allocator. Heap allocator knows the capabilities of this type of memory, so there's - //no need to explicitly specify them. - - return heap_caps_add_region((intptr_t)SOC_EXTRAM_DATA_HIGH - spiram_size + size_for_flash, (intptr_t)SOC_EXTRAM_DATA_HIGH - 1); + ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", (s_allocable_vaddr_end - s_allocable_vaddr_start) / 1024); + return heap_caps_add_region(s_allocable_vaddr_start, s_allocable_vaddr_end - 1); } @@ -289,23 +286,13 @@ size_t esp_spiram_get_size(void) abort(); } - psram_size_t size = psram_get_size(); - if (size == PSRAM_SIZE_16MBITS) { - return 2 * 1024 * 1024; + uint32_t size = 0; //in bytes + esp_err_t ret = psram_get_available_size(&size); + if (ret == ESP_OK) { + return size; + } else { + return 0; } - if (size == PSRAM_SIZE_32MBITS) { - return 4 * 1024 * 1024; - } - if (size == PSRAM_SIZE_64MBITS) { - return 8 * 1024 * 1024; - } - if (size == PSRAM_SIZE_128MBITS) { - return 16 * 1024 * 1024; - } - if (size == PSRAM_SIZE_256MBITS) { - return 32 * 1024 * 1024; - } - return CONFIG_SPIRAM_SIZE; } /* diff --git a/components/esp_hw_support/port/esp32s3/spiram_psram.c b/components/esp_hw_support/port/esp32s3/spiram_psram.c index 280300a91d..9ca43271ec 100644 --- a/components/esp_hw_support/port/esp32s3/spiram_psram.c +++ b/components/esp_hw_support/port/esp32s3/spiram_psram.c @@ -26,7 +26,6 @@ #include "soc/efuse_periph.h" #include "soc/soc_caps.h" #include "soc/io_mux_reg.h" -#include "soc/syscon_reg.h" #include "soc/efuse_reg.h" #include "soc/soc.h" #include "soc/io_mux_reg.h" @@ -104,11 +103,6 @@ static const char* TAG = "psram"; #define SPI0_NUM 0 -typedef enum { - PSRAM_EID_SIZE_16MBITS = 0, - PSRAM_EID_SIZE_32MBITS = 1, - PSRAM_EID_SIZE_64MBITS = 2, -} psram_eid_size_t; typedef enum { PSRAM_CMD_QPI, @@ -118,6 +112,7 @@ typedef enum { typedef esp_rom_spi_cmd_t psram_cmd_t; static uint32_t s_psram_id = 0; +static uint32_t s_psram_size = 0; //this stands for physical psram size in bytes static void config_psram_spi_phases(void); extern void esp_rom_spi_set_op_mode(int spi_num, esp_rom_spiflash_read_mode_t mode); @@ -325,20 +320,6 @@ static void psram_gpio_config(void) esp_rom_spiflash_select_qio_pins(wp_io, spiconfig); } -psram_size_t psram_get_size(void) -{ - if ((PSRAM_SIZE_ID(s_psram_id) == PSRAM_EID_SIZE_64MBITS) || PSRAM_IS_64MBIT_TRIAL(s_psram_id)) { - return PSRAM_SIZE_64MBITS; - } else if (PSRAM_SIZE_ID(s_psram_id) == PSRAM_EID_SIZE_32MBITS) { - return PSRAM_SIZE_32MBITS; - } else if (PSRAM_SIZE_ID(s_psram_id) == PSRAM_EID_SIZE_16MBITS) { - return PSRAM_SIZE_16MBITS; - } else { - return PSRAM_SIZE_MAX; - } - return PSRAM_SIZE_MAX; -} - /* * Psram mode init will overwrite original flash speed mode, so that it is possible to change psram and flash speed after OTA. * Flash read mode(QIO/QOUT/DIO/DOUT) will not be changed in app bin. It is decided by bootloader, OTA can not change this mode. @@ -368,6 +349,15 @@ esp_err_t psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vaddrmode) } } + if (PSRAM_IS_64MBIT_TRIAL(s_psram_id)) { + s_psram_size = PSRAM_SIZE_8MB; + } else { + uint8_t density = PSRAM_SIZE_ID(s_psram_id); + s_psram_size = density == 0x0 ? PSRAM_SIZE_2MB : + density == 0x1 ? PSRAM_SIZE_4MB : + density == 0x2 ? PSRAM_SIZE_8MB : 0; + } + //SPI1: send psram reset command psram_reset_mode(SPI1_NUM); //SPI1: send QPI enable command @@ -410,4 +400,28 @@ static void config_psram_spi_phases(void) CLEAR_PERI_REG_MASK(SPI_MEM_MISC_REG(0), SPI_MEM_CS1_DIS_M); //ENABLE SPI0 CS1 TO PSRAM(CS0--FLASH; CS1--SRAM) } + + +/*--------------------------------------------------------------------------------- + * Following APIs are not required to be IRAM-Safe + * + * Consider moving these to another file if this kind of APIs grows dramatically + *-------------------------------------------------------------------------------*/ +esp_err_t psram_get_physical_size(uint32_t *out_size_bytes) +{ + *out_size_bytes = s_psram_size; + return (s_psram_size ? ESP_OK : ESP_ERR_INVALID_STATE); +} + +/** + * This function is to get the available physical psram size in bytes. + * + * When ECC is enabled, the available size will be reduced. + * On S3 Quad PSRAM, ECC is not enabled for now. + */ +esp_err_t psram_get_available_size(uint32_t *out_size_bytes) +{ + *out_size_bytes = s_psram_size; + return (s_psram_size ? ESP_OK : ESP_ERR_INVALID_STATE); +} #endif // CONFIG_SPIRAM diff --git a/components/esp_hw_support/port/esp32s3/spiram_psram.h b/components/esp_hw_support/port/esp32s3/spiram_psram.h index 06128ccfcc..dde57cb938 100644 --- a/components/esp_hw_support/port/esp32s3/spiram_psram.h +++ b/components/esp_hw_support/port/esp32s3/spiram_psram.h @@ -3,28 +3,28 @@ * * SPDX-License-Identifier: Apache-2.0 */ +#pragma once - -#ifndef _PSRAM_H -#define _PSRAM_H #include "soc/spi_mem_reg.h" #include "esp_err.h" #include "sdkconfig.h" +#define PSRAM_SIZE_2MB (2 * 1024 * 1024) +#define PSRAM_SIZE_4MB (4 * 1024 * 1024) +#define PSRAM_SIZE_8MB (8 * 1024 * 1024) +#define PSRAM_SIZE_16MB (16 * 1024 * 1024) +#define PSRAM_SIZE_32MB (32 * 1024 * 1024) + +#ifdef __cplusplus +extern "C" { +#endif + typedef enum { PSRAM_CACHE_S80M = 1, PSRAM_CACHE_S40M, PSRAM_CACHE_MAX, } psram_cache_mode_t; -typedef enum { - PSRAM_SIZE_16MBITS = 0, - PSRAM_SIZE_32MBITS = 1, - PSRAM_SIZE_64MBITS = 2, - PSRAM_SIZE_128MBITS = 3, - PSRAM_SIZE_256MBITS = 4, - PSRAM_SIZE_MAX, -} psram_size_t; /* See the TRM, chapter PID/MPU/MMU, header 'External RAM' for the definitions of these modes. @@ -40,12 +40,22 @@ typedef enum { } psram_vaddr_mode_t; /** - * @brief get psram size - * @return - * - PSRAM_SIZE_MAX if psram not enabled or not valid - * - PSRAM size + * @brief To get the physical psram size in bytes. + * + * @param[out] out_size_bytes physical psram size in bytes. */ -psram_size_t psram_get_size(void); +esp_err_t psram_get_physical_size(uint32_t *out_size_bytes); + +/** + * @brief To get the available physical psram size in bytes. + * + * If ECC is enabled, available PSRAM size will be 15/16 times its physical size. + * If not, it equals to the physical psram size. + * @note For now ECC is only enabled on ESP32S3 Octal PSRAM + * + * @param[out] out_size_bytes availabe physical psram size in bytes. + */ +esp_err_t psram_get_available_size(uint32_t *out_size_bytes); /** * @brief psram cache enable function @@ -75,4 +85,6 @@ esp_err_t esp_spiram_wrap_set(spiram_wrap_mode_t mode); */ uint8_t psram_get_cs_io(void); +#ifdef __cplusplus +} #endif diff --git a/tools/test_apps/system/flash_psram/sdkconfig.ci.f8r8_80ddr_80ddr_ecc b/tools/test_apps/system/flash_psram/sdkconfig.ci.f8r8_80ddr_80ddr_ecc new file mode 100644 index 0000000000..f433757c24 --- /dev/null +++ b/tools/test_apps/system/flash_psram/sdkconfig.ci.f8r8_80ddr_80ddr_ecc @@ -0,0 +1,11 @@ +# Legacy, F8R8, Flash 80M DDR, PSRAM 80M DDR + +CONFIG_SPI_FLASH_USE_LEGACY_IMPL=y +CONFIG_ESPTOOLPY_OCT_FLASH=y +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_DTR=y +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESP32S3_SPIRAM_SUPPORT=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_80M=y +CONFIG_SPIRAM_ECC_ENABLE = y