esp32: Add option to place.rtc_data and .rtc_rodata into the RTC_FAST segment

Changes:
KConfig: The option CONFIG_ESP32_RTCDATA_IN_FAST_MEM is added in Kconfig file for esp32 component.
esp32.common.ld: added support of RTC_DATA_ATTR, RTC_RODATA_ATTR data placement into appropriate segment according to Kconfig option.
esp32.ld: linker script is modified to set alias for memory segment selected by Kconfig option to place data. The segments for force placement are added for RTC_FAST_ATTR, RTC_SLOW_ATTR attributes.
esp_attr.h: added new attributes RTC_FAST_ATTR, RTC_SLOW_ATTR for force placement into fest/slow memory.
test_rtc_fast.c: Added unit test cases to check data placement into appropriate memory segment.
Updated documentation for RTC_DATA_ATTR, RTC_FAST_ATTR, RTC_SLOW_ATTR in deep_sleep_stub.rst file.

TW#18001
Closes https://github.com/espressif/esp-idf/issues/1553
This commit is contained in:
Alex Lisitsyn 2018-03-22 18:39:19 +05:00
parent 5acf886324
commit ec5eb1e5c2
8 changed files with 387 additions and 8 deletions

View file

@ -1090,6 +1090,20 @@ UT_004_11:
- UT_T1_1
- psram
UT_004_12:
<<: *unit_test_template
tags:
- ESP32_IDF
- UT_T1_1
- psram
UT_004_13:
<<: *unit_test_template
tags:
- ESP32_IDF
- UT_T1_1
- psram
UT_005_01:
<<: *unit_test_template
tags:
@ -1104,6 +1118,13 @@ UT_005_02:
- UT_T1_SPIMODE
- psram
UT_005_03:
<<: *unit_test_template
tags:
- ESP32_IDF
- UT_T1_SPIMODE
- psram
UT_006_01:
<<: *unit_test_template
tags:

View file

@ -867,6 +867,16 @@ config ESP_ERR_TO_NAME_LOOKUP
order to save memory but this comes at the price of sacrificing
distinguishable (meaningful) output string representations.
config ESP32_RTCDATA_IN_FAST_MEM
bool "Place RTC_DATA_ATTR and RTC_RODATA_ATTR variables into RTC fast memory segment"
default n
depends on FREERTOS_UNICORE
help
This option allows to place .rtc_data and .rtc_rodata sections into
RTC fast memory segment to free the slow memory region for ULP programs.
This option depends on the CONFIG_FREERTOS_UNICORE option because RTC fast memory
can be accessed only by PRO_CPU core.
endmenu # ESP32-Specific
menu Wi-Fi

View file

@ -44,9 +44,15 @@
// during a deep sleep / wake cycle.
#define RTC_DATA_ATTR __attribute__((section(".rtc.data")))
// Forces read-only data into RTC slow memory. See "docs/deep-sleep-stub.rst"
// Forces read-only data into RTC memory. See "docs/deep-sleep-stub.rst"
#define RTC_RODATA_ATTR __attribute__((section(".rtc.rodata")))
// Allows to place data into RTC_SLOW memory.
#define RTC_SLOW_ATTR __attribute__((section(".rtc.force_slow")))
// Allows to place data into RTC_FAST memory.
#define RTC_FAST_ATTR __attribute__((section(".rtc.force_fast")))
// Forces data into noinit section to avoid initialization after restart.
#define __NOINIT_ATTR __attribute__((section(".noinit")))

View file

