diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index a6eaa1b31d..376de09d67 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -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 diff --git a/components/esp32/component.mk b/components/esp32/component.mk index ebd7a7d59b..1b831fd21f 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -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 diff --git a/components/esp_common/include/esp_attr.h b/components/esp_common/include/esp_attr.h index 21e52bd67b..6be0d4e678 100644 --- a/components/esp_common/include/esp_attr.h +++ b/components/esp_common/include/esp_attr.h @@ -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__ */ - diff --git a/components/esp_hw_support/Kconfig.spiram.common b/components/esp_hw_support/Kconfig.spiram.common index e10d5cf588..adc7872332 100644 --- a/components/esp_hw_support/Kconfig.spiram.common +++ b/components/esp_hw_support/Kconfig.spiram.common @@ -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. diff --git a/components/esp_hw_support/include/soc/esp32/spiram.h b/components/esp_hw_support/include/soc/esp32/spiram.h index e58712d1fa..b931cb79b0 100644 --- a/components/esp_hw_support/include/soc/esp32/spiram.h +++ b/components/esp_hw_support/include/soc/esp32/spiram.h @@ -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); /** diff --git a/components/esp_hw_support/port/esp32/spiram.c b/components/esp_hw_support/port/esp32/spiram.c index eea2a49bc5..8c6da9afe2 100644 --- a/components/esp_hw_support/port/esp32/spiram.c +++ b/components/esp_hw_support/port/esp32/spiram.c @@ -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); } diff --git a/components/esp_system/ld/esp32/esp32.extram.noinit.ld b/components/esp_system/ld/esp32/esp32.extram.noinit.ld new file mode 100644 index 0000000000..86c7b5341c --- /dev/null +++ b/components/esp_system/ld/esp32/esp32.extram.noinit.ld @@ -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 +} diff --git a/components/esp_system/port/cpu_start.c b/components/esp_system/port/cpu_start.c index faf2a38eac..3258bdbe6c 100644 --- a/components/esp_system/port/cpu_start.c +++ b/components/esp_system/port/cpu_start.c @@ -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();