feat(esp_system): allow .bss to spill over into L2MEM above 0x4ff40000

This commit introduce SOC_MEM_NON_CONTIGUOUS_SRAM flag (that enebled for
esp32p4). If SOC_MEM_NON_CONTIGUOUS_SRAM is enabled:

- LDFLAGS+=--enable-non-contiguous-regions
- ldgen.py replaces "arrays[*]" from sections.ld.in with objects under
  SURROUND keyword. (e.g. from linker.lf: data -> dram0_data SURROUND(foo))
- "mapping[*]" - refers to all other data

If SOC_MEM_NON_CONTIGUOUS_SRAM, sections.ld.in file should contain at
least one block of code like this (otherwise it does not make sense):

  .dram0.bss (NOLOAD) :
  {
    arrays[dram0_bss]
    mapping[dram0_bss]
  } > sram_low

  .dram1.bss (NOLOAD) :
  {
    /* do not place here arrays[dram0_bss] because it may be splited
     * between segments */
    mapping[dram0_bss]
  } > sram_high
This commit is contained in:
Alexey Lapshin
2024-02-12 09:51:25 +04:00
parent 4b5b064caf
commit 824c8e0593
47 changed files with 604 additions and 554 deletions

View File

@@ -16,6 +16,10 @@ tools/test_apps/build_system/embed_test:
temporary: false
reason: Hardware independent feature, no need to test on all targets
tools/test_apps/build_system/ld_non_contiguous_memory:
disable:
- if: SOC_MEM_NON_CONTIGUOUS_SRAM != 1
tools/test_apps/linux_compatible/driver_mock:
enable:
- if: IDF_TARGET == "linux"

View File

@@ -0,0 +1,4 @@
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(test_non_contiguous_regions)

View File

@@ -0,0 +1,2 @@
| Supported Targets | ESP32-P4 |
| ----------------- | -------- |

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "test_app_main.c"
LDFRAGMENTS "linker.lf")

View File

@@ -0,0 +1,13 @@
[sections:main_dram]
entries:
buf1
buf2
[scheme:main_dram_config]
entries:
main_dram -> dram0_bss
[mapping:main]
archive: libmain.a
entries:
* (main_dram_config)

View File

@@ -0,0 +1,55 @@
/*
* SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
#include <stdio.h>
#include <inttypes.h>
extern int _bss_start_low, _bss_start_high;
extern int _bss_end_low, _bss_end_high;
char buf1[100 * 1024];
char buf2[100 * 1024];
static void test_mem_write(char* buf, size_t size_bytes, int seed)
{
srand(seed);
for (size_t i = 0; i < size_bytes; ++i) {
buf[i] = (char) (rand() % 256);
}
}
static void test_mem_read(char* buf, size_t size_bytes, int seed)
{
size_t num_errors = 0;
srand(seed);
printf("Testing at %p ... ", buf);
for (size_t i = 0; i < size_bytes; ++i) {
if (buf[i] != (char) (rand() % 256)) {
++num_errors;
}
}
printf("%s!\n", num_errors == 0 ? "OK" : "ERROR");
}
void app_main(void)
{
if (! (((void *)&_bss_start_low <= (void *)buf2) && ((void *)buf2 < (void *)&_bss_end_low)))
printf("buf2 (%p) is expected to be placed in low sram (%p .. %p)\n", buf2, &_bss_start_low, &_bss_end_low);
else
printf("buf2 placed in low sram\n");
if (! ((void *)&_bss_start_high <= (void *)buf1 && (void *)buf1 < (void *)&_bss_end_high))
printf("buf1 (%p) is expected to be placed in high sram (%p .. %p)\n", buf1, &_bss_start_high, &_bss_end_high);
else
printf("buf1 placed in high sram\n");
test_mem_write(buf2, sizeof(buf1), 1);
test_mem_write(buf1, sizeof(buf1), 0);
test_mem_read(buf1, sizeof(buf1), 0);
test_mem_read(buf2, sizeof(buf2), 1);
test_mem_write(buf2, sizeof(buf1), 1);
test_mem_read(buf1, sizeof(buf1), 0);
test_mem_read(buf2, sizeof(buf2), 1);
}

View File

@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded import Dut
TEST_APP_IN_FLASH = [pytest.param('app_in_flash', marks=pytest.mark.esp32p4)]
@pytest.mark.esp32p4
@pytest.mark.generic
@pytest.mark.parametrize('config', TEST_APP_IN_FLASH, indirect=True)
def test_ld_non_contiguous_memory(dut: Dut) -> None:
dut.expect_exact('buf2 placed in low sram')
dut.expect_exact('buf1 placed in high sram')
for _ in range(0, 4):
dut.expect(r'Testing at 0x[0-9a-f]+ ... OK!')

View File

@@ -1,22 +0,0 @@
# The following lines of boilerplate have to be in your project's
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
#"Trim" the build. Include the minimal set of components, main, and anything it depends on.
set(COMPONENTS main)
project(ldalign_test)
idf_build_get_property(python PYTHON)
idf_build_get_property(elf EXECUTABLE)
set(check_alignment "${CMAKE_CURRENT_LIST_DIR}/check_alignment.py")
set(readelf "${_CMAKE_TOOLCHAIN_PREFIX}readelf")
add_custom_command(
TARGET ${elf}
POST_BUILD
COMMAND ${python} ${check_alignment} ${readelf} $<TARGET_FILE:${elf}>
)

View File

@@ -1,2 +0,0 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |

View File

@@ -1,7 +0,0 @@
Runs a build test to check alignment and position of `.flash.appdesc` and
`.flash.rodata` sections. Indeed, `.flash.appdesc` shall ALWAYS be aligned on
a 16-byte bounds, whereas `.flash.rodata` can have any alignment. In any case,
the end address of first one shall match the start address of the second one.
This will let both of them be merged when generating the final bin image.
The Python script that performs the checks, `check_alignment.py`, automatically
runs after the app is built.

View File

@@ -1,53 +0,0 @@
#!/usr/bin/env python
#
# SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
#
import argparse
import re
import subprocess
from typing import Tuple
argparser = argparse.ArgumentParser()
argparser.add_argument('readelf')
argparser.add_argument('elf')
args = argparser.parse_args()
# Get the content of the readelf command
contents = subprocess.check_output([args.readelf, '-S', args.elf]).decode()
# Define a class for readelf parsing error
class ParsingError(Exception):
pass
# Look for the start address and size of any section
def find_partition_info(sectionname): # type: (str) -> Tuple[int, int, int]
match = re.search(sectionname + r'\s+PROGBITS\s+([a-f0-9]+) [a-f0-9]+ ([a-f0-9]+) \d+\s+[A-Z]+ 0 0 (\d+)',
contents)
if not match:
raise ParsingError('ELF header parsing error')
# Return the address of the section, the size and the alignment
address = match.group(1)
size = match.group(2)
alignment = match.group(3)
return (int(address, 16), int(size, 16), int(alignment, 10))
# Get address and size for .flash.appdesc section
app_address, app_size, app_align = find_partition_info('.flash.appdesc')
# Same goes for .flash.rodata section
rodata_address, _, rodata_align = find_partition_info('.flash.rodata')
# Assert than everything is as expected:
# appdesc is aligned on 16
# rodata is aligned on 64
# appdesc ends where rodata starts
assert app_align == 16, '.flash.appdesc section should have been aligned on 16!'
assert rodata_align == 64, '.flash.rodata section should have been aligned on 64!'
assert app_address + app_size == rodata_address, ".flash.appdesc's end address and .flash.rodata's begin start must have no gap in between!"

View File

@@ -1,3 +0,0 @@
idf_component_register(SRCS "test_main.c"
INCLUDE_DIRS ".")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

View File

@@ -1,16 +0,0 @@
#include <stdio.h>
const static uint32_t __attribute__ ((aligned (64))) testTab[] =
{
0xff445566, 0x44556677, 0x33221100,
0x88997755, 0x99887755, 0x88997755,
0x99546327, 0x7946fa9e, 0xa6b5f8ee,
0x12345678
};
void app_main(void)
{
/* Do something with the array, in order to avoid it being discarded. */
for (uint32_t i = 0; i < 10; i++)
printf ("%x\n", testTab[i]);
}

