xtensa/esp32s3: Add SPIRAM high memory support

1. Configurable mapping of virtual address to psram physical address
  2. Access SPIRAM memory at high physical address through bank switching

Signed-off-by: chenwen@espressif.com <chenwen@espressif.com>
This commit is contained in:
chenwen@espressif.com 2023-11-14 19:32:34 +08:00 committed by Xiang Xiao
parent 88dd492e4d
commit 53beaf1a67
8 changed files with 1465 additions and 7 deletions

View File

@ -919,6 +919,53 @@ config ESP32S3_RTC_HEAP
select ARCH_HAVE_EXTRA_HEAPS
default n
config ESP32S3_SPIRAM_MAP
bool "Remap the size and offset of SPIRAM virtual address"
depends on ESP32S3_SPIRAM
default n
config ESP32S3_SPIRAM_VADDR_OFFSET
hex "Map virtual address offset"
default 0x30000
depends on ESP32S3_SPIRAM_MAP
range 0 0x1FF0000
config ESP32S3_SPIRAM_VADDR_MAP_SIZE
hex "Map virtual address size"
default 0x400000
depends on ESP32S3_SPIRAM_MAP
range 0x10000 0x2000000
config ESP32S3_SPIRAM_BANKSWITCH_ENABLE
bool "Enable bank switching for external RAM"
default n
depends on ESP32S3_SPIRAM_MAP
---help---
The MMU table of ESP32-S3 is up to 512, and the page size of each MMU
is 64KB. The MMU table can be used for mapping instructions and data.
External RAM mapped into data space in 64 KB blocks no larger than 32MB.
The hardware does support larger memories, but these have to be
bank-switched in and out of this address space. Enabling this allows
you to reserve some MMU pages for this, which allows the use of the
esp32s3_himem api to manage these
banks.
config SPIRAM_BANKSWITCH_RESERVE
int "Amount of 64K pages to reserve for bank switching"
depends on ESP32S3_SPIRAM_BANKSWITCH_ENABLE
default 4
range 1 256
---help---
Select the amount of banks reserved for bank switching. Note
that the amount of RAM allocatable with malloc will decrease
by 64KB for each page reserved here.
Note that the amount of banks reserved is smaller than the number
of 64KB blocks of configured external RAM mapped to the data space.
Note that this reservation is only actually done if your
program actually uses the himem API. Without any himem
calls, the reservation is not done and the original amount
of memory will be available.
endmenu # Memory Configuration
config ESP32S3_GPIO_IRQ

View File

@ -138,6 +138,7 @@ endif
ifeq ($(CONFIG_ESP32S3_SPIRAM),y)
CHIP_CSRCS += esp32s3_spiram.c
CHIP_CSRCS += esp32s3_himem.c
ifeq ($(CONFIG_ESP32S3_SPIRAM_MODE_QUAD),y)
CHIP_CSRCS += esp32s3_psram_quad.c

View File

