From 4a2999f72995a86a9caa653bde40b748f9c18164 Mon Sep 17 00:00:00 2001 From: Wangjialin Date: Tue, 7 Feb 2017 17:58:01 +0800 Subject: [PATCH] update psram example files 1. update psram code 2. add missing files 3. add psram example 4. add mmu and psram init --- .../bootloader/src/main/bootloader_start.c | 3 + components/esp32/cpu_start.c | 2 + components/esp32/include/psram.h | 33 + components/esp32/psram.c | 1077 +++++++++++++++++ examples/14_psram/Makefile | 9 + examples/14_psram/README.md | 31 + examples/14_psram/bootloader_start.c | 620 ++++++++++ examples/14_psram/main/component.mk | 5 + examples/14_psram/main/psram_test.c | 143 +++ 9 files changed, 1923 insertions(+) create mode 100644 components/esp32/include/psram.h create mode 100755 components/esp32/psram.c create mode 100755 examples/14_psram/Makefile create mode 100755 examples/14_psram/README.md create mode 100644 examples/14_psram/bootloader_start.c create mode 100755 examples/14_psram/main/component.mk create mode 100755 examples/14_psram/main/psram_test.c diff --git a/components/bootloader/src/main/bootloader_start.c b/components/bootloader/src/main/bootloader_start.c index 0df7d0294..6c1e274d5 100644 --- a/components/bootloader/src/main/bootloader_start.c +++ b/components/bootloader/src/main/bootloader_start.c @@ -563,6 +563,9 @@ static void set_cache_and_start_app( ESP_LOGV(TAG, "rc=%d", rc ); rc = cache_flash_mmu_set( 1, 0, irom_load_addr & 0xffff0000, irom_addr & 0xffff0000, 64, irom_page_count ); ESP_LOGV(TAG, "rc=%d", rc ); + + cache_sram_mmu_set( 0, 0, 0x3f800000, 0, 32, 128 ); + REG_CLR_BIT( DPORT_PRO_CACHE_CTRL1_REG, (DPORT_PRO_CACHE_MASK_IRAM0) | (DPORT_PRO_CACHE_MASK_IRAM1 & 0) | (DPORT_PRO_CACHE_MASK_IROM0 & 0) | DPORT_PRO_CACHE_MASK_DROM0 | DPORT_PRO_CACHE_MASK_DRAM1 ); REG_CLR_BIT( DPORT_APP_CACHE_CTRL1_REG, (DPORT_APP_CACHE_MASK_IRAM0) | (DPORT_APP_CACHE_MASK_IRAM1 & 0) | (DPORT_APP_CACHE_MASK_IROM0 & 0) | DPORT_APP_CACHE_MASK_DROM0 | DPORT_APP_CACHE_MASK_DRAM1 ); Cache_Read_Enable( 0 ); diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 495a17803..81c7198de 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -58,6 +58,8 @@ #include "esp_core_dump.h" #include "trax.h" +#include "psram.h" + #define STRINGIFY(s) STRINGIFY2(s) #define STRINGIFY2(s) #s diff --git a/components/esp32/include/psram.h b/components/esp32/include/psram.h new file mode 100644 index 000000000..ef3ec7609 --- /dev/null +++ b/components/esp32/include/psram.h @@ -0,0 +1,33 @@ +#ifndef _PSRAM_H +#define _PSRAM_H +#include "soc/spi_reg.h" +#include "esp_err.h" +#define PSRAM_READ 0x03 +#define PSRAM_FAST_READ 0x0B +#define PSRAM_FAST_READ_QUAD 0xEB +#define PSRAM_WRITE 0x02 +#define PSRAM_QUAD_WRITE 0x38 +#define PSRAM_ENTER_QMODE 0x35 +#define PSRAM_EXIT_QMODE 0xF5 +#define PSRAM_RESET_EN 0x66 +#define PSRAM_RESET 0x99 +#define PSRAM_SET_BURST_LEN 0xC0 +#define PSRAM_DEVICE_ID 0x9F + +typedef enum { + PSRAM_SPI_1 = 0x1, + PSRAM_SPI_2, + PSRAM_SPI_3, + PSRAM_SPI_MAX , +} psram_spi_num_t; + +typedef enum { + PSRAM_CACHE_F80M_S40M = 0,//DO NOT USE FOR NOW + PSRAM_CACHE_F40M_S40M, //SUPPORTED + PSRAM_CACHE_F80M_S80M, //DO NOT USE FOR NOW + PSRAM_CACHE_MAX, +} psram_cache_mode_t; + +esp_err_t psram_enable(psram_cache_mode_t mode); + +#endif diff --git a/components/esp32/psram.c b/components/esp32/psram.c new file mode 100755 index 000000000..7c891def8 --- /dev/null +++ b/components/esp32/psram.c @@ -0,0 +1,1077 @@ +#include "esp_types.h" +#include "rom/ets_sys.h" +#include "psram.h" +//#include "spi.h" +#include "soc/io_mux_reg.h" +#include "soc/dport_reg.h" +#include "rom/gpio.h" +#include "soc/gpio_sig_map.h" +#include "esp_attr.h" +#include "rom/cache.h" +#include "freertos/FreeRTOS.h" +#include "freertos/timers.h" +#include "freertos/task.h" +#include "string.h" +#include "rom/spi_flash.h" +#include "esp_err.h" + +static psram_cache_mode_t g_PsramMode = PSRAM_CACHE_MAX; +extern void Cache_Flush(int); + +//For now, we only use F40M + S40M, and we don't have to go through gpio matrix +#define GPIO_MATRIX_FOR_40M 0 +static int extra_dummy = 0; + +typedef enum { + PSRAM_CMD_QPI, + PSRAM_CMD_SPI, +} psram_cmd_mode_t; + +typedef struct { + uint16_t cmd; /*!< Command value */ + uint16_t cmdBitLen; /*!< Command byte length*/ + uint32_t *addr; /*!< Point to address value*/ + uint16_t addrBitLen; /*!< Address byte length*/ + uint32_t *txData; /*!< Point to send data buffer*/ + uint16_t txDataBitLen; /*!< Send data byte length.*/ + uint32_t *rxData; /*!< Point to recevie data buffer*/ + uint16_t rxDataBitLen; /*!< Recevie Data byte length.*/ + uint32_t dummyBitLen; +} psram_cmd_t; + +static void IRAM_ATTR psram_cache_init(psram_cache_mode_t psram_cache_mode); + + + +static void psram_clear_spi_fifo(psram_spi_num_t spiNum) +{ + int i; + for(i=0;i<16;i++){ + WRITE_PERI_REG(SPI_W0_REG(spiNum)+i*4,0); + } +} + +static void disp_fifo(psram_spi_num_t spiNum) +{ + int i; + for(i=0;i<16;i++){ + ets_printf(" FIFO[%d]: 0x%08x\n",i, READ_PERI_REG(SPI_W0_REG(spiNum)+i*4)); + } +} + +//set basic SPI write mode +static void psram_set_basic_write_mode(psram_spi_num_t spiNum) +{ + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum),SPI_FWRITE_QIO); //F WRITE QIO + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum),SPI_FWRITE_DIO); //F WRITE DIO + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum),SPI_FWRITE_QUAD); //F WRITE QUAD + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum),SPI_FWRITE_DUAL); //F WRITE DUAL +} +//set QPI write mode +static void psram_set_qio_write_mode(psram_spi_num_t spiNum) +{ + SET_PERI_REG_MASK(SPI_USER_REG(spiNum),SPI_FWRITE_QIO); //F WRITE QIO + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum),SPI_FWRITE_DIO); //F WRITE DIO + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum),SPI_FWRITE_QUAD); //F WRITE QUAD + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum),SPI_FWRITE_DUAL); //F WRITE DUAL +} +//set QPI read mode +static void psram_set_qio_read_mode(psram_spi_num_t spiNum) +{ + SET_PERI_REG_MASK(SPI_CTRL_REG(spiNum),SPI_FREAD_QIO); //f read qio + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum),SPI_FREAD_QUAD); //f read quad + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum),SPI_FREAD_DUAL); //f read dual + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum),SPI_FREAD_DIO); //f read dio +} +//set SPI read mode +static void psram_set_basic_read_mode(psram_spi_num_t spiNum) +{ + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum),SPI_FREAD_QIO); //f read qio + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum),SPI_FREAD_QUAD); //f read quad + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum),SPI_FREAD_DUAL); //f read dual + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum),SPI_FREAD_DIO); //f read dio +} + +//start sending and wait for finishing +static IRAM_ATTR void psram_cmd_start(psram_spi_num_t spiNum, psram_cmd_mode_t cmd_mode) +{ + //get cs1 + CLEAR_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS1_DIS_M); + SET_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS0_DIS_M); + + uint32_t wr_mode_bkp = (READ_PERI_REG(SPI_USER_REG(spiNum)) >> SPI_FWRITE_DUAL_S) & 0xf; + uint32_t rd_mode_bkp = READ_PERI_REG(SPI_CTRL_REG(spiNum)) & (SPI_FREAD_DIO_M|SPI_FREAD_DUAL_M|SPI_FREAD_QUAD_M|SPI_FREAD_QIO_M); + if(cmd_mode == PSRAM_CMD_SPI) { + psram_set_basic_write_mode(spiNum); + psram_set_basic_read_mode(spiNum); + } else if (cmd_mode == PSRAM_CMD_QPI) { + psram_set_qio_write_mode(spiNum); + psram_set_qio_read_mode(spiNum); + } + + //WAIT SPI0 IDLE + //READ THREE TIMES TO MAKE SURE? + while( READ_PERI_REG(SPI_EXT2_REG(0))!= 0); + while( READ_PERI_REG(SPI_EXT2_REG(0))!= 0); + while( READ_PERI_REG(SPI_EXT2_REG(0))!= 0); + SET_PERI_REG_MASK( DPORT_HOST_INF_SEL_REG, 1<<14); + + // Start send data + SET_PERI_REG_MASK(SPI_CMD_REG(spiNum), SPI_USR); + while ((READ_PERI_REG(SPI_CMD_REG(spiNum))&SPI_USR)); + + CLEAR_PERI_REG_MASK( DPORT_HOST_INF_SEL_REG, 1<<14); + + //recover spi mode + SET_PERI_REG_BITS(SPI_USER_REG(spiNum), 0xf, wr_mode_bkp, SPI_FWRITE_DUAL_S); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum), (SPI_FREAD_DIO_M|SPI_FREAD_DUAL_M|SPI_FREAD_QUAD_M|SPI_FREAD_QIO_M)); + SET_PERI_REG_MASK(SPI_CTRL_REG(spiNum), rd_mode_bkp); + + //return cs to cs0 + SET_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1),SPI_CS1_DIS_M); + CLEAR_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1),SPI_CS0_DIS_M); +} + +//start sending cmd/addr and receving data +static void IRAM_ATTR psram_recv_start(psram_spi_num_t spiNum,uint32_t* pRxData,uint16_t rxByteLen, psram_cmd_mode_t cmd_mode) +{ + //get cs1 + CLEAR_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS1_DIS_M); + SET_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1), SPI_CS0_DIS_M); + + uint32_t cmd_mode_bkp = (READ_PERI_REG(SPI_USER_REG(spiNum)) >> SPI_FWRITE_DUAL_S) & 0xf; + uint32_t rd_mode_bkp = READ_PERI_REG(SPI_CTRL_REG(spiNum)) & (SPI_FREAD_DIO_M|SPI_FREAD_DUAL_M|SPI_FREAD_QUAD_M|SPI_FREAD_QIO_M); + if(cmd_mode == PSRAM_CMD_SPI) { + psram_set_basic_write_mode(spiNum); + psram_set_basic_read_mode(spiNum); + } else if (cmd_mode == PSRAM_CMD_QPI) { + psram_set_qio_write_mode(spiNum); + psram_set_qio_read_mode(spiNum); + } + + //WAIT SPI0 IDLE + //READ THREE TIMES TO MAKE SURE? + while( READ_PERI_REG(SPI_EXT2_REG(0))!= 0); + while( READ_PERI_REG(SPI_EXT2_REG(0))!= 0); + while( READ_PERI_REG(SPI_EXT2_REG(0))!= 0); + SET_PERI_REG_MASK( DPORT_HOST_INF_SEL_REG, 1<<14); + + // Start send data + SET_PERI_REG_MASK(SPI_CMD_REG(spiNum), SPI_USR); + while ((READ_PERI_REG(SPI_CMD_REG(spiNum))&SPI_USR)); + + CLEAR_PERI_REG_MASK( DPORT_HOST_INF_SEL_REG, 1<<14); + + //recover spi mode + SET_PERI_REG_BITS(SPI_USER_REG(spiNum), 0xf, cmd_mode_bkp, SPI_FWRITE_DUAL_S); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum), (SPI_FREAD_DIO_M|SPI_FREAD_DUAL_M|SPI_FREAD_QUAD_M|SPI_FREAD_QIO_M)); + SET_PERI_REG_MASK(SPI_CTRL_REG(spiNum), rd_mode_bkp); + + //return cs to cs0 + SET_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1),SPI_CS1_DIS_M); + CLEAR_PERI_REG_MASK(SPI_PIN_REG(PSRAM_SPI_1),SPI_CS0_DIS_M); + + int idx = 0; + // Read data out + do { + *pRxData++ = READ_PERI_REG(SPI_W0_REG(spiNum) + (idx << 2)); + } while (++idx < ((rxByteLen / 4) + ((rxByteLen % 4) ? 1 : 0))); +} + +//setup spi command/addr/data/dummy in user mode +static int psram_cmd_config(psram_spi_num_t spiNum, psram_cmd_t* pInData) +{ + uint8_t idx = 0; + while (READ_PERI_REG(SPI_CMD_REG(spiNum))&SPI_USR); + + // Set command by user. + if (pInData->cmdBitLen != 0) { + // Max command length 16 bits. + SET_PERI_REG_BITS(SPI_USER2_REG(spiNum), SPI_USR_COMMAND_BITLEN,pInData->cmdBitLen-1, SPI_USR_COMMAND_BITLEN_S); + // Enable command + SET_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_USR_COMMAND); + // Load command,bit15-0 is cmd value. + SET_PERI_REG_BITS(SPI_USER2_REG(spiNum), SPI_USR_COMMAND_VALUE, pInData->cmd, SPI_USR_COMMAND_VALUE_S); + } else { + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_USR_COMMAND); + SET_PERI_REG_BITS(SPI_USER2_REG(spiNum), SPI_USR_COMMAND_BITLEN,0, SPI_USR_COMMAND_BITLEN_S); + } + // Set Address by user. + if (pInData->addrBitLen != 0) { + SET_PERI_REG_BITS(SPI_USER1_REG(spiNum), SPI_USR_ADDR_BITLEN,(pInData->addrBitLen- 1), SPI_USR_ADDR_BITLEN_S); + // Enable address + SET_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_USR_ADDR); + // Set address + //SET_PERI_REG_BITS(SPI_ADDR_REG(spiNum), SPI_USR_ADDR_VALUE, *pInData->addr, SPI_USR_ADDR_VALUE_S); + WRITE_PERI_REG(SPI_ADDR_REG(spiNum), *pInData->addr); + + } else{ + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_USR_ADDR); + SET_PERI_REG_BITS(SPI_USER1_REG(spiNum), SPI_USR_ADDR_BITLEN,0, SPI_USR_ADDR_BITLEN_S); + } + // Set data by user. + uint32_t* pTxVal = pInData->txData; + if (pInData->txDataBitLen != 0 ) { + // Enable MOSI + SET_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_USR_MOSI); + // Load send buffer + int len = ((pInData->txDataBitLen / 32) + ((pInData->txDataBitLen % 32) ? 1 : 0)); + if(pTxVal != NULL) { + do { + WRITE_PERI_REG((SPI_W0_REG(spiNum) + (idx << 2)), *pTxVal++); + } while(++idx < len); + } + // Set data send buffer length.Max data length 64 bytes. + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(spiNum), SPI_USR_MOSI_DBITLEN, (pInData->txDataBitLen - 1), SPI_USR_MOSI_DBITLEN_S); + } else { + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_USR_MOSI); + SET_PERI_REG_BITS(SPI_MOSI_DLEN_REG(spiNum), SPI_USR_MOSI_DBITLEN,0, SPI_USR_MOSI_DBITLEN_S); + } + // Set rx data by user. + if (pInData->rxDataBitLen != 0 ) { + // Enable MOSI + SET_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_USR_MISO); + // Set data send buffer length.Max data length 64 bytes. + SET_PERI_REG_BITS(SPI_MISO_DLEN_REG(spiNum), SPI_USR_MISO_DBITLEN, (pInData->rxDataBitLen -1 ), SPI_USR_MISO_DBITLEN_S); + } else { + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_USR_MISO); + SET_PERI_REG_BITS(SPI_MISO_DLEN_REG(spiNum), SPI_USR_MISO_DBITLEN, 0, SPI_USR_MISO_DBITLEN_S); + } + if(pInData->dummyBitLen != 0){ + SET_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1),SPI_USR_DUMMY); // dummy en + SET_PERI_REG_BITS(SPI_USER1_REG(PSRAM_SPI_1),SPI_USR_DUMMY_CYCLELEN_V,pInData->dummyBitLen-1,SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + }else{ + CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1),SPI_USR_DUMMY); // dummy en + SET_PERI_REG_BITS(SPI_USER1_REG(PSRAM_SPI_1),SPI_USR_DUMMY_CYCLELEN_V,0,SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + } + return 0; +} + +//read psram data in fast read mode +static void psram_read_data(psram_spi_num_t spiNum,uint32_t* dst,uint32_t src,uint32_t len) +{ + uint32_t addr = 0; + uint32_t dummy_bits = 0; + psram_cmd_t pDat; + addr = (PSRAM_FAST_READ <<24) | src; + switch(g_PsramMode){ + case PSRAM_CACHE_F80M_S80M: + dummy_bits = 4+extra_dummy; + pDat.cmdBitLen = 0; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + dummy_bits = 4+extra_dummy; + pDat.cmdBitLen = 2; + break; + } + pDat.cmd = 0; + pDat.addr = &addr; + pDat.addrBitLen = 4*8; + pDat.txDataBitLen = 0; + pDat.txData = NULL; + pDat.rxDataBitLen = len*8 ; + pDat.rxData = dst; + pDat.dummyBitLen = dummy_bits; + psram_cmd_config(spiNum,&pDat); + psram_clear_spi_fifo(spiNum); + psram_recv_start(spiNum,pDat.rxData,pDat.rxDataBitLen/8, PSRAM_CMD_QPI); +} + +//read psram data in fast read quad mode +static void psram_read_data_quad(psram_spi_num_t spiNum,uint32_t* dst,uint32_t src,uint32_t len) +{ + uint32_t addr = (PSRAM_FAST_READ_QUAD <<24) | src; + uint32_t dummy_bits = 0; + psram_cmd_t pDat; + switch(g_PsramMode){ + case PSRAM_CACHE_F80M_S80M: + dummy_bits = 6+extra_dummy; + pDat.cmdBitLen = 0; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + dummy_bits = 6+extra_dummy; + pDat.cmdBitLen = 2; + break; + } + pDat.cmd = 0; + pDat.addr = &addr; + pDat.addrBitLen = 4*8; + pDat.txDataBitLen = 0; + pDat.txData = NULL; + pDat.rxDataBitLen = len*8 ; + pDat.rxData = dst; + pDat.dummyBitLen = dummy_bits; + psram_cmd_config(spiNum,&pDat); + psram_clear_spi_fifo(spiNum); + psram_recv_start(spiNum,pDat.rxData,pDat.rxDataBitLen/8, PSRAM_CMD_QPI); +} + +//write data to psram +static void psram_write_data(uint32_t dst,uint32_t* src,uint32_t len) +{ + uint32_t addr = (PSRAM_QUAD_WRITE <<24) | dst; + psram_cmd_t pDat; + int dummy_bits = 0; + switch(g_PsramMode){ + case PSRAM_CACHE_F80M_S80M: + dummy_bits = 0 + 0; + pDat.cmdBitLen = 0; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + dummy_bits = 0 + 0; + pDat.cmdBitLen = 2; + break; + } + pDat.cmd = 0; + pDat.addr = &addr; + pDat.addrBitLen = 32; + pDat.txData = src; + pDat.txDataBitLen = len*8; + pDat.rxData = NULL; + pDat.rxDataBitLen = 0; + pDat.dummyBitLen = dummy_bits; + psram_cmd_config(PSRAM_SPI_1, &pDat); + psram_cmd_start(PSRAM_SPI_1, PSRAM_CMD_QPI); +} + +static void psram_dma_cmd_write_config(uint32_t dst, uint32_t len, uint32_t dummy_bits) +{ + uint32_t addr = (PSRAM_QUAD_WRITE << 24) | dst; + psram_cmd_t pDat; + switch(g_PsramMode) { + case PSRAM_CACHE_F80M_S80M: + pDat.cmdBitLen = 0; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + pDat.cmdBitLen = 2; + break; + } + pDat.cmd = 0; + pDat.addr = &addr; + pDat.addrBitLen = 32; + pDat.txData = NULL; + pDat.txDataBitLen = len * 8; + pDat.rxData = NULL; + pDat.rxDataBitLen = 0; + pDat.dummyBitLen = dummy_bits; + psram_cmd_config(PSRAM_SPI_1, &pDat); +} + +static void psram_dma_qio_read_config(psram_spi_num_t spiNum, uint32_t src, uint32_t len) +{ + uint32_t addr = (PSRAM_FAST_READ_QUAD <<24) | src; + uint32_t dummy_bits = 0; + psram_cmd_t pDat; + switch(g_PsramMode){ + case PSRAM_CACHE_F80M_S80M: + dummy_bits = 6+extra_dummy; + pDat.cmdBitLen = 0; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + dummy_bits = 6+extra_dummy; + pDat.cmdBitLen = 2; + break; + } + pDat.cmd = 0; + pDat.addr = &addr; + pDat.addrBitLen = 4*8; + pDat.txDataBitLen = 0; + pDat.txData = NULL; + pDat.rxDataBitLen = len*8 ; + pDat.rxData = NULL; + pDat.dummyBitLen = dummy_bits; + psram_cmd_config(spiNum,&pDat); +// psram_clear_spi_fifo(spiNum); +} + +//read psram id +static void psram_read_id(uint32_t* dev_id) +{ + psram_spi_num_t spiNum = PSRAM_SPI_1; +// psram_set_basic_write_mode(spiNum); +// psram_set_basic_read_mode(spiNum); + uint32_t addr = (PSRAM_DEVICE_ID <<24) | 0; + uint32_t dummy_bits = 0; + psram_cmd_t pDat; + switch(g_PsramMode){ + case PSRAM_CACHE_F80M_S80M: + dummy_bits = 0+extra_dummy; + pDat.cmdBitLen = 0; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + dummy_bits = 0+extra_dummy; + pDat.cmdBitLen = 2; //this two bits is used for delay one byte in qio mode + break; + } + pDat.cmd = 0; + pDat.addr = &addr; + pDat.addrBitLen = 4*8; + pDat.txDataBitLen = 0; + pDat.txData = NULL; + pDat.rxDataBitLen = 4*8 ; + pDat.rxData = dev_id; + pDat.dummyBitLen = dummy_bits; + psram_cmd_config(spiNum,&pDat); + psram_clear_spi_fifo(spiNum); + psram_recv_start(spiNum,pDat.rxData,pDat.rxDataBitLen/8, PSRAM_CMD_SPI); +} + +//switch psram burst length(32 bytes or 1024 bytes) +//datasheet says it should be 1024 bytes by default +//but they sent us a correction doc and told us it is 32 bytes for these samples +static void psram_set_burst_length(psram_spi_num_t spiNum) +{ + psram_cmd_t pDat; + switch(g_PsramMode){ + case PSRAM_CACHE_F80M_S80M: + pDat.cmd = 0xC0; + pDat.cmdBitLen = 8; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + pDat.cmd = 0x0030; + pDat.cmdBitLen = 10; + break; + } + pDat.addr = 0; + pDat.addrBitLen = 0; + pDat.txData = NULL; + pDat.txDataBitLen = 0; + pDat.rxData = NULL; + pDat.rxDataBitLen = 0; + pDat.dummyBitLen = 0; + psram_cmd_config(spiNum, &pDat); + psram_cmd_start(spiNum, PSRAM_CMD_QPI); +} + +//send reset command to psram(right now,we only send this command in QPI mode) +//seems not working +static void psram_reset_mode(psram_spi_num_t spiNum) +{ + psram_cmd_t pDat; + uint32_t cmd_rst = 0x99066; + pDat.txData = &cmd_rst; + pDat.txDataBitLen = 20; + pDat.addr = NULL; + pDat.addrBitLen = 0; + pDat.cmd = 0; + pDat.cmdBitLen = 0; + pDat.rxData = NULL; + pDat.rxDataBitLen = 0; + pDat.dummyBitLen = 0; + psram_cmd_config(spiNum, &pDat); + psram_cmd_start(spiNum, PSRAM_CMD_QPI); +} +//exit QPI mode(set back to SPI mode) +static void psram_disable_qio_mode(psram_spi_num_t spiNum) +{ + psram_cmd_t pDat; + uint32_t cmd_exit_qpi; + switch(g_PsramMode){ + case PSRAM_CACHE_F80M_S80M: + cmd_exit_qpi = PSRAM_EXIT_QMODE; + pDat.txDataBitLen = 8; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + cmd_exit_qpi = PSRAM_EXIT_QMODE<<8; + pDat.txDataBitLen = 16; + break; + } + pDat.txData = &cmd_exit_qpi; + pDat.cmd = 0; + pDat.cmdBitLen = 0; + pDat.addr = 0; + pDat.addrBitLen = 0; + pDat.rxData = NULL; + pDat.rxDataBitLen = 0; + pDat.dummyBitLen = 0; + psram_cmd_config(spiNum, &pDat); + psram_cmd_start(spiNum, PSRAM_CMD_QPI); +} +//enter QPI mode +static void IRAM_ATTR psram_enable_qio_mode(psram_spi_num_t spiNum) +{ + psram_cmd_t pDat; + switch(g_PsramMode){ + case PSRAM_CACHE_F80M_S80M: + pDat.cmd = PSRAM_ENTER_QMODE; + pDat.cmdBitLen = 8; + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + pDat.cmd = 0x400d; + pDat.cmdBitLen = 10; + break; + } + pDat.addr = 0; + pDat.addrBitLen = 0; + pDat.txData = NULL; + pDat.txDataBitLen = 0; + pDat.rxData = NULL; + pDat.rxDataBitLen = 0; + pDat.dummyBitLen = 0; + psram_cmd_config(spiNum, &pDat); + psram_cmd_start(spiNum, PSRAM_CMD_SPI); +} + + +static void IRAM_ATTR psram_gpio_config(psram_cache_mode_t mode) +{ + gpio_matrix_out(6, SPICLK_OUT_IDX, 0, 0); + gpio_matrix_out(11, SPICS0_OUT_IDX, 0, 0); + + gpio_matrix_out(7, SPIQ_OUT_IDX, 0, 0); + gpio_matrix_in(7,SPIQ_IN_IDX, 0); + gpio_matrix_out(8, SPID_OUT_IDX, 0, 0); + gpio_matrix_in(8, SPID_IN_IDX, 0); + gpio_matrix_out(10, SPIWP_OUT_IDX, 0, 0); + gpio_matrix_in(10, SPIWP_IN_IDX, 0); + gpio_matrix_out(9, SPIHD_OUT_IDX, 0, 0); + gpio_matrix_in(9, SPIHD_IN_IDX, 0); + + switch(mode){ + case PSRAM_CACHE_F80M_S80M: + case PSRAM_CACHE_F80M_S40M: + SET_PERI_REG_MASK(SPI_USER_REG(0),SPI_USR_DUMMY); // dummy en + SET_PERI_REG_BITS(SPI_USER1_REG(0),SPI_USR_DUMMY_CYCLELEN_V,3+extra_dummy,SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + break; + case PSRAM_CACHE_F40M_S40M: + default: + SET_PERI_REG_MASK(SPI_USER_REG(0),SPI_USR_DUMMY); // dummy en + SET_PERI_REG_BITS(SPI_USER1_REG(0),SPI_USR_DUMMY_CYCLELEN_V,3+extra_dummy,SPI_USR_DUMMY_CYCLELEN_S); //DUMMY + break; + } + //drive ability + SET_PERI_REG_BITS( PERIPHS_IO_MUX_SD_CLK_U, FUN_DRV, 3 ,FUN_DRV_S); + SET_PERI_REG_BITS( PERIPHS_IO_MUX_SD_DATA0_U,FUN_DRV, 3 ,FUN_DRV_S); + SET_PERI_REG_BITS( PERIPHS_IO_MUX_SD_DATA1_U,FUN_DRV, 3 ,FUN_DRV_S); + SET_PERI_REG_BITS( PERIPHS_IO_MUX_SD_DATA2_U,FUN_DRV, 3 ,FUN_DRV_S); + SET_PERI_REG_BITS( PERIPHS_IO_MUX_SD_DATA3_U,FUN_DRV, 3 ,FUN_DRV_S); + SET_PERI_REG_BITS( PERIPHS_IO_MUX_SD_CMD_U, FUN_DRV, 3 ,FUN_DRV_S); + //select pin function gpio + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA0_U,2); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA1_U,2); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA2_U,2); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_DATA3_U,2); + + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CLK_U,2); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_SD_CMD_U,2); +} + + +//spi param init for psram +void IRAM_ATTR psram_spi_init(psram_spi_num_t spiNum,psram_cache_mode_t mode) +{ + uint8_t i, k; + CLEAR_PERI_REG_MASK(SPI_SLAVE_REG(spiNum), SPI_TRANS_DONE << 5); + SET_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_CS_SETUP); + // SPI_CPOL & SPI_CPHA + CLEAR_PERI_REG_MASK(SPI_PIN_REG(spiNum), SPI_CK_IDLE_EDGE); + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_CK_OUT_EDGE); + // SPI bit order + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum), SPI_WR_BIT_ORDER); + CLEAR_PERI_REG_MASK(SPI_CTRL_REG(spiNum), SPI_RD_BIT_ORDER); + // SPI bit order + CLEAR_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_DOUTDIN); + // May be not must to do. + WRITE_PERI_REG(SPI_USER1_REG(spiNum), 0); + // SPI mode type + CLEAR_PERI_REG_MASK(SPI_SLAVE_REG(spiNum), SPI_SLAVE_MODE); + switch(mode){ + case PSRAM_CACHE_F80M_S80M: + WRITE_PERI_REG(SPI_CLOCK_REG(spiNum), SPI_CLK_EQU_SYSCLK); // 80Mhz speed + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: + i = (2 / 40) ? (2 / 40) : 1; + k = 2 / i; + CLEAR_PERI_REG_MASK(SPI_CLOCK_REG(spiNum), SPI_CLK_EQU_SYSCLK); + WRITE_PERI_REG(SPI_CLOCK_REG(spiNum), + (((i - 1) & SPI_CLKDIV_PRE) << SPI_CLKDIV_PRE_S) | + (((k - 1) & SPI_CLKCNT_N) << SPI_CLKCNT_N_S) | + ((((k + 1) / 2 - 1) & SPI_CLKCNT_H) << SPI_CLKCNT_H_S) | + (((k - 1) & SPI_CLKCNT_L) << SPI_CLKCNT_L_S)); //clear bit 31,set SPI clock div + break; + } + // Enable MOSI + SET_PERI_REG_MASK(SPI_USER_REG(spiNum), SPI_CS_SETUP | SPI_CS_HOLD | SPI_USR_MOSI); + for (i = 0; i < 16; ++i) { + WRITE_PERI_REG((SPI_W0_REG(spiNum) + (i << 2)), 0); + } +} + + +//psram gpio init , different working frequency we have different solutions +esp_err_t IRAM_ATTR psram_enable(psram_cache_mode_t mode) //psram init +{ + WRITE_PERI_REG(GPIO_ENABLE_W1TC_REG,BIT16|BIT17);//DISALBE OUPUT FOR IO16/17 + + g_PsramMode = mode; + + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,BIT16);//DPORT_SPI_CLK_EN + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,BIT16);//DPORT_SPI_RST + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,BIT1);//DPORT_SPI_CLK_EN_1 + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,BIT1);//DPORT_SPI_RST_1 + SET_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,BIT6);//DPORT_SPI_CLK_EN_2 + CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG,BIT6);//DPORT_SPI_RST_2 + + WRITE_PERI_REG( SPI_EXT3_REG(0), 0x1); + CLEAR_PERI_REG_MASK( SPI_USER_REG(PSRAM_SPI_1), SPI_USR_PREP_HOLD_M); + + + switch(mode){ + case PSRAM_CACHE_F80M_S80M: + psram_spi_init(PSRAM_SPI_1, mode); + extra_dummy = 2; + CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_CS_HOLD); + gpio_matrix_out(16, SPICS1_OUT_IDX, 0, 0); + gpio_matrix_out(17, VSPICLK_OUT_IDX, 0, 0); + //use spi3 clock,but use spi1 data/cs wires + WRITE_PERI_REG(SPI_ADDR_REG(PSRAM_SPI_3), 32<<24); + WRITE_PERI_REG(SPI_CLOCK_REG(PSRAM_SPI_3),SPI_CLK_EQU_SYSCLK_M);//SET 80M AND CLEAR OTHERS + SET_PERI_REG_MASK(SPI_CMD_REG(PSRAM_SPI_3),SPI_FLASH_READ_M); + uint32_t spi_status; + while(1){ + spi_status = READ_PERI_REG(SPI_EXT2_REG(PSRAM_SPI_3)); + if(spi_status != 0 && spi_status != 1){ + CLEAR_PERI_REG_MASK(DPORT_PERIP_CLK_EN_REG,BIT16);//DPORT_SPI_CLK_EN + break; + } + } + break; + case PSRAM_CACHE_F80M_S40M: + case PSRAM_CACHE_F40M_S40M: + default: +#if GPIO_MATRIX_FOR_40M + extra_dummy = 1; +#else + extra_dummy = 0; +#endif + psram_spi_init(PSRAM_SPI_1, mode); + CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1), SPI_CS_HOLD); + gpio_matrix_out(16, SPICS1_OUT_IDX, 0, 0); + gpio_matrix_in(6,SIG_IN_FUNC224_IDX,0); + gpio_matrix_out(20,SIG_IN_FUNC224_IDX,0,0); + gpio_matrix_in(20,SIG_IN_FUNC225_IDX,0); + gpio_matrix_out(17,SIG_IN_FUNC225_IDX,0,0); + break; + } + CLEAR_PERI_REG_MASK(SPI_USER_REG(PSRAM_SPI_1),SPI_CS_SETUP_M); + +#if GPIO_MATRIX_FOR_40M + psram_gpio_config(mode); +// /* @param uint32_t ishspi: 0 for spi, 1 for hspi, flash pad decided by strapping +// * else, bit[5:0] spiclk, bit[11:6] spiq, bit[17:12] spid, bit[23:18] spics0, bit[29:24] spihd +// * +// * @return None +// */ +// uint32_t ishspi = ( (6 & 0x3f) << 0) //clk +// | ( (7 & 0x3f) << 6) //d0 +// | ( (8 & 0x3f) << 12) //d1 +// | ( (11 & 0x3f) << 18) //cs +// | ( (9 & 0x3f) << 24); //d2 +// SelectSpiFunction(ishspi); +// spi_dummy_len_fix(1, 2); +#endif + + + WRITE_PERI_REG(GPIO_ENABLE_W1TS_REG,BIT16|BIT17);//GPIO_Pin_16 | GPIO_Pin_17 + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO16_U,2); + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO17_U,2); + + uint32_t id; + psram_read_id(&id); + if(((id >> 8) & 0xff )!= 0x5d) { + return ESP_FAIL; + } + psram_enable_qio_mode(PSRAM_SPI_1); + psram_cache_init(mode); + return ESP_OK; +} + +//register initialization for sram cache params and r/w commands +static void IRAM_ATTR psram_cache_init(psram_cache_mode_t psram_cache_mode) +{ + CLEAR_PERI_REG_MASK(SPI_CLOCK_REG(0),SPI_CLK_EQU_SYSCLK_M); + SET_PERI_REG_BITS(SPI_CLOCK_REG(0),SPI_CLKDIV_PRE_V,0,SPI_CLKDIV_PRE_S); + SET_PERI_REG_BITS(SPI_CLOCK_REG(0),SPI_CLKCNT_N,1,SPI_CLKCNT_N_S); + SET_PERI_REG_BITS(SPI_CLOCK_REG(0),SPI_CLKCNT_H,0,SPI_CLKCNT_H_S); + SET_PERI_REG_BITS(SPI_CLOCK_REG(0),SPI_CLKCNT_L,1,SPI_CLKCNT_L_S); + + switch(psram_cache_mode){ + case PSRAM_CACHE_F80M_S80M: + CLEAR_PERI_REG_MASK(SPI_DATE_REG(0),BIT(31));//flash 1 div clk,80+40; + CLEAR_PERI_REG_MASK(SPI_DATE_REG(0),BIT(30));//pre clk div , ONLY IF SPI/SRAM@ DIFFERENT SPEED,JUST FOR SPI0. FLASH DIV 2+SRAM DIV4 + WRITE_PERI_REG(SPI_CLOCK_REG(0),SPI_CLK_EQU_SYSCLK_M);//SET 1DIV CLOCK AND RESET OTHER PARAMS + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0),SPI_USR_RD_SRAM_DUMMY_M);//enable cache read dummy + SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0),SPI_SRAM_DUMMY_CYCLELEN_V,3+extra_dummy,SPI_SRAM_DUMMY_CYCLELEN_S); //dummy, psram cache : 40m--+1dummy,80m--+2dummy + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0),SPI_CACHE_SRAM_USR_RCMD_M);//enable user mode for cache read command + break; + case PSRAM_CACHE_F80M_S40M: + SET_PERI_REG_MASK(SPI_DATE_REG(0),BIT(31));//flash 1 div clk + CLEAR_PERI_REG_MASK(SPI_DATE_REG(0),BIT(30));//pre clk div , ONLY IF SPI/SRAM@ DIFFERENT SPEED,JUST FOR SPI0. + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0),SPI_USR_RD_SRAM_DUMMY_M);//enable cache read dummy + SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0),SPI_SRAM_DUMMY_CYCLELEN_V,3+extra_dummy,SPI_SRAM_DUMMY_CYCLELEN_S); //dummy, psram cache : 40m--+1dummy,80m--+2dummy + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0),SPI_CACHE_SRAM_USR_RCMD_M);//enable user mode for cache read command + break; + case PSRAM_CACHE_F40M_S40M: + default: + CLEAR_PERI_REG_MASK(SPI_DATE_REG(0),BIT(31));//flash 1 div clk + CLEAR_PERI_REG_MASK(SPI_DATE_REG(0),BIT(30));//pre clk div + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0),SPI_USR_RD_SRAM_DUMMY_M);//enable cache read dummy + SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0),SPI_SRAM_DUMMY_CYCLELEN_V,3+extra_dummy,SPI_SRAM_DUMMY_CYCLELEN_S); //dummy, psram cache : 40m--+1dummy,80m--+2dummy + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0),SPI_CACHE_SRAM_USR_RCMD_M);//enable user mode for cache read command + break; + } + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0),SPI_CACHE_SRAM_USR_WCMD_M); // cache write command enable + SET_PERI_REG_BITS(SPI_CACHE_SCTRL_REG(0),SPI_SRAM_ADDR_BITLEN_V,23,SPI_SRAM_ADDR_BITLEN_S);//write address for cache command. + SET_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0),SPI_USR_SRAM_QIO_M);//enable qio mode for cache command + CLEAR_PERI_REG_MASK(SPI_CACHE_SCTRL_REG(0),SPI_USR_SRAM_DIO_M);//disable dio mode for cache command + + //config sram cache r/w command + switch(psram_cache_mode) { + case PSRAM_CACHE_F80M_S80M: //in this mode , no delay is needed + SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_BITLEN, 7, + SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S); + SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_VALUE, 0x38, + SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S); //0x38 + SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V, 7, + SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S); + SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V, 0x0b, + SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S); //0x0b + break; + case PSRAM_CACHE_F80M_S40M: //is sram is @40M, need 2 cycles of delay + case PSRAM_CACHE_F40M_S40M: + default: + SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_V, 15, + SPI_CACHE_SRAM_USR_RD_CMD_BITLEN_S); //read command length, 2 bytes(1byte for delay),sending in qio mode in cache + SET_PERI_REG_BITS(SPI_SRAM_DRD_CMD_REG(0), SPI_CACHE_SRAM_USR_RD_CMD_VALUE_V, 0x0b00, + SPI_CACHE_SRAM_USR_RD_CMD_VALUE_S); //0x0b, read command value,(0x00 for delay,0x0b for cmd) + SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_BITLEN, 15, + SPI_CACHE_SRAM_USR_WR_CMD_BITLEN_S); //write command length,2 bytes(1byte for delay,send in qio mode in cache) + SET_PERI_REG_BITS(SPI_SRAM_DWR_CMD_REG(0), SPI_CACHE_SRAM_USR_WR_CMD_VALUE, 0x3800, + SPI_CACHE_SRAM_USR_WR_CMD_VALUE_S); //0x38, write command value,(0x00 for delay) + break; + } + CLEAR_PERI_REG_MASK(DPORT_PRO_CACHE_CTRL1_REG , DPORT_PRO_CACHE_MASK_DRAM1);//use Dram1 to visit ext sram. + SET_PERI_REG_BITS(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CMMU_SRAM_PAGE_MODE, 0, DPORT_PRO_CMMU_SRAM_PAGE_MODE_S); //cache page mode : 1 -->16k 4 -->2k 0-->32k,(accord with the settings in cache_sram_mmu_set) + CLEAR_PERI_REG_MASK(SPI_PIN_REG(0), SPI_CS1_DIS_M); //ENABLE SPI0 CS1 TO PSRAM(CS0--FLASH; CS1--SRAM) +} + +typedef enum { + SPI_INT_SRC_TRANS_DONE = SPI_TRANS_DONE, + SPI_INT_SRC_WR_STA_DONE = SPI_SLV_WR_STA_DONE, + SPI_INT_SRC_RD_STA_DONE = SPI_SLV_RD_STA_DONE, + SPI_INT_SRC_WR_BUF_DONE = SPI_SLV_WR_BUF_DONE, + SPI_INT_SRC_RD_BUF_DONE = SPI_SLV_RD_BUF_DONE, + SPI_INT_SRC_ONE_BUF_RECV_DONE = SPI_IN_SUC_EOF_INT_ENA, + SPI_INT_SRC_ONE_BUF_SEND_DONE = SPI_OUT_EOF_INT_ENA, +} spi_int_src_t; + +/** + * @brief DMA queue description. + */ +typedef struct { + uint32_t block_size: 12; + uint32_t data_length: 12; + uint32_t unused: 5; + uint32_t sub_sof: 1; + uint32_t eof: 1; + uint32_t owner: 1; + uint32_t buf_ptr; + uint32_t next_link_ptr; +} dma_queue_t; + +/** + * @brief Initialize DMA and create a SPI DMA instance. + * + */ +//int spi_dma_init(spi_dma_attr_t *obj, void *isr) +int psram_dma_tx(int dma_channel, uint32_t addr, uint32_t* buf, size_t data_len) +{ + int spi_num = 1; + // Reset DMA + SET_PERI_REG_MASK(SPI_DMA_CONF_REG(spi_num), SPI_OUT_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST); + CLEAR_PERI_REG_MASK(SPI_DMA_OUT_LINK_REG(spi_num), SPI_OUTLINK_START); + CLEAR_PERI_REG_MASK(SPI_DMA_IN_LINK_REG(spi_num), SPI_INLINK_START); + CLEAR_PERI_REG_MASK(SPI_DMA_CONF_REG(spi_num), SPI_OUT_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST); + + // Select DMA channel. + SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 3, dma_channel, ((spi_num - 1) * 2)); + + SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_MOSI);//////add + + // enable send intr + SET_PERI_REG_MASK(SPI_DMA_INT_ENA_REG(spi_num ), SPI_INT_SRC_ONE_BUF_SEND_DONE); + SET_PERI_REG_MASK(SPI_DMA_INT_ENA_REG(spi_num ), SPI_INT_SRC_ONE_BUF_RECV_DONE); + + // Clear all of interrupt source + //spi_int_clear(obj->spi_num); + CLEAR_PERI_REG_MASK(SPI_SLAVE_REG(spi_num), SPI_INT_SRC_TRANS_DONE + | SPI_INT_SRC_WR_STA_DONE + | SPI_INT_SRC_RD_STA_DONE + | SPI_INT_SRC_WR_BUF_DONE + | SPI_INT_SRC_RD_BUF_DONE); + + + dma_queue_t* dma_link = (dma_queue_t*) malloc( sizeof(dma_queue_t)); + dma_link->block_size = data_len; + dma_link->data_length = data_len; + dma_link->buf_ptr = (uint32_t)buf; + dma_link->eof = 1; + dma_link->next_link_ptr = (uint32_t)NULL; + dma_link->owner = 1;//0: cpu 1: dma + dma_link->sub_sof = 0; + dma_link->unused = 0; + + SET_PERI_REG_BITS(SPI_DMA_OUT_LINK_REG(spi_num), SPI_OUTLINK_ADDR, ((uint32_t )dma_link), + SPI_OUTLINK_ADDR_S); + SET_PERI_REG_MASK(SPI_DMA_OUT_LINK_REG(spi_num), SPI_OUTLINK_START); + + // 1. Waiting DMA controller fill TX FIFO + while ((READ_PERI_REG(SPI_DMA_RSTATUS_REG(spi_num))&0x80000000)); + psram_dma_cmd_write_config(addr, data_len, 0); + psram_cmd_start(spi_num, PSRAM_CMD_QPI); + free(dma_link); + return 0; +} + + + +int psram_dma_rx(int dma_channel, uint32_t addr, uint32_t* buf, size_t data_len) +{ + int spi_num = 1; + // Reset DMA + SET_PERI_REG_MASK(SPI_DMA_CONF_REG(spi_num), SPI_OUT_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST); + CLEAR_PERI_REG_MASK(SPI_DMA_OUT_LINK_REG(spi_num), SPI_OUTLINK_START); + CLEAR_PERI_REG_MASK(SPI_DMA_IN_LINK_REG(spi_num), SPI_INLINK_START); + CLEAR_PERI_REG_MASK(SPI_DMA_CONF_REG(spi_num), SPI_OUT_RST | SPI_AHBM_RST | SPI_AHBM_FIFO_RST); + + // Select DMA channel. + SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, DPORT_SPI3_DMA_CHAN_SEL_V, dma_channel, ((spi_num - 1) * 2)); + + SET_PERI_REG_MASK(SPI_USER_REG(spi_num), SPI_USR_MISO);//////add + + // enable send intr + SET_PERI_REG_MASK(SPI_DMA_INT_ENA_REG(spi_num ), SPI_INT_SRC_ONE_BUF_SEND_DONE); + SET_PERI_REG_MASK(SPI_DMA_INT_ENA_REG(spi_num ), SPI_INT_SRC_ONE_BUF_RECV_DONE); + + // Clear all of interrupt source + //spi_int_clear(obj->spi_num); + CLEAR_PERI_REG_MASK(SPI_SLAVE_REG(spi_num), SPI_INT_SRC_TRANS_DONE + | SPI_INT_SRC_WR_STA_DONE + | SPI_INT_SRC_RD_STA_DONE + | SPI_INT_SRC_WR_BUF_DONE + | SPI_INT_SRC_RD_BUF_DONE); + + + dma_queue_t* rx_dma_link = (dma_queue_t*) malloc( sizeof(dma_queue_t)); + rx_dma_link->block_size = data_len; + rx_dma_link->data_length = data_len; + rx_dma_link->buf_ptr = (uint32_t)buf; + rx_dma_link->eof = 1; + rx_dma_link->next_link_ptr = (uint32_t)NULL; + rx_dma_link->owner = 1;//0: cpu 1: dma + rx_dma_link->sub_sof = 0; + rx_dma_link->unused = 0; + + SET_PERI_REG_BITS(SPI_DMA_IN_LINK_REG(spi_num), SPI_INLINK_ADDR, ((uint32_t )rx_dma_link), + SPI_INLINK_ADDR_S); + SET_PERI_REG_MASK(SPI_DMA_IN_LINK_REG(spi_num), SPI_INLINK_START); + + psram_dma_qio_read_config( spi_num, addr, data_len); + psram_cmd_start(spi_num, PSRAM_CMD_QPI); + + //add semaphore to wait trans done, instead of while loop. + free(rx_dma_link); + return 0; +} + +//--------------------------- +//-- below is test code -- +//--------------------------- +#if 1 +void psram_write_once(uint32_t loop_num,uint32_t write_addr,uint32_t mode,uint32_t repeat) +{ +// psram_enable(PSRAM_CACHE_F80M_S40M); +// psram_enable_qio_mode(PSRAM_SPI_1); + uint32_t data_w[8]; + int i; + for(i=0;i<8;i++) { + data_w[i]= ((i+1)<<24)|((i+1)<<16)|((i+1)<<8)|(i+1); + } + ets_printf("WRITE DATA IN QMODE\n"); + for(i = 0;i +#include +#include + +#include "esp_attr.h" +#include "esp_log.h" + +#include "rom/cache.h" +#include "rom/ets_sys.h" +#include "rom/spi_flash.h" +#include "rom/crc.h" +#include "rom/rtc.h" + +#include "soc/soc.h" +#include "soc/cpu.h" +#include "soc/dport_reg.h" +#include "soc/io_mux_reg.h" +#include "soc/efuse_reg.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/timer_group_reg.h" + +#include "sdkconfig.h" +#include "esp_image_format.h" +#include "esp_secure_boot.h" +#include "bootloader_flash.h" + +#include "bootloader_config.h" + +extern int _bss_start; +extern int _bss_end; + +static const char* TAG = "boot"; +/* +We arrive here after the bootloader finished loading the program from flash. The hardware is mostly uninitialized, +flash cache is down and the app CPU is in reset. We do have a stack, so we can do the initialization in C. +*/ + +// TODO: make a nice header file for ROM functions instead of adding externs all over the place +extern void Cache_Flush(int); + +void bootloader_main(); +static void unpack_load_app(const esp_partition_pos_t *app_node); +void print_flash_info(const esp_image_header_t* pfhdr); +void set_cache_and_start_app(uint32_t drom_addr, + uint32_t drom_load_addr, + uint32_t drom_size, + uint32_t irom_addr, + uint32_t irom_load_addr, + uint32_t irom_size, + uint32_t entry_addr); +static void update_flash_config(const esp_image_header_t* pfhdr); + + +void IRAM_ATTR call_start_cpu0() +{ + cpu_configure_region_protection(); + + //Clear bss + memset(&_bss_start, 0, (&_bss_end - &_bss_start) * sizeof(_bss_start)); + + /* completely reset MMU for both CPUs + (in case serial bootloader was running) */ + Cache_Read_Disable(0); + Cache_Read_Disable(1); + Cache_Flush(0); + Cache_Flush(1); + mmu_init(0); + REG_SET_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR); + mmu_init(1); + REG_CLR_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MMU_IA_CLR); + /* (above steps probably unnecessary for most serial bootloader + usage, all that's absolutely needed is that we unmask DROM0 + cache on the following two lines - normal ROM boot exits with + DROM0 cache unmasked, but serial bootloader exits with it + masked. However can't hurt to be thorough and reset + everything.) + + The lines which manipulate DPORT_APP_CACHE_MMU_IA_CLR bit are + necessary to work around a hardware bug. + */ + REG_CLR_BIT(DPORT_PRO_CACHE_CTRL1_REG, DPORT_PRO_CACHE_MASK_DROM0); + REG_CLR_BIT(DPORT_APP_CACHE_CTRL1_REG, DPORT_APP_CACHE_MASK_DROM0); + + bootloader_main(); +} + + +/** + * @function : load_partition_table + * @description: Parse partition table, get useful data such as location of + * OTA info sector, factory app sector, and test app sector. + * + * @inputs: bs bootloader state structure used to save the data + * @return: return true, if the partition table is loaded (and MD5 checksum is valid) + * + */ +bool load_partition_table(bootloader_state_t* bs) +{ + const esp_partition_info_t *partitions; + const int ESP_PARTITION_TABLE_DATA_LEN = 0xC00; /* length of actual data (signature is appended to this) */ + const int MAX_PARTITIONS = ESP_PARTITION_TABLE_DATA_LEN / sizeof(esp_partition_info_t); + char *partition_usage; + + ESP_LOGI(TAG, "Partition Table:"); + ESP_LOGI(TAG, "## Label Usage Type ST Offset Length"); + +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + if(esp_secure_boot_enabled()) { + ESP_LOGI(TAG, "Verifying partition table signature..."); + esp_err_t err = esp_secure_boot_verify_signature(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify partition table signature."); + return false; + } + ESP_LOGD(TAG, "Partition table signature verified"); + } +#endif + + partitions = bootloader_mmap(ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + if (!partitions) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", ESP_PARTITION_TABLE_ADDR, ESP_PARTITION_TABLE_DATA_LEN); + return false; + } + ESP_LOGD(TAG, "mapped partition table 0x%x at 0x%x", ESP_PARTITION_TABLE_ADDR, (intptr_t)partitions); + + for(int i = 0; i < MAX_PARTITIONS; i++) { + const esp_partition_info_t *partition = &partitions[i]; + ESP_LOGD(TAG, "load partition table entry 0x%x", (intptr_t)partition); + ESP_LOGD(TAG, "type=%x subtype=%x", partition->type, partition->subtype); + partition_usage = "unknown"; + + if (partition->magic != ESP_PARTITION_MAGIC) { + /* invalid partition definition indicates end-of-table */ + break; + } + + /* valid partition table */ + switch(partition->type) { + case PART_TYPE_APP: /* app partition */ + switch(partition->subtype) { + case PART_SUBTYPE_FACTORY: /* factory binary */ + bs->factory = partition->pos; + partition_usage = "factory app"; + break; + case PART_SUBTYPE_TEST: /* test binary */ + bs->test = partition->pos; + partition_usage = "test app"; + break; + default: + /* OTA binary */ + if ((partition->subtype & ~PART_SUBTYPE_OTA_MASK) == PART_SUBTYPE_OTA_FLAG) { + bs->ota[partition->subtype & PART_SUBTYPE_OTA_MASK] = partition->pos; + ++bs->app_count; + partition_usage = "OTA app"; + } + else { + partition_usage = "Unknown app"; + } + break; + } + break; /* PART_TYPE_APP */ + case PART_TYPE_DATA: /* data partition */ + switch(partition->subtype) { + case PART_SUBTYPE_DATA_OTA: /* ota data */ + bs->ota_info = partition->pos; + partition_usage = "OTA data"; + break; + case PART_SUBTYPE_DATA_RF: + partition_usage = "RF data"; + break; + case PART_SUBTYPE_DATA_WIFI: + partition_usage = "WiFi data"; + break; + default: + partition_usage = "Unknown data"; + break; + } + break; /* PARTITION_USAGE_DATA */ + default: /* other partition type */ + break; + } + + /* print partition type info */ + ESP_LOGI(TAG, "%2d %-16s %-16s %02x %02x %08x %08x", i, partition->label, partition_usage, + partition->type, partition->subtype, + partition->pos.offset, partition->pos.size); + } + + bootloader_munmap(partitions); + + ESP_LOGI(TAG,"End of partition table"); + return true; +} + +static uint32_t ota_select_crc(const esp_ota_select_entry_t *s) +{ + return crc32_le(UINT32_MAX, (uint8_t*)&s->ota_seq, 4); +} + +static bool ota_select_valid(const esp_ota_select_entry_t *s) +{ + return s->ota_seq != UINT32_MAX && s->crc == ota_select_crc(s); +} + +/** + * @function : bootloader_main + * @description: entry function of 2nd bootloader + * + * @inputs: void + */ + +void bootloader_main() +{ + ESP_LOGI(TAG, "Espressif ESP32 2nd stage bootloader v. %s", BOOT_VERSION); + + esp_image_header_t fhdr; + bootloader_state_t bs; + SpiFlashOpResult spiRet1,spiRet2; + esp_ota_select_entry_t sa,sb; + const esp_ota_select_entry_t *ota_select_map; + + memset(&bs, 0, sizeof(bs)); + + ESP_LOGI(TAG, "compile time " __TIME__ ); + /* disable watch dog here */ + REG_CLR_BIT( RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN ); + REG_CLR_BIT( TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN ); + SPIUnlock(); + + if(esp_image_load_header(0x1000, &fhdr) != ESP_OK) { + ESP_LOGE(TAG, "failed to load bootloader header!"); + return; + } + + print_flash_info(&fhdr); + + update_flash_config(&fhdr); + + if (!load_partition_table(&bs)) { + ESP_LOGE(TAG, "load partition table error!"); + return; + } + + esp_partition_pos_t load_part_pos; + + if (bs.ota_info.offset != 0) { // check if partition table has OTA info partition + //ESP_LOGE("OTA info sector handling is not implemented"); + if (bs.ota_info.size < 2 * sizeof(esp_ota_select_entry_t)) { + ESP_LOGE(TAG, "ERROR: ota_info partition size %d is too small (minimum %d bytes)", bs.ota_info.size, sizeof(esp_ota_select_entry_t)); + return; + } + ota_select_map = bootloader_mmap(bs.ota_info.offset, bs.ota_info.size); + if (!ota_select_map) { + ESP_LOGE(TAG, "bootloader_mmap(0x%x, 0x%x) failed", bs.ota_info.offset, bs.ota_info.size); + return; + } + sa = ota_select_map[0]; + sb = ota_select_map[1]; + bootloader_munmap(ota_select_map); + + if(sa.ota_seq == 0xFFFFFFFF && sb.ota_seq == 0xFFFFFFFF) { + // init status flash + if (bs.factory.offset != 0) { // if have factory bin,boot factory bin + load_part_pos = bs.factory; + } else { + load_part_pos = bs.ota[0]; + sa.ota_seq = 0x01; + sa.crc = ota_select_crc(&sa); + sb.ota_seq = 0x00; + sb.crc = ota_select_crc(&sb); + + Cache_Read_Disable(0); + spiRet1 = SPIEraseSector(bs.ota_info.offset/0x1000); + spiRet2 = SPIEraseSector(bs.ota_info.offset/0x1000+1); + if (spiRet1 != SPI_FLASH_RESULT_OK || spiRet2 != SPI_FLASH_RESULT_OK ) { + ESP_LOGE(TAG, SPI_ERROR_LOG); + return; + } + spiRet1 = SPIWrite(bs.ota_info.offset,(uint32_t *)&sa,sizeof(esp_ota_select_entry_t)); + spiRet2 = SPIWrite(bs.ota_info.offset + 0x1000,(uint32_t *)&sb,sizeof(esp_ota_select_entry_t)); + if (spiRet1 != SPI_FLASH_RESULT_OK || spiRet2 != SPI_FLASH_RESULT_OK ) { + ESP_LOGE(TAG, SPI_ERROR_LOG); + return; + } + Cache_Read_Enable(0); + } + //TODO:write data in ota info + } else { + if(ota_select_valid(&sa) && ota_select_valid(&sb)) { + load_part_pos = bs.ota[(((sa.ota_seq > sb.ota_seq)?sa.ota_seq:sb.ota_seq) - 1)%bs.app_count]; + }else if(ota_select_valid(&sa)) { + load_part_pos = bs.ota[(sa.ota_seq - 1) % bs.app_count]; + }else if(ota_select_valid(&sb)) { + load_part_pos = bs.ota[(sb.ota_seq - 1) % bs.app_count]; + }else { + ESP_LOGE(TAG, "ota data partition info error"); + return; + } + } + } else if (bs.factory.offset != 0) { // otherwise, look for factory app partition + load_part_pos = bs.factory; + } else if (bs.test.offset != 0) { // otherwise, look for test app parition + load_part_pos = bs.test; + } else { // nothing to load, bail out + ESP_LOGE(TAG, "nothing to load"); + return; + } + + ESP_LOGI(TAG, "Loading app partition at offset %08x", load_part_pos); + +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + /* Generate secure digest from this bootloader to protect future + modifications */ + esp_err_t err = esp_secure_boot_permanently_enable(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Bootloader digest generation failed (%d). SECURE BOOT IS NOT ENABLED.", err); + /* Allow booting to continue, as the failure is probably + due to user-configured EFUSEs for testing... + */ + } +#endif + + if(fhdr.encrypt_flag == 0x01) { + /* encrypt flash */ + if (false == flash_encrypt(&bs)) { + ESP_LOGE(TAG, "flash encrypt failed"); + return; + } + } + + // copy loaded segments to RAM, set up caches for mapped segments, and start application + unpack_load_app(&load_part_pos); +} + + +static void unpack_load_app(const esp_partition_pos_t* partition) +{ + esp_err_t err; + esp_image_header_t image_header; + uint32_t image_length; + + /* TODO: verify the app image as part of OTA boot decision, so can have fallbacks */ + err = esp_image_basic_verify(partition->offset, &image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to verify app image @ 0x%x (%d)", partition->offset, err); + return; + } + +#ifdef CONFIG_SECURE_BOOTLOADER_ENABLED + if (esp_secure_boot_enabled()) { + ESP_LOGI(TAG, "Verifying app signature @ 0x%x (length 0x%x)", partition->offset, image_length); + err = esp_secure_boot_verify_signature(partition->offset, image_length); + if (err != ESP_OK) { + ESP_LOGE(TAG, "App image @ 0x%x failed signature verification (%d)", partition->offset, err); + return; + } + ESP_LOGD(TAG, "App signature is valid"); + } +#endif + + if (esp_image_load_header(partition->offset, &image_header) != ESP_OK) { + ESP_LOGE(TAG, "Failed to load app image header @ 0x%x", partition->offset); + return; + } + + uint32_t drom_addr = 0; + uint32_t drom_load_addr = 0; + uint32_t drom_size = 0; + uint32_t irom_addr = 0; + uint32_t irom_load_addr = 0; + uint32_t irom_size = 0; + + /* Reload the RTC memory segments whenever a non-deepsleep reset + is occurring */ + bool load_rtc_memory = rtc_get_reset_reason(0) != DEEPSLEEP_RESET; + + ESP_LOGD(TAG, "bin_header: %u %u %u %u %08x", image_header.magic, + image_header.segment_count, + image_header.spi_mode, + image_header.spi_size, + (unsigned)image_header.entry_addr); + + for (int segment = 0; segment < image_header.segment_count; segment++) { + esp_image_segment_header_t segment_header; + uint32_t data_offs; + if(esp_image_load_segment_header(segment, partition->offset, + &image_header, &segment_header, + &data_offs) != ESP_OK) { + ESP_LOGE(TAG, "failed to load segment header #%d", segment); + return; + } + + const uint32_t address = segment_header.load_addr; + bool load = true; + bool map = false; + if (address == 0x00000000) { // padding, ignore block + load = false; + } + if (address == 0x00000004) { + load = false; // md5 checksum block + // TODO: actually check md5 + } + + if (address >= DROM_LOW && address < DROM_HIGH) { + ESP_LOGD(TAG, "found drom segment, map from %08x to %08x", data_offs, + segment_header.load_addr); + drom_addr = data_offs; + drom_load_addr = segment_header.load_addr; + drom_size = segment_header.data_len + sizeof(segment_header); + load = false; + map = true; + } + + if (address >= IROM_LOW && address < IROM_HIGH) { + ESP_LOGD(TAG, "found irom segment, map from %08x to %08x", data_offs, + segment_header.load_addr); + irom_addr = data_offs; + irom_load_addr = segment_header.load_addr; + irom_size = segment_header.data_len + sizeof(segment_header); + load = false; + map = true; + } + + if (!load_rtc_memory && address >= RTC_IRAM_LOW && address < RTC_IRAM_HIGH) { + ESP_LOGD(TAG, "Skipping RTC code segment at %08x\n", data_offs); + load = false; + } + + if (!load_rtc_memory && address >= RTC_DATA_LOW && address < RTC_DATA_HIGH) { + ESP_LOGD(TAG, "Skipping RTC data segment at %08x\n", data_offs); + load = false; + } + + ESP_LOGI(TAG, "segment %d: paddr=0x%08x vaddr=0x%08x size=0x%05x (%6d) %s", segment, data_offs - sizeof(esp_image_segment_header_t), + segment_header.load_addr, segment_header.data_len, segment_header.data_len, (load)?"load":(map)?"map":""); + + if (load) { + const void *data = bootloader_mmap(data_offs, segment_header.data_len); + if(!data) { + ESP_LOGE(TAG, "bootloader_mmap(0x%xc, 0x%x) failed", + data_offs, segment_header.data_len); + return; + } + memcpy((void *)segment_header.load_addr, data, segment_header.data_len); + bootloader_munmap(data); + } + } + + set_cache_and_start_app(drom_addr, + drom_load_addr, + drom_size, + irom_addr, + irom_load_addr, + irom_size, + image_header.entry_addr); +} + +void set_cache_and_start_app( + uint32_t drom_addr, + uint32_t drom_load_addr, + uint32_t drom_size, + uint32_t irom_addr, + uint32_t irom_load_addr, + uint32_t irom_size, + uint32_t entry_addr) +{ + ESP_LOGD(TAG, "configure drom and irom and start"); + Cache_Read_Disable( 0 ); + Cache_Flush( 0 ); + cache_sram_mmu_set( 0, 0, 0x3f800000, 0, 32, 128 ); + + uint32_t drom_page_count = (drom_size + 64*1024 - 1) / (64*1024); // round up to 64k + ESP_LOGV(TAG, "d mmu set paddr=%08x vaddr=%08x size=%d n=%d", drom_addr & 0xffff0000, drom_load_addr & 0xffff0000, drom_size, drom_page_count ); + int rc = cache_flash_mmu_set( 0, 0, drom_load_addr & 0xffff0000, drom_addr & 0xffff0000, 64, drom_page_count ); + ESP_LOGV(TAG, "rc=%d", rc ); + rc = cache_flash_mmu_set( 1, 0, drom_load_addr & 0xffff0000, drom_addr & 0xffff0000, 64, drom_page_count ); + ESP_LOGV(TAG, "rc=%d", rc ); + uint32_t irom_page_count = (irom_size + 64*1024 - 1) / (64*1024); // round up to 64k + ESP_LOGV(TAG, "i mmu set paddr=%08x vaddr=%08x size=%d n=%d", irom_addr & 0xffff0000, irom_load_addr & 0xffff0000, irom_size, irom_page_count ); + rc = cache_flash_mmu_set( 0, 0, irom_load_addr & 0xffff0000, irom_addr & 0xffff0000, 64, irom_page_count ); + ESP_LOGV(TAG, "rc=%d", rc ); + rc = cache_flash_mmu_set( 1, 0, irom_load_addr & 0xffff0000, irom_addr & 0xffff0000, 64, irom_page_count ); + ESP_LOGV(TAG, "rc=%d", rc ); + REG_CLR_BIT( DPORT_PRO_CACHE_CTRL1_REG, (DPORT_PRO_CACHE_MASK_IRAM0) | (DPORT_PRO_CACHE_MASK_IRAM1 & 0) | (DPORT_PRO_CACHE_MASK_IROM0 & 0) | DPORT_PRO_CACHE_MASK_DROM0 | DPORT_PRO_CACHE_MASK_DRAM1 ); + REG_CLR_BIT( DPORT_APP_CACHE_CTRL1_REG, (DPORT_APP_CACHE_MASK_IRAM0) | (DPORT_APP_CACHE_MASK_IRAM1 & 0) | (DPORT_APP_CACHE_MASK_IROM0 & 0) | DPORT_APP_CACHE_MASK_DROM0 | DPORT_APP_CACHE_MASK_DRAM1 ); + Cache_Read_Enable( 0 ); + + // Application will need to do Cache_Flush(1) and Cache_Read_Enable(1) + + ESP_LOGD(TAG, "start: 0x%08x", entry_addr); + typedef void (*entry_t)(void); + entry_t entry = ((entry_t) entry_addr); + + // TODO: we have used quite a bit of stack at this point. + // use "movsp" instruction to reset stack back to where ROM stack starts. + (*entry)(); +} + +static void update_flash_config(const esp_image_header_t* pfhdr) +{ + uint32_t size; + switch(pfhdr->spi_size) { + case ESP_IMAGE_FLASH_SIZE_1MB: + size = 1; + break; + case ESP_IMAGE_FLASH_SIZE_2MB: + size = 2; + break; + case ESP_IMAGE_FLASH_SIZE_4MB: + size = 4; + break; + case ESP_IMAGE_FLASH_SIZE_8MB: + size = 8; + break; + case ESP_IMAGE_FLASH_SIZE_16MB: + size = 16; + break; + default: + size = 2; + } + Cache_Read_Disable( 0 ); + // Set flash chip size + SPIParamCfg(g_rom_flashchip.deviceId, size * 0x100000, 0x10000, 0x1000, 0x100, 0xffff); + // TODO: set mode + // TODO: set frequency + Cache_Flush(0); + Cache_Read_Enable( 0 ); +} + +void print_flash_info(const esp_image_header_t* phdr) +{ +#if (BOOT_LOG_LEVEL >= BOOT_LOG_LEVEL_NOTICE) + + ESP_LOGD(TAG, "magic %02x", phdr->magic ); + ESP_LOGD(TAG, "segments %02x", phdr->segment_count ); + ESP_LOGD(TAG, "spi_mode %02x", phdr->spi_mode ); + ESP_LOGD(TAG, "spi_speed %02x", phdr->spi_speed ); + ESP_LOGD(TAG, "spi_size %02x", phdr->spi_size ); + + const char* str; + switch ( phdr->spi_speed ) { + case ESP_IMAGE_SPI_SPEED_40M: + str = "40MHz"; + break; + case ESP_IMAGE_SPI_SPEED_26M: + str = "26.7MHz"; + break; + case ESP_IMAGE_SPI_SPEED_20M: + str = "20MHz"; + break; + case ESP_IMAGE_SPI_SPEED_80M: + str = "80MHz"; + break; + default: + str = "20MHz"; + break; + } + ESP_LOGI(TAG, "SPI Speed : %s", str ); + + switch ( phdr->spi_mode ) { + case ESP_IMAGE_SPI_MODE_QIO: + str = "QIO"; + break; + case ESP_IMAGE_SPI_MODE_QOUT: + str = "QOUT"; + break; + case ESP_IMAGE_SPI_MODE_DIO: + str = "DIO"; + break; + case ESP_IMAGE_SPI_MODE_DOUT: + str = "DOUT"; + break; + case ESP_IMAGE_SPI_MODE_FAST_READ: + str = "FAST READ"; + break; + case ESP_IMAGE_SPI_MODE_SLOW_READ: + str = "SLOW READ"; + break; + default: + str = "DIO"; + break; + } + ESP_LOGI(TAG, "SPI Mode : %s", str ); + + switch ( phdr->spi_size ) { + case ESP_IMAGE_FLASH_SIZE_1MB: + str = "1MB"; + break; + case ESP_IMAGE_FLASH_SIZE_2MB: + str = "2MB"; + break; + case ESP_IMAGE_FLASH_SIZE_4MB: + str = "4MB"; + break; + case ESP_IMAGE_FLASH_SIZE_8MB: + str = "8MB"; + break; + case ESP_IMAGE_FLASH_SIZE_16MB: + str = "16MB"; + break; + default: + str = "2MB"; + break; + } + ESP_LOGI(TAG, "SPI Flash Size : %s", str ); +#endif +} diff --git a/examples/14_psram/main/component.mk b/examples/14_psram/main/component.mk new file mode 100755 index 000000000..0b9d7585e --- /dev/null +++ b/examples/14_psram/main/component.mk @@ -0,0 +1,5 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + diff --git a/examples/14_psram/main/psram_test.c b/examples/14_psram/main/psram_test.c new file mode 100755 index 000000000..f0183bda4 --- /dev/null +++ b/examples/14_psram/main/psram_test.c @@ -0,0 +1,143 @@ +/* Timer group-hardware timer example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include "esp_types.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "freertos/semphr.h" +#include +#include +#include +#include "psram.h" + +#define TEST_DATA_LEN 1024 +#define PSRAM_START_ADR (0x3F800000) //0x3F800000 is psram start address + +#define mutex_t xSemaphoreHandle +mutex_t psram_mem_mutex = NULL; + +uint8_t inRam0[TEST_DATA_LEN] = {0}; +uint8_t inRam1[TEST_DATA_LEN] = {0}; + +int mutex_init(mutex_t *pxMutex); +void mutex_lock(mutex_t *pxMutex); +void mutex_unlock(mutex_t *pxMutex); + +/** Create a new mutex + * @param mutex pointer to the mutex to create + * @return 0: successed;1:failed; + */ +int mutex_init(mutex_t *pxMutex) +{ + int xReturn = -1; + *pxMutex = xSemaphoreCreateMutex(); + if (*pxMutex != NULL) { + xReturn = 0; + } + return xReturn; +} + +/** Lock a mutex + * @param mutex the mutex to lock + */ +void mutex_lock(mutex_t *pxMutex) +{ + while (xSemaphoreTake(*pxMutex, portMAX_DELAY) != pdPASS); +} + +/** Unlock a mutex + * @param mutex the mutex to unlock + */ +void mutex_unlock(mutex_t *pxMutex) +{ + xSemaphoreGive(*pxMutex); +} + +/** + * @brief Memcpy with a cache, atomic protected, speaking, reading and writing + */ +void psram_cache_memcpy(uint8_t *dst, uint8_t *src, uint16_t len) +{ + if(psram_mem_mutex == NULL) + { + mutex_init(&psram_mem_mutex); + } + mutex_lock(&psram_mem_mutex); + memcpy(dst, src, len); + mutex_unlock(&psram_mem_mutex); +} + +/** + * @brief Memset with a cache, atomic protected, speaking, reading and writing + */ +void psram_cache_memset(uint8_t *addr, uint8_t c, uint32_t len) +{ + if(psram_mem_mutex == NULL) + { + mutex_init(&psram_mem_mutex); + } + mutex_lock(&psram_mem_mutex); + memset(addr, c, len); + mutex_unlock(&psram_mem_mutex); +} + +/** + * @brief Print multiple bytes + */ +void printfByteS(uint8_t *indata, uint16_t len) +{ + int i = 0; + for(; i < len ; i++) + { + printf("%02X", indata[i]); + } + printf("\n"); +} + +/** + * @brief psram test task + */ +void psram_test_task() +{ + uint8_t *psram_buf_ptr = (uint8_t*) PSRAM_START_ADR; + printf("*****************psram demo start*****************\n"); + psram_enable(PSRAM_CACHE_F40M_S40M); + + psram_cache_memset(psram_buf_ptr, 0x00, TEST_DATA_LEN); //clear zero + + memset(inRam0, 0x5A, TEST_DATA_LEN); + memset(inRam1, 0xA5, TEST_DATA_LEN); + + printf("1.write psram data\n"); + psram_cache_memcpy(psram_buf_ptr, inRam0, TEST_DATA_LEN); + + printf("2.read psram data (expect 0x5A)\n"); + printfByteS(psram_buf_ptr, TEST_DATA_LEN); + + printf("3.write psram data\n"); + psram_cache_memcpy(psram_buf_ptr, inRam1, TEST_DATA_LEN); + + printf("4.read psram data (expect 0xA5)\n"); + printfByteS(psram_buf_ptr, TEST_DATA_LEN); + printf("*****************psram demo end******************\n"); + + while(1) + { + vTaskDelay(100); + } +} + +/** + * @brief In this test + */ +void app_main() +{ + xTaskCreate(psram_test_task, "psram_test_task", 1024 * 5, NULL, 5, NULL); +} +