mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
psram: add octal psram ECC feature
This commit is contained in:
parent
fee1d38467
commit
9a6f894046
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user