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
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 default 32 if ESP32S3_INSTRUCTION_CACHE_LINE_32B
config ESP32S3_INSTRUCTION_CACHE_WRAP config ESP32S3_INSTRUCTION_CACHE_WRAP
bool "Enable instruction cache wrap mode" bool ## TODO IDF-4307
default "n" default "n"
depends on !SPIRAM_ECC_ENABLE
help help
If enabled, instruction cache will use wrap mode to read spi flash or spi ram. 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. 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 default 64 if ESP32S3_DATA_CACHE_LINE_64B
config ESP32S3_DATA_CACHE_WRAP config ESP32S3_DATA_CACHE_WRAP
bool "Enable data cache wrap mode" bool ## TODO IDF-4307
default "n" default "n"
depends on !SPIRAM_ECC_ENABLE
help help
If enabled, data cache will use wrap mode to read spi flash or spi ram. 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. The wrap length equals to ESP32S3_DATA_CACHE_LINE_SIZE.
@@ -259,8 +261,13 @@ menu "ESP32S3-Specific"
bool "40Mhz clock speed" bool "40Mhz clock speed"
endchoice endchoice
# insert non-chip-specific items here config SPIRAM_SPEED
source "$IDF_PATH/components/esp_hw_support/Kconfig.spiram.common" 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 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 Note the values placed into this section will not be initialized at startup and should keep its value
after software restart. 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); 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. * @return Size in bytes, or 0 if no external RAM chip support compiled in.
*/ */
size_t esp_spiram_get_size(void); 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 * @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 * 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_SETUP_TIME 3
#define OCT_PSRAM_CS_HOLD_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_CS_HOLD_DELAY 2
#define OCT_PSRAM_PAGE_SIZE 2 //2 for 1024B
#define OCT_PSRAM_ECC_ENABLE_MASK BIT(8)
typedef struct { typedef struct {
union { union {
struct { struct {
@@ -99,7 +104,7 @@ typedef struct {
} opi_psram_mode_reg_t; } opi_psram_mode_reg_t;
static const char* TAG = "opi psram"; 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); static void s_config_psram_spi_phases(void);
uint8_t psram_get_cs_io(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; esp_rom_spiflash_read_mode_t mode = ESP_ROM_SPIFLASH_OPI_DTR_MODE;
int cmd_len = 16; int cmd_len = 16;
uint32_t addr = 0x0; uint32_t addr = 0x0; //0x0 is the MR0 register
int addr_bit_len = 32; int addr_bit_len = 32;
int dummy = OCT_PSRAM_RD_DUMMY_BITLEN; int dummy = OCT_PSRAM_RD_DUMMY_BITLEN;
opi_psram_mode_reg_t mode_reg = {0}; 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, &mode_reg.mr0.val, data_bit_len,
BIT(1), BIT(1),
false); false);
//modify //modify
mode_reg.mr0.lt = mode_reg_config->mr0.lt; mode_reg.mr0.lt = mode_reg_config->mr0.lt;
mode_reg.mr0.read_latency = mode_reg_config->mr0.read_latency; 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, NULL, 0,
BIT(1), BIT(1),
false); 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) 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) 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, "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, "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 : 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 == 0X3 ? 64 :
reg_val->mr2.density == 0x5 ? 128 : reg_val->mr2.density == 0x5 ? 128 :
reg_val->mr2.density == 0x7 ? 256 : 0); 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, "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, "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, "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, "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, "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, "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 == 0x01 ? 32 :
reg_val->mr8.bl == 0x10 ? 64 : 1024); 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, 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); 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 //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_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_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); 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 //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); 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); 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) esp_err_t psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vaddrmode)
{ {
s_init_psram_pins(); 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 //enter MSPI slow mode to init PSRAM device registers
spi_timing_enter_mspi_low_speed_mode(true); 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.lt = 1;
mode_reg.mr0.read_latency = 2; mode_reg.mr0.read_latency = 2;
mode_reg.mr0.drive_str = 0; mode_reg.mr0.drive_str = 0;
mode_reg.mr8.bl = 3;
mode_reg.mr8.bt = 0;
s_init_psram_mode_reg(1, &mode_reg); s_init_psram_mode_reg(1, &mode_reg);
//Print PSRAM info //Print PSRAM info
s_get_psram_mode_reg(1, &mode_reg); s_get_psram_mode_reg(1, &mode_reg);
s_print_psram_info(&mode_reg); s_print_psram_info(&mode_reg);
s_psram_size = mode_reg.mr2.density == 0x1 ? PSRAM_SIZE_32MBITS : s_psram_size = mode_reg.mr2.density == 0x1 ? PSRAM_SIZE_4MB :
mode_reg.mr2.density == 0X3 ? PSRAM_SIZE_64MBITS : mode_reg.mr2.density == 0X3 ? PSRAM_SIZE_8MB :
mode_reg.mr2.density == 0x5 ? PSRAM_SIZE_128MBITS : mode_reg.mr2.density == 0x5 ? PSRAM_SIZE_16MB :
mode_reg.mr2.density == 0x7 ? PSRAM_SIZE_256MBITS : 0; 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 //Do PSRAM timing tuning, we use SPI1 to do the tuning, and set the SPI0 PSRAM timing related registers accordingly
spi_timing_psram_tuning(); spi_timing_psram_tuning();
@@ -310,9 +374,30 @@ static void s_config_psram_spi_phases(void)
Cache_Resume_DCache(0); 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 #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/cache_memory.h"
#include "soc/extmem_reg.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 #define PSRAM_MODE PSRAM_VADDR_MODE_NORMAL
#if CONFIG_SPIRAM #if CONFIG_SPIRAM
@@ -42,6 +47,41 @@ static const char *TAG = "spiram";
static bool s_spiram_inited = false; 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 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) bool esp_spiram_test(void)
{ {
size_t spiram_size = esp_spiram_get_size(); volatile int *spiram = (volatile int *)s_mapped_vaddr_start;
volatile int *spiram = (volatile int *)(SOC_EXTRAM_DATA_HIGH - spiram_size);
size_t s = s_mapped_size;
size_t p; size_t p;
size_t s = spiram_size;
int errct = 0; int errct = 0;
int initial_err = -1; 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) { for (p = 0; p < (s / sizeof(int)); p += 8) {
spiram[p] = p ^ 0xAAAAAAAA; spiram[p] = p ^ 0xAAAAAAAA;
} }
@@ -85,23 +120,8 @@ bool esp_spiram_test(void)
} }
} }
void IRAM_ATTR esp_spiram_init_cache(void) //TODO IDF-4318
{ // static uint32_t pages_for_flash = 0;
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;
static uint32_t instruction_in_spiram = 0; static uint32_t instruction_in_spiram = 0;
static uint32_t rodata_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 #if CONFIG_SPIRAM_FETCH_INSTRUCTIONS
esp_err_t esp_spiram_enable_instruction_access(void) esp_err_t esp_spiram_enable_instruction_access(void)
{ {
size_t spiram_size = esp_spiram_get_size(); //TODO IDF-4318, `pages_for_flash` will be overwritten, however it influences the psram size to be added to the heap allocator.
uint32_t pages_in_flash = 0; abort();
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;
} }
#endif #endif
#if CONFIG_SPIRAM_RODATA #if CONFIG_SPIRAM_RODATA
esp_err_t esp_spiram_enable_rodata_access(void) esp_err_t esp_spiram_enable_rodata_access(void)
{ {
size_t spiram_size = esp_spiram_get_size(); //TODO IDF-4318, `pages_for_flash` will be overwritten, however it influences the psram size to be added to the heap allocator.
uint32_t pages_in_flash = 0; abort();
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;
} }
#endif #endif
@@ -228,6 +223,7 @@ int IRAM_ATTR rodata_flash2spiram_offset(void)
esp_err_t esp_spiram_init(void) esp_err_t esp_spiram_init(void)
{ {
esp_err_t r; esp_err_t r;
uint32_t psram_physical_size = 0;
r = psram_enable(PSRAM_SPEED, PSRAM_MODE); r = psram_enable(PSRAM_SPEED, PSRAM_MODE);
if (r != ESP_OK) { if (r != ESP_OK) {
#if CONFIG_SPIRAM_IGNORE_NOTFOUND #if CONFIG_SPIRAM_IGNORE_NOTFOUND
@@ -236,33 +232,34 @@ esp_err_t esp_spiram_init(void)
return r; return r;
} }
s_spiram_inited = true; s_spiram_inited = true;
r = psram_get_physical_size(&psram_physical_size);
if (r != ESP_OK) {
abort();
}
#if (CONFIG_SPIRAM_SIZE != -1) #if (CONFIG_SPIRAM_SIZE != -1)
if (esp_spiram_get_size() != CONFIG_SPIRAM_SIZE) { if (psram_physical_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); 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; return ESP_ERR_INVALID_SIZE;
} }
#endif #endif
ESP_EARLY_LOGI(TAG, "Found %dMB SPI RAM device", psram_physical_size / (1024 * 1024));
ESP_EARLY_LOGI(TAG, "Found %dMBit SPI RAM device", ESP_EARLY_LOGI(TAG, "Speed: %dMHz", CONFIG_SPIRAM_SPEED);
(esp_spiram_get_size() * 8) / (1024 * 1024)); ESP_EARLY_LOGI(TAG, "Initialized, cache is in %s mode.", \
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.", \
(PSRAM_MODE == PSRAM_VADDR_MODE_EVENODD) ? "even/odd (2-core)" : \ (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_LOWHIGH) ? "low/high (2-core)" : \
(PSRAM_MODE == PSRAM_VADDR_MODE_NORMAL) ? "normal (1-core)" : "ERROR"); (PSRAM_MODE == PSRAM_VADDR_MODE_NORMAL) ? "normal (1-core)" : "ERROR");
return ESP_OK; 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) esp_err_t esp_spiram_add_to_heapalloc(void)
{ {
size_t spiram_size = esp_spiram_get_size(); ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", (s_allocable_vaddr_end - s_allocable_vaddr_start) / 1024);
uint32_t size_for_flash = (pages_for_flash << 16); return heap_caps_add_region(s_allocable_vaddr_start, s_allocable_vaddr_end - 1);
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);
} }
@@ -289,23 +286,13 @@ size_t esp_spiram_get_size(void)
abort(); abort();
} }
psram_size_t size = psram_get_size(); uint32_t size = 0; //in bytes
if (size == PSRAM_SIZE_16MBITS) { esp_err_t ret = psram_get_available_size(&size);
return 2 * 1024 * 1024; 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/efuse_periph.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
#include "soc/syscon_reg.h"
#include "soc/efuse_reg.h" #include "soc/efuse_reg.h"
#include "soc/soc.h" #include "soc/soc.h"
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
@@ -104,11 +103,6 @@ static const char* TAG = "psram";
#define SPI0_NUM 0 #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 { typedef enum {
PSRAM_CMD_QPI, PSRAM_CMD_QPI,
@@ -118,6 +112,7 @@ typedef enum {
typedef esp_rom_spi_cmd_t psram_cmd_t; typedef esp_rom_spi_cmd_t psram_cmd_t;
static uint32_t s_psram_id = 0; 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); 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); 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 */ false); /* whether is program/erase operation */
} }
//TODO IDF-4307
//switch psram burst length(32 bytes or 1024 bytes) //switch psram burst length(32 bytes or 1024 bytes)
//datasheet says it should be 1024 bytes by default //datasheet says it should be 1024 bytes by default
static void psram_set_wrap_burst_length(int spi_num, psram_cmd_mode_t mode) 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) esp_err_t psram_enable_wrap(uint32_t wrap_size)
{ {
//TODO: IDF-4307
static uint32_t current_wrap_size = 0; static uint32_t current_wrap_size = 0;
if (current_wrap_size == wrap_size) { if (current_wrap_size == wrap_size) {
return ESP_OK; return ESP_OK;
@@ -323,20 +320,6 @@ static void psram_gpio_config(void)
esp_rom_spiflash_select_qio_pins(wp_io, spiconfig); 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. * 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. * 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 //SPI1: send psram reset command
psram_reset_mode(SPI1_NUM); psram_reset_mode(SPI1_NUM);
//SPI1: send QPI enable command //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) 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 #endif // CONFIG_SPIRAM

View File

@@ -3,28 +3,28 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#pragma once
#ifndef _PSRAM_H
#define _PSRAM_H
#include "soc/spi_mem_reg.h" #include "soc/spi_mem_reg.h"
#include "esp_err.h" #include "esp_err.h"
#include "sdkconfig.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 { typedef enum {
PSRAM_CACHE_S80M = 1, PSRAM_CACHE_S80M = 1,
PSRAM_CACHE_S40M, PSRAM_CACHE_S40M,
PSRAM_CACHE_MAX, PSRAM_CACHE_MAX,
} psram_cache_mode_t; } 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. 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; } psram_vaddr_mode_t;
/** /**
* @brief get psram size * @brief To get the physical psram size in bytes.
* @return *
* - PSRAM_SIZE_MAX if psram not enabled or not valid * @param[out] out_size_bytes physical psram size in bytes.
* - PSRAM size
*/ */
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 * @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); uint8_t psram_get_cs_io(void);
#ifdef __cplusplus
}
#endif #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