mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
esp32: Add support for noinit variables in SPIRAM
Add Kconfig option SPIRAM_ALLOW_NOINIT_EXTERNAL_MEMORY When enabled, a new linker script rule (from esp32.extram.noinit.ld) places any variables in the .ext_ram.noinit section in SPIRAM. This section is exempted from the startup SPIRAM memory test and is not zero-initialized or added to the malloc pool, making it usable for noinit variables that persist across reset. The EXT_RAM_NOINIT_ATTR macro places variables in this section.
This commit is contained in:
parent
47e1b41563
commit
b85011c15f
@ -3,10 +3,89 @@ if(NOT "${target}" STREQUAL "esp32")
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(NOT BOOTLOADER_BUILD)
|
||||
# [refactor-todo] propagate these requirements for compatibility
|
||||
# remove in the future
|
||||
set(legacy_reqs driver efuse soc)
|
||||
idf_build_get_property(sdkconfig_header SDKCONFIG_HEADER)
|
||||
|
||||
if(BOOTLOADER_BUILD)
|
||||
# For bootloader, all we need from esp32 is headers
|
||||
idf_component_register(INCLUDE_DIRS include REQUIRES xtensa)
|
||||
target_linker_script(${COMPONENT_LIB} INTERFACE "ld/esp32.peripherals.ld")
|
||||
else()
|
||||
# Regular app build
|
||||
set(srcs
|
||||
"cache_sram_mmu.c"
|
||||
"dport_access.c"
|
||||
"esp_himem.c"
|
||||
"spiram.c"
|
||||
"spiram_psram.c")
|
||||
|
||||
set(include_dirs "include")
|
||||
|
||||
set(requires driver efuse soc xtensa) #unfortunately rom/uart uses SOC registers directly
|
||||
|
||||
# app_update is added here because cpu_start.c uses esp_ota_get_app_description() function.
|
||||
# esp_timer is added here because cpu_start.c uses esp_timer
|
||||
set(priv_requires app_trace app_update bootloader_support esp_system log mbedtls nvs_flash pthread
|
||||
spi_flash vfs espcoredump esp_common perfmon esp_timer esp_ipc esp_pm)
|
||||
|
||||
idf_component_register(SRCS "${srcs}"
|
||||
INCLUDE_DIRS "${include_dirs}"
|
||||
REQUIRES "${requires}"
|
||||
PRIV_REQUIRES "${priv_requires}"
|
||||
REQUIRED_IDF_TARGETS esp32)
|
||||
|
||||
target_linker_script(${COMPONENT_LIB} INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/esp32_out.ld")
|
||||
|
||||
if(CONFIG_SPIRAM_ALLOW_NOINIT_EXTERNAL_MEMORY)
|
||||
# This has to be linked before esp32.project.ld
|
||||
target_linker_script(${COMPONENT_LIB} INTERFACE "ld/esp32.extram.noinit.ld")
|
||||
endif()
|
||||
|
||||
# Process the template file through the linker script generation mechanism, and use the output for linking the
|
||||
# final binary
|
||||
target_linker_script(${COMPONENT_LIB} INTERFACE "${CMAKE_CURRENT_LIST_DIR}/ld/esp32.project.ld.in"
|
||||
PROCESS "${CMAKE_CURRENT_BINARY_DIR}/ld/esp32.project.ld")
|
||||
|
||||
target_linker_script(${COMPONENT_LIB} INTERFACE "ld/esp32.peripherals.ld")
|
||||
target_link_libraries(${COMPONENT_LIB} PUBLIC gcc)
|
||||
target_link_libraries(${COMPONENT_LIB} INTERFACE "-u call_user_start_cpu0")
|
||||
|
||||
idf_build_get_property(config_dir CONFIG_DIR)
|
||||
# Preprocess esp32.ld linker script to include configuration, becomes esp32_out.ld
|
||||
set(LD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ld)
|
||||
add_custom_command(
|
||||
OUTPUT esp32_out.ld
|
||||
COMMAND "${CMAKE_C_COMPILER}" -C -P -x c -E -o esp32_out.ld -I ${config_dir} ${LD_DIR}/esp32.ld
|
||||
MAIN_DEPENDENCY ${LD_DIR}/esp32.ld
|
||||
DEPENDS ${sdkconfig_header}
|
||||
COMMENT "Generating linker script..."
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(esp32_linker_script DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/esp32_out.ld)
|
||||
add_dependencies(${COMPONENT_LIB} esp32_linker_script)
|
||||
|
||||
if(CONFIG_SPIRAM_CACHE_WORKAROUND)
|
||||
# Note: Adding as a PUBLIC compile option here causes this option to propagate to all
|
||||
# components that depend on esp32.
|
||||
#
|
||||
# To handle some corner cases, the same flag is set in project_include.cmake
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC -mfix-esp32-psram-cache-issue)
|
||||
# also, make sure we link with this option so correct toolchain libs are pulled in
|
||||
target_link_libraries(${COMPONENT_LIB} PUBLIC -mfix-esp32-psram-cache-issue)
|
||||
# set strategy selected
|
||||
# note that we don't need to set link options as the library linked is independent of this
|
||||
if(CONFIG_SPIRAM_CACHE_WORKAROUND_STRATEGY_DUPLDST)
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC -mfix-esp32-psram-cache-strategy=dupldst)
|
||||
target_link_libraries(${COMPONENT_LIB} PUBLIC -mfix-esp32-psram-cache-strategy=dupldst)
|
||||
endif()
|
||||
if(CONFIG_SPIRAM_CACHE_WORKAROUND_STRATEGY_MEMW)
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC -mfix-esp32-psram-cache-strategy=memw)
|
||||
target_link_libraries(${COMPONENT_LIB} PUBLIC -mfix-esp32-psram-cache-strategy=memw)
|
||||
endif()
|
||||
if(CONFIG_SPIRAM_CACHE_WORKAROUND_STRATEGY_NOPS)
|
||||
target_compile_options(${COMPONENT_LIB} PUBLIC -mfix-esp32-psram-cache-strategy=nops)
|
||||
target_link_libraries(${COMPONENT_LIB} PUBLIC -mfix-esp32-psram-cache-strategy=nops)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
idf_component_register(INCLUDE_DIRS include
|
||||
|
@ -1,3 +1,39 @@
|
||||
#
|
||||
# Component Makefile
|
||||
#
|
||||
|
||||
COMPONENT_SRCDIRS := .
|
||||
|
||||
ifdef CONFIG_SPIRAM_ALLOW_NOINIT_EXTERNAL_MEMORY
|
||||
# This linker script must come before esp32.project.ld
|
||||
LINKER_SCRIPTS += esp32.extram.noinit.ld
|
||||
endif
|
||||
|
||||
#Linker scripts used to link the final application.
|
||||
#Warning: These linker scripts are only used when the normal app is compiled; the bootloader
|
||||
#specifies its own scripts.
|
||||
LINKER_SCRIPTS += $(COMPONENT_BUILD_DIR)/esp32.project.ld esp32.peripherals.ld
|
||||
|
||||
#ld_include_panic_highint_hdl is added as an undefined symbol because otherwise the
|
||||
#linker will ignore panic_highint_hdl.S as it has no other files depending on any
|
||||
#symbols in it.
|
||||
COMPONENT_ADD_LDFLAGS += -L $(COMPONENT_PATH)/ld \
|
||||
-T esp32_out.ld \
|
||||
-u ld_include_panic_highint_hdl \
|
||||
$(addprefix -T ,$(LINKER_SCRIPTS)) \
|
||||
|
||||
# final linking of project ELF depends on all binary libraries, and
|
||||
# all linker scripts (except esp32_out.ld, as this is code generated here.)
|
||||
COMPONENT_ADD_LINKER_DEPS := $(addprefix ld/, $(filter-out $(COMPONENT_BUILD_DIR)/esp32.project.ld, $(LINKER_SCRIPTS))) \
|
||||
$(COMPONENT_BUILD_DIR)/esp32.project.ld
|
||||
|
||||
# Preprocess esp32.ld linker script into esp32_out.ld
|
||||
#
|
||||
# The library doesn't really depend on esp32_out.ld, but it
|
||||
# saves us from having to add the target to a Makefile.projbuild
|
||||
$(COMPONENT_LIBRARY): esp32_out.ld
|
||||
|
||||
esp32_out.ld: $(COMPONENT_PATH)/ld/esp32.ld ../include/sdkconfig.h
|
||||
$(CC) -I ../include -C -P -x c -E $< -o $@
|
||||
|
||||
COMPONENT_EXTRA_CLEAN := esp32_out.ld $(COMPONENT_BUILD_DIR)/esp32.project.ld
|
||||
|
@ -72,6 +72,14 @@ extern "C" {
|
||||
#define EXT_RAM_ATTR
|
||||
#endif
|
||||
|
||||
#if CONFIG_SPIRAM_ALLOW_NOINIT_EXTERNAL_MEMORY
|
||||
// Forces data into external memory noinit section to avoid initialization after restart.
|
||||
#define EXT_RAM_NOINIT_ATTR _SECTION_ATTR_IMPL(".ext_ram.noinit", __COUNTER__)
|
||||
#else
|
||||
// Place in internal noinit section
|
||||
#define EXT_RAM_NOINIT_ATTR __NOINIT_ATTR
|
||||
#endif
|
||||
|
||||
// Forces data into RTC slow memory. See "docs/deep-sleep-stub.rst"
|
||||
// Any variable marked with this attribute will keep its value
|
||||
// during a deep sleep / wake cycle.
|
||||
@ -155,4 +163,3 @@ FORCE_INLINE_ATTR TYPE& operator<<=(TYPE& a, int b) { a <<= b; return a; }
|
||||
}
|
||||
#endif
|
||||
#endif /* __ESP_ATTR_H__ */
|
||||
|
||||
|
@ -101,3 +101,11 @@ config SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
|
||||
linker fragment scheme `extram_bss`.
|
||||
|
||||
Note that the variables placed in SPIRAM using EXT_RAM_ATTR will be zero initialized.
|
||||
|
||||
config SPIRAM_ALLOW_NOINIT_EXTERNAL_MEMORY
|
||||
bool "Enable placement of noinit segments in external memory"
|
||||
default n
|
||||
depends on SPIRAM
|
||||
help
|
||||
If enabled, noinit variables can be placed in PSRAM using EXT_RAM_NOINIT_ATTR.
|
||||
If disabled, EXT_RAM_NOINIT_ATTR will act like __NOINIT_ATTR.
|
||||
|
@ -59,7 +59,7 @@ void esp_spiram_init_cache(void);
|
||||
*
|
||||
* @return true on success, false on failed memory test
|
||||
*/
|
||||
bool esp_spiram_test(void);
|
||||
bool esp_spiram_test(const void* keepout_addr_low, const void* keepout_addr_high);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -55,6 +55,10 @@ static const char* TAG = "spiram";
|
||||
#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
|
||||
extern uint8_t _ext_ram_bss_start, _ext_ram_bss_end;
|
||||
#endif
|
||||
#if CONFIG_SPIRAM_ALLOW_NOINIT_EXTERNAL_MEMORY
|
||||
extern uint8_t _ext_ram_noinit_start, _ext_ram_noinit_end;
|
||||
#endif
|
||||
|
||||
static bool spiram_inited=false;
|
||||
|
||||
|
||||
@ -79,7 +83,7 @@ static size_t spiram_size_usable_for_malloc(void)
|
||||
true when RAM seems OK, false when test fails. WARNING: Do not run this before the 2nd cpu has been
|
||||
initialized (in a two-core system) or after the heap allocator has taken ownership of the memory.
|
||||
*/
|
||||
bool esp_spiram_test(void)
|
||||
bool esp_spiram_test(const void* keepout_addr_low, const void* keepout_addr_high)
|
||||
{
|
||||
volatile int *spiram=(volatile int*)SOC_EXTRAM_DATA_LOW;
|
||||
size_t p;
|
||||
@ -87,9 +91,19 @@ bool esp_spiram_test(void)
|
||||
int errct=0;
|
||||
int initial_err=-1;
|
||||
for (p=0; p<(s/sizeof(int)); p+=8) {
|
||||
if ((keepout_addr_low <= (const void*)&spiram[p]) && ((const void*)&spiram[p] < keepout_addr_high)) {
|
||||
continue;
|
||||
} else if ((keepout_addr_low < (const void*)&spiram[p+1]) && ((const void*)&spiram[p+1] <= keepout_addr_high)) {
|
||||
continue;
|
||||
}
|
||||
spiram[p]=p^0xAAAAAAAA;
|
||||
}
|
||||
for (p=0; p<(s/sizeof(int)); p+=8) {
|
||||
if ((keepout_addr_low <= (const void*)&spiram[p]) && ((const void*)&spiram[p] < keepout_addr_high)) {
|
||||
continue;
|
||||
} else if ((keepout_addr_low < (const void*)&spiram[p+1]) && ((const void*)&spiram[p+1] <= keepout_addr_high)) {
|
||||
continue;
|
||||
}
|
||||
if (spiram[p]!=(p^0xAAAAAAAA)) {
|
||||
errct++;
|
||||
if (errct==1) initial_err=p*4;
|
||||
@ -172,13 +186,20 @@ esp_err_t esp_spiram_add_to_heapalloc(void)
|
||||
{
|
||||
//Add entire external RAM region to heap allocator. Heap allocator knows the capabilities of this type of memory, so there's
|
||||
//no need to explicitly specify them.
|
||||
intptr_t mallocable_ram_start = (intptr_t)SOC_EXTRAM_DATA_LOW;
|
||||
#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
|
||||
ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", (spiram_size_usable_for_malloc() - (&_ext_ram_bss_end - &_ext_ram_bss_start))/1024);
|
||||
return heap_caps_add_region((intptr_t)&_ext_ram_bss_end, (intptr_t)SOC_EXTRAM_DATA_LOW + spiram_size_usable_for_malloc()-1);
|
||||
#else
|
||||
ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", spiram_size_usable_for_malloc()/1024);
|
||||
return heap_caps_add_region((intptr_t)SOC_EXTRAM_DATA_LOW, (intptr_t)SOC_EXTRAM_DATA_LOW + spiram_size_usable_for_malloc()-1);
|
||||
if (mallocable_ram_start < (intptr_t)&_ext_ram_bss_end) {
|
||||
mallocable_ram_start = (intptr_t)&_ext_ram_bss_end;
|
||||
}
|
||||
#endif
|
||||
#if CONFIG_SPIRAM_ALLOW_NOINIT_EXTERNAL_MEMORY
|
||||
if (mallocable_ram_start < (intptr_t)&_ext_ram_noinit_end) {
|
||||
mallocable_ram_start = (intptr_t)&_ext_ram_noinit_end;
|
||||
}
|
||||
#endif
|
||||
intptr_t mallocable_ram_end = (intptr_t)SOC_EXTRAM_DATA_LOW + spiram_size_usable_for_malloc() - 1;
|
||||
ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", (mallocable_ram_end - mallocable_ram_start)/1024);
|
||||
return heap_caps_add_region(mallocable_ram_start, mallocable_ram_end);
|
||||
}
|
||||
|
||||
|
||||
|
14
components/esp_system/ld/esp32/esp32.extram.noinit.ld
Normal file
14
components/esp_system/ld/esp32/esp32.extram.noinit.ld
Normal file
@ -0,0 +1,14 @@
|
||||
/* This section is only included if CONFIG_SPIRAM_ALLOW_NOINIT_EXTERNAL_MEMORY
|
||||
is set, to link some NOINIT sections in PSRAM */
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* external memory bss, from any global variable with EXT_RAM_NOINIT_ATTR attribute*/
|
||||
.ext_ram.noinit (NOLOAD) :
|
||||
{
|
||||
_ext_ram_noinit_start = ABSOLUTE(.);
|
||||
*(.ext_ram.noinit*)
|
||||
. = ALIGN(4);
|
||||
_ext_ram_noinit_end = ABSOLUTE(.);
|
||||
} > extern_ram_seg
|
||||
}
|
@ -129,6 +129,10 @@ static const char *TAG = "cpu_start";
|
||||
extern int _ext_ram_bss_start;
|
||||
extern int _ext_ram_bss_end;
|
||||
#endif
|
||||
#if CONFIG_SPIRAM_ALLOW_NOINIT_EXTERNAL_MEMORY
|
||||
extern int _ext_ram_noinit_start;
|
||||
extern int _ext_ram_noinit_end;
|
||||
#endif
|
||||
#ifdef CONFIG_ESP32_IRAM_AS_8BIT_ACCESSIBLE_MEMORY
|
||||
extern int _iram_bss_start;
|
||||
extern int _iram_bss_end;
|
||||
@ -419,7 +423,11 @@ void IRAM_ATTR call_start_cpu0(void)
|
||||
|
||||
#if CONFIG_SPIRAM_MEMTEST
|
||||
if (g_spiram_ok) {
|
||||
bool ext_ram_ok = esp_spiram_test();
|
||||
#if CONFIG_SPIRAM_ALLOW_NOINIT_EXTERNAL_MEMORY
|
||||
bool ext_ram_ok = esp_spiram_test(&_ext_ram_noinit_start, &_ext_ram_noinit_end);
|
||||
#else
|
||||
bool ext_ram_ok = esp_spiram_test(0, 0);
|
||||
#endif
|
||||
if (!ext_ram_ok) {
|
||||
ESP_EARLY_LOGE(TAG, "External RAM failed memory test!");
|
||||
abort();
|
||||
|
Loading…
Reference in New Issue
Block a user