@ -11,11 +11,40 @@ SECTIONS
. = ALIGN(4);
*(.rtc.literal .rtc.text)
*rtc_wake_stub*.*(.literal .text .literal.* .text.*)
_rtc_text_end = ABSOLUTE(.);
} > rtc_iram_seg
/*
This section is required to skip rtc.text area because rtc_iram_seg and
rtc_data_seg are reflect the same address space on different buses.
*/
.rtc.dummy :
{
_rtc_dummy_start = ABSOLUTE(.);
_rtc_fast_start = ABSOLUTE(.);
. = SIZEOF(.rtc.text);
_rtc_dummy_end = ABSOLUTE(.);
} > rtc_data_seg
/* RTC slow memory holds RTC wake stub
/* This section located in RTC FAST Memory area.
It holds data marked with RTC_FAST_ATTR attribute.
See the file "esp_attr.h" for more information.
*/
.rtc.force_fast :
{
. = ALIGN(4);
_rtc_force_fast_start = ABSOLUTE(.);
*(.rtc.force_fast .rtc.force_fast.*)
. = ALIGN(4) ;
_rtc_force_fast_end = ABSOLUTE(.);
} > rtc_data_seg
/* RTC data section holds RTC wake stub
data/rodata, including from any source file
named rtc_wake_stub*.c
named rtc_wake_stub*.c and the data marked with
RTC_DATA_ATTR, RTC_RODATA_ATTR attributes.
The memory location of the data dependent on
CONFIG_ESP32_RTCDATA_IN_FAST_MEM option.
*/
.rtc.data :
{
@ -24,7 +53,7 @@ SECTIONS
*(.rtc.rodata)
*rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .bss .bss.*)
_rtc_data_end = ABSOLUTE(.);
} > rtc_slow_seg
} > rtc_data_location
/* RTC bss, from any source file named rtc_wake_stub*.c */
.rtc.bss (NOLOAD) :
@ -34,6 +63,19 @@ SECTIONS
*rtc_wake_stub*.*(COMMON)
*(.rtc.bss)
_rtc_bss_end = ABSOLUTE(.);
} > rtc_data_location
/* This section located in RTC SLOW Memory area.
It holds data marked with RTC_SLOW_ATTR attribute.
See the file "esp_attr.h" for more information.
*/
.rtc.force_slow :
{
. = ALIGN(4);
_rtc_force_slow_start = ABSOLUTE(.);
*(.rtc.force_slow .rtc.force_slow.*)
. = ALIGN(4) ;
_rtc_force_slow_end = ABSOLUTE(.);
} > rtc_slow_seg
/* This section holds data that should not be initialized at power up
@ -50,6 +92,21 @@ SECTIONS
_rtc_noinit_end = ABSOLUTE(.);
} > rtc_slow_seg
/* Get size of rtc slow data based on rtc_data_location alias */
_rtc_slow_length = (ORIGIN(rtc_slow_seg) == ORIGIN(rtc_data_location))
? (_rtc_noinit_end - _rtc_data_start)
: (_rtc_noinit_end - _rtc_force_slow_start);
_rtc_fast_length = (ORIGIN(rtc_slow_seg) == ORIGIN(rtc_data_location))
? (_rtc_force_fast_end - _rtc_fast_start)
: (_rtc_bss_end - _rtc_fast_start);
ASSERT((_rtc_slow_length <= LENGTH(rtc_slow_seg)),
"RTC_SLOW segment data does not fit.")
ASSERT((_rtc_fast_length <= LENGTH(rtc_data_seg)),
"RTC_FAST segment data does not fit.")
/* Send .iram0 code to iram */
.iram0.vectors :
{
@ -87,7 +144,7 @@ SECTIONS
*(.init.literal)
*(.init)
_init_end = ABSOLUTE(.);
} > iram0_0_seg
} > iram0_0_seg
.iram0.text :
{
@ -113,6 +170,9 @@ SECTIONS
_iram_end = ABSOLUTE(.);
} > iram0_0_seg
ASSERT(((_iram_text_end - ORIGIN(iram0_0_seg)) <= LENGTH(iram0_0_seg)),
"IRAM0 segment data does not fit.")
.dram0.data :
{
_data_start = ABSOLUTE(.);
@ -195,6 +255,9 @@ SECTIONS
_heap_start = ABSOLUTE(.);
} > dram0_0_seg
ASSERT(((_bss_end - ORIGIN(dram0_0_seg)) <= LENGTH(dram0_0_seg)),
"DRAM segment data does not fit.")
.flash.rodata :
{
_rodata_start = ABSOLUTE(.);

View file

@ -61,6 +61,9 @@ MEMORY
/* RTC fast memory (executable). Persists over deep sleep.
*/
rtc_iram_seg(RWX) : org = 0x400C0000, len = 0x2000
/* RTC fast memory (same block as above), viewed from data bus */
rtc_data_seg(RW) : org = 0x3ff80000, len = 0x2000
/* RTC slow memory (data accessible). Persists over deep sleep.
@ -72,3 +75,14 @@ MEMORY
/* Heap ends at top of dram0_0_seg */
_heap_end = 0x40000000 - CONFIG_TRACEMEM_RESERVE_DRAM;
_data_seg_org = ORIGIN(rtc_data_seg);
/* The lines below define location alias for .rtc.data section based on Kconfig option.
When the option is not defined then use slow memory segment
else the data will be placed in fast memory segment */
#ifndef CONFIG_ESP32_RTCDATA_IN_FAST_MEM
REGION_ALIAS("rtc_data_location", rtc_slow_seg );
#else
REGION_ALIAS("rtc_data_location", rtc_data_seg );
#endif

View file

@ -0,0 +1,260 @@
// The lines below allow to skip test in configuration other then
// single_core which is required for this test to check placement in RTC_FAST memory
#include "sdkconfig.h"
#ifdef CONFIG_ESP32_RTCDATA_IN_FAST_MEM
#include "unity.h"
#include "esp_system.h"
#include "rom/rtc.h" // for rtc defines
#include "rom/uart.h" // for uart_tx_wait_idle()
#include "esp_log.h" // for log write functionality
#include "driver/rtc_io.h" // for gpio configuration
#include "esp_sleep.h" // include sleep related functionality
#include "soc/soc.h" // include access to soc macros
#include "soc/timer_group_reg.h" // for watchdog register defines
#include "soc/rtc_cntl_reg.h" // for rtc cntl register defines
#include "freertos/FreeRTOS.h" // for xPortGetCoreID
// Test notes:
// This test case sequence checks behavior of placement .rtc_data and .rtc_rodata sections
// into RTC_FAST memory. The Kconfig option CONFIG_ESP32_RTCDATA_IN_FAST_MEM
// is used to configure this behavior. The RTC_DATA_ATTR, RTC_RODATA_ATTR attributes
// can be used to place data into this area. If option is not set the .rtc_data
// and .rtc_rodata are placed in slow memory segment for compatibility.
// The only PRO_CPU can access the RTC_FAST memory, so the option CONFIG_FREERTOS_UNICORE
// should be enabled to place data into RTC_FAST memory segment.
#define ESP_EXT0_WAKEUP_LEVEL_LOW 0
#define ESP_EXT0_WAKEUP_LEVEL_HIGH 1
#define RTC_DATA_PATTERN 0xAAAAAAAA
#define RTC_RODATA_PATTERN 0x55555555
#define RTC_FAST_PATTERN 0xDDDDDDDD
#define RTC_SLOW_PATTERN 0x99999999
#define WAKE_STUB_PATTERN 0x77777777
#ifdef CONFIG_ESP32_RTCDATA_IN_FAST_MEM
#define CHECK_RTC_FAST_OPTION_ENABLED 1
#else
#define CHECK_RTC_FAST_OPTION_ENABLED 0
#endif
static RTC_DATA_ATTR uint32_t rtc_data = RTC_DATA_PATTERN;
static RTC_RODATA_ATTR uint32_t rtc_rodata = RTC_RODATA_PATTERN;
static RTC_FAST_ATTR uint32_t rtc_force_fast = RTC_FAST_PATTERN;
static RTC_SLOW_ATTR uint32_t rtc_force_slow = RTC_SLOW_PATTERN;
extern int _rtc_data_start;
extern int _rtc_data_end;
extern int _rtc_force_fast_start;
extern int _rtc_force_fast_end;
extern int _rtc_force_slow_start;
extern int _rtc_force_slow_end;
// This points to the values in RTC memory
static uint32_t *rtc_data_val_addr = (uint32_t*)&rtc_data;
static uint32_t *rtc_rodata_val_addr = (uint32_t*)&rtc_rodata;
static uint32_t *rtc_fast_val_addr = (uint32_t*)&rtc_force_fast;
static uint32_t *rtc_slow_val_addr = (uint32_t*)&rtc_force_slow;
static const char* tag = "rtc_data_fast_UnitTestMain";
static void RTC_IRAM_ATTR wake_stub(void);
static void RTC_IRAM_ATTR wake_stub_dummy(void)
{
rtc_data = WAKE_STUB_PATTERN;
set_rtc_memory_crc(); // update rtc memory CRC
}
static esp_err_t check_data_placement(uint32_t *value_address,
uint32_t *seg_start, uint32_t *seg_end)
{
esp_err_t result = ESP_FAIL;
if (((uint32_t)value_address <= (uint32_t)seg_end)
&& ((uint32_t)value_address >= (uint32_t)seg_start)){
result = ESP_OK;
}
return result;
}
static esp_err_t check_wake_stub_status(void)
{
esp_err_t result = ESP_FAIL;
uint32_t entry_addr = REG_READ(RTC_ENTRY_ADDR_REG);
if (entry_addr == (uint32_t)&wake_stub_dummy) {
result = ESP_OK;
} else if (entry_addr == (uint32_t)&wake_stub) {
result = ESP_ERR_INVALID_STATE;
} else {
result = ESP_FAIL;
}
return result;
}
static void RTC_IRAM_ATTR wake_stub(void)
{
esp_default_wake_deep_sleep();
// Set the pointer of the new wake stub function.
// It will be checked in test to make sure the wake stub entered
REG_WRITE(RTC_ENTRY_ADDR_REG, (uint32_t)&wake_stub_dummy);
// Set this value to check it in test later
rtc_data = WAKE_STUB_PATTERN;
set_rtc_memory_crc(); // update rtc memory CRC
}
static void setup_deep_sleep(void)
{
// Set wake stub function to check its behavior
// This function sets checksum of RTC fast memory appropriately
esp_set_deep_sleep_wake_stub(&wake_stub);
// Setup ext0 configuration to wake up immediately
ESP_ERROR_CHECK(rtc_gpio_init(GPIO_NUM_13));
ESP_ERROR_CHECK(gpio_pullup_en(GPIO_NUM_13));
ESP_ERROR_CHECK(gpio_pulldown_dis(GPIO_NUM_13));
ESP_ERROR_CHECK(esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, ESP_EXT0_WAKEUP_LEVEL_HIGH));
uart_tx_wait_idle(0);
esp_deep_sleep_start();
}
// The lines below are required to suppress GCC warnings about casting of function pointers
// in unity macro expansion. These warnings may be treated as errors during automated test.
#pragma GCC diagnostic push // required for GCC
#pragma GCC diagnostic ignored "-Wdiscarded-qualifiers"
static void reset_reason_power_on(void)
{
printf("This test case verifies behavior of RTC_DATA variables after reset. \n");
RESET_REASON reason = rtc_get_reset_reason(0);
TEST_ASSERT(reason == POWERON_RESET || reason == RTCWDT_RTC_RESET);
ESP_LOGI(tag, "Reset reason=(%d)", (uint16_t)reason);
printf("rtc_data_val = (0x%X), rtc_rodata_val = (0x%X), rtc_fast_val = (0x%X), rtc_slow_val = (0x%X)\r\n",
(uint32_t)*rtc_data_val_addr, (uint32_t)*rtc_rodata_val_addr,
(uint32_t)*rtc_fast_val_addr, (uint32_t)*rtc_slow_val_addr);
printf("This test case called by CPU%u\r\n", (uint16_t)xPortGetCoreID());
TEST_ASSERT(CHECK_RTC_FAST_OPTION_ENABLED == 1);
printf("Check that values are placed in correct sections and then compare its values with patterns.\r\n");
TEST_ASSERT(check_data_placement(rtc_fast_val_addr,
(uint32_t*)&_rtc_force_fast_start,
(uint32_t*)&_rtc_force_fast_end) == ESP_OK);
TEST_ASSERT(check_data_placement(rtc_data_val_addr,
(uint32_t*)&_rtc_data_start,
(uint32_t*)&_rtc_data_end) == ESP_OK);
TEST_ASSERT(check_data_placement(rtc_rodata_val_addr,
(uint32_t*)&_rtc_data_start,
(uint32_t*)&_rtc_data_end) == ESP_OK);
printf("Values RTC_DATA_ATTR, RTC_FAST_ATTR are placed in section (0x%x - 0x%x).\r\n",
(uint32_t)&_rtc_data_start, (uint32_t)&_rtc_data_end);
TEST_ASSERT(check_data_placement(rtc_slow_val_addr,
(uint32_t*)&_rtc_force_slow_start,
(uint32_t*)&_rtc_force_slow_end) == ESP_OK);
printf("The RTC_SLOW values are placed in slow memory (0x%X - 0x%X).\r\n",
(uint32_t)&_rtc_force_slow_start, (uint32_t)&_rtc_force_slow_end);
TEST_ASSERT(RTC_RODATA_PATTERN == *rtc_rodata_val_addr);
TEST_ASSERT(RTC_DATA_PATTERN == *rtc_data_val_addr);
TEST_ASSERT(RTC_FAST_PATTERN == *rtc_fast_val_addr);
TEST_ASSERT(RTC_SLOW_PATTERN == *rtc_slow_val_addr);
printf("The values correspond to its patterns.\r\n");
printf("Go to deep sleep to check DEEP_SLEEP_RESET behavior. \r\n");
setup_deep_sleep();
}
static void reset_reason_deep_sleep(void)
{
printf("This test case checks behavior of RTC_DATA variables after deep sleep reset. \r\n");
RESET_REASON reason = rtc_get_reset_reason(0);
ESP_LOGI(tag, "Reset reason=(%d)", (uint16_t)reason);
printf("rtc_data_val = (0x%X), rtc_rodata_val = (0x%X), rtc_fast_val = (0x%X), rtc_slow_val = (0x%X)\r\n",
(uint32_t)*rtc_data_val_addr, (uint32_t)*rtc_rodata_val_addr,
(uint32_t)*rtc_fast_val_addr, (uint32_t)*rtc_slow_val_addr);
TEST_ASSERT(reason == DEEPSLEEP_RESET);
if (CHECK_RTC_FAST_OPTION_ENABLED == 1) {
printf("The CONFIG_ESP32_RTCDATA_IN_FAST_MEM is active means placement of RTC_DATA in fast segment.\r\n");
} else {
printf("The CONFIG_ESP32_RTCDATA_IN_FAST_MEM is not set. Please set this option\r\n");
printf("in menuconfig to check .rtc.data placement in RTC fast memory.\r\n");
}
TEST_ASSERT(CHECK_RTC_FAST_OPTION_ENABLED == 1);
// Check if deep sleep wake stub has been entered
// this means CRC was correct
if (check_wake_stub_status() == ESP_OK) {
printf("The wake stub has been executed means CRC was correct.\r\n");
} else {
printf("The wake stub is not executed.\r\n");
}
TEST_ASSERT(check_wake_stub_status() == ESP_OK);
// If the RTC CRC is incorrect the function return NULL
if (esp_get_deep_sleep_wake_stub() == NULL){
printf("The wake stub has changed the value of rtc_data as expected.\r\n");
} else {
printf("The rtc_data value is not changed that is incorrect. \r\n");
}
TEST_ASSERT(esp_get_deep_sleep_wake_stub() == NULL);
TEST_ASSERT(check_data_placement(rtc_data_val_addr,
(uint32_t*)&_rtc_data_start,
(uint32_t*)&_rtc_data_end) == ESP_OK);
TEST_ASSERT(check_data_placement(rtc_rodata_val_addr,
(uint32_t*)&_rtc_data_start,
(uint32_t*)&_rtc_data_end) == ESP_OK);
TEST_ASSERT(check_data_placement(rtc_fast_val_addr,
(uint32_t*)&_rtc_force_fast_start,
(uint32_t*)&_rtc_force_fast_end) == ESP_OK);
printf("Values RTC_DATA_ATTR, RTC_FAST_ATTR are placed in RTC fast segment (0x%x - 0x%x).\r\n",
(uint32_t)&_rtc_data_start, (uint32_t)&_rtc_data_end);
TEST_ASSERT(check_data_placement(rtc_slow_val_addr,
(uint32_t*)&_rtc_force_slow_start,
(uint32_t*)&_rtc_force_slow_end) == ESP_OK);
printf("The RTC_SLOW values are placed in slow memory (0x%X - 0x%X).\r\n",
(uint32_t)&_rtc_force_slow_start, (uint32_t)&_rtc_force_slow_end);
TEST_ASSERT(RTC_DATA_PATTERN == *rtc_data_val_addr);
TEST_ASSERT(RTC_RODATA_PATTERN == *rtc_rodata_val_addr);
TEST_ASSERT(RTC_FAST_PATTERN == *rtc_fast_val_addr);
TEST_ASSERT(RTC_SLOW_PATTERN == *rtc_slow_val_addr);
printf("Values correspond to its patterns.\r\n");
printf("The test cases are done.. \r\n");
}
// The multiple stages test case to check values after certain reset reason
TEST_CASE_MULTIPLE_STAGES("RTC_DATA attributes behavior", \
"[restart][reset=DEEPSLEEP_RESET]",
reset_reason_power_on, reset_reason_deep_sleep);
#pragma GCC diagnostic pop // require GCC
#endif // CONFIG_ESP32_RTCDATA_IN_FAST_MEM

