From 62a6a0ab4de4825df356341e35cac41698e52eac Mon Sep 17 00:00:00 2001 From: "chenwen@espressif.com" Date: Thu, 30 Nov 2023 19:24:28 +0800 Subject: [PATCH] xtensa/esp32s3: Tasks use SPIRAM as stack can do SPI flash read/write/erase/map/unmap Signed-off-by: chenwen@espressif.com --- arch/xtensa/Kconfig | 7 - arch/xtensa/src/common/xtensa_mm.h | 16 +- arch/xtensa/src/esp32s3/Kconfig | 19 +- .../xtensa/src/esp32s3/esp32s3_spiflash_mtd.c | 287 +++++++++++++++--- .../configs/psram_usrheap/defconfig | 2 + 5 files changed, 262 insertions(+), 69 deletions(-) diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 7efcb1b360..0c7f1f60ab 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -258,13 +258,6 @@ config XTENSA_INTBACKTRACE ---help--- Add necessary logic to be able to have a full backtrace from an interrupt context. -config XTENSA_USE_SPIRAM_HEAP - bool "Enable SPI RAM heap" - default n - ---help--- - If enabled, SPIRAM can be selected as the heap, of course, the premise is that - the device needs to support SPIRAM. - config XTENSA_IMEM_USE_SEPARATE_HEAP bool "Use a separate heap for internal memory" select ARCH_HAVE_EXTRA_HEAPS diff --git a/arch/xtensa/src/common/xtensa_mm.h b/arch/xtensa/src/common/xtensa_mm.h index 75f4ca7919..9c1d6ddebb 100644 --- a/arch/xtensa/src/common/xtensa_mm.h +++ b/arch/xtensa/src/common/xtensa_mm.h @@ -40,18 +40,10 @@ # define UMM_FREE(p) xtensa_imm_free(p) # define UMM_HEAPMEMEBER(p) xtensa_imm_heapmember(p) #else -# ifdef CONFIG_XTENSA_USE_SPIRAM_HEAP -# define UMM_MALLOC(s) kmm_malloc(s) -# define UMM_MEMALIGN(a,s) kmm_memalign(a,s) -# define UMM_FREE(p) kmm_free(p) -# define UMM_HEAPMEMEBER(p) mm_heapmember(p) -# else -# define UMM_MALLOC(s) kumm_malloc(s) -# define UMM_MEMALIGN(a,s) kumm_memalign(a,s) -# define UMM_FREE(p) kumm_free(p) -# define UMM_HEAPMEMEBER(p) umm_heapmember(p) -# endif /* CONFIG_XTENSA_USE_SPIRAM_HEAP */ - +# define UMM_MALLOC(s) kumm_malloc(s) +# define UMM_MEMALIGN(a,s) kumm_memalign(a,s) +# define UMM_FREE(p) kumm_free(p) +# define UMM_HEAPMEMEBER(p) umm_heapmember(p) #endif /* CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP */ #endif /* __ARCH_XTENSA_SRC_COMMON_XTENSA_MM_H */ diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig index a7ecb1f5ff..11a5c3bee3 100644 --- a/arch/xtensa/src/esp32s3/Kconfig +++ b/arch/xtensa/src/esp32s3/Kconfig @@ -905,12 +905,10 @@ choice ESP32S3_SPIRAM_HEAP config ESP32S3_SPIRAM_COMMON_HEAP bool "Additional region to kernel heap" - select XTENSA_USE_SPIRAM_HEAP config ESP32S3_SPIRAM_USER_HEAP bool "Separated userspace heap" select MM_KERNEL_HEAP - select XTENSA_USE_SPIRAM_HEAP endchoice # ESP32S3_SPIRAM_HEAP @@ -1843,6 +1841,23 @@ config ESP32S3_SPIFLASH_OP_TASK_STACKSIZE to disable non-IRAM interrupts and wait for the SPI flash operation to be finished. +config ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK + bool "Support PSRAM As Task Stack" + default n + depends on ESP32S3_SPIRAM + select SCHED_LPWORK + ---help--- + Enable this option, Tasks which use PSRAM as stack + can do SPI Flash read/write/erase/map/unmap. Otherwise, + it may cause exception, the root cause is as following: + 1. When operating SPI flash, cache is also disable, + then software can't access PSRAM by data cache. + 2. SPI flash read/write/erase functions have instruction like + stack-pop and stack-push which may use stack buffer which is + PSRAM space or load/store temp variables which locate in PSRAM space too. + 3. Once operation in step 2 triggers, CPU will trigger exception. + So related SPI flash functions should be sent and run in tasks which use SRAM as task stack. + if ESP32S3_APP_FORMAT_LEGACY comment "Partition Table configuration" diff --git a/arch/xtensa/src/esp32s3/esp32s3_spiflash_mtd.c b/arch/xtensa/src/esp32s3/esp32s3_spiflash_mtd.c index fad0d5a959..2ac6d66424 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_spiflash_mtd.c +++ b/arch/xtensa/src/esp32s3/esp32s3_spiflash_mtd.c @@ -63,6 +63,19 @@ * Private Types ****************************************************************************/ +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK +/* SPI flash work operation code */ + +enum spiflash_op_code_e +{ + SPIFLASH_OP_CODE_WRITE = 0, + SPIFLASH_OP_CODE_READ, + SPIFLASH_OP_CODE_ERASE, + SPIFLASH_OP_CODE_ENCRYPT_READ, + SPIFLASH_OP_CODE_ENCRYPT_WRITE +}; +#endif + /* ESP32-S3 SPI Flash device private data */ struct esp32s3_mtd_dev_s @@ -74,6 +87,26 @@ struct esp32s3_mtd_dev_s const struct spiflash_legacy_data_s **data; }; +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK +/* SPI flash work operation arguments */ + +struct spiflash_work_arg +{ + enum spiflash_op_code_e op_code; + + struct + { + uint32_t addr; + uint8_t *buffer; + uint32_t size; + } op_arg; + + volatile int ret; + + sem_t sem; +}; +#endif + /**************************************************************************** * Private Functions Prototypes ****************************************************************************/ @@ -105,6 +138,15 @@ static ssize_t esp32s3_bwrite_encrypt(struct mtd_dev_s *dev, static int esp32s3_ioctl(struct mtd_dev_s *dev, int cmd, unsigned long arg); +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK +static inline bool IRAM_ATTR stack_is_psram(void); +static void esp32s3_spiflash_work(void *arg); +static int esp32s3_async_op(enum spiflash_op_code_e opcode, + uint32_t addr, + const uint8_t *buffer, + uint32_t size); +#endif + /**************************************************************************** * Private Data ****************************************************************************/ @@ -149,10 +191,141 @@ static const struct esp32s3_mtd_dev_s g_esp32s3_spiflash_encrypt = static mutex_t g_lock = NXMUTEX_INITIALIZER; +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK +static struct work_s g_work; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: stack_is_psram + * + * Description: + * Check if current task's stack space is in PSRAM. + * + * Input Parameters: + * None + * + * Returned Value: + * true if it is in PSRAM or false if not. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK +static inline bool IRAM_ATTR stack_is_psram(void) +{ + void *sp = (void *)up_getsp(); + + return esp32s3_ptr_extram(sp); +} +#endif + +/**************************************************************************** + * Name: esp32s3_spiflash_work + * + * Description: + * Do SPI Flash operation, cache result and send semaphore to wake up + * blocked task. + * + * Input Parameters: + * arg - Reference to SPI flash work arguments structure (cast to void*) + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK +static void esp32s3_spiflash_work(void *arg) +{ + struct spiflash_work_arg *work_arg = (struct spiflash_work_arg *)arg; + + if (work_arg->op_code == SPIFLASH_OP_CODE_WRITE) + { + work_arg->ret = spi_flash_write(work_arg->op_arg.addr, + work_arg->op_arg.buffer, + work_arg->op_arg.size); + } + else if (work_arg->op_code == SPIFLASH_OP_CODE_READ) + { + work_arg->ret = spi_flash_read(work_arg->op_arg.addr, + work_arg->op_arg.buffer, + work_arg->op_arg.size); + } + else if (work_arg->op_code == SPIFLASH_OP_CODE_ERASE) + { + work_arg->ret = spi_flash_erase_range(work_arg->op_arg.addr, + work_arg->op_arg.size); + } + else if (work_arg->op_code == SPIFLASH_OP_CODE_ENCRYPT_READ) + { + work_arg->ret = spi_flash_read_encrypted(work_arg->op_arg.addr, + work_arg->op_arg.buffer, + work_arg->op_arg.size); + } + else if (work_arg->op_code == SPIFLASH_OP_CODE_ENCRYPT_WRITE) + { + work_arg->ret = spi_flash_write_encrypted(work_arg->op_arg.addr, + work_arg->op_arg.buffer, + work_arg->op_arg.size); + } + else + { + ferr("ERROR: op_code=%d is not supported\n", work_arg->op_code); + } + + nxsem_post(&work_arg->sem); +} + +/**************************************************************************** + * Name: esp32s3_async_op + * + * Description: + * Send operation code and arguments to workqueue so that workqueue do SPI + * Flash operation actually. + * + * Input Parameters: + * opcode - SPI flash work operation code + * addr - target address + * buffer - data buffer pointer + * size - data number + * + * Returned Value: + * 0 if success or a negative value if fail. + * + ****************************************************************************/ + +static int esp32s3_async_op(enum spiflash_op_code_e opcode, + uint32_t addr, + const uint8_t *buffer, + uint32_t size) +{ + int ret; + struct spiflash_work_arg work_arg = + { + .op_code = opcode, + .op_arg = + { + .addr = addr, + .buffer = (uint8_t *)buffer, + .size = size, + }, + .sem = NXSEM_INITIALIZER(0, 0) + }; + + ret = work_queue(LPWORK, &g_work, esp32s3_spiflash_work, &work_arg, 0); + if (ret == 0) + { + nxsem_wait(&work_arg.sem); + ret = work_arg.ret; + } + + return ret; +} +#endif + /**************************************************************************** * Name: esp32s3_erase * @@ -194,9 +367,18 @@ static int esp32s3_erase(struct mtd_dev_s *dev, off_t startblock, return ret; } - ret = spi_flash_erase_range(offset, nbytes); - nxmutex_unlock(&g_lock); +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK + if (stack_is_psram()) + { + ret = esp32s3_async_op(SPIFLASH_OP_CODE_ERASE, offset, NULL, nbytes); + } + else +#endif + { + ret = spi_flash_erase_range(offset, nbytes); + } + nxmutex_unlock(&g_lock); if (ret == OK) { ret = nblocks; @@ -252,9 +434,19 @@ static ssize_t esp32s3_read(struct mtd_dev_s *dev, off_t offset, return ret; } - ret = spi_flash_read(offset, buffer, nbytes); - nxmutex_unlock(&g_lock); +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK + if (stack_is_psram()) + { + ret = esp32s3_async_op(SPIFLASH_OP_CODE_READ, offset, + buffer, nbytes); + } + else +#endif + { + ret = spi_flash_read(offset, buffer, nbytes); + } + nxmutex_unlock(&g_lock); if (ret == OK) { ret = nbytes; @@ -294,20 +486,10 @@ static ssize_t esp32s3_bread(struct mtd_dev_s *dev, off_t startblock, #ifdef CONFIG_ESP32S3_STORAGE_MTD_DEBUG finfo("%s(%p, 0x%x, %d, %p)\n", __func__, dev, startblock, nblocks, buffer); - - finfo("spi_flash_read(0x%x, %p, %d)\n", addr, buffer, size); #endif - ret = nxmutex_lock(&g_lock); - if (ret < 0) - { - return ret; - } - - ret = spi_flash_read(addr, buffer, size); - nxmutex_unlock(&g_lock); - - if (ret == OK) + ret = esp32s3_read(dev, addr, size, buffer); + if (ret == size) { ret = nblocks; } @@ -347,8 +529,7 @@ static ssize_t esp32s3_read_decrypt(struct mtd_dev_s *dev, #ifdef CONFIG_ESP32S3_STORAGE_MTD_DEBUG finfo("%s(%p, 0x%x, %d, %p)\n", __func__, dev, offset, nbytes, buffer); - finfo("spi_flash_read_encrypted(0x%x, %p, %d)\n", offset, buffer, - nbytes); + finfo("spi_flash_read_encrypted(0x%x, %p, %d)\n", offset, buffer, nbytes); #endif /* Acquire the mutex. */ @@ -359,9 +540,19 @@ static ssize_t esp32s3_read_decrypt(struct mtd_dev_s *dev, return ret; } - ret = spi_flash_read_encrypted(offset, buffer, nbytes); - nxmutex_unlock(&g_lock); +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK + if (stack_is_psram()) + { + ret = esp32s3_async_op(SPIFLASH_OP_CODE_ENCRYPT_READ, offset, + buffer, nbytes); + } + else +#endif + { + ret = spi_flash_read_encrypted(offset, buffer, nbytes); + } + nxmutex_unlock(&g_lock); if (ret == OK) { ret = nbytes; @@ -403,20 +594,10 @@ static ssize_t esp32s3_bread_decrypt(struct mtd_dev_s *dev, #ifdef CONFIG_ESP32S3_STORAGE_MTD_DEBUG finfo("%s(%p, 0x%x, %d, %p)\n", __func__, dev, startblock, nblocks, buffer); - - finfo("spi_flash_read_encrypted(0x%x, %p, %d)\n", addr, buffer, size); #endif - ret = nxmutex_lock(&g_lock); - if (ret < 0) - { - return ret; - } - - ret = spi_flash_read_encrypted(addr, buffer, size); - nxmutex_unlock(&g_lock); - - if (ret == OK) + ret = esp32s3_read_decrypt(dev, addr, size, buffer); + if (ret == size) { ret = nblocks; } @@ -471,9 +652,19 @@ static ssize_t esp32s3_write(struct mtd_dev_s *dev, off_t offset, return ret; } - ret = spi_flash_write(offset, buffer, nbytes); - nxmutex_unlock(&g_lock); +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK + if (stack_is_psram()) + { + ret = esp32s3_async_op(SPIFLASH_OP_CODE_WRITE, offset, + buffer, nbytes); + } + else +#endif + { + ret = spi_flash_write(offset, buffer, nbytes); + } + nxmutex_unlock(&g_lock); if (ret == OK) { ret = nbytes; @@ -525,9 +716,19 @@ static ssize_t esp32s3_bwrite_encrypt(struct mtd_dev_s *dev, return ret; } - ret = spi_flash_write_encrypted(addr, buffer, size); - nxmutex_unlock(&g_lock); +#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK + if (stack_is_psram()) + { + ret = esp32s3_async_op(SPIFLASH_OP_CODE_ENCRYPT_WRITE, addr, + buffer, size); + } + else +#endif + { + ret = spi_flash_write_encrypted(addr, buffer, size); + } + nxmutex_unlock(&g_lock); if (ret == OK) { ret = nblocks; @@ -567,20 +768,10 @@ static ssize_t esp32s3_bwrite(struct mtd_dev_s *dev, off_t startblock, #ifdef CONFIG_ESP32S3_STORAGE_MTD_DEBUG finfo("%s(%p, 0x%x, %d, %p)\n", __func__, dev, startblock, nblocks, buffer); - - finfo("spi_flash_write(0x%x, %p, %d)\n", addr, buffer, size); #endif - ret = nxmutex_lock(&g_lock); - if (ret < 0) - { - return ret; - } - - ret = spi_flash_write(addr, buffer, size); - nxmutex_unlock(&g_lock); - - if (ret == OK) + ret = esp32s3_write(dev, addr, size, buffer); + if (ret == size) { ret = nblocks; } diff --git a/boards/xtensa/esp32s3/esp32s3-devkit/configs/psram_usrheap/defconfig b/boards/xtensa/esp32s3/esp32s3-devkit/configs/psram_usrheap/defconfig index 47a5f71ce6..6124a2b8ad 100644 --- a/boards/xtensa/esp32s3/esp32s3-devkit/configs/psram_usrheap/defconfig +++ b/boards/xtensa/esp32s3/esp32s3-devkit/configs/psram_usrheap/defconfig @@ -21,9 +21,11 @@ CONFIG_ARCH_XTENSA=y CONFIG_BOARD_LOOPSPERMSEC=16717 CONFIG_BUILTIN=y CONFIG_ESP32S3_FLASH_FREQ_80M=y +CONFIG_ESP32S3_SPIFLASH=y CONFIG_ESP32S3_SPIRAM=y CONFIG_ESP32S3_SPIRAM_MODE_OCT=y CONFIG_ESP32S3_SPIRAM_USER_HEAP=y +CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK=y CONFIG_ESP32S3_UART0=y CONFIG_FS_PROCFS=y CONFIG_HAVE_CXX=y