forked from espressif/esp-idf
Merge branch 'feature/support_noinit_section_in_psram_on_esp32' into 'master'
memory: support noinit section in psram on esp32 Closes IDFGH-2621 See merge request espressif/esp-idf!14088
This commit is contained in:
@@ -89,6 +89,14 @@ extern "C" {
|
||||
// Forces data into noinit section to avoid initialization after restart.
|
||||
#define __NOINIT_ATTR _SECTION_ATTR_IMPL(".noinit", __COUNTER__)
|
||||
|
||||
#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_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 of .noinit section.
|
||||
// Any variable marked with this attribute will keep its value
|
||||
// after restart or 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__ */
|
||||
|
||||
|
@@ -1,7 +1,12 @@
|
||||
#include "inttypes.h"
|
||||
#include "unity.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/soc.h"
|
||||
#include "esp_system.h"
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
#include "spiram.h"
|
||||
#endif
|
||||
|
||||
static __NOINIT_ATTR uint32_t s_noinit;
|
||||
static RTC_NOINIT_ATTR uint32_t s_rtc_noinit;
|
||||
@@ -9,6 +14,9 @@ static RTC_DATA_ATTR uint32_t s_rtc_data;
|
||||
static RTC_RODATA_ATTR uint32_t s_rtc_rodata;
|
||||
static RTC_FAST_ATTR uint32_t s_rtc_force_fast;
|
||||
static RTC_SLOW_ATTR uint32_t s_rtc_force_slow;
|
||||
#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
|
||||
static EXT_RAM_NOINIT_ATTR uint32_t s_noinit_ext;
|
||||
#endif
|
||||
|
||||
extern int _rtc_noinit_start;
|
||||
extern int _rtc_noinit_end;
|
||||
@@ -20,6 +28,10 @@ extern int _rtc_force_fast_start;
|
||||
extern int _rtc_force_fast_end;
|
||||
extern int _rtc_force_slow_start;
|
||||
extern int _rtc_force_slow_end;
|
||||
extern int _ext_ram_noinit_start;
|
||||
extern int _ext_ram_noinit_end;
|
||||
extern int _ext_ram_bss_start;
|
||||
extern int _ext_ram_bss_end;
|
||||
|
||||
|
||||
static bool data_in_segment(void *ptr, int *seg_start, int *seg_end)
|
||||
@@ -52,4 +64,61 @@ TEST_CASE("Attributes place variables into correct sections", "[ld]")
|
||||
|
||||
TEST_ASSERT(data_in_segment(&s_rtc_force_fast, (int*) SOC_RTC_DRAM_LOW, (int*) SOC_RTC_DRAM_HIGH));
|
||||
TEST_ASSERT(data_in_segment(&s_rtc_force_slow, (int*) SOC_RTC_DATA_LOW, (int*) SOC_RTC_DATA_HIGH));
|
||||
|
||||
#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
|
||||
TEST_ASSERT(data_in_segment(&s_noinit_ext, &_ext_ram_noinit_start, &_ext_ram_noinit_end));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
|
||||
|
||||
#define TEST_BUFFER_SIZE (16*1024/4)
|
||||
static EXT_RAM_NOINIT_ATTR uint32_t s_noinit_buffer[TEST_BUFFER_SIZE];
|
||||
|
||||
static void write_spiram_and_reset(void)
|
||||
{
|
||||
// Fill the noinit buffer
|
||||
printf("Filling buffer\n");
|
||||
for (uint32_t i = 0; i < TEST_BUFFER_SIZE; i++) {
|
||||
s_noinit_buffer[i] = i ^ 0x55555555U;
|
||||
}
|
||||
printf("Flushing cache\n");
|
||||
// Flush the cache out to SPIRAM before resetting.
|
||||
esp_spiram_writeback_cache();
|
||||
|
||||
printf("Restarting\n");
|
||||
// Reset to test that noinit memory is left intact.
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
static void check_spiram_contents(void)
|
||||
{
|
||||
// Confirm that the memory contents are still what we expect
|
||||
uint32_t error_count = 0;
|
||||
for (uint32_t i = 0; i < TEST_BUFFER_SIZE; i++) {
|
||||
if (s_noinit_buffer[i] != (i ^ 0x55555555U)) {
|
||||
error_count++;
|
||||
}
|
||||
}
|
||||
printf("Found %" PRIu32 " memory errors\n", error_count);
|
||||
TEST_ASSERT(error_count == 0);
|
||||
}
|
||||
|
||||
TEST_CASE_MULTIPLE_STAGES("Spiram test noinit memory", "[spiram]", write_spiram_and_reset, check_spiram_contents);
|
||||
|
||||
#endif // CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
|
||||
|
||||
|
||||
#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
|
||||
#define TEST_BSS_NUM 256
|
||||
static EXT_RAM_ATTR uint32_t s_bss_buffer[TEST_BSS_NUM];
|
||||
|
||||
TEST_CASE("Test variables placed in external .bss segment", "[ld]")
|
||||
{
|
||||
for (int i = 0; i < TEST_BSS_NUM; i++) {
|
||||
TEST_ASSERT(data_in_segment(&s_bss_buffer[i], &_ext_ram_bss_start, &_ext_ram_bss_end));
|
||||
TEST_ASSERT_EQUAL(0, s_bss_buffer[i]);
|
||||
}
|
||||
}
|
||||
#endif //#if CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY
|
||||
|
@@ -101,3 +101,13 @@ 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_SEG_EXTERNAL_MEMORY
|
||||
bool "Allow .noinit segment placed in external memory"
|
||||
default n
|
||||
depends on SPIRAM && IDF_TARGET_ESP32
|
||||
help
|
||||
If enabled, noinit variables can be placed in PSRAM using EXT_RAM_NOINIT_ATTR.
|
||||
|
||||
Note the values placed into this section will not be initialized at startup and should keep its value
|
||||
after software restart.
|
||||
|
@@ -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_SEG_EXTERNAL_MEMORY
|
||||
extern uint8_t _ext_ram_noinit_start, _ext_ram_noinit_end;
|
||||
#endif
|
||||
|
||||
static bool spiram_inited=false;
|
||||
|
||||
|
||||
@@ -81,15 +85,32 @@ static size_t spiram_size_usable_for_malloc(void)
|
||||
*/
|
||||
bool esp_spiram_test(void)
|
||||
{
|
||||
|
||||
#if CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY
|
||||
const void *keepout_addr_low = (const void*)&_ext_ram_noinit_start;
|
||||
const void *keepout_addr_high = (const void*)&_ext_ram_noinit_end;
|
||||
#else
|
||||
const void *keepout_addr_low = 0;
|
||||
const void *keepout_addr_high = 0;
|
||||
#endif
|
||||
|
||||
volatile int *spiram=(volatile int*)SOC_EXTRAM_DATA_LOW;
|
||||
size_t p;
|
||||
size_t s=spiram_size_usable_for_malloc();
|
||||
int errct=0;
|
||||
int initial_err=-1;
|
||||
for (p=0; p<(s/sizeof(int)); p+=8) {
|
||||
const void *addr = (const void *)&spiram[p];
|
||||
if ((keepout_addr_low <= addr) && (addr < keepout_addr_high)) {
|
||||
continue;
|
||||
}
|
||||
spiram[p]=p^0xAAAAAAAA;
|
||||
}
|
||||
for (p=0; p<(s/sizeof(int)); p+=8) {
|
||||
const void *addr = (const void *)&spiram[p];
|
||||
if ((keepout_addr_low <= addr) && (addr < keepout_addr_high)) {
|
||||
continue;
|
||||
}
|
||||
if (spiram[p]!=(p^0xAAAAAAAA)) {
|
||||
errct++;
|
||||
if (errct==1) initial_err=p*4;
|
||||
@@ -172,13 +193,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_SEG_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);
|
||||
}
|
||||
|
||||
|
||||
|
@@ -197,6 +197,18 @@ SECTIONS
|
||||
. = ALIGN(4);
|
||||
} > dram0_0_seg
|
||||
|
||||
/**
|
||||
* This section holds data that won't be initialised when startup.
|
||||
* This section locates in External RAM region.
|
||||
*/
|
||||
.ext_ram.noinit (NOLOAD) :
|
||||
{
|
||||
_ext_ram_noinit_start = ABSOLUTE(.);
|
||||
*(.ext_ram.noinit*)
|
||||
. = ALIGN(4);
|
||||
_ext_ram_noinit_end = ABSOLUTE(.);
|
||||
} > extern_ram_seg
|
||||
|
||||
/*This section holds data that should not be initialized at power up.
|
||||
The section located in Internal SRAM memory region. The macro _NOINIT
|
||||
can be used as attribute to place data into this section.
|
||||
|
@@ -38,6 +38,7 @@ ESP-IDF fully supports the use of external memory in applications. Once the exte
|
||||
* :ref:`external_ram_config_capability_allocator`
|
||||
* :ref:`external_ram_config_malloc` (default)
|
||||
:esp32: * :ref:`external_ram_config_bss`
|
||||
:esp32: * :ref:`external_ram_config_noinit`
|
||||
|
||||
.. _external_ram_config_memory_map:
|
||||
|
||||
@@ -104,6 +105,14 @@ Because some buffers can only be allocated in internal memory, a second configur
|
||||
|
||||
Remaining external RAM can also be added to the capability heap allocator using the method shown above.
|
||||
|
||||
.. _external_ram_config_noinit:
|
||||
|
||||
Allow .noinit segment placed in external memory
|
||||
-----------------------------------------------
|
||||
|
||||
Enable this option by checking :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY`. If enabled, a region of the address space provided in external RAM will be used to store non-initialized data. The values placed in this segment will not be initialized or modified even during startup or restart.
|
||||
|
||||
By applying the macro ``EXT_RAM_NOINIT_ATTR``, data could be moved from the internal NOINIT segment to external RAM. Remaining external RAM can still be added to the capability heap allocator using the method shown above, :ref:`external_ram_config_capability_allocator`.
|
||||
|
||||
Restrictions
|
||||
============
|
||||
|
@@ -16,6 +16,8 @@ Non-constant static data (.data) and zero-initialized data (.bss) is placed by t
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
By applying the ``EXT_RAM_ATTR`` macro, zero-initialized data can also be placed into external RAM. To use this macro, the :ref:`CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY` needs to be enabled. See :ref:`external_ram_config_bss`.
|
||||
|
||||
The available size of the internal DRAM region is reduced by 64kB (by shifting start address to ``0x3FFC0000``) if Bluetooth stack is used. Length of this region is also reduced by 16 kB or 32kB if trace memory is used. Due to some memory fragmentation issues caused by ROM, it is also not possible to use all available DRAM for static allocations - however the remaining DRAM is still available as heap at runtime.
|
||||
|
||||
.. only:: not esp32
|
||||
@@ -31,6 +33,10 @@ Constant data may also be placed into DRAM, for example if it is used in an non-
|
||||
|
||||
The macro ``__NOINIT_ATTR`` can be used as attribute to place data into ``.noinit`` section. The values placed into this section will not be initialized at startup and should keep its value after software restart.
|
||||
|
||||
.. only:: esp32
|
||||
|
||||
By applying the ``EXT_RAM_NOINIT_ATTR`` macro, Non-initialized value could also be placed in external RAM. To do this, the :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY` needs to be enabled. See :ref:`external_ram_config_noinit`. If the :ref:`CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY` is not enabled, ``EXT_RAM_NOINIT_ATTR`` will behave just as ``__NOINIT_ATTR``, it will make data to be placed into ``.noinit`` segment in internal RAM.
|
||||
|
||||
Example::
|
||||
|
||||
__NOINIT_ATTR uint32_t noinit_data;
|
||||
@@ -209,5 +215,3 @@ Placing DMA buffers in the stack is possible but discouraged. If doing so, pay a
|
||||
spi_device_transmit(spi, &temp);
|
||||
// other stuff
|
||||
}
|
||||
|
||||
|
||||
|
@@ -8,3 +8,5 @@ CONFIG_ESP32_WIFI_IRAM_OPT=n
|
||||
CONFIG_ESP_TIMER_PROFILING=n
|
||||
# Disable encrypted flash reads/writes to save IRAM in this build configuration
|
||||
CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=n
|
||||
CONFIG_SPIRAM_ALLOW_NOINIT_SEG_EXTERNAL_MEMORY=y
|
||||
CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y
|
||||
|
Reference in New Issue
Block a user