@ -41,6 +41,7 @@
#include "hardware/esp32s3_rom_layout.h"
#ifdef CONFIG_ESP32S3_SPIRAM
# include "esp32s3_spiram.h"
# include "esp32s3_himem.h"
#endif
/****************************************************************************
@ -101,7 +102,8 @@ void up_allocate_heap(void **heap_start, size_t *heap_size)
# elif defined(CONFIG_BUILD_FLAT)
# ifdef MM_USER_HEAP_EXTRAM
ubase = (uintptr_t)esp_spiram_allocable_vaddr_start();
utop = (uintptr_t)esp_spiram_allocable_vaddr_end();
utop = (uintptr_t)(esp_spiram_allocable_vaddr_end() -
esp_himem_reserved_area_size());
# elif defined(MM_USER_HEAP_IRAM)
ubase = (uintptr_t)_sheap + XTENSA_IMEM_REGION_SIZE;
utop = (uintptr_t)HEAP_REGION1_END;
@ -220,7 +222,8 @@ void xtensa_add_region(void)
#if defined(CONFIG_ESP32S3_SPIRAM_COMMON_HEAP) && !defined(MM_USER_HEAP_EXTRAM)
start = (void *)esp_spiram_allocable_vaddr_start();
end = (void *)esp_spiram_allocable_vaddr_end();
end = (void *)(esp_spiram_allocable_vaddr_end() -
esp_himem_reserved_area_size());
size = (size_t)(end - start);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,220 @@
/****************************************************************************
* arch/xtensa/src/esp32s3/esp32s3_himem.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_HIMEM_H
#define __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_HIMEM_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <stddef.h>
#include <nuttx/himem/himem.h>
#ifdef __cplusplus
extern "C"
{
#endif
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: esp_himem_reserved_area_size
*
* Description:
* Get amount of SPI memory address space needed for bankswitching.
*
* Input Parameters:
* None
*
* Returned Value:
* Amount of reserved area, in bytes.
*
****************************************************************************/
size_t esp_himem_reserved_area_size(void);
/****************************************************************************
* Name: esp_himem_get_phys_size
*
* Description:
* Get total amount of memory under control of himem API.
*
* Input Parameters:
* None
*
* Returned Value:
* Amount of memory, in bytes.
*
****************************************************************************/
size_t esp_himem_get_phys_size(void);
/****************************************************************************
* Name: esp_himem_get_free_size
*
* Description:
* Get free amount of memory under control of himem API.
*
* Input Parameters:
* None
*
* Returned Value:
* Amount of memory, in bytes.
*
****************************************************************************/
size_t esp_himem_get_free_size(void);
/****************************************************************************
* Name: esp_himem_alloc
*
* Description:
* Allocate a block in high memory.
*
* Input Parameters:
* size - Size of the to-be-allocated block, in bytes. Note that
* this needs to be a multiple of the external RAM mmu block
* size(64K).
* handle_out - Handle to be returned
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
int esp_himem_alloc(size_t size, esp_himem_handle_t *handle_out);
/****************************************************************************
* Name: esp_himem_free
*
* Description:
* Free a block of physical memory, this clears out the associated handle
* making the memory available for re-allocation again, this will only
* succeed if none of the memory blocks currently have a mapping.
*
* Input Parameters:
* handle - Handle to the block of memory, as given by esp_himem_alloc.
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
int esp_himem_free(esp_himem_handle_t handle);
/****************************************************************************
* Name: esp_himem_alloc_map_range
*
* Description:
* Allocate a memory region to map blocks into, this allocates a
* contiguous CPU memory region that can be used to map blocks of
* physical memory into.
*
* Input Parameters:
* size - Size of the range to be allocated. Note this needs to be a
* multiple of the external RAM mmu block size (64K).
* handle_out - Handle to be returned
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
int esp_himem_alloc_map_range(size_t size, esp_himem_rangehandle_t
*handle_out);
/****************************************************************************
* Name: esp_himem_free_map_range
*
* Description:
* Free a mapping range, this clears out the associated handle making the
* range available for re-allocation again, This will only succeed if none
* of the range blocks currently are used for a mapping.
*
* Input Parameters:
* handle - Handle to the range block, as given by
* esp_himem_alloc_map_range
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
int esp_himem_free_map_range(esp_himem_rangehandle_t handle);
/****************************************************************************
* Name: esp_himem_map
*
* Description:
* Map a block of high memory into the CPUs address space, this effectively
* makes the block available for read/write operations.
*
* Input Parameters:
* handle - Handle to the block of memory, as given by
* esp_himem_alloc
* range - Range handle to map the memory in
* ram_offset - Offset into the block of physical memory of the block to
* map
* range_offset - Offset into the address range where the block will be
* mapped
* len - Length of region to map
* flags - One of ESP_HIMEM_MAPFLAG_*
* out_ptr - Pointer to variable to store resulting memory pointer in
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
int esp_himem_map(esp_himem_handle_t handle,
esp_himem_rangehandle_t range,
size_t ram_offset,
size_t range_offset,
size_t len,
int flags,
void **out_ptr);
/****************************************************************************
* Name: esp_himem_unmap
*
* Description:
* Unmap a region.
*
* Input Parameters:
* range - Range handle
* ptr - Pointer returned by esp_himem_map
* len - Length of the block to be unmapped, must be aligned to the
* SPI RAM MMU blocksize (64K)
*
* Returned Value:
* OK if success or a negative value if fail.
*
****************************************************************************/
int esp_himem_unmap(esp_himem_rangehandle_t range, void *ptr, size_t len);
#ifdef __cplusplus
}
#endif
#endif /* __ARCH_XTENSA_SRC_ESP32S3_ESP32S3_HIMEM_H */

View File

