diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index 8577dc4340..007a6c4358 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -75,8 +75,14 @@ else() endif() target_linker_script(esp32 "${CMAKE_CURRENT_BINARY_DIR}/esp32_out.ld") + if(CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY) + # This has to be linked before esp32.common.ld + target_linker_script(esp32 "ld/esp32.extram.bss.ld") + endif() + + target_linker_script(esp32 "ld/esp32.common.ld") + target_linker_script(esp32 - "ld/esp32.common.ld" "ld/esp32.rom.ld" "ld/esp32.peripherals.ld" "ld/esp32.rom.libgcc.ld" @@ -113,6 +119,7 @@ else() MAIN_DEPENDENCY ${LD_DIR}/esp32.ld ${SDKCONFIG_H} COMMENT "Generating linker script..." VERBATIM) + add_custom_target(esp32_linker_script DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/esp32_out.ld) add_dependencies(esp32 esp32_linker_script) diff --git a/components/esp32/Kconfig b/components/esp32/Kconfig index 2caafd9826..8402caeccb 100644 --- a/components/esp32/Kconfig +++ b/components/esp32/Kconfig @@ -41,7 +41,7 @@ config SPIRAM_BOOT_INIT config SPIRAM_IGNORE_NOTFOUND bool "Ignore PSRAM when not found" default "n" - depends on SPIRAM_BOOT_INIT + depends on SPIRAM_BOOT_INIT && !SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY help Normally, if psram initialization is enabled during compile time but not found at runtime, it is seen as an error making the ESP32 panic. If this is enabled, the ESP32 will keep on @@ -172,6 +172,15 @@ config SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY ROM code in any way (no direct calls, but also no Bluetooth/WiFi), you can try to disable this and use xTaskCreateStatic to create the tasks stack in external memory. +config SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY + bool "Allow .bss segment placed in external memory" + default n + depends on SPIRAM_SUPPORT + help + If enabled the option,and add EXT_RAM_ATTR defined your variable,then your variable will be placed + in PSRAM instead of internal memory, and placed most of variables of lwip,net802.11,pp,bluedroid library + to external memory defaultly. + endmenu config MEMMAP_TRACEMEM diff --git a/components/esp32/component.mk b/components/esp32/component.mk index 6c03587a36..605bb56373 100644 --- a/components/esp32/component.mk +++ b/components/esp32/component.mk @@ -8,6 +8,11 @@ ifndef CONFIG_NO_BLOBS LIBS += core rtc net80211 pp wpa smartconfig coexist wps wpa2 espnow phy mesh endif +ifdef CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY + # This linker script must come before esp32.common.ld + LINKER_SCRIPTS += esp32.extram.bss.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. diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index c815b60d36..c4e3a32916 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -93,6 +93,10 @@ extern int _bss_start; extern int _bss_end; extern int _rtc_bss_start; extern int _rtc_bss_end; +#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY +extern int _ext_ram_bss_start; +extern int _ext_ram_bss_end; +#endif extern int _init_start; extern void (*__init_array_start)(void); extern void (*__init_array_end)(void); @@ -154,6 +158,11 @@ void IRAM_ATTR call_start_cpu0() #if CONFIG_SPIRAM_BOOT_INIT esp_spiram_init_cache(); if (esp_spiram_init() != ESP_OK) { +#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY + ESP_EARLY_LOGE(TAG, "Failed to init external RAM, needed for external .bss segment"); + abort(); +#endif + #if CONFIG_SPIRAM_IGNORE_NOTFOUND ESP_EARLY_LOGI(TAG, "Failed to init external RAM; continuing without it."); s_spiram_okay = false; @@ -207,7 +216,9 @@ void IRAM_ATTR call_start_cpu0() } } #endif - +#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY + memset(&_ext_ram_bss_start, 0, (&_ext_ram_bss_end - &_ext_ram_bss_start) * sizeof(_ext_ram_bss_start)); +#endif /* Initialize heap allocator. WARNING: This *needs* to happen *after* the app cpu has booted. If the heap allocator is initialized first, it will put free memory linked list items into memory also used by the ROM. Starting the app cpu will let its ROM initialize that memory, diff --git a/components/esp32/include/esp_attr.h b/components/esp32/include/esp_attr.h index b158224d34..72f13d364b 100644 --- a/components/esp32/include/esp_attr.h +++ b/components/esp32/include/esp_attr.h @@ -39,6 +39,13 @@ // Forces code into RTC fast memory. See "docs/deep-sleep-stub.rst" #define RTC_IRAM_ATTR __attribute__((section(".rtc.text"))) +#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY +// Forces bss variable into external memory. " +#define EXT_RAM_ATTR __attribute__((section(".ext_ram.bss"))) +#else +#define EXT_RAM_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. diff --git a/components/esp32/ld/esp32.common.ld b/components/esp32/ld/esp32.common.ld index 2751aeef20..4e8382913b 100644 --- a/components/esp32/ld/esp32.common.ld +++ b/components/esp32/ld/esp32.common.ld @@ -229,6 +229,7 @@ SECTIONS { . = ALIGN (8); _bss_start = ABSOLUTE(.); + *(.ext_ram.bss*) _bt_bss_start = ABSOLUTE(.); *libbt.a:(.bss .bss.* COMMON) . = ALIGN (4); diff --git a/components/esp32/ld/esp32.extram.bss.ld b/components/esp32/ld/esp32.extram.bss.ld new file mode 100644 index 0000000000..6e0ab496e5 --- /dev/null +++ b/components/esp32/ld/esp32.extram.bss.ld @@ -0,0 +1,17 @@ +/* This section is only included if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY + is set, to link some sections to BSS in PSRAM */ + +SECTIONS +{ + /* external memory bss, from any global variable with EXT_RAM_ATTR attribute*/ + .ext_ram.bss (NOLOAD) : + { + _ext_ram_bss_start = ABSOLUTE(.); + *(.ext_ram.bss*) + *libnet80211.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON) + *libpp.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON) + *liblwip.a:(.dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON) + *libbt.a:(EXCLUDE_FILE (libbtdm_app.a) .dynsbss .sbss .sbss.* .gnu.linkonce.sb.* .scommon .sbss2.* .gnu.linkonce.sb2.* .dynbss .bss .bss.* .share.mem .gnu.linkonce.b.* COMMON) + _ext_ram_bss_end = ABSOLUTE(.); + } > extern_ram_seg +} diff --git a/components/esp32/ld/esp32.ld b/components/esp32/ld/esp32.ld index 3c404e1aad..fd02e0d210 100644 --- a/components/esp32/ld/esp32.ld +++ b/components/esp32/ld/esp32.ld @@ -71,6 +71,10 @@ MEMORY */ rtc_slow_seg(RW) : org = 0x50000000 + CONFIG_ULP_COPROC_RESERVE_MEM, len = 0x1000 - CONFIG_ULP_COPROC_RESERVE_MEM + + /* external memory ,including data and text */ + extern_ram_seg(RWX) : org = 0x3F800000, + len = 0x400000 } /* Heap ends at top of dram0_0_seg */ diff --git a/components/esp32/spiram.c b/components/esp32/spiram.c index b09ff25d6e..5345c5a374 100644 --- a/components/esp32/spiram.c +++ b/components/esp32/spiram.c @@ -59,7 +59,9 @@ static const char* TAG = "spiram"; #error "FLASH speed can only be equal to or higher than SRAM speed while SRAM is enabled!" #endif - +#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY +extern int _ext_ram_bss_start, _ext_ram_bss_end; +#endif static bool spiram_inited=false; @@ -145,11 +147,16 @@ esp_err_t esp_spiram_init() esp_err_t esp_spiram_add_to_heapalloc() -{ - ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", CONFIG_SPIRAM_SIZE/1024); +{ //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. +#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY + ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", (CONFIG_SPIRAM_SIZE - (&_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 + CONFIG_SPIRAM_SIZE-1); +#else + ESP_EARLY_LOGI(TAG, "Adding pool of %dK of external SPI memory to heap allocator", CONFIG_SPIRAM_SIZE/1024); return heap_caps_add_region((intptr_t)SOC_EXTRAM_DATA_LOW, (intptr_t)SOC_EXTRAM_DATA_LOW + CONFIG_SPIRAM_SIZE-1); +#endif } diff --git a/docs/en/api-guides/external-ram.rst b/docs/en/api-guides/external-ram.rst index 52704365db..860bc4f357 100644 --- a/docs/en/api-guides/external-ram.rst +++ b/docs/en/api-guides/external-ram.rst @@ -46,6 +46,9 @@ ESP-IDF fully supports integrating external memory use into your applications. E ``heap_caps_malloc(size, MALLOC_CAP_SPIRAM)``. This memory can be used and subsequently freed using a normal ``free()`` call. * Initialize RAM, add it to the capability allocator and add memory to the pool of RAM that can be returned by ``malloc()``. This allows any application to use the external RAM without having to rewrite the code to use ``heap_caps_malloc``. + * Initialize RAM, use a region start from 0x3F800000 for storing zero initialized data(BSS segment) of lwip,net802.11,pp,bluedroid library + by enabling :ref: `CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY` in menuconfig,this way can save some internal memory,because the BSS segment + originally stored in internal memory,and the rest of external RAM can be add the capability allocator and add memory to the pool of RAM as above way All these options can be selected from the menuconfig menu. @@ -67,7 +70,11 @@ The use of external RAM has a few restrictions: for stack and task TCBs and xTaskCreateStatic-type functions will check if the buffers passed are internal. However, for tasks not calling on code in ROM in any way, directly or indirectly, the menuconfig option :ref:`CONFIG_SPIRAM_ALLOW_STACK_EXTERNAL_MEMORY` will eliminate the check in xTaskCreateStatic, allowing task stack in external RAM. Using this is not advised, however. - + * External RAM initialized failed can not be ignored if enabled :ref:`CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY`; because of this, some BSS segment + can not be placed into external memory if PSRAM can't work normally and can not be moved to internal memory at runtime because the address of + them is defined by linkfile, the :ref:`CONFIG_SPIRAM_IGNORE_NOTFOUND` can't handle this situation,if you want to enable :ref: + `CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY` the :ref:`CONFIG_SPIRAM_IGNORE_NOTFOUND` will be disabled, and if initialize SPIRAM failed,the system + will invoke abort. Because there are a fair few situations that have a specific need for internal memory, but it is also possible to use malloc() to exhaust internal memory, there is a pool reserved specifically for requests that cannot be resolved from external memory; allocating task