xtensa/esp32s3: Fix issue of system blocking when SPIRAM is used as stack

1. Fix issue of system blocking due to disable dcache.
   2. Support Ext-SRAM-Cache mmu mapping in SMP mode.

Signed-off-by: chenwen@espressif.com <chenwen@espressif.com>
This commit is contained in:
chenwen@espressif.com 2023-12-28 11:48:41 +08:00 committed by Alan Carvalho de Assis
parent 5b1486f9ce
commit 638df3329b
5 changed files with 293 additions and 51 deletions

View File

@ -32,6 +32,7 @@
#include "xtensa.h"
#include "esp32s3_spiram.h"
#include "esp32s3_himem.h"
#include "esp32s3_spiflash_mtd.h"
#include "hardware/esp32s3_soc.h"
#include "hardware/esp32s3_cache_memory.h"
#include "hardware/esp32s3_extmem.h"
@ -82,7 +83,6 @@
# define SPIRAM_BANKSWITCH_RESERVE 0
#endif
#define MMU_PAGE_SIZE 0x10000
#define MMU_PAGE_TO_BYTES(page_num) ((page_num) * MMU_PAGE_SIZE)
#define BYTES_TO_MMU_PAGE(bytes) ((bytes) / MMU_PAGE_SIZE)
@ -129,7 +129,6 @@ extern int cache_dbus_mmu_set(uint32_t ext_ram, uint32_t vaddr,
static inline bool ramblock_idx_valid(int ramblock_idx);
static inline bool rangeblock_idx_valid(int rangeblock_idx);
static void set_bank(int virt_bank, int phys_bank, int ct);
static uint32_t esp_himem_get_range_start(void);
static uint32_t esp_himem_get_range_block(void);
static uint32_t esp_himem_get_phy_block(void);
@ -209,47 +208,6 @@ static inline bool rangeblock_idx_valid(int rangeblock_idx)
return (rangeblock_idx >= 0 && rangeblock_idx < g_rangeblockcnt);
}
/****************************************************************************
* Name: set_bank
*
* Description:
* Set DCache mmu mapping.
*
* Input Parameters:
* virt_bank - Beginning of the virtual bank
* phys_bank - Beginning of the physical bank
* ct - Number of banks
*
* Returned Value:
* None.
*
****************************************************************************/
static void set_bank(int virt_bank, int phys_bank, int ct)
{
uint32_t regval;
/* Suspend DRAM Case during configuration */
cache_suspend_dcache();
ASSERT(cache_dbus_mmu_set(MMU_ACCESS_SPIRAM,
SOC_EXTRAM_DATA_LOW +
MMU_PAGE_TO_BYTES(virt_bank),
MMU_PAGE_TO_BYTES(phys_bank), 64, ct, 0) == 0);
regval = getreg32(EXTMEM_DCACHE_CTRL1_REG);
regval &= ~EXTMEM_DCACHE_SHUT_CORE0_BUS;
putreg32(regval, EXTMEM_DCACHE_CTRL1_REG);
#if defined(CONFIG_SMP)
regval = getreg32(EXTMEM_DCACHE_CTRL1_REG);
regval &= ~EXTMEM_DCACHE_SHUT_CORE1_BUS;
putreg32(regval, EXTMEM_DCACHE_CTRL1_REG);
#endif
cache_resume_dcache(0);
}
/****************************************************************************
* Name: esp_himem_get_range_start
*
@ -974,7 +932,7 @@ int esp_himem_map(esp_himem_handle_t handle,
{
virt_bank = himem_mmu_start + range->block_start + i + range_block;
phys_bank = himem_phy_start + handle->block[i + ram_block];
set_bank(virt_bank, phys_bank, 1);
esp32s3_set_bank(virt_bank, phys_bank, 1);
}
/* Set out pointer */

View File

