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

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

See merge request espressif/esp-idf!8330
This commit is contained in:
Jiang Jiang Jian
2020-05-22 21:10:44 +08:00
2 changed files with 213 additions and 5 deletions

View File

@ -198,6 +198,18 @@ config SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY
ROM code in any way (no direct calls, but also no Bluetooth/WiFi), you can try to disable this and use
xTaskCreateStatic to create the tasks stack in external memory.
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, but applications
will not be affected.
endmenu
config MEMMAP_TRACEMEM

View File

@ -113,7 +113,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[];
@ -340,11 +341,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;
@ -368,14 +370,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
@ -410,6 +413,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(PSRAM_CS_IO, 1);
gpio_matrix_out(PSRAM_CS_IO, SIG_GPIO_OUT_IDX, 0, 0);
gpio_matrix_out(PSRAM_SPID_IO, SPIQ_OUT_IDX, 0, 0);
gpio_matrix_in(PSRAM_SPID_IO, SPIQ_IN_IDX, 0);
gpio_matrix_out(PSRAM_SPIQ_IO, SPID_OUT_IDX, 0, 0);
gpio_matrix_in(PSRAM_SPIQ_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_IO, SPIQ_OUT_IDX, 0, 0);
gpio_matrix_in(PSRAM_SPIQ_IO, SPIQ_IN_IDX, 0);
gpio_matrix_out(PSRAM_SPID_IO, SPID_OUT_IDX, 0, 0);
gpio_matrix_in(PSRAM_SPID_IO, SPID_IN_IDX, 0);
gpio_matrix_out(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) {
@ -543,7 +722,7 @@ psram_size_t psram_get_size()
if (PSRAM_IS_32MBIT_VER0(s_psram_id)) {
return PSRAM_SIZE_32MBITS;
} else if (PSRAM_IS_64MBIT(s_psram_id)) {
return PSRAM_SIZE_64MBITS;
return s_2t_mode_enabled ? PSRAM_SIZE_32MBITS : PSRAM_SIZE_64MBITS;
} else {
return PSRAM_SIZE_MAX;
}
@ -655,6 +834,23 @@ 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_IS_64MBIT(s_psram_id)) {
#if CONFIG_SPIRAM_2T_MODE
/* 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;
}