@ -33,6 +33,7 @@
#include <sys/param.h>
#include <nuttx/config.h>
#include <nuttx/spinlock.h>
#include <assert.h>
#include "xtensa.h"
#include "xtensa_attr.h"
@ -53,6 +54,18 @@
# define PSRAM_SPEED PSRAM_CACHE_S80M
#endif
#if CONFIG_ESP32S3_SPIRAM_VADDR_OFFSET
#define SPIRAM_VADDR_OFFSET CONFIG_ESP32S3_SPIRAM_VADDR_OFFSET
#else
#define SPIRAM_VADDR_OFFSET 0
#endif
#if CONFIG_ESP32S3_SPIRAM_VADDR_MAP_SIZE
#define SPIRAM_VADDR_MAP_SIZE CONFIG_ESP32S3_SPIRAM_VADDR_MAP_SIZE
#else
#define SPIRAM_VADDR_MAP_SIZE 0
#endif
static bool g_spiram_inited;
/* These variables are in bytes */
@ -143,6 +156,8 @@ void IRAM_ATTR esp_spiram_init_cache(void)
uint32_t regval;
uint32_t psram_size;
uint32_t mapped_vaddr_size;
uint32_t target_mapped_vaddr_start;
uint32_t target_mapped_vaddr_end;
int ret = psram_get_available_size(&psram_size);
if (ret != OK)
@ -151,9 +166,38 @@ void IRAM_ATTR esp_spiram_init_cache(void)
}
minfo("PSRAM available size = %d\n", psram_size);
mapped_vaddr_size = mmu_valid_space(&g_mapped_vaddr_start);
minfo("Virtual address size = %d\n", mapped_vaddr_size);
if ((SPIRAM_VADDR_OFFSET + SPIRAM_VADDR_MAP_SIZE) > 0)
{
ASSERT(SPIRAM_VADDR_OFFSET % MMU_PAGE_SIZE == 0);
ASSERT(SPIRAM_VADDR_MAP_SIZE % MMU_PAGE_SIZE == 0);
target_mapped_vaddr_start = DRAM0_CACHE_ADDRESS_LOW +
SPIRAM_VADDR_OFFSET;
target_mapped_vaddr_end = target_mapped_vaddr_start +
SPIRAM_VADDR_MAP_SIZE;
if (target_mapped_vaddr_start < g_mapped_vaddr_start)
{
mwarn("Invalid target vaddr = 0x%x, change vaddr to: 0x%x\n",
target_mapped_vaddr_start, g_mapped_vaddr_start);
target_mapped_vaddr_start = g_mapped_vaddr_start;
}
if (target_mapped_vaddr_end >
(g_mapped_vaddr_start + mapped_vaddr_size))
{
mwarn("Invalid vaddr map size: 0x%x, change vaddr end: 0x%x\n",
SPIRAM_VADDR_MAP_SIZE,
g_mapped_vaddr_start + mapped_vaddr_size);
target_mapped_vaddr_end = g_mapped_vaddr_start + mapped_vaddr_size;
}
ASSERT(target_mapped_vaddr_end > target_mapped_vaddr_start);
ASSERT(target_mapped_vaddr_end <= DRAM0_CACHE_ADDRESS_HIGH);
mapped_vaddr_size = target_mapped_vaddr_end -
target_mapped_vaddr_start;
g_mapped_vaddr_start = target_mapped_vaddr_start;
}
if (mapped_vaddr_size < psram_size)
{
@ -168,6 +212,10 @@ void IRAM_ATTR esp_spiram_init_cache(void)
g_mapped_size = psram_size;
}
minfo("Virtual address size = 0x%x, start: 0x%x, end: 0x%x\n",
mapped_vaddr_size, g_mapped_vaddr_start,
g_mapped_vaddr_start + g_mapped_size);
/* Suspend DRAM Case during configuration */
cache_suspend_dcache();
@ -336,7 +384,7 @@ int IRAM_ATTR g_rodata_flash2spiram_offset(void)
}
#endif
int esp_spiram_init(void)
int IRAM_ATTR esp_spiram_init(void)
{
int r;
uint32_t psram_physical_size = 0;

View File

@ -37,6 +37,7 @@
#include <errno.h>
#include <nuttx/fs/fs.h>
#include <nuttx/himem/himem.h>
#ifdef CONFIG_ESP32S3_TIMER
# include "esp32s3_board_tim.h"
@ -123,6 +124,15 @@ int esp32s3_bringup(void)
bool i2s_enable_rx;
#endif
#if defined(CONFIG_ESP32S3_SPIRAM) && \
defined(CONFIG_ESP32S3_SPIRAM_BANKSWITCH_ENABLE)
ret = esp_himem_init();
if (ret < 0)
{
syslog(LOG_ERR, "ERROR: Failed to init HIMEM: %d\n", ret);
}
#endif
#if defined(CONFIG_ESP32S3_EFUSE)
ret = esp32s3_efuse_initialize("/dev/efuse");
if (ret < 0)

View File

@ -29,15 +29,22 @@
#include <signal.h>
#ifdef CONFIG_ESP32_SPIRAM
#if defined(CONFIG_ESP32_SPIRAM) || defined(CONFIG_ESP32S3_SPIRAM)
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_ESP32S3_SPIRAM
/* ESP32S3 MMU block size */
# define ESP_HIMEM_BLKSZ (0x10000)
#else
/* ESP32 MMU block size */
#define ESP_HIMEM_BLKSZ (0x8000)
# define ESP_HIMEM_BLKSZ (0x8000)
#endif
/* Command: HIMEMIOC_ALLOC_BLOCKS
* Description: Allocate a certain number of physical RAM blocks.