@ -39,9 +39,11 @@
#include <nuttx/mtd/mtd.h>
#include "hardware/esp32s3_soc.h"
#include "hardware/esp32s3_cache_memory.h"
#include "xtensa_attr.h"
#include "esp32s3_spiflash.h"
#include "esp32s3_spiram.h"
#include "rom/esp32s3_spiflash.h"
#include "esp32s3_spiflash_mtd.h"
@ -71,6 +73,7 @@ enum spiflash_op_code_e
SPIFLASH_OP_CODE_WRITE = 0,
SPIFLASH_OP_CODE_READ,
SPIFLASH_OP_CODE_ERASE,
SPIFLASH_OP_CODE_SET_BANK,
SPIFLASH_OP_CODE_ENCRYPT_READ,
SPIFLASH_OP_CODE_ENCRYPT_WRITE
};
@ -99,6 +102,7 @@ struct spiflash_work_arg
uint32_t addr;
uint8_t *buffer;
uint32_t size;
uint32_t paddr;
} op_arg;
volatile int ret;
@ -144,7 +148,8 @@ 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);
uint32_t size,
uint32_t paddr);
#endif
/****************************************************************************
@ -259,6 +264,12 @@ static void esp32s3_spiflash_work(void *arg)
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_SET_BANK)
{
work_arg->ret = cache_dbus_mmu_map(work_arg->op_arg.addr,
work_arg->op_arg.paddr,
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,
@ -300,7 +311,8 @@ 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)
uint32_t size,
uint32_t paddr)
{
int ret;
struct spiflash_work_arg work_arg =
@ -311,6 +323,7 @@ static int esp32s3_async_op(enum spiflash_op_code_e opcode,
.addr = addr,
.buffer = (uint8_t *)buffer,
.size = size,
.paddr = paddr,
},
.sem = NXSEM_INITIALIZER(0, 0)
};
@ -370,7 +383,8 @@ static int esp32s3_erase(struct mtd_dev_s *dev, off_t startblock,
#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK
if (stack_is_psram())
{
ret = esp32s3_async_op(SPIFLASH_OP_CODE_ERASE, offset, NULL, nbytes);
ret = esp32s3_async_op(SPIFLASH_OP_CODE_ERASE, offset, NULL,
nbytes, 0);
}
else
#endif
@ -438,7 +452,7 @@ static ssize_t esp32s3_read(struct mtd_dev_s *dev, off_t offset,
if (stack_is_psram())
{
ret = esp32s3_async_op(SPIFLASH_OP_CODE_READ, offset,
buffer, nbytes);
buffer, nbytes, 0);
}
else
#endif
@ -544,7 +558,7 @@ static ssize_t esp32s3_read_decrypt(struct mtd_dev_s *dev,
if (stack_is_psram())
{
ret = esp32s3_async_op(SPIFLASH_OP_CODE_ENCRYPT_READ, offset,
buffer, nbytes);
buffer, nbytes, 0);
}
else
#endif
@ -656,7 +670,7 @@ static ssize_t esp32s3_write(struct mtd_dev_s *dev, off_t offset,
if (stack_is_psram())
{
ret = esp32s3_async_op(SPIFLASH_OP_CODE_WRITE, offset,
buffer, nbytes);
buffer, nbytes, 0);
}
else
#endif
@ -720,7 +734,7 @@ static ssize_t esp32s3_bwrite_encrypt(struct mtd_dev_s *dev,
if (stack_is_psram())
{
ret = esp32s3_async_op(SPIFLASH_OP_CODE_ENCRYPT_WRITE, addr,
buffer, size);
buffer, size, 0);
}
else
#endif
@ -863,6 +877,43 @@ static int esp32s3_ioctl(struct mtd_dev_s *dev, int cmd,
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: esp32s3_set_bank
*
* Description:
* Set Ext-SRAM-Cache mmu mapping.
*
* Input Parameters:
* virt_bank - Beginning of the virtual bank
* phys_bank - Beginning of the physical bank
* ct - Number of banks
*
* Returned Value:
* None.
*
****************************************************************************/
void esp32s3_set_bank(int virt_bank, int phys_bank, int ct)
{
int ret;
uint32_t vaddr = SOC_EXTRAM_DATA_LOW + MMU_PAGE_SIZE * virt_bank;
uint32_t paddr = phys_bank * MMU_PAGE_SIZE;
#ifdef CONFIG_ESP32S3_SPI_FLASH_SUPPORT_PSRAM_STACK
if (stack_is_psram())
{
ret = esp32s3_async_op(SPIFLASH_OP_CODE_SET_BANK, vaddr, NULL, ct,
paddr);
}
else
#endif
{
ret = cache_dbus_mmu_map(vaddr, paddr, ct);
}
DEBUGASSERT(ret == 0);
UNUSED(ret);
}
/****************************************************************************
* Name: esp32s3_spiflash_alloc_mtdpart
*

View File

@ -46,6 +46,24 @@ extern "C"
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: esp32s3_set_bank
*
* Description:
* Set Ext-SRAM-Cache mmu mapping.
*
* Input Parameters:
* virt_bank - Beginning of the virtual bank
* phys_bank - Beginning of the physical bank
* ct - Number of banks
*
* Returned Value:
* None.
*
****************************************************************************/
void esp32s3_set_bank(int virt_bank, int phys_bank, int ct);
/****************************************************************************
* Name: esp32s3_spiflash_mtd
*

View File

@ -33,6 +33,7 @@
#include <sys/param.h>
#include <nuttx/config.h>
#include <nuttx/spinlock.h>
#include <nuttx/init.h>
#include <assert.h>
#include "xtensa.h"
@ -66,6 +67,22 @@
#define SPIRAM_VADDR_MAP_SIZE 0
#endif
/* Max MMU available paddr page num.
* `MMU_MAX_PADDR_PAGE_NUM * MMU_PAGE_SIZE` means the max paddr
* address supported by the MMU. e.g.: 16384 * 64KB, means MMU can
* support 1GB paddr at most
*/
#define MMU_MAX_PADDR_PAGE_NUM 16384
/* This is the mask used for mapping. e.g.: 0x4200_0000 & MMU_VADDR_MASK */
#define MMU_VADDR_MASK 0x1FFFFFF
/* MMU entry num */
#define MMU_ENTRY_NUM 512
static bool g_spiram_inited;
/* These variables are in bytes */
@ -109,6 +126,7 @@ extern void cache_resume_dcache(uint32_t val);
extern int cache_dbus_mmu_set(uint32_t ext_ram, uint32_t vaddr,
uint32_t paddr, uint32_t psize,
uint32_t num, uint32_t fixed);
extern int cache_invalidate_addr(uint32_t addr, uint32_t size);
/****************************************************************************
* Private Functions
@ -142,10 +160,189 @@ static inline uint32_t mmu_valid_space(uint32_t *start_address)
return 0;
}
/****************************************************************************
* Name: mmu_check_valid_paddr_region
*
* Description:
* Check if the paddr region is valid.
*
* Input Parameters:
* paddr_start - start of the physical address
* len - length, in bytes
*
* Returned Value:
* True for valid.
*
****************************************************************************/
static inline bool mmu_check_valid_paddr_region(uint32_t paddr_start,
uint32_t len)
{
return (paddr_start < (MMU_PAGE_SIZE * MMU_MAX_PADDR_PAGE_NUM)) &&
(len < (MMU_PAGE_SIZE * MMU_MAX_PADDR_PAGE_NUM)) &&
((paddr_start + len - 1) <
(MMU_PAGE_SIZE * MMU_MAX_PADDR_PAGE_NUM));
}
/****************************************************************************
* Name: mmu_check_valid_ext_vaddr_region
*
* Description:
* Check if the external memory vaddr region is valid.
*
* Input Parameters:
* vaddr_start - start of the virtual address
* len - length, in bytes
*
* Returned Value:
* True for valid.
*
****************************************************************************/
static inline bool mmu_check_valid_ext_vaddr_region(uint32_t vaddr_start,
uint32_t len)
{
uint32_t vaddr_end = vaddr_start + len - 1;
bool valid = false;
valid |= (ADDRESS_IN_IRAM0_CACHE(vaddr_start) &&
ADDRESS_IN_IRAM0_CACHE(vaddr_end)) |
(ADDRESS_IN_DRAM0_CACHE(vaddr_start) &&
ADDRESS_IN_DRAM0_CACHE(vaddr_end));
return valid;
}
/****************************************************************************
* Name: esp_mmu_map_region
*
* Description:
* To map a virtual address block to a physical memory block.
*
* Input Parameters:
* vaddr - Virtual address in CPU address space
* paddr - Physical address in Ext-SRAM
* len - Length to be mapped, in bytes
* mem_type - MMU target physical memory
*
* Returned Value:
* Actual mapped length.
*
****************************************************************************/
static int IRAM_ATTR esp_mmu_map_region(uint32_t vaddr, uint32_t paddr,
uint32_t len, uint32_t mem_type)
{
DEBUGASSERT(vaddr % MMU_PAGE_SIZE == 0);
DEBUGASSERT(paddr % MMU_PAGE_SIZE == 0);
DEBUGASSERT(mmu_check_valid_paddr_region(paddr, len));
DEBUGASSERT(mmu_check_valid_ext_vaddr_region(vaddr, len));
uint32_t mmu_val;
uint32_t entry_id;
uint32_t page_num = (len + MMU_PAGE_SIZE - 1) / MMU_PAGE_SIZE;
uint32_t ret = page_num * MMU_PAGE_SIZE;
mmu_val = paddr >> 16;
bool write_back = false;
while (page_num)
{
entry_id = (vaddr & MMU_VADDR_MASK) >> 16;
DEBUGASSERT(entry_id < MMU_ENTRY_NUM);
if (write_back == false && FLASH_MMU_TABLE[entry_id] != MMU_INVALID)
{
esp_spiram_writeback_cache();
write_back = true;
}
FLASH_MMU_TABLE[entry_id] = mmu_val | mem_type;
vaddr += MMU_PAGE_SIZE;
mmu_val++;
page_num--;
}
return ret;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: cache_dbus_mmu_map
*
* Description:
* Set Ext-SRAM-Cache mmu mapping.
*
* Input Parameters:
* vaddr - Virtual address in CPU address space
* paddr - Physical address in Ext-SRAM
* num - Pages to be set
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
int IRAM_ATTR cache_dbus_mmu_map(int vaddr, int paddr, int num)
{
uint32_t regval;
irqstate_t flags;
uint32_t actual_mapped_len;
uint32_t cache_state[CONFIG_SMP_NCPUS];
int cpu = up_cpu_index();
#ifdef CONFIG_SMP
bool smp_start = OSINIT_OS_READY();
int other_cpu = cpu ? 0 : 1;
#endif
/* The MMU registers are implemented in such a way that lookups from the
* cache subsystem may collide with CPU access to the MMU registers. We use
* cache_suspend_dcache to make sure the cache is disabled.
*/
flags = enter_critical_section();
#ifdef CONFIG_SMP
/* The other CPU might be accessing the cache at the same time, just by
* using variables in external RAM.
*/
if (smp_start)
{
up_cpu_pause(other_cpu);
}
cache_state[other_cpu] = cache_suspend_dcache();
#endif
cache_state[cpu] = cache_suspend_dcache();
esp_mmu_map_region(vaddr, paddr, num * MMU_PAGE_SIZE, MMU_ACCESS_SPIRAM);
regval = getreg32(EXTMEM_DCACHE_CTRL1_REG);
regval &= ~EXTMEM_DCACHE_SHUT_CORE0_BUS;
putreg32(regval, EXTMEM_DCACHE_CTRL1_REG);
#if defined(CONFIG_SMP)
regval = getreg32(EXTMEM_DCACHE_CTRL1_REG);
regval &= ~EXTMEM_DCACHE_SHUT_CORE1_BUS;
putreg32(regval, EXTMEM_DCACHE_CTRL1_REG);
#endif
cache_invalidate_addr(vaddr, num * MMU_PAGE_SIZE);
cache_resume_dcache(cache_state[cpu]);
#ifdef CONFIG_SMP
cache_resume_dcache(cache_state[other_cpu]);
if (smp_start)
{
up_cpu_resume(other_cpu);
}
#endif
leave_critical_section(flags);
return 0;
}
/* Initially map all psram physical address to virtual address.
* If psram physical size is larger than virtual address range, then only
* map the virtual address range.

View File

@ -34,6 +34,24 @@ extern "C"
{
#endif
/****************************************************************************
* Name: cache_dbus_mmu_map
*
* Description:
* Set Ext-SRAM-Cache mmu mapping.
*
* Input Parameters:
* vaddr - Virtual address in CPU address space
* paddr - Physical address in Ext-SRAM
* num - Pages to be set
*
* Returned Value:
* 0 if success or a negative value if fail.
*
****************************************************************************/
int cache_dbus_mmu_map(int vaddr, int paddr, int num);
/* Initialize spiram interface/hardware. Normally called from
* cpu_start.c.
*