View file

@ -55,11 +55,11 @@ The first way is simpler for very short and simple code, or for source files whe
Loading Data Into RTC Memory
----------------------------
Data used by stub code must be resident in RTC Slow Memory. This memory is also used by the ULP.
Data used by stub code must be resident in RTC memory. The data can be placed in RTC Fast memory or in RTC Slow memory which is also used by the ULP.
Specifying this data can be done in one of two ways:
The first way is to use the ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` to specify any data (writeable or read-only, respectivley) which should be loaded into RTC slow memory::
The first way is to use the ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` to specify any data (writeable or read-only, respectively) which should be loaded into RTC memory::
RTC_DATA_ATTR int wake_count;
@ -69,6 +69,10 @@ The first way is to use the ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` to specify
ets_printf(fmt_str, wake_count++);
}
The RTC memory area where this data will be placed can be configured via menuconfig option named ``CONFIG_ESP32_RTCDATA_IN_FAST_MEM``. This option allows to keep slow memory area for ULP programs and once it is enabled the data marked with ``RTC_DATA_ATTR`` and ``RTC_RODATA_ATTR`` are placed in the RTC fast memory segment otherwise it goes to RTC slow memory (default option). This option depends on the ``CONFIG_FREERTOS_UNICORE`` because RTC fast memory can be accessed only by PRO_CPU.
The similar attributes ``RTC_FAST_ATTR`` and ``RTC_SLOW_ATTR`` can be used to specify data that will be force placed into RTC_FAST and RTC_SLOW memory respectively. Any access to data marked with ``RTC_FAST_ATTR`` is allowed by PRO_CPU only and it is responsibility of user to make sure about it.
Unfortunately, any string constants used in this way must be declared as arrays and marked with RTC_RODATA_ATTR, as shown in the example above.
The second way is to place the data into any source file whose name starts with ``rtc_wake_stub``.

View file

@ -1,3 +1,4 @@
TEST_EXCLUDE_COMPONENTS=libsodium bt app_update
CONFIG_MEMMAP_SMP=n
CONFIG_FREERTOS_UNICORE=y
CONFIG_FREERTOS_UNICORE=y
CONFIG_ESP32_RTCDATA_IN_FAST_MEM=y