Merge branch 'bugfix/psram_single_bit_error_v3.2' into 'release/v3.2'

psram: support psram 2T mode to fix single bit error (backport v3.2)

See merge request espressif/esp-idf!7111
This commit is contained in:
Jiang Jiang Jian 2020-04-15 13:43:36 +08:00
commit 2c21b0b2b3
2 changed files with 225 additions and 7 deletions

View file

@ -169,8 +169,12 @@ config SPIRAM_BANKSWITCH_ENABLE
memories, but these have to be bank-switched in and out of this address space. Enabling this allows you
to reserve some MMU pages for this, which allows the use of the esp_himem api to manage these banks.
#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 enabled.
#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 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
int "Amount of 32K pages to reserve for bank switching"
depends on SPIRAM_BANKSWITCH_ENABLE
@ -334,6 +338,20 @@ config SPIRAM_SPIWP_SD3_PIN
bootloader.
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 SPIRAM_SUPPORT
default "n"
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.
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
config MEMMAP_TRACEMEM

View file

@ -173,7 +173,8 @@ typedef enum {
static psram_cache_mode_t s_psram_mode = PSRAM_CACHE_MAX;
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 */
extern uint8_t g_rom_spiflash_dummy_len_plus[];
@ -398,11 +399,12 @@ static void psram_disable_qio_mode(psram_spi_num_t spi_num)
}
//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_disable_qio_mode(spi_num);
uint32_t dummy_bits = 0 + extra_dummy;
uint32_t psram_id[2] = {0};
psram_cmd_t ps_cmd;
uint32_t addr = 0;
@ -426,14 +428,15 @@ static void psram_read_id(uint32_t* dev_id)
ps_cmd.addr = &addr;
ps_cmd.txDataBitLen = 0;
ps_cmd.txData = NULL;
ps_cmd.rxDataBitLen = 4 * 8;
ps_cmd.rxData = dev_id;
ps_cmd.rxDataBitLen = 8 * 8;
ps_cmd.rxData = psram_id;
ps_cmd.dummyBitLen = dummy_bits;
psram_cmd_config(spi_num, &ps_cmd);
psram_clear_spi_fifo(spi_num);
psram_cmd_recv_start(spi_num, ps_cmd.rxData, ps_cmd.rxDataBitLen / 8, PSRAM_CMD_SPI);
psram_cmd_end(spi_num);
*dev_id = (uint64_t)(((uint64_t)psram_id[1] << 32) | psram_id[0]);
}
//enter QPI mode
@ -468,6 +471,182 @@ static esp_err_t IRAM_ATTR psram_enable_qio_mode(psram_spi_num_t spi_num)
return ESP_OK;
}
#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)
{
if (clk_mode == PSRAM_CLK_MODE_NORM) {
@ -601,7 +780,7 @@ static void IRAM_ATTR psram_gpio_config(psram_io_t *psram_io, psram_cache_mode_t
psram_size_t psram_get_size()
{
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) {
return PSRAM_SIZE_32MBITS;
} else if (PSRAM_SIZE_ID(s_psram_id) == PSRAM_EID_SIZE_16MBITS) {
@ -760,6 +939,27 @@ 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(_SPI_CACHE_PORT, s_clk_mode);
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))) {
#if CONFIG_SPIRAM_2T_MODE
#if CONFIG_SPIRAM_BANKSWITCH_ENABLE
ESP_EARLY_LOGE(TAG, "PSRAM 2T mode and SPIRAM bank switching can not enabled meanwhile. 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
}
psram_cache_init(mode, vaddrmode);
return ESP_OK;
}