bugfix(psram): support psram 2T mode to fix single bit error

1. add enable PSRAM 2T mode function
2. enable PSRAM 2T mode base on PSRAM ID
3. abort when himem and 2T mode are enabled meanwhile
4. set SPIRAM_2T_MODE as "y" by default and modify SPIRAM_BANKSWITCH_ENABLE as "n" by default
This commit is contained in:
chenjianqiang
2019-10-16 10:45:42 +08:00
parent de1f56466b
commit 55a20033e7
3 changed files with 326 additions and 6 deletions

View File

@@ -131,7 +131,7 @@ menu "ESP32-specific"
config SPIRAM_BANKSWITCH_ENABLE config SPIRAM_BANKSWITCH_ENABLE
bool "Enable bank switching for >4MiB external RAM" bool "Enable bank switching for >4MiB external RAM"
default y default n
depends on SPIRAM_USE_MEMMAP || SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC depends on SPIRAM_USE_MEMMAP || SPIRAM_USE_CAPS_ALLOC || SPIRAM_USE_MALLOC
help help
The ESP32 only supports 4MiB of external RAM in its address space. The hardware does support larger The ESP32 only supports 4MiB of external RAM in its address space. The hardware does support larger
@@ -141,6 +141,9 @@ menu "ESP32-specific"
#Note that this is limited to 62 banks, as esp_spiram_writeback_cache needs some kind of mapping of #Note that this is limited to 62 banks, as esp_spiram_writeback_cache needs some kind of mapping of
#some banks below that mark to work. We cannot at this moment guarantee this to exist when himem is #some banks below that mark to work. We cannot at this moment guarantee this to exist when himem is
#enabled. #enabled.
If spiram 2T mode is enabled, the size of 64Mbit psram will be changed as 32Mbit, so himem will be
unusable.
config SPIRAM_BANKSWITCH_RESERVE config SPIRAM_BANKSWITCH_RESERVE
int "Amount of 32K pages to reserve for bank switching" int "Amount of 32K pages to reserve for bank switching"
depends on SPIRAM_BANKSWITCH_ENABLE depends on SPIRAM_BANKSWITCH_ENABLE
@@ -260,6 +263,24 @@ menu "ESP32-specific"
For ESP32-PICO chip, the default value of this config should be 7. For ESP32-PICO chip, the default value of this config should be 7.
config SPIRAM_2T_MODE
bool "Enable SPI PSRAM 2T mode"
depends on ESP32_SPIRAM_SUPPORT
default "y"
help
Enable this option to fix single bit errors inside 64Mbit PSRAM.
Some 64Mbit PSRAM chips have a hardware issue in the RAM which causes bit errors at multiple
fixed bit positions.
This option only applies to 64Mbit PSRAM chips which have this issue, detected at startup by
reading the PSRAM ID. It has no effect on other PSRAM sizes, or 64Mbit PSRAM chips which do not
have this issue.
Note: If this option is enabled, the 64Mbit PSRAM chip will appear to be 32Mbit in size.
Applications will not be affected unless the use the esp_himem APIs, which are not supported
in 2T mode.
endmenu # "SPI RAM config" endmenu # "SPI RAM config"
config ESP32_MEMMAP_TRACEMEM config ESP32_MEMMAP_TRACEMEM

View File

