Merge branch 'feature/support_s3_octal_psram_ecc_feature' into 'master'

psram: add octal psram Error Correcting Code (ECC) feature

Closes IDF-4258

See merge request espressif/esp-idf!15832
This commit is contained in:
Michael (XIAO Xufeng) 2021-12-14 06:56:21 +00:00
commit 590c18f5c8
9 changed files with 329 additions and 150 deletions

View File

@ -83,8 +83,9 @@ menu "ESP32S3-Specific"
default 32 if ESP32S3_INSTRUCTION_CACHE_LINE_32B
config ESP32S3_INSTRUCTION_CACHE_WRAP
bool "Enable instruction cache wrap mode"
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.
@ -152,8 +153,9 @@ menu "ESP32S3-Specific"
default 64 if ESP32S3_DATA_CACHE_LINE_64B
config ESP32S3_DATA_CACHE_WRAP
bool "Enable data cache wrap mode"
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

View File

@ -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.

View File

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

View File

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

View File

@ -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;
}
/*

View File

@ -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);
@ -195,6 +190,7 @@ static void psram_disable_qio_mode(int spi_num)
false); /* whether is program/erase operation */
}
//TODO IDF-4307
//switch psram burst length(32 bytes or 1024 bytes)
//datasheet says it should be 1024 bytes by default
static void psram_set_wrap_burst_length(int spi_num, psram_cmd_mode_t mode)
@ -233,6 +229,7 @@ static void psram_reset_mode(int spi_num)
esp_err_t psram_enable_wrap(uint32_t wrap_size)
{
//TODO: IDF-4307
static uint32_t current_wrap_size = 0;
if (current_wrap_size == wrap_size) {
return ESP_OK;
@ -323,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.
@ -366,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
@ -408,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

View File

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

View File

@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
#include <sys/param.h>
#include <string.h>
#include "esp_log.h"
#include "test_utils.h"
#include "unity.h"
#include "esp_heap_caps.h"
#if CONFIG_SPIRAM
#include "spiram.h"
const static char *TAG = "PSRAM";
#if CONFIG_SPIRAM_MODE_OCT
#define TEST_ALLOC_SIZE (4 * 1024 * 1024)
#else
#define TEST_ALLOC_SIZE (1 * 1024 * 1024)
#endif
TEST_CASE("test psram heap allocable","[psram]")
{
uint32_t *ext_buffer = (uint32_t *)heap_caps_calloc(TEST_ALLOC_SIZE, 1, MALLOC_CAP_SPIRAM);
TEST_ASSERT(ext_buffer);
uintptr_t start = (uintptr_t)ext_buffer;
uintptr_t end = (uintptr_t)ext_buffer + TEST_ALLOC_SIZE;
ESP_LOGI(TAG, "test ext buffer start addr is %x, end addr is %x", start, end);
TEST_ASSERT((start >= SOC_EXTRAM_DATA_LOW) && (end <= SOC_EXTRAM_DATA_HIGH));
for (int i = 0; i < TEST_ALLOC_SIZE / sizeof(uint32_t); i++) {
ext_buffer[i] = (i + 1) ^ 0xaaaaaaaa;
}
for (int i = 0; i < TEST_ALLOC_SIZE / sizeof(uint32_t); i++) {
TEST_ASSERT(ext_buffer[i] == ((i + 1) ^ 0xaaaaaaaa));
}
free(ext_buffer);
}
#endif //#if CONFIG_SPIRAM

View File

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