View File

@@ -16,6 +16,10 @@ if(NOT CONFIG_SOC_RTC_MEM_SUPPORTED)
set(args "--no-rtc")
endif()
if(CONFIG_SOC_MEM_NON_CONTIGUOUS_SRAM)
set(args "--non-contiguous-sram")
endif()
add_custom_command(
TARGET ${elf}
POST_BUILD

View File

@@ -1,22 +1,27 @@
#!/usr/bin/env python
#
# SPDX-FileCopyrightText: 2020-2023 Espressif Systems (Shanghai) CO LTD
# SPDX-FileCopyrightText: 2020-2024 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Apache-2.0
#
# Check placements in this test app for main
# specified in main/linker.lf
import argparse
import subprocess
from pyparsing import LineEnd, LineStart, Literal, Optional, Word, alphanums, hexnums
from pyparsing import alphanums
from pyparsing import hexnums
from pyparsing import LineEnd
from pyparsing import LineStart
from pyparsing import Literal
from pyparsing import Optional
from pyparsing import Word
argparser = argparse.ArgumentParser()
argparser.add_argument('objdump')
argparser.add_argument('elf')
argparser.add_argument('--no-rtc', action='store_true')
argparser.add_argument('--non-contiguous-sram', action='store_true')
args = argparser.parse_args()
@@ -57,6 +62,15 @@ assert sym1_start % 9 == 0, '_sym1_start is not aligned as specified in linker f
assert sym1_end % 12 == 0, '_sym1_end is not aligned as specified in linker fragment'
print('check placement pass: _sym1_start < func1 < __sym1_end and alignments checked')
func0 = check_location('func0', '.iram0.text')
if args.non_contiguous_sram:
assert func1 < func0, 'check placement fail: func1 comes after func0'
print('check placement pass: func1 < func0 checked')
else:
assert func1 > func0, 'check placement fail: func0 comes after func1'
print('check placement pass: func1 > func0 checked')
# src1:func2 (rtc) - explicit mapping for func2 using 'rtc' scheme
if not args.no_rtc:
check_location('func2', '.rtc.text')

View File

@@ -5,5 +5,7 @@ entries:
src1 (default)
src1:func1 (noflash);
text->iram0_text KEEP() ALIGN(9) ALIGN(12, post) SURROUND(sym1)
src1:func0 (noflash);
text->iram0_text KEEP()
if SOC_RTC_MEM_SUPPORTED = y:
src1:func2 (rtc)

View File

@@ -1,5 +1,10 @@
#include <stdio.h>
void func0(void)
{
printf("Hello from func0!\n");
}
void func1(void)
{
printf("Hello from func1!\n");