@@ -172,7 +172,8 @@ typedef enum {
static psram_cache_mode_t s_psram_mode = PSRAM_CACHE_MAX; static psram_cache_mode_t s_psram_mode = PSRAM_CACHE_MAX;
static psram_clk_mode_t s_clk_mode = PSRAM_CLK_MODE_DCLK; static psram_clk_mode_t s_clk_mode = PSRAM_CLK_MODE_DCLK;
static uint32_t s_psram_id = 0; static uint64_t s_psram_id = 0;
static bool s_2t_mode_enabled = false;
/* dummy_len_plus values defined in ROM for SPI flash configuration */ /* dummy_len_plus values defined in ROM for SPI flash configuration */
extern uint8_t g_rom_spiflash_dummy_len_plus[]; extern uint8_t g_rom_spiflash_dummy_len_plus[];
@@ -397,11 +398,12 @@ static void psram_disable_qio_mode(psram_spi_num_t spi_num)
} }
//read psram id //read psram id
static void psram_read_id(uint32_t* dev_id) static void psram_read_id(uint64_t* dev_id)
{ {
psram_spi_num_t spi_num = PSRAM_SPI_1; psram_spi_num_t spi_num = PSRAM_SPI_1;
psram_disable_qio_mode(spi_num); psram_disable_qio_mode(spi_num);
uint32_t dummy_bits = 0 + extra_dummy; uint32_t dummy_bits = 0 + extra_dummy;
uint32_t psram_id[2] = {0};
psram_cmd_t ps_cmd; psram_cmd_t ps_cmd;
uint32_t addr = 0; uint32_t addr = 0;
@@ -425,14 +427,15 @@ static void psram_read_id(uint32_t* dev_id)
ps_cmd.addr = &addr; ps_cmd.addr = &addr;
ps_cmd.txDataBitLen = 0; ps_cmd.txDataBitLen = 0;
ps_cmd.txData = NULL; ps_cmd.txData = NULL;
ps_cmd.rxDataBitLen = 4 * 8; ps_cmd.rxDataBitLen = 8 * 8;
ps_cmd.rxData = dev_id; ps_cmd.rxData = psram_id;
ps_cmd.dummyBitLen = dummy_bits; ps_cmd.dummyBitLen = dummy_bits;
psram_cmd_config(spi_num, &ps_cmd); psram_cmd_config(spi_num, &ps_cmd);
psram_clear_spi_fifo(spi_num); psram_clear_spi_fifo(spi_num);
psram_cmd_recv_start(spi_num, ps_cmd.rxData, ps_cmd.rxDataBitLen / 8, PSRAM_CMD_SPI); psram_cmd_recv_start(spi_num, ps_cmd.rxData, ps_cmd.rxDataBitLen / 8, PSRAM_CMD_SPI);
psram_cmd_end(spi_num); psram_cmd_end(spi_num);
*dev_id = (uint64_t)(((uint64_t)psram_id[1] << 32) | psram_id[0]);
} }
//enter QPI mode //enter QPI mode
@@ -467,6 +470,275 @@ static esp_err_t IRAM_ATTR psram_enable_qio_mode(psram_spi_num_t spi_num)
return ESP_OK; return ESP_OK;
} }
#define PSRAM_2T_EID_M 0xf0ffffff
#define PSRAM_2T_EID_S 16
#define PSRAM_2T_CHECK_ID(id) (((id) >> PSRAM_2T_EID_S) & PSRAM_2T_EID_M)
/*
* If the psram id is included in below list, 2T mode need to be enabled
* Note:In order to save RAM memory, DRAM_ATTR is omitted intentionally
*/
static const uint32_t psram_check_id[] = {
// need check bits // original ID //
0x0053B24A, // xxxx0x53B24A5D0D
0x0064A246, // xxxx0x64A2465D0D
0x00B9A046, // xxxx0xB9A0465D0D
0x00BAA046, // xxxx0xBAA0465D0D
0x00C0A046, // xxxx0xC0A0465D0D
0x1053B24A, // xxxx1x53B24A5D0D
0x1064A246, // xxxx1x64A2465D0D
0x10B9A046, // xxxx1xB9A0465D0D
0x10BAA046, // xxxx1xBAA0465D0D
0x10C0A046, // xxxx1xC0A0465D0D
0x2022A146, // xxxx2x22A1465D0D
0x20A8A146, // xxxx2xA8A1465D0D
0x20C0A046, // xxxx2xC0A0465D0D
0x20C0B158, // xxxx2xC0B1585D0D
0x3022A146, // xxxx3x22A1465D0D
0x30A8A146, // xxxx3xA8A1465D0D
0x30C0A046, // xxxx3xC0A0465D0D
0x30C0B158, // xxxx3xC0B1585D0D
0x4026A158, // xxxx4x26A1585D0D
0x402AA046, // xxxx4x2AA0465D0D
0x402FA046, // xxxx4x2FA0465D0D
0x402FA048, // xxxx4x2FA0485D0D
0x4064A246, // xxxx4x64A2465D0D
0x40A8A15A, // xxxx4xA8A15A5D0D
0x40B9D14A, // xxxx4xB9D14A5D0D
0x5026A158, // xxxx5x26A1585D0D
0x502AA046, // xxxx5x2AA0465D0D
0x502FA046, // xxxx5x2FA0465D0D
0x5064A246, // xxxx5x64A2465D0D
0x50A8A15A, // xxxx5xA8A15A5D0D
0x50B9D14A, // xxxx5xB9D14A5D0D
0x50C0A046, // xxxx5xC0A0465D0D
0x602AA046, // xxxx6x2AA0465D0D
0x6064A246, // xxxx6x64A2465D0D
0x702AA046, // xxxx7x2AA0465D0D
0x7064A246, // xxxx7x64A2465D0D
0x8026A146, // xxxx8x26A1465D0D
0x9026A146, // xxxx9x26A1465D0D
0xA02FA046, // xxxxAx2FA0465D0D
0xB02FA046, // xxxxBx2FA0465D0D
0xC003B246, // xxxxCx03B2465D0D
0xC003B248, // xxxxCx03B2485D0D
0xC091D14A, // xxxxCx91D14A5D0D
0xC0B8A046, // xxxxCxB8A0465D0D
0xC0BFB148, // xxxxCxBFB1485D0D
0xD091D14A, // xxxxDx91D14A5D0D
0xD0B8A046, // xxxxDxB8A0465D0D
0xD0BFB148, // xxxxDxBFB1485D0D
0xE021A146, // xxxxEx21A1465D0D
0xE0B8A046, // xxxxExB8A0465D0D
0xE0B9A046, // xxxxExB9A0465D0D
0xE0BFA046, // xxxxExBFA0465D0D
0xE0BFB158, // xxxxExBFB1585D0D
0xF021A146, // xxxxFx21A1465D0D
0xF0B8A046, // xxxxFxB8A0465D0D
0xF0B9A046, // xxxxFxB9A0465D0D
0xF0BFA046, // xxxxFxBFA0465D0D
0xF0BFB158, // xxxxFxBFB1585D0D
};
/*
* Note:In order to save RAM memory, IRAM_ATTR is omitted intentionally
*/
static bool psram_should_use_2t_mode(uint64_t psram_id)
{
uint32_t idx = 0;
uint32_t low_idx = 0;
uint32_t high_idx = sizeof(psram_check_id) / 4;
uint32_t check_id = PSRAM_2T_CHECK_ID(psram_id);
while (low_idx <= high_idx) {
idx = (low_idx + high_idx) / 2;
if (check_id < psram_check_id[idx]) {
high_idx = idx - 1;
} else if (check_id > psram_check_id[idx]) {
low_idx = idx + 1;
} else {
return true;
}
}
return false;
}
#if CONFIG_SPIRAM_2T_MODE
// use SPI user mode to write psram
static void spi_user_psram_write(psram_spi_num_t spi_num, uint32_t address, uint32_t *data_buffer, uint32_t data_len)
{
uint32_t addr = (PSRAM_QUAD_WRITE << 24) | (address & 0x7fffff);
psram_cmd_t ps_cmd;
ps_cmd.cmdBitLen = 0;
ps_cmd.cmd = 0;
ps_cmd.addr = &addr;
ps_cmd.addrBitLen = 4 * 8;
ps_cmd.txDataBitLen = 32 * 8;
ps_cmd.txData = NULL;
ps_cmd.rxDataBitLen = 0;
ps_cmd.rxData = NULL;
ps_cmd.dummyBitLen = 0;
for(uint32_t i=0; i<data_len; i+=32) {
psram_clear_spi_fifo(spi_num);
addr = (PSRAM_QUAD_WRITE << 24) | ((address & 0x7fffff) + i);
ps_cmd.txData = data_buffer + (i / 4);
psram_cmd_config(spi_num, &ps_cmd);
psram_cmd_recv_start(spi_num, ps_cmd.rxData, ps_cmd.rxDataBitLen / 8, PSRAM_CMD_QPI);
}
psram_cmd_end(spi_num);
}
// use SPI user mode to read psram
static void spi_user_psram_read(psram_spi_num_t spi_num, uint32_t address, uint32_t *data_buffer, uint32_t data_len)
{
uint32_t addr = (PSRAM_FAST_READ_QUAD << 24) | (address & 0x7fffff);
uint32_t dummy_bits = PSRAM_FAST_READ_QUAD_DUMMY + 1;
psram_cmd_t ps_cmd;
ps_cmd.cmdBitLen = 0;
ps_cmd.cmd = 0;
ps_cmd.addr = &addr;
ps_cmd.addrBitLen = 4 * 8;
ps_cmd.txDataBitLen = 0;
ps_cmd.txData = NULL;
ps_cmd.rxDataBitLen = 32 * 8;
ps_cmd.dummyBitLen = dummy_bits + extra_dummy;
for(uint32_t i=0; i<data_len; i+=32) {
psram_clear_spi_fifo(spi_num);
addr = (PSRAM_FAST_READ_QUAD << 24) | ((address & 0x7fffff) + i);
ps_cmd.rxData = data_buffer + (i / 4);
psram_cmd_config(spi_num, &ps_cmd);
psram_cmd_recv_start(spi_num, ps_cmd.rxData, ps_cmd.rxDataBitLen / 8, PSRAM_CMD_QPI);
}
psram_cmd_end(spi_num);
}
//enable psram 2T mode
static esp_err_t IRAM_ATTR psram_2t_mode_enable(psram_spi_num_t spi_num)
{
psram_disable_qio_mode(spi_num);
// configure psram clock as 5 MHz
uint32_t div = rtc_clk_apb_freq_get() / 5000000;
esp_rom_spiflash_config_clk(div, spi_num);
psram_cmd_t ps_cmd;
// setp1: send cmd 0x5e
// send one more bit clock after send cmd
ps_cmd.cmd = 0x5e;
ps_cmd.cmdBitLen = 8;
ps_cmd.addrBitLen = 0;
ps_cmd.addr = 0;
ps_cmd.txDataBitLen = 0;
ps_cmd.txData = NULL;
ps_cmd.rxDataBitLen =0;
ps_cmd.rxData = NULL;
ps_cmd.dummyBitLen = 1;
psram_cmd_config(spi_num, &ps_cmd);
psram_clear_spi_fifo(spi_num);
psram_cmd_recv_start(spi_num, NULL, 0, PSRAM_CMD_SPI);
psram_cmd_end(spi_num);
// setp2: send cmd 0x5f
// send one more bit clock after send cmd
ps_cmd.cmd = 0x5f;
psram_cmd_config(spi_num, &ps_cmd);
psram_clear_spi_fifo(spi_num);
psram_cmd_recv_start(spi_num, NULL, 0, PSRAM_CMD_SPI);
psram_cmd_end(spi_num);
// setp3: keep cs as high level
// send 128 cycles clock
// send 1 bit high levle in ninth clock from the back to PSRAM SIO1
GPIO_OUTPUT_SET(D0WD_PSRAM_CS_IO, 1);
gpio_matrix_out(D0WD_PSRAM_CS_IO, SIG_GPIO_OUT_IDX, 0, 0);
gpio_matrix_out(PSRAM_SPID_SD1_IO, SPIQ_OUT_IDX, 0, 0);
gpio_matrix_in(PSRAM_SPID_SD1_IO, SPIQ_IN_IDX, 0);
gpio_matrix_out(PSRAM_SPIQ_SD0_IO, SPID_OUT_IDX, 0, 0);
gpio_matrix_in(PSRAM_SPIQ_SD0_IO, SPID_IN_IDX, 0);
uint32_t w_data_2t[4] = {0x0, 0x0, 0x0, 0x00010000};
ps_cmd.cmd = 0;
ps_cmd.cmdBitLen = 0;
ps_cmd.txDataBitLen = 128;
ps_cmd.txData = w_data_2t;
ps_cmd.dummyBitLen = 0;
psram_clear_spi_fifo(spi_num);
psram_cmd_config(spi_num, &ps_cmd);
psram_cmd_recv_start(spi_num, NULL, 0, PSRAM_CMD_SPI);
psram_cmd_end(spi_num);
gpio_matrix_out(PSRAM_SPIQ_SD0_IO, SPIQ_OUT_IDX, 0, 0);
gpio_matrix_in(PSRAM_SPIQ_SD0_IO, SPIQ_IN_IDX, 0);
gpio_matrix_out(PSRAM_SPID_SD1_IO, SPID_OUT_IDX, 0, 0);
gpio_matrix_in(PSRAM_SPID_SD1_IO, SPID_IN_IDX, 0);
gpio_matrix_out(D0WD_PSRAM_CS_IO, SPICS1_OUT_IDX, 0, 0);
// setp4: send cmd 0x5f
// send one more bit clock after send cmd
ps_cmd.cmd = 0x5f;
ps_cmd.cmdBitLen = 8;
ps_cmd.txDataBitLen = 0;
ps_cmd.txData = NULL;
ps_cmd.dummyBitLen = 1;
psram_cmd_config(spi_num, &ps_cmd);
psram_clear_spi_fifo(spi_num);
psram_cmd_recv_start(spi_num, NULL, 0, PSRAM_CMD_SPI);
psram_cmd_end(spi_num);
// configure psram clock back to the default value
switch (s_psram_mode) {
case PSRAM_CACHE_F80M_S40M:
case PSRAM_CACHE_F40M_S40M:
esp_rom_spiflash_config_clk(_SPI_40M_CLK_DIV, spi_num);
break;
case PSRAM_CACHE_F80M_S80M:
esp_rom_spiflash_config_clk(_SPI_80M_CLK_DIV, spi_num);
break;
default:
break;
}
psram_enable_qio_mode(spi_num);
return ESP_OK;
}
#define CHECK_DATA_LEN (1024)
#define CHECK_ADDR_STEP (0x100000)
#define SIZE_32MBIT (0x400000)
#define SIZE_64MBIT (0x800000)
static esp_err_t psram_2t_mode_check(psram_spi_num_t spi_num)
{
uint8_t w_check_data[CHECK_DATA_LEN] = {0};
uint8_t r_check_data[CHECK_DATA_LEN] = {0};
for (uint32_t addr=0; addr<SIZE_32MBIT; addr+=CHECK_ADDR_STEP) {
spi_user_psram_write(spi_num, addr, (uint32_t *)w_check_data, CHECK_DATA_LEN);
}
memset(w_check_data, 0xff, sizeof(w_check_data));
for (uint32_t addr=SIZE_32MBIT; addr<SIZE_64MBIT; addr+=CHECK_ADDR_STEP) {
spi_user_psram_write(spi_num, addr, (uint32_t *)w_check_data, CHECK_DATA_LEN);
}
for (uint32_t addr=0; addr<SIZE_32MBIT; addr+=CHECK_ADDR_STEP) {
spi_user_psram_read(spi_num, addr, (uint32_t *)r_check_data, CHECK_DATA_LEN);
for (uint32_t j=0; j<CHECK_DATA_LEN; j++) {
if (r_check_data[j] != 0xff) {
return ESP_FAIL;
}
}
}
return ESP_OK;
}
#endif
void psram_set_cs_timing(psram_spi_num_t spi_num, psram_clk_mode_t clk_mode) void psram_set_cs_timing(psram_spi_num_t spi_num, psram_clk_mode_t clk_mode)
{ {
if (clk_mode == PSRAM_CLK_MODE_NORM) { if (clk_mode == PSRAM_CLK_MODE_NORM) {
@@ -600,7 +872,7 @@ static void IRAM_ATTR psram_gpio_config(psram_io_t *psram_io, psram_cache_mode_t
psram_size_t psram_get_size(void) 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)) { if ((PSRAM_SIZE_ID(s_psram_id) == PSRAM_EID_SIZE_64MBITS) || PSRAM_IS_64MBIT_TRIAL(s_psram_id)) {
return PSRAM_SIZE_64MBITS; return s_2t_mode_enabled ? PSRAM_SIZE_32MBITS : PSRAM_SIZE_64MBITS;
} else if (PSRAM_SIZE_ID(s_psram_id) == PSRAM_EID_SIZE_32MBITS) { } else if (PSRAM_SIZE_ID(s_psram_id) == PSRAM_EID_SIZE_32MBITS) {
return PSRAM_SIZE_32MBITS; return PSRAM_SIZE_32MBITS;
} else if (PSRAM_SIZE_ID(s_psram_id) == PSRAM_EID_SIZE_16MBITS) { } else if (PSRAM_SIZE_ID(s_psram_id) == PSRAM_EID_SIZE_16MBITS) {
@@ -770,6 +1042,32 @@ esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode, psram_vaddr_mode_t vad
psram_set_cs_timing(PSRAM_SPI_1, s_clk_mode); psram_set_cs_timing(PSRAM_SPI_1, s_clk_mode);
psram_set_cs_timing(_SPI_CACHE_PORT, s_clk_mode); psram_set_cs_timing(_SPI_CACHE_PORT, s_clk_mode);
psram_enable_qio_mode(PSRAM_SPI_1); psram_enable_qio_mode(PSRAM_SPI_1);
if(((PSRAM_SIZE_ID(s_psram_id) == PSRAM_EID_SIZE_64MBITS) || PSRAM_IS_64MBIT_TRIAL(s_psram_id))
&& (psram_should_use_2t_mode(s_psram_id) == true)) {
#if !CONFIG_SPIRAM_2T_MODE
ESP_EARLY_LOGW(TAG, "This PSRAM has single bit error risk, suggest to enable PSRAM 2T mode, and disable SPIRAM bank switching. Please read the help text for SPIRAM_2T_MODE in the project configuration menu.");
#else
#if CONFIG_SPIRAM_BANKSWITCH_ENABLE
ESP_EARLY_LOGE(TAG, "This PSRAM has single bit error risk, suggest to enable PSRAM 2T mode, and disable SPIRAM bank switching. Please read the help text for SPIRAM_2T_MODE in the project configuration menu.");
abort();
#endif
/* Note: 2T mode command should not be sent twice,
otherwise psram would get back to normal mode. */
if (psram_2t_mode_check(PSRAM_SPI_1) != ESP_OK) {
psram_2t_mode_enable(PSRAM_SPI_1);
if (psram_2t_mode_check(PSRAM_SPI_1) != ESP_OK) {
ESP_EARLY_LOGE(TAG, "PSRAM 2T mode enable fail!");
return ESP_FAIL;
}
}
s_2t_mode_enabled = true;
ESP_EARLY_LOGI(TAG, "PSRAM is in 2T mode");
#endif
} else {
ESP_EARLY_LOGD(TAG, "This 64Mbit PSRAM ID is not at risk of bit errors, not enabling 2T mode workaround.");
}
psram_cache_init(mode, vaddrmode); psram_cache_init(mode, vaddrmode);
return ESP_OK; return ESP_OK;
} }

View File

@@ -1,5 +1,6 @@
TEST_COMPONENTS=esp32 esp_timer TEST_COMPONENTS=esp32 esp_timer
CONFIG_ESP32_SPIRAM_SUPPORT=y CONFIG_ESP32_SPIRAM_SUPPORT=y
CONFIG_SPIRAM_2T_MODE=n
CONFIG_SPIRAM_BANKSWITCH_ENABLE=y CONFIG_SPIRAM_BANKSWITCH_ENABLE=y
CONFIG_SPIRAM_BANKSWITCH_RESERVE=8 CONFIG_SPIRAM_BANKSWITCH_RESERVE=8
CONFIG_ESP_INT_WDT_TIMEOUT_MS=800 CONFIG_ESP_INT_WDT_TIMEOUT_MS=800