Merge branch 'feature/linker_script_generator' into 'master'
Linker script generator See merge request idf/esp-idf!2286
This commit is contained in:
commit
8915f48208
62 changed files with 13981 additions and 67 deletions
|
@ -441,6 +441,13 @@ test_fatfs_on_host:
|
|||
- cd components/fatfs/test_fatfs_host/
|
||||
- make test
|
||||
|
||||
test_ldgen_on_host:
|
||||
<<: *host_test_template
|
||||
script:
|
||||
- cd tools/ldgen/test
|
||||
- ./test_fragments.py
|
||||
- ./test_generation.py
|
||||
|
||||
.host_fuzzer_test_template: &host_fuzzer_test_template
|
||||
stage: host_test
|
||||
image: $CI_DOCKER_REGISTRY/afl-fuzzer-test
|
||||
|
|
|
@ -18,6 +18,7 @@ endif()
|
|||
|
||||
set(COMPONENT_REQUIRES)
|
||||
set(COMPONENT_PRIV_REQUIRES xtensa-debug-module)
|
||||
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
|
||||
|
||||
register_component()
|
||||
|
||||
|
|
|
@ -26,4 +26,6 @@ COMPONENT_SRCDIRS += \
|
|||
sys_view/esp32
|
||||
else
|
||||
COMPONENT_SRCDIRS += gcov
|
||||
|
||||
COMPONENT_ADD_LDFRAGMENTS += linker.lf
|
||||
endif
|
||||
|
|
5
components/app_trace/linker.lf
Normal file
5
components/app_trace/linker.lf
Normal file
|
@ -0,0 +1,5 @@
|
|||
[mapping]
|
||||
archive: libapp_trace.a
|
||||
entries:
|
||||
* (noflash)
|
||||
|
|
@ -71,6 +71,8 @@ else()
|
|||
app_trace bootloader_support ethernet log mbedtls nvs_flash
|
||||
pthread smartconfig_ack spi_flash vfs wpa_supplicant xtensa-debug-module)
|
||||
|
||||
set(COMPONENT_ADD_LDFRAGMENTS linker.lf ld/esp32_fragments.lf)
|
||||
|
||||
register_component()
|
||||
|
||||
target_link_libraries(esp32 "-L ${CMAKE_CURRENT_SOURCE_DIR}/lib")
|
||||
|
@ -84,7 +86,16 @@ else()
|
|||
target_linker_script(esp32 "ld/esp32.extram.bss.ld")
|
||||
endif()
|
||||
|
||||
target_linker_script(esp32 "ld/esp32.common.ld")
|
||||
# Process the template file through the linker script generation mechanism, and use the output for linking the
|
||||
# final binary
|
||||
set(esp32_common_script "${CMAKE_CURRENT_BINARY_DIR}/esp32.common.ld")
|
||||
set(esp32_common_template "${CMAKE_CURRENT_LIST_DIR}/ld/esp32.common.ld.in")
|
||||
|
||||
ldgen_process_template(${esp32_common_template} ${esp32_common_script})
|
||||
|
||||
target_link_libraries(esp32 "-T ${esp32_common_script}")
|
||||
|
||||
set_property(TARGET ${PROJECT_NAME}.elf APPEND PROPERTY LINK_DEPENDS ${esp32_common_script})
|
||||
|
||||
target_linker_script(esp32
|
||||
"ld/esp32.rom.ld"
|
||||
|
|
|
@ -42,3 +42,9 @@ endif
|
|||
ifneq ("$(TEST_COMPONENTS_LIST)","")
|
||||
CPPFLAGS += -DESP_TIMER_DYNAMIC_OVERFLOW_VAL
|
||||
endif
|
||||
ESP32_LINKER_SCRIPT_TEMPLATE := $(COMPONENT_PATH)/ld/esp32.common.ld.in
|
||||
ESP32_LINKER_SCRIPT_OUTPUT_DIR := $(abspath $(BUILD_DIR_BASE)/esp32)
|
||||
|
||||
# Target to generate linker script generator from fragments presented by each of
|
||||
# the components
|
||||
$(eval $(call ldgen_process_template, $(ESP32_LINKER_SCRIPT_TEMPLATE), $(ESP32_LINKER_SCRIPT_OUTPUT_DIR)/esp32.common.ld))
|
||||
|
|
|
@ -16,7 +16,7 @@ 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 += esp32.common.ld esp32.rom.ld esp32.peripherals.ld
|
||||
LINKER_SCRIPTS += $(COMPONENT_BUILD_DIR)/esp32.common.ld esp32.rom.ld esp32.peripherals.ld
|
||||
|
||||
#Force pure functions from libgcc.a to be linked from ROM
|
||||
LINKER_SCRIPTS += esp32.rom.libgcc.ld
|
||||
|
@ -44,7 +44,9 @@ COMPONENT_ADD_LDFLAGS += $(COMPONENT_PATH)/libhal.a \
|
|||
-L $(COMPONENT_PATH)/ld \
|
||||
-T esp32_out.ld \
|
||||
-u ld_include_panic_highint_hdl \
|
||||
$(addprefix -T ,$(LINKER_SCRIPTS))
|
||||
$(addprefix -T ,$(LINKER_SCRIPTS)) \
|
||||
|
||||
COMPONENT_ADD_LDFRAGMENTS += ld/esp32_fragments.lf linker.lf
|
||||
|
||||
ALL_LIB_FILES := $(patsubst %,$(COMPONENT_PATH)/lib/lib%.a,$(LIBS))
|
||||
|
||||
|
@ -52,7 +54,9 @@ COMPONENT_SUBMODULES += lib
|
|||
|
||||
# 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 := $(ALL_LIB_FILES) $(addprefix ld/,$(LINKER_SCRIPTS))
|
||||
COMPONENT_ADD_LINKER_DEPS := $(ALL_LIB_FILES) \
|
||||
$(addprefix ld/, $(filter-out $(COMPONENT_BUILD_DIR)/esp32.common.ld, $(LINKER_SCRIPTS))) \
|
||||
$(COMPONENT_BUILD_DIR)/esp32.common.ld
|
||||
|
||||
# Preprocess esp32.ld linker script into esp32_out.ld
|
||||
#
|
||||
|
@ -63,7 +67,7 @@ $(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_EXTRA_CLEAN := esp32_out.ld $(COMPONENT_BUILD_DIR)/esp32.common.ld
|
||||
|
||||
# disable stack protection in files which are involved in initialization of that feature
|
||||
stack_check.o: CFLAGS := $(filter-out -fstack-protector%, $(CFLAGS))
|
||||
|
|
|
@ -9,7 +9,9 @@ SECTIONS
|
|||
.rtc.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.rtc.literal .rtc.text)
|
||||
|
||||
mapping[rtc_text]
|
||||
|
||||
*rtc_wake_stub*.*(.literal .text .literal.* .text.*)
|
||||
_rtc_text_end = ABSOLUTE(.);
|
||||
} > rtc_iram_seg
|
||||
|
@ -49,8 +51,9 @@ SECTIONS
|
|||
.rtc.data :
|
||||
{
|
||||
_rtc_data_start = ABSOLUTE(.);
|
||||
*(.rtc.data)
|
||||
*(.rtc.rodata)
|
||||
|
||||
mapping[rtc_data]
|
||||
|
||||
*rtc_wake_stub*.*(.data .rodata .data.* .rodata.* .bss .bss.*)
|
||||
_rtc_data_end = ABSOLUTE(.);
|
||||
} > rtc_data_location
|
||||
|
@ -61,7 +64,9 @@ SECTIONS
|
|||
_rtc_bss_start = ABSOLUTE(.);
|
||||
*rtc_wake_stub*.*(.bss .bss.*)
|
||||
*rtc_wake_stub*.*(COMMON)
|
||||
*(.rtc.bss)
|
||||
|
||||
mapping[rtc_bss]
|
||||
|
||||
_rtc_bss_end = ABSOLUTE(.);
|
||||
} > rtc_data_location
|
||||
|
||||
|
@ -152,22 +157,9 @@ SECTIONS
|
|||
{
|
||||
/* Code marked as runnning out of IRAM */
|
||||
_iram_text_start = ABSOLUTE(.);
|
||||
*(.iram1 .iram1.*)
|
||||
*libesp_ringbuf.a:(.literal .text .literal.* .text.*)
|
||||
*libfreertos.a:(.literal .text .literal.* .text.*)
|
||||
*libheap.a:multi_heap.*(.literal .text .literal.* .text.*)
|
||||
*libheap.a:multi_heap_poisoning.*(.literal .text .literal.* .text.*)
|
||||
*libesp32.a:panic.*(.literal .text .literal.* .text.*)
|
||||
*libesp32.a:core_dump.*(.literal .text .literal.* .text.*)
|
||||
*libapp_trace.a:(.literal .text .literal.* .text.*)
|
||||
*libxtensa-debug-module.a:eri.*(.literal .text .literal.* .text.*)
|
||||
*librtc.a:(.literal .text .literal.* .text.*)
|
||||
*libsoc.a:rtc_*.*(.literal .text .literal.* .text.*)
|
||||
*libsoc.a:cpu_util.*(.literal .text .literal.* .text.*)
|
||||
*libhal.a:(.literal .text .literal.* .text.*)
|
||||
*libgcc.a:lib2funcs.*(.literal .text .literal.* .text.*)
|
||||
*libspi_flash.a:spi_flash_rom_patch.*(.literal .text .literal.* .text.*)
|
||||
*libgcov.a:(.literal .text .literal.* .text.*)
|
||||
|
||||
mapping[iram0_text]
|
||||
|
||||
INCLUDE esp32.spiram.rom-functions-iram.ld
|
||||
_iram_text_end = ABSOLUTE(.);
|
||||
_iram_end = ABSOLUTE(.);
|
||||
|
@ -187,8 +179,6 @@ SECTIONS
|
|||
*libbtdm_app.a:(.data .data.*)
|
||||
. = ALIGN (4);
|
||||
_btdm_data_end = ABSOLUTE(.);
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.gnu.linkonce.d.*)
|
||||
*(.data1)
|
||||
*(.sdata)
|
||||
|
@ -198,14 +188,9 @@ SECTIONS
|
|||
*(.sdata2.*)
|
||||
*(.gnu.linkonce.s2.*)
|
||||
*(.jcr)
|
||||
*(.dram1 .dram1.*)
|
||||
*libesp32.a:panic.*(.rodata .rodata.*)
|
||||
*libphy.a:(.rodata .rodata.*)
|
||||
*libsoc.a:rtc_clk.*(.rodata .rodata.*)
|
||||
*libapp_trace.a:(.rodata .rodata.*)
|
||||
*libgcov.a:(.rodata .rodata.*)
|
||||
*libheap.a:multi_heap.*(.rodata .rodata.*)
|
||||
*libheap.a:multi_heap_poisoning.*(.rodata .rodata.*)
|
||||
|
||||
mapping[dram0_data]
|
||||
|
||||
INCLUDE esp32.spiram.rom-functions-dram.ld
|
||||
_data_end = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
|
@ -239,6 +224,9 @@ SECTIONS
|
|||
*libbtdm_app.a:(.bss .bss.* COMMON)
|
||||
. = ALIGN (4);
|
||||
_btdm_bss_end = ABSOLUTE(.);
|
||||
|
||||
mapping[dram0_bss]
|
||||
|
||||
*(.dynsbss)
|
||||
*(.sbss)
|
||||
*(.sbss.*)
|
||||
|
@ -248,11 +236,9 @@ SECTIONS
|
|||
*(.sbss2.*)
|
||||
*(.gnu.linkonce.sb2.*)
|
||||
*(.dynbss)
|
||||
*(.bss)
|
||||
*(.bss.*)
|
||||
*(.share.mem)
|
||||
*(.gnu.linkonce.b.*)
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN (8);
|
||||
_bss_end = ABSOLUTE(.);
|
||||
/* The heap starts right after end of this section */
|
||||
|
@ -265,8 +251,9 @@ SECTIONS
|
|||
.flash.rodata :
|
||||
{
|
||||
_rodata_start = ABSOLUTE(.);
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
|
||||
mapping[flash_rodata]
|
||||
|
||||
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
|
||||
*(.gnu.linkonce.r.*)
|
||||
*(.rodata1)
|
||||
|
@ -324,7 +311,10 @@ SECTIONS
|
|||
{
|
||||
_stext = .;
|
||||
_text_start = ABSOLUTE(.);
|
||||
*(.literal .text .literal.* .text.* .stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
|
||||
|
||||
mapping[flash_text]
|
||||
|
||||
*(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
|
||||
*(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */
|
||||
*(.fini.literal)
|
||||
*(.fini)
|
80
components/esp32/ld/esp32_fragments.lf
Normal file
80
components/esp32/ld/esp32_fragments.lf
Normal file
|
@ -0,0 +1,80 @@
|
|||
[sections:text]
|
||||
entries:
|
||||
.text+
|
||||
.literal+
|
||||
|
||||
[sections:data]
|
||||
entries:
|
||||
.data+
|
||||
|
||||
[sections:bss]
|
||||
entries:
|
||||
.bss+
|
||||
|
||||
[sections:common]
|
||||
entries:
|
||||
COMMON
|
||||
|
||||
[sections:rodata]
|
||||
entries:
|
||||
.rodata+
|
||||
|
||||
[sections:rtc_text]
|
||||
entries:
|
||||
.rtc.text
|
||||
.rtc.literal
|
||||
|
||||
[sections:rtc_data]
|
||||
entries:
|
||||
.rtc.data
|
||||
|
||||
[sections:rtc_rodata]
|
||||
entries:
|
||||
.rtc.rodata
|
||||
|
||||
[sections:rtc_bss]
|
||||
entries:
|
||||
.rtc.bss
|
||||
|
||||
[sections:iram]
|
||||
entries:
|
||||
.iram1+
|
||||
|
||||
[sections:dram]
|
||||
entries:
|
||||
.dram1+
|
||||
|
||||
[scheme:default]
|
||||
entries:
|
||||
text -> flash_text
|
||||
rodata -> flash_rodata
|
||||
data -> dram0_data
|
||||
bss -> dram0_bss
|
||||
common -> dram0_bss
|
||||
iram -> iram0_text
|
||||
dram -> dram0_data
|
||||
rtc_text -> rtc_text
|
||||
rtc_data -> rtc_data
|
||||
rtc_rodata -> rtc_data
|
||||
rtc_bss -> rtc_bss
|
||||
|
||||
[scheme:rtc]
|
||||
entries:
|
||||
text -> rtc_text
|
||||
data -> rtc_data
|
||||
rodata -> rtc_data
|
||||
bss -> rtc_bss
|
||||
common -> rtc_bss
|
||||
|
||||
[scheme:noflash]
|
||||
entries:
|
||||
text -> iram0_text
|
||||
rodata -> dram0_data
|
||||
|
||||
[scheme:noflash_data]
|
||||
entries:
|
||||
rodata -> dram0_data
|
||||
|
||||
[scheme:noflash_text]
|
||||
entries:
|
||||
text -> iram0_text
|
30
components/esp32/linker.lf
Normal file
30
components/esp32/linker.lf
Normal file
|
@ -0,0 +1,30 @@
|
|||
[mapping]
|
||||
archive: libesp32.a
|
||||
entries:
|
||||
core_dump (noflash_text)
|
||||
panic (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libphy.a
|
||||
entries:
|
||||
* (noflash_data)
|
||||
|
||||
[mapping]
|
||||
archive: libhal.a
|
||||
entries:
|
||||
* (noflash_text)
|
||||
|
||||
[mapping]
|
||||
archive: librtc.a
|
||||
entries:
|
||||
* (noflash_text)
|
||||
|
||||
[mapping]
|
||||
archive: libgcc.a
|
||||
entries:
|
||||
lib2funcs (noflash_text)
|
||||
|
||||
[mapping]
|
||||
archive: libgcov.a
|
||||
entries:
|
||||
* (noflash)
|
|
@ -1,5 +1,6 @@
|
|||
set(COMPONENT_ADD_INCLUDEDIRS "include")
|
||||
set(COMPONENT_SRCS "ringbuf.c")
|
||||
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
|
||||
|
||||
set(COMPONENT_REQUIRES)
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
COMPONENT_ADD_LDFRAGMENTS += linker.lf
|
4
components/esp_ringbuf/linker.lf
Normal file
4
components/esp_ringbuf/linker.lf
Normal file
|
@ -0,0 +1,4 @@
|
|||
[mapping]
|
||||
archive: libesp_ringbuf.a
|
||||
entries:
|
||||
* (noflash_text)
|
|
@ -20,7 +20,7 @@ set(COMPONENT_SRCS "FreeRTOS-openocd.c"
|
|||
# app_trace is required by FreeRTOS headers only when CONFIG_SYSVIEW_ENABLE=y,
|
||||
# but requirements can't depend on config options, so always require it.
|
||||
set(COMPONENT_REQUIRES app_trace)
|
||||
|
||||
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
|
||||
register_component()
|
||||
|
||||
target_link_libraries(freertos "-Wl,--undefined=uxTopUsedPriority")
|
||||
|
|
|
@ -7,3 +7,4 @@ COMPONENT_ADD_INCLUDEDIRS := include
|
|||
COMPONENT_PRIV_INCLUDEDIRS := include/freertos
|
||||
|
||||
tasks.o event_groups.o timers.o queue.o: CFLAGS += -D_ESP_FREERTOS_INTERNAL
|
||||
COMPONENT_ADD_LDFRAGMENTS += linker.lf
|
||||
|
|
5
components/freertos/linker.lf
Normal file
5
components/freertos/linker.lf
Normal file
|
@ -0,0 +1,5 @@
|
|||
[mapping]
|
||||
archive: libfreertos.a
|
||||
entries:
|
||||
* (noflash_text)
|
||||
|
|
@ -12,7 +12,7 @@ if(CONFIG_HEAP_TASK_TRACKING)
|
|||
endif()
|
||||
|
||||
set(COMPONENT_ADD_INCLUDEDIRS "include")
|
||||
|
||||
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
|
||||
set(COMPONENT_REQUIRES "")
|
||||
|
||||
register_component()
|
||||
|
|
|
@ -20,3 +20,5 @@ WRAP_ARGUMENT := -Wl,--wrap=
|
|||
COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME) $(addprefix $(WRAP_ARGUMENT),$(WRAP_FUNCTIONS))
|
||||
|
||||
endif
|
||||
|
||||
COMPONENT_ADD_LDFRAGMENTS += linker.lf
|
||||
|
|
5
components/heap/linker.lf
Normal file
5
components/heap/linker.lf
Normal file
|
@ -0,0 +1,5 @@
|
|||
[mapping]
|
||||
archive: libheap.a
|
||||
entries:
|
||||
multi_heap (noflash)
|
||||
multi_heap_poisoning (noflash)
|
|
@ -11,5 +11,8 @@ endif()
|
|||
list(APPEND COMPONENT_ADD_INCLUDEDIRS include)
|
||||
list(APPEND COMPONENT_SRCS "src/memory_layout_utils.c")
|
||||
|
||||
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
|
||||
|
||||
set(COMPONENT_REQUIRES)
|
||||
|
||||
register_component()
|
||||
|
|
|
@ -5,3 +5,4 @@ COMPONENT_SRCDIRS := $(SOC_NAME) src/
|
|||
COMPONENT_ADD_INCLUDEDIRS := $(SOC_NAME)/include include
|
||||
|
||||
-include $(COMPONENT_PATH)/$(SOC_NAME)/component.mk
|
||||
COMPONENT_ADD_LDFRAGMENTS += linker.lf
|
||||
|
|
12
components/soc/linker.lf
Normal file
12
components/soc/linker.lf
Normal file
|
@ -0,0 +1,12 @@
|
|||
[mapping]
|
||||
archive: libsoc.a
|
||||
entries:
|
||||
cpu_util (noflash_text)
|
||||
rtc_clk (noflash)
|
||||
rtc_clk_init (noflash_text)
|
||||
rtc_init (noflash_text)
|
||||
rtc_periph (noflash_text)
|
||||
rtc_pm (noflash_text)
|
||||
rtc_sleep (noflash_text)
|
||||
rtc_time (noflash_text)
|
||||
rtc_wdt (noflash_text)
|
|
@ -15,4 +15,6 @@ endif()
|
|||
set(COMPONENT_ADD_INCLUDEDIRS include)
|
||||
set(COMPONENT_REQUIRES)
|
||||
|
||||
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
|
||||
|
||||
register_component()
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
|
||||
COMPONENT_ADD_LDFRAGMENTS += linker.lf
|
||||
|
||||
ifdef IS_BOOTLOADER_BUILD
|
||||
# Bootloader needs updated SPIUnlock from this file
|
||||
COMPONENT_OBJS := spi_flash_rom_patch.o
|
||||
endif
|
||||
|
||||
|
|
5
components/spi_flash/linker.lf
Normal file
5
components/spi_flash/linker.lf
Normal file
|
@ -0,0 +1,5 @@
|
|||
[mapping]
|
||||
archive: libspi_flash.a
|
||||
entries:
|
||||
spi_flash_rom_patch (noflash_text)
|
||||
|
|
@ -4,4 +4,6 @@ set(COMPONENT_ADD_INCLUDEDIRS "include")
|
|||
|
||||
set(COMPONENT_REQUIRES "")
|
||||
|
||||
set(COMPONENT_ADD_LDFRAGMENTS linker.lf)
|
||||
|
||||
register_component()
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
# Component Makefile
|
||||
#
|
||||
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
|
||||
COMPONENT_ADD_LDFRAGMENTS += linker.lf
|
||||
|
|
4
components/xtensa-debug-module/linker.lf
Normal file
4
components/xtensa-debug-module/linker.lf
Normal file
|
@ -0,0 +1,4 @@
|
|||
[mapping]
|
||||
archive: libxtensa-debug-module.a
|
||||
entries:
|
||||
eri (noflash_text)
|
|
@ -350,6 +350,7 @@ The following variables can be set inside ``component.mk`` to control the build
|
|||
``*.c``, ``*.S``). Source files are globbed from the listed directories and compiled as part of the component in place of ``COMPONENT_SRCS``, i.e. setting this will cause ``COMPONENT_SRCS`` to be ignored.
|
||||
This can be a convenient way of including source files to the components en masse, but is generally not recommended due to caveats attached to CMake globbing (see `File Globbing & Incremental Builds`).
|
||||
- ``COMPONENT_SRCEXCLUDE``: Paths to source files to exclude from component. Can be set in conjunction with ``COMPONENT_SRCDIRS`` if there is a directory with a large number of source files to include in the component but one or more source files which should not be. Paths can be specified relative to the component directory or absolute.
|
||||
- ``COMPONENT_ADD_LDFRAGMENTS``: Paths to linker fragment files for the linker script generation functionality. See :doc:`Linker Script Generation <linker-script-generation>`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -688,6 +689,14 @@ Place this line after the ``project()`` line in your project CMakeLists.txt file
|
|||
|
||||
For an example of using this technique, see :example:`protocols/https_request` - the certificate file contents are loaded from the text .pem file at compile time.
|
||||
|
||||
Code and Data Placements
|
||||
------------------------
|
||||
|
||||
ESP-IDF has a feature called linker script generation that enables components to define where its code and data will be placed in memory through
|
||||
linker fragment files. These files are processed by the build system, and is used to augment the linker script used for linking
|
||||
app binary. See :doc:`Linker Script Generation <linker-script-generation>` for a quick start guide as well as a detailed discussion
|
||||
of the mechanism.
|
||||
|
||||
.. _component-build-full-override:
|
||||
|
||||
Fully Overriding The Component Build Process
|
||||
|
|
|
@ -274,6 +274,8 @@ The following variables can be set inside ``component.mk`` to control the build
|
|||
settings. Component-specific additions can be made via ``CXXFLAGS
|
||||
+=``. It is also possible (although not recommended) to override
|
||||
this variable completely for a component.
|
||||
- ``COMPONENT_ADD_LDFRAGMENTS``: Paths to linker fragment files for the linker
|
||||
script generation functionality. See :doc:`Linker Script Generation <linker-script-generation>`.
|
||||
|
||||
To apply compilation flags to a single source file, you can add a variable override as a target, ie::
|
||||
|
||||
|
@ -570,6 +572,13 @@ The names are generated from the full name of the file, as given in COMPONENT_EM
|
|||
|
||||
For an example of using this technique, see :example:`protocols/https_request` - the certificate file contents are loaded from the text .pem file at compile time.
|
||||
|
||||
Code and Data Placements
|
||||
------------------------
|
||||
|
||||
ESP-IDF has a feature called linker script generation that enables components to define where its code and data will be placed in memory through
|
||||
linker fragment files. These files are processed by the build system, and is used to augment the linker script used for linking
|
||||
app binary. See :doc:`Linker Script Generation <linker-script-generation>` for a quick start guide as well as a detailed discussion
|
||||
of the mechanism.
|
||||
|
||||
Fully Overriding The Component Makefile
|
||||
---------------------------------------
|
||||
|
|
|
@ -30,3 +30,4 @@ API Guides
|
|||
ESP-MESH <mesh>
|
||||
BluFi <blufi>
|
||||
External SPI-connected RAM <external-ram>
|
||||
Linker Script Generation <linker-script-generation>
|
546
docs/en/api-guides/linker-script-generation.rst
Normal file
546
docs/en/api-guides/linker-script-generation.rst
Normal file
|
@ -0,0 +1,546 @@
|
|||
Linker Script Generation
|
||||
========================
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
There are several :ref:`memory regions<memory-layout>` where code and data can be placed. Usually, code and read-only data are placed in flash regions,
|
||||
writable data in RAM, etc. A common action is changing where code/data are mapped by default, say placing critical code/rodata in RAM for performance
|
||||
reasons or placing code/data/rodata in RTC memory for use in a wake stub or the ULP coprocessor.
|
||||
|
||||
IDF provides the ability for defining these placements at the component level using the linker script generation mechanism. The component presents
|
||||
how it would like to map the input sections of its object files (or even functions/data) through :ref:`linker fragment files<ldgen-fragment-files>`. During app build,
|
||||
the linker fragment files are collected, parsed and processed; and the :ref:`linker script template<ldgen-script-templates>` is augmented with
|
||||
information generated from the fragment files to produce the final linker script. This linker script is then used for the linking
|
||||
the final app binary.
|
||||
|
||||
Quick Start
|
||||
------------
|
||||
|
||||
This section presents a guide for quickly placing code/data to RAM and RTC memory; as well as demonstrating how to make these placements
|
||||
dependent on project configuration values. In a true quick start fashion, this section glosses over terms and concepts that will be discussed
|
||||
at a later part of the document. However, whenever it does so, it provides a link to the relevant section on the first mention.
|
||||
|
||||
.. _ldgen-add-fragment-file :
|
||||
|
||||
Preparation
|
||||
^^^^^^^^^^^
|
||||
|
||||
Make
|
||||
""""
|
||||
|
||||
Create a linker fragment file inside the component directory, which is just a text file with a .lf extension. In order for the build system to collect your fragment file,
|
||||
add an entry to it from the component, set the variable ``COMPONENT_ADD_LDFRAGMENTS`` to your linker file/s before the ``register_component`` call.
|
||||
|
||||
.. code-block:: make
|
||||
|
||||
# file paths relative to component Makefile
|
||||
COMPONENT_ADD_LDFRAGMENTS += "path/to/linker_fragment_file.lf" "path/to/another_linker_fragment_file.lf"
|
||||
|
||||
CMake
|
||||
"""""
|
||||
|
||||
For CMake set the variable ``COMPONENT_ADD_LDFRAGMENTS`` to your linker file/s before the ``register_component`` call.
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
# file paths relative to CMakeLists.txt
|
||||
set(COMPONENT_ADD_LDFRAGMENTS "path/to/linker_fragment_file.lf" "path/to/another_linker_fragment_file.lf")
|
||||
|
||||
register_component()
|
||||
|
||||
|
||||
Specifying placements
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
This mechanism allows specifying placement of the following entities:
|
||||
|
||||
- one or multiple object files within the component
|
||||
- one or multiple function/variable using their names
|
||||
- the entire component library
|
||||
|
||||
For the following text, suppose we have the following:
|
||||
|
||||
- a component named ``component`` that is archived as library ``libcomponent.a`` during build
|
||||
- three object files archived under the library, ``object1.o``, ``object2.o`` and ``object3.o``
|
||||
- under ``object1.o``, the function ``function1`` is defined; under ``object2.o``, the function ``function2`` is defined
|
||||
- there exists configuration ``PERFORMANCE_MODE`` and ``PERFORMANCE_LEVEL`` in one of the IDF KConfig files, with the set value indicated by entries ``CONFIG_PERFORMANCE_MODE`` and ``CONFIG_PERFORMANCE_LEVEL`` in the project sdkconfig
|
||||
|
||||
In the created linker fragment file, we write:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[mapping]
|
||||
archive: libcomponent.a
|
||||
entries:
|
||||
|
||||
This creates an empty :ref:`mapping fragment<ldgen-mapping-fragment>`, which doesn't do anything yet. During linking the :ref:`default placements<ldgen-default-placements>`
|
||||
will still be used for ``libcomponent.a``, unless the ``entries`` key is populated.
|
||||
|
||||
.. _ldgen-placing-object-files :
|
||||
|
||||
Placing object files
|
||||
""""""""""""""""""""
|
||||
|
||||
Suppose the entirety of ``object1.o`` is performance-critical, so it is desirable to place it in RAM. On the other hand, all of ``object2.o``
|
||||
contains things to be executed coming out of deep sleep, so it needs to be put under RTC memory. We can write:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[mapping]
|
||||
archive: libcomponent.a
|
||||
entries:
|
||||
object1 (noflash) # places all code / read-only data under IRAM/ DRAM
|
||||
object2 (rtc) # places all code/ data and read-only data under RTC fast memory/ RTC slow memory
|
||||
|
||||
What happens to ``object3.o``? Since it is not specified, default placements are used for ``object3.o``.
|
||||
|
||||
Placing functions/data using their names
|
||||
""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
Continuing our example, suppose that among functions defined under ``object1.o``, only ``function1`` is performance-critical; and under ``object2.o``,
|
||||
only ``function2`` needs to execute after the chip comes out of deep sleep. This could be accomplished by writing:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[mapping]
|
||||
archive: libcomponent.a
|
||||
entries:
|
||||
object1:function1 (noflash)
|
||||
object2:function2 (rtc)
|
||||
|
||||
The default placements are used for the rest of the functions in ``object1.o`` and ``object2.o`` and the entire ``object3.o``. Something similar
|
||||
can be achieved for placing data by writing the variable name instead of the function name after ``:``.
|
||||
|
||||
.. warning::
|
||||
|
||||
There are :ref:`limitations<ldgen-type3-limitations>` in placing code/data using their symbol names. In order to ensure proper placements, an alternative would be to group
|
||||
relevant code and data into source files, and :ref:`use object file placement<ldgen-placing-object-files>`.
|
||||
|
||||
Placing entire component
|
||||
""""""""""""""""""""""""
|
||||
|
||||
In this example, suppose that the entire component needs to be placed in RAM. This can be written as:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[mapping]
|
||||
archive: libcomponent.a
|
||||
entries:
|
||||
* (noflash)
|
||||
|
||||
Similarly, this places the entire component in RTC memory:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[mapping]
|
||||
archive: libcomponent.a
|
||||
entries:
|
||||
* (rtc)
|
||||
|
||||
Configuration-dependent placements
|
||||
""""""""""""""""""""""""""""""""""
|
||||
|
||||
Suppose that the entire component library should only be placed when ``CONFIG_PERFORMANCE_MODE == y`` in the sdkconfig. This could be written as:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[mapping]
|
||||
archive: libcomponent.a
|
||||
entries:
|
||||
: PERFORMANCE_MODE = y
|
||||
* (noflash)
|
||||
|
||||
In pseudocode, this translates to:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
if PERFORMANCE_MODE = y
|
||||
place entire libcomponent.a in RAM
|
||||
else
|
||||
use default placements
|
||||
|
||||
It is also possible to have multiple conditions to test. Suppose the following requirements: when ``CONFIG_PERFORMANCE_LEVEL == 1``, only ``object1.o`` is put in RAM;
|
||||
when ``CONFIG_PERFORMANCE_LEVEL == 2``, ``object1.o`` and ``object2.o``; and when ``CONFIG_PERFORMANCE_LEVEL == 3`` all object files under the archive
|
||||
are to be put into RAM. When these three are false however, put entire library in RTC memory. This scenario is a bit contrived, but,
|
||||
it can be written as:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[mapping]
|
||||
archive: libcomponent.a
|
||||
entries:
|
||||
: PERFORMANCE_LEVEL = 3
|
||||
* (noflash)
|
||||
: PERFORMANCE_LEVEL = 2
|
||||
object1 (noflash)
|
||||
object2 (noflash)
|
||||
: PERFORMANCE_LEVEL = 1
|
||||
object1 (noflash)
|
||||
: default
|
||||
* (rtc)
|
||||
|
||||
Which reads:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
if CONFIG_PERFORMANCE_LEVEL == 3
|
||||
place entire libcomponent.a in RAM
|
||||
else if CONFIG_PERFORMANCE_LEVEL == 2
|
||||
only place object1.o and object2.o in RAM
|
||||
else if CONFIG_PERFORMANCE_LEVEL == 1
|
||||
only place object1.o in RAM
|
||||
else
|
||||
place entire libcomponent.a in RTC memory
|
||||
|
||||
The conditions test :ref:`support other operations<ldgen-condition-entries>`.
|
||||
|
||||
.. _ldgen-default-placements:
|
||||
|
||||
The 'default' placements
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Up until this point, the term 'default placements' has been mentioned as fallback placements for when the
|
||||
placement rules ``rtc`` and ``noflash`` are not specified. The tokens ``noflash`` or ``rtc`` are not merely keywords known by the mechanism, but are actually
|
||||
objects called :ref:`scheme fragments<ldgen-scheme-fragment>` that are specified by the user. Due to the commonness of these placement use cases,
|
||||
they are pre-defined in IDF.
|
||||
|
||||
Similarly, there exists a ``default`` scheme fragment which defines what the default placement rules should be, which is discussed :ref:`here<ldgen-default-scheme>`.
|
||||
|
||||
.. note::
|
||||
For an example of an IDF component using this feature, see :component_file:`freertos/CMakeLists.txt`. The ``freertos`` component uses this
|
||||
mechanism to place all code, literal and rodata of all of its object files to the instruction RAM memory region for performance reasons.
|
||||
|
||||
This marks the end of the quick start guide. The following text discusses this mechanism in a little bit more detail, such its components, essential concepts,
|
||||
the syntax, how it is integrated with the build system, etc. The following sections should be helpful in creating custom mappings or modifying default
|
||||
behavior.
|
||||
|
||||
Components
|
||||
----------
|
||||
|
||||
.. _ldgen-fragment-files :
|
||||
|
||||
Linker Fragment Files
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The fragment files contain objects called 'fragments'. These fragments contain pieces of information which, when put together, form
|
||||
placement rules that tell where to place sections of object files in the output binary.
|
||||
|
||||
Another way of putting it is that processing linker fragment files aims to create the section placement rules inside GNU LD ``SECTIONS`` command.
|
||||
Where to collect and put these section placement rules is represented internally as a ``target`` token.
|
||||
|
||||
The three types of fragments are discussed below.
|
||||
|
||||
.. note::
|
||||
|
||||
Fragments have a name property (except mapping fragments) and are known globally.
|
||||
Fragment naming follows C variable naming rules, i.e. case sensitive, must begin with a letter or underscore, alphanumeric/underscore after
|
||||
initial characters are allowed, no spaces/special characters. Each type of fragment has its own namespace. In cases where multiple fragments
|
||||
of the same type and name are encountered, an exception is thrown.
|
||||
|
||||
.. _ldgen-sections-fragment :
|
||||
|
||||
I. Sections
|
||||
"""""""""""
|
||||
|
||||
Sections fragments defines a list of object file sections that the GCC compiler emits. It may be a default section (e.g. ``.text``, ``.data``) or
|
||||
it may be user defined section through the ``__attribute__`` keyword.
|
||||
|
||||
The use of an optional '+' indicates the inclusion of the section in the list, as well as sections that start with it. This is the preferred method over listing both explicitly.
|
||||
|
||||
**Syntax**
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[sections:name]
|
||||
entries:
|
||||
.section+
|
||||
.section
|
||||
...
|
||||
|
||||
**Example**
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# Non-preferred
|
||||
[sections:text]
|
||||
entries:
|
||||
.text
|
||||
.text.*
|
||||
.literal
|
||||
.literal.*
|
||||
|
||||
# Preferred, equivalent to the one above
|
||||
[sections:text]
|
||||
entries:
|
||||
.text+ # means .text and .text.*
|
||||
.literal+ # means .literal and .literal.*
|
||||
|
||||
.. _ldgen-scheme-fragment :
|
||||
|
||||
II. Scheme
|
||||
""""""""""
|
||||
|
||||
Scheme fragments define what ``target`` a sections fragment is assigned to.
|
||||
|
||||
**Syntax**
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[scheme:name]
|
||||
entries:
|
||||
sections -> target
|
||||
sections -> target
|
||||
...
|
||||
|
||||
**Example**
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[scheme:noflash]
|
||||
entries:
|
||||
text -> iram0_text # the entries under the sections fragment named text will go to iram0_text
|
||||
rodata -> dram0_data # the entries under the sections fragment named rodata will go to dram0_data
|
||||
|
||||
.. _ldgen-default-scheme:
|
||||
|
||||
**The** ``default`` **scheme**
|
||||
|
||||
There exists a special scheme with the name ``default``. This scheme is special because catch-all placement rules are generated from
|
||||
its entries. This means that, if one of its entries is ``text -> flash_text``, the placement rule
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
*(.literal .literal.* .text .text.*)
|
||||
|
||||
will be generated for the target ``flash_text``.
|
||||
|
||||
These catch-all rules then effectively serve as fallback rules for those whose mappings were not specified.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``default scheme`` is defined in :component:`esp32/ld/esp32_fragments.lf`. The ``noflash`` and ``rtc`` scheme fragments which are
|
||||
built-in schemes referenced in the quick start guide are also defined in this file.
|
||||
|
||||
.. _ldgen-mapping-fragment :
|
||||
|
||||
III. Mapping
|
||||
""""""""""""
|
||||
|
||||
Mapping fragments define what scheme fragment to use for mappable entities, i.e. object files, function names, variable names. There are two types of entries
|
||||
for this fragment: mapping entries and condition entries.
|
||||
|
||||
.. note::
|
||||
|
||||
Mapping fragments have no explicit name property. Internally, the name is constructed from the value of the archive entry.
|
||||
|
||||
**Syntax**
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[mapping]
|
||||
archive: archive # output archive file name, as built (i.e. libxxx.a)
|
||||
entries:
|
||||
: condition # condition entry, non-default
|
||||
object:symbol (scheme) # mapping entry, Type I
|
||||
object (scheme) # mapping entry, Type II
|
||||
* (scheme) # mapping entry, Type III
|
||||
|
||||
# optional separation/comments, for readability
|
||||
|
||||
: default # condition entry, default
|
||||
* (scheme) # mapping entry, Type III
|
||||
|
||||
.. _ldgen-mapping-entries :
|
||||
|
||||
**Mapping Entries**
|
||||
|
||||
There are three types of mapping entries:
|
||||
|
||||
``Type I``
|
||||
The object file name and symbol name are specified. The symbol name can be a function name or a variable name.
|
||||
|
||||
``Type II``
|
||||
Only the object file name is specified.
|
||||
|
||||
``Type III``
|
||||
``*`` is specified, which is a short-hand for all the object files under the archive.
|
||||
|
||||
To know what a mapping entry means, let us expand a ``Type II`` entry. Originally:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
object (scheme)
|
||||
|
||||
Then expanding the scheme fragment from its entries definitions, we have:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
object (sections -> target,
|
||||
sections -> target,
|
||||
...)
|
||||
|
||||
Expanding the sections fragment with its entries definition:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
object (.section, # given this object file
|
||||
.section, # put its sections listed here at this
|
||||
... -> target, # target
|
||||
|
||||
.section,
|
||||
.section, # same should be done for these sections
|
||||
... -> target,
|
||||
|
||||
...) # and so on
|
||||
|
||||
.. _ldgen-type3-limitations :
|
||||
|
||||
**On** ``Type I`` **Mapping Entries**
|
||||
|
||||
``Type I`` mapping entry is possible due to compiler flags ``-ffunction-sections`` and ``-ffdata-sections``. If the user opts to remove these flags, then
|
||||
the ``Type I`` mapping will not work. Furthermore, even if the user does not opt to compile without these flags, there are still limitations
|
||||
as the implementation is dependent on the emitted output sections.
|
||||
|
||||
For example, with ``-ffunction-sections``, separate sections are emitted for each function; with section names predictably constructed i.e. ``.text.{func_name}``
|
||||
and ``.literal.{func_name}``. This is not the case for string literals within the function, as they go to pooled or generated section names.
|
||||
|
||||
With ``-fdata-sections``, for global scope data the compiler predictably emits either ``.data.{var_name}``, ``.rodata.{var_name}`` or ``.bss.{var_name}``; and so ``Type I`` mapping entry works for these.
|
||||
However, this is not the case for static data declared in function scope, as the generated section name is a result of mangling the variable name with some other information.
|
||||
|
||||
.. _ldgen-condition-entries :
|
||||
|
||||
**Condition Entries**
|
||||
|
||||
Condition entries enable the linker script generation to be configuration-aware. Depending on whether expressions involving configuration values
|
||||
are true or not, a particular set of mapping entries can be used. The evaluation uses ``eval_string`` from ``:idf_file:`tools/kconfig_new/kconfiglib.py``` and adheres to its required syntax and limitations.
|
||||
|
||||
All mapping entries defined after a condition entry until the next one or the end of the mapping fragment belongs to that condition entry. During processing
|
||||
conditions are tested sequentially, and the mapping entries under the first condition that evaluates to ``TRUE`` are used.
|
||||
|
||||
A default condition can be defined (though every mapping contains an implicit, empty one), whose mapping entries get used in the event no conditions evaluates to ``TRUE``.
|
||||
|
||||
**Example**
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[scheme:noflash]
|
||||
entries:
|
||||
text -> iram0_text
|
||||
rodata -> dram0_data
|
||||
|
||||
[mapping:lwip]
|
||||
archive: liblwip.a
|
||||
entries:
|
||||
: LWIP_IRAM_OPTIMIZATION = y # if CONFIG_LWIP_IRAM_OPTIMIZATION is set to 'y' in sdkconfig
|
||||
ip4:ip4_route_src_hook (noflash) # map ip4.o:ip4_route_src_hook, ip4.o:ip4_route_src and
|
||||
ip4:ip4_route_src (noflash) # ip4.o:ip4_route using the noflash scheme, which puts
|
||||
ip4:ip4_route (noflash) # them in RAM
|
||||
|
||||
: default # else no special mapping rules apply
|
||||
|
||||
.. _ldgen-script-templates :
|
||||
|
||||
Linker Script Template
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The linker script template is the skeleton in which the generated placement rules are put into. It is an otherwise ordinary linker script, with a specific marker syntax
|
||||
that indicates where the generated placement rules are placed.
|
||||
|
||||
**Syntax**
|
||||
|
||||
To reference the placement rules collected under a ``target`` token, the following syntax is used:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
mapping[target]
|
||||
|
||||
**Example**
|
||||
|
||||
The example below is an excerpt from a possible linker script template. It defines an output section ``.iram0.text``, and inside is a marker referencing
|
||||
the target ``iram0_text``.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
.iram0.text :
|
||||
{
|
||||
/* Code marked as runnning out of IRAM */
|
||||
_iram_text_start = ABSOLUTE(.);
|
||||
|
||||
/* Marker referencing iram0_text */
|
||||
mapping[iram0_text]
|
||||
|
||||
INCLUDE esp32.spiram.rom-functions-iram.ld
|
||||
_iram_text_end = ABSOLUTE(.);
|
||||
} > iram0_0_seg
|
||||
|
||||
Suppose the generator collected the fragment definitions below:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
[sections:text]
|
||||
.text+
|
||||
.literal+
|
||||
|
||||
[sections:iram]
|
||||
.iram1+
|
||||
|
||||
[scheme:default]
|
||||
entries:
|
||||
text -> flash_text
|
||||
iram -> iram0_text
|
||||
|
||||
[scheme:noflash]
|
||||
entries:
|
||||
text -> iram0_text
|
||||
|
||||
[mapping:freertos]
|
||||
archive: libfreertos.a
|
||||
entries:
|
||||
* (noflash)
|
||||
|
||||
Then the corresponding excerpt from the generated linker script will be as follows:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
.iram0.text :
|
||||
{
|
||||
/* Code marked as runnning out of IRAM */
|
||||
_iram_text_start = ABSOLUTE(.);
|
||||
|
||||
/* Placement rules generated from the processed fragments, placed where the marker was in the template */
|
||||
*(.iram1 .iram1.*)
|
||||
*libfreertos.a:(.literal .text .literal.* .text.*)
|
||||
|
||||
INCLUDE esp32.spiram.rom-functions-iram.ld
|
||||
_iram_text_end = ABSOLUTE(.);
|
||||
} > iram0_0_seg
|
||||
|
||||
``*libfreertos.a:(.literal .text .literal.* .text.*)``
|
||||
|
||||
Rule generated from the entry ``* (noflash)`` of the ``freertos`` mapping fragment. All ``text`` sections of all
|
||||
object files under the archive ``libfreertos.a`` will be collected under the target ``iram0_text`` (as per the ``noflash`` scheme)
|
||||
and placed wherever in the template ``iram0_text`` is referenced by a marker.
|
||||
|
||||
``*(.iram1 .iram1.*)``
|
||||
|
||||
Rule generated from the default scheme entry ``iram -> iram0_text``. Since the default scheme specifies an ``iram -> iram0_text`` entry,
|
||||
it too is placed wherever ``iram0_text`` is referenced by a marker. Since it is a rule generated from the default scheme, it comes first
|
||||
among all other rules collected under the same target name.
|
||||
|
||||
|
||||
Integration with Build System
|
||||
-----------------------------
|
||||
|
||||
The linker script generation occurs during application build, before the final output binary is linked. The tool that implements the mechanism
|
||||
lives under ``$(IDF_PATH)/tools/ldgen``.
|
||||
|
||||
Linker Script Template
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
Currently, the linker script template used is :component:`esp32/ld/esp32.common.ld.in`, and is used only for the app build. The generated output script is
|
||||
put under the build directory of the same component. Modifying this linker script template triggers a re-link of the app binary.
|
||||
|
||||
Linker Fragment File
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
Any component can add a fragment file to the build. In order to add a fragment file to process, use the command ``ldgen_add_fragment_file`` as mentioned :ref:`here<ldgen-add-fragment-file>`.
|
||||
Modifying any fragment file presented to the build system triggers a re-link of the app binary.
|
|
@ -30,3 +30,4 @@ API 指南
|
|||
ESP-MESH <mesh>
|
||||
BluFi <blufi>
|
||||
External SPI-connected RAM <external-ram>
|
||||
Linker Script Generation <linker-script-generation>
|
||||
|
|
1
docs/zh_CN/api-guides/linker-script-generation.rst
Normal file
1
docs/zh_CN/api-guides/linker-script-generation.rst
Normal file
|
@ -0,0 +1 @@
|
|||
.. include:: ../../en/api-guides/linker-script-generation.rst
|
|
@ -46,6 +46,10 @@ COMPONENT_EMBED_TXTFILES ?=
|
|||
COMPONENT_ADD_INCLUDEDIRS = include
|
||||
COMPONENT_ADD_LDFLAGS = -l$(COMPONENT_NAME)
|
||||
|
||||
# Name of the linker fragment files this component presents to the Linker
|
||||
# script generator
|
||||
COMPONENT_ADD_LDFRAGMENTS ?=
|
||||
|
||||
# Define optional compiling macros
|
||||
define compile_exclude
|
||||
COMPONENT_OBJEXCLUDE += $(1)
|
||||
|
@ -151,6 +155,8 @@ OWN_INCLUDES:=$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_PRIV_INCLUDED
|
|||
COMPONENT_INCLUDES := $(OWN_INCLUDES) $(filter-out $(OWN_INCLUDES),$(COMPONENT_INCLUDES))
|
||||
|
||||
|
||||
include $(IDF_PATH)/make/ldgen.mk
|
||||
|
||||
################################################################################
|
||||
# 4) Define a target to generate component_project_vars.mk Makefile which
|
||||
# contains common per-component settings which are included directly in the
|
||||
|
@ -193,8 +199,8 @@ component_project_vars.mk::
|
|||
@echo 'COMPONENT_LINKER_DEPS += $(call MakeVariablePath,$(call resolvepath,$(COMPONENT_ADD_LINKER_DEPS),$(COMPONENT_PATH)))' >> $@
|
||||
@echo 'COMPONENT_SUBMODULES += $(call MakeVariablePath,$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_SUBMODULES))))' >> $@
|
||||
@echo 'COMPONENT_LIBRARIES += $(COMPONENT_NAME)' >> $@
|
||||
@echo 'COMPONENT_LDFRAGMENTS += $(call MakeVariablePath,$(abspath $(addprefix $(COMPONENT_PATH)/,$(COMPONENT_ADD_LDFRAGMENTS))))' >> $@
|
||||
@echo 'component-$(COMPONENT_NAME)-build: $(addprefix component-,$(addsuffix -build,$(COMPONENT_DEPENDS)))' >> $@
|
||||
|
||||
################################################################################
|
||||
# 5) Where COMPONENT_OWNBUILDTARGET / COMPONENT_OWNCLEANTARGET
|
||||
# is not set by component.mk, define default build, clean, etc. targets
|
||||
|
@ -212,7 +218,7 @@ build: $(COMPONENT_LIBRARY)
|
|||
$(COMPONENT_LIBRARY): $(COMPONENT_OBJS) $(COMPONENT_EMBED_OBJS)
|
||||
$(summary) AR $(patsubst $(PWD)/%,%,$(CURDIR))/$@
|
||||
rm -f $@
|
||||
$(AR) $(ARFLAGS) $@ $^
|
||||
$(AR) $(ARFLAGS) $@ $(COMPONENT_OBJS) $(COMPONENT_EMBED_OBJS)
|
||||
endif
|
||||
|
||||
# If COMPONENT_OWNCLEANTARGET is not set, define a phony clean target
|
||||
|
@ -332,7 +338,7 @@ clean:
|
|||
$(summary) RM component_project_vars.mk
|
||||
rm -f component_project_vars.mk
|
||||
|
||||
component_project_vars.mk:: # no need to add variables via component.mk
|
||||
component_project_vars.mk:: # no need to add variables via component.mk
|
||||
@echo '# COMPONENT_CONFIG_ONLY target sets no variables here' > $@
|
||||
|
||||
endif # COMPONENT_CONFIG_ONLY
|
||||
|
|
37
make/ldgen.mk
Normal file
37
make/ldgen.mk
Normal file
|
@ -0,0 +1,37 @@
|
|||
# Makefile to support the linker script generation mechanism
|
||||
|
||||
LDGEN_SECTIONS_INFO_FILES = $(foreach lib, $(COMPONENT_LIBRARIES), $(BUILD_DIR_BASE)/$(lib)/lib$(lib).a.sections_info)
|
||||
LDGEN_FRAGMENT_FILES = $(COMPONENT_LDFRAGMENTS)
|
||||
|
||||
# Target to generate linker script generator from fragments presented by each of
|
||||
# the components
|
||||
define ldgen_process_template
|
||||
$(2): $(1) $(LDGEN_FRAGMENT_FILES) $(SDKCONFIG) $(LDGEN_SECTIONS_INFO_FILES)
|
||||
@echo 'Generating $(notdir $(2))'
|
||||
$(PYTHON) $(IDF_PATH)/tools/ldgen/ldgen.py \
|
||||
--input $(1) \
|
||||
--config $(SDKCONFIG) \
|
||||
--fragments $(LDGEN_FRAGMENT_FILES) \
|
||||
--output $(2) \
|
||||
--sections $(LDGEN_SECTIONS_INFO_FILES) \
|
||||
--kconfig $(IDF_PATH)/Kconfig \
|
||||
--env "COMPONENT_KCONFIGS=$(COMPONENT_KCONFIGS)" \
|
||||
--env "COMPONENT_KCONFIGS_PROJBUILD=$(COMPONENT_KCONFIGS_PROJBUILD)" \
|
||||
--env "IDF_CMAKE=n"
|
||||
--env "IDF_TARGET=$(IDF_TARGET)"
|
||||
endef
|
||||
|
||||
define ldgen_create_commands
|
||||
$(foreach lib, $(COMPONENT_LIBRARIES), \
|
||||
$(eval $(call ldgen_generate_target_sections_info, $(BUILD_DIR_BASE)/$(lib)/lib$(lib).a)))
|
||||
|
||||
ldgen-clean:
|
||||
rm -f $(LDGEN_SECTIONS_INFO_FILES)
|
||||
endef
|
||||
|
||||
# Target to generate sections info file from objdump of component archive
|
||||
define ldgen_generate_target_sections_info
|
||||
$(1).sections_info: $(1)
|
||||
@echo 'Generating $(notdir $(1).sections_info)'
|
||||
$(OBJDUMP) -h $(1) > $(1).sections_info
|
||||
endef
|
|
@ -204,6 +204,7 @@ COMPONENT_INCLUDES :=
|
|||
COMPONENT_LDFLAGS :=
|
||||
COMPONENT_SUBMODULES :=
|
||||
COMPONENT_LIBRARIES :=
|
||||
COMPONENT_LDFRAGMENTS :=
|
||||
|
||||
# COMPONENT_PROJECT_VARS is the list of component_project_vars.mk generated makefiles
|
||||
# for each component.
|
||||
|
@ -381,6 +382,7 @@ CC ?= gcc
|
|||
LD ?= ld
|
||||
AR ?= ar
|
||||
OBJCOPY ?= objcopy
|
||||
OBJDUMP ?= objdump
|
||||
SIZE ?= size
|
||||
|
||||
# Set host compiler and binutils
|
||||
|
@ -398,8 +400,9 @@ CXX := $(call dequote,$(CONFIG_TOOLPREFIX))c++
|
|||
LD := $(call dequote,$(CONFIG_TOOLPREFIX))ld
|
||||
AR := $(call dequote,$(CONFIG_TOOLPREFIX))ar
|
||||
OBJCOPY := $(call dequote,$(CONFIG_TOOLPREFIX))objcopy
|
||||
OBJDUMP := $(call dequote,$(CONFIG_TOOLPREFIX))objdump
|
||||
SIZE := $(call dequote,$(CONFIG_TOOLPREFIX))size
|
||||
export CC CXX LD AR OBJCOPY SIZE
|
||||
export CC CXX LD AR OBJCOPY OBJDUMP SIZE
|
||||
|
||||
COMPILER_VERSION_STR := $(shell $(CC) -dumpversion)
|
||||
COMPILER_VERSION_NUM := $(subst .,,$(COMPILER_VERSION_STR))
|
||||
|
@ -416,6 +419,18 @@ APP_ELF:=$(BUILD_DIR_BASE)/$(PROJECT_NAME).elf
|
|||
APP_MAP:=$(APP_ELF:.elf=.map)
|
||||
APP_BIN:=$(APP_ELF:.elf=.bin)
|
||||
|
||||
# once we know component paths, we can include the config generation targets
|
||||
#
|
||||
# (bootloader build doesn't need this, config is exported from top-level)
|
||||
ifndef IS_BOOTLOADER_BUILD
|
||||
include $(IDF_PATH)/make/project_config.mk
|
||||
endif
|
||||
|
||||
# include linker script generation utils makefile
|
||||
include $(IDF_PATH)/make/ldgen.mk
|
||||
|
||||
$(eval $(call ldgen_create_commands))
|
||||
|
||||
# Include any Makefile.projbuild file letting components add
|
||||
# configuration at the project level
|
||||
define includeProjBuildMakefile
|
||||
|
@ -427,13 +442,6 @@ $(foreach componentpath,$(COMPONENT_PATHS), \
|
|||
$(if $(wildcard $(componentpath)/Makefile.projbuild), \
|
||||
$(eval $(call includeProjBuildMakefile,$(componentpath)))))
|
||||
|
||||
# once we know component paths, we can include the config generation targets
|
||||
#
|
||||
# (bootloader build doesn't need this, config is exported from top-level)
|
||||
ifndef IS_BOOTLOADER_BUILD
|
||||
include $(IDF_PATH)/make/project_config.mk
|
||||
endif
|
||||
|
||||
# ELF depends on the library archive files for COMPONENT_LIBRARIES
|
||||
# the rules to build these are emitted as part of GenerateComponentTarget below
|
||||
#
|
||||
|
@ -538,7 +546,7 @@ endif
|
|||
# _config-clean), so config remains valid during all component clean
|
||||
# targets
|
||||
config-clean: app-clean bootloader-clean
|
||||
clean: app-clean bootloader-clean config-clean
|
||||
clean: app-clean bootloader-clean config-clean ldgen-clean
|
||||
|
||||
# phony target to check if any git submodule listed in COMPONENT_SUBMODULES are missing
|
||||
# or out of date, and exit if so. Components can add paths to this variable.
|
||||
|
|
|
@ -64,3 +64,6 @@ docs/gen-dxd.py
|
|||
tools/ci/multirun_with_pyenv.sh
|
||||
components/espcoredump/test/test_espcoredump.py
|
||||
components/espcoredump/test/test_espcoredump.sh
|
||||
tools/ldgen/ldgen.py
|
||||
tools/ldgen/test/test_fragments.py
|
||||
tools/ldgen/test/test_generation.py
|
|
@ -157,9 +157,16 @@ function run_tests()
|
|||
make
|
||||
assert_rebuilt ${APP_BINS} ${BOOTLOADER_BINS}
|
||||
|
||||
print_status "Touching app-only ld file should only re-link app"
|
||||
print_status "Touching app-only template ld file should only re-link app"
|
||||
take_build_snapshot
|
||||
touch ${IDF_PATH}/components/esp32/ld/esp32.common.ld
|
||||
touch ${IDF_PATH}/components/esp32/ld/esp32.common.ld.in
|
||||
make
|
||||
assert_rebuilt ${APP_BINS}
|
||||
assert_not_rebuilt ${BOOTLOADER_BINS}
|
||||
|
||||
print_status "Touching a linker fragment file should trigger re-link of app" # only app linker script is generated by tool for now
|
||||
take_build_snapshot
|
||||
touch ${IDF_PATH}/components/esp32/linker.lf
|
||||
make
|
||||
assert_rebuilt ${APP_BINS}
|
||||
assert_not_rebuilt ${BOOTLOADER_BINS}
|
||||
|
|
|
@ -143,13 +143,23 @@ function run_tests()
|
|||
|
||||
print_status "Updating app-only ld file should only re-link app"
|
||||
take_build_snapshot
|
||||
cp ${IDF_PATH}/components/esp32/ld/esp32.common.ld .
|
||||
cp ${IDF_PATH}/components/esp32/ld/esp32.common.ld.in .
|
||||
sleep 1 # ninja may ignore if the timestamp delta is too low
|
||||
echo "/* (Build test comment) */" >> ${IDF_PATH}/components/esp32/ld/esp32.common.ld
|
||||
echo "/* (Build test comment) */" >> ${IDF_PATH}/components/esp32/ld/esp32.common.ld.in
|
||||
idf.py build || failure "Failed to rebuild with modified linker script"
|
||||
assert_rebuilt ${APP_BINS}
|
||||
assert_not_rebuilt ${BOOTLOADER_BINS}
|
||||
mv esp32.common.ld ${IDF_PATH}/components/esp32/ld/
|
||||
mv esp32.common.ld.in ${IDF_PATH}/components/esp32/ld/
|
||||
|
||||
print_status "Updating fragment file should only re-link app" # only app linker script is generated by tool for now
|
||||
take_build_snapshot
|
||||
cp ${IDF_PATH}/components/esp32/ld/esp32_fragments.lf .
|
||||
sleep 1 # ninja may ignore if the timestamp delta is too low
|
||||
echo "# (Build test comment)" >> ${IDF_PATH}/components/esp32/ld/esp32_fragments.lf
|
||||
idf.py build || failure "Failed to rebuild with modified linker fragment file"
|
||||
assert_rebuilt ${APP_BINS}
|
||||
assert_not_rebuilt ${BOOTLOADER_BINS}
|
||||
mv esp32_fragments.lf ${IDF_PATH}/components/esp32/ld/
|
||||
|
||||
print_status "sdkconfig update triggers full recompile"
|
||||
clean_build_dir
|
||||
|
|
|
@ -107,6 +107,11 @@ function(register_component)
|
|||
target_link_libraries(${component} "-L${CMAKE_CURRENT_BINARY_DIR}")
|
||||
target_link_libraries(${component} "-Wl,--whole-archive -l${component} -Wl,--no-whole-archive")
|
||||
endif()
|
||||
|
||||
if(COMPONENT_ADD_LDFRAGMENTS)
|
||||
spaces2list(COMPONENT_ADD_LDFRAGMENTS)
|
||||
ldgen_add_fragment_files(${component} "${COMPONENT_ADD_LDFRAGMENTS}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(register_config_only_component)
|
||||
|
|
72
tools/cmake/ldgen.cmake
Normal file
72
tools/cmake/ldgen.cmake
Normal file
|
@ -0,0 +1,72 @@
|
|||
# Utilities for supporting linker script generation in the build system
|
||||
|
||||
# ldgen_create_target
|
||||
#
|
||||
# Create the custom target to attach the fragment files and template files
|
||||
# for the build to.
|
||||
function(ldgen_set_variables)
|
||||
add_custom_target(ldgen_section_infos)
|
||||
add_custom_target(ldgen DEPENDS ldgen_section_infos)
|
||||
endfunction()
|
||||
|
||||
# ldgen_add_fragment_file
|
||||
#
|
||||
# Add one or more linker fragment files, and append it to the list of fragment
|
||||
# files found so far.
|
||||
function(ldgen_add_fragment_files target fragment_files)
|
||||
spaces2list(fragment_files)
|
||||
|
||||
foreach(fragment_file ${fragment_files})
|
||||
get_filename_component(fragment_file_abs_dir ${fragment_file} ABSOLUTE BASE_DIR ${component_dir})
|
||||
list(APPEND fragment_files_full_path ${fragment_file_abs_dir})
|
||||
endforeach()
|
||||
|
||||
set_property(TARGET ldgen APPEND PROPERTY FRAGMENT_FILES ${fragment_files_full_path})
|
||||
|
||||
get_filename_component(target_sections_info ${CMAKE_CURRENT_BINARY_DIR}/${target}.sections_info ABSOLUTE)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${target_sections_info}
|
||||
COMMAND ${CMAKE_OBJDUMP} $<TARGET_FILE:${target}> -h > ${target_sections_info}
|
||||
DEPENDS ${target}
|
||||
)
|
||||
|
||||
add_custom_target(${target}_sections_info DEPENDS ${target_sections_info})
|
||||
add_dependencies(ldgen_section_infos ${target}_sections_info)
|
||||
|
||||
set_property(TARGET ldgen_section_infos APPEND PROPERTY SECTIONS_INFO_FILES ${target_sections_info})
|
||||
endfunction()
|
||||
|
||||
# ldgen_process_template
|
||||
#
|
||||
# Passes a linker script template to the linker script generation tool for
|
||||
# processing
|
||||
function(ldgen_process_template template output)
|
||||
# Create command to invoke the linker script generator tool.
|
||||
add_custom_command(
|
||||
OUTPUT ${output}
|
||||
COMMAND ${IDF_PATH}/tools/ldgen/ldgen.py
|
||||
--config ${SDKCONFIG}
|
||||
--fragments "$<JOIN:$<TARGET_PROPERTY:ldgen,FRAGMENT_FILES>,\t>"
|
||||
--input ${template}
|
||||
--output ${output}
|
||||
--sections "$<JOIN:$<TARGET_PROPERTY:ldgen_section_infos,SECTIONS_INFO_FILES>,\t>"
|
||||
--kconfig ${IDF_PATH}/Kconfig
|
||||
--env "COMPONENT_KCONFIGS=${COMPONENT_KCONFIGS}"
|
||||
--env "COMPONENT_KCONFIGS_PROJBUILD=${COMPONENT_KCONFIGS_PROJBUILD}"
|
||||
--env "IDF_CMAKE=y"
|
||||
--env "IDF_TARGET=${IDF_TARGET}"
|
||||
DEPENDS ${template} $<TARGET_PROPERTY:ldgen,FRAGMENT_FILES> ${SDKCONFIG} ldgen_section_infos
|
||||
)
|
||||
|
||||
get_filename_component(output_name ${output} NAME)
|
||||
add_custom_target(ldgen_${output_name}_script DEPENDS ${output})
|
||||
add_dependencies(ldgen ldgen_${output_name}_script)
|
||||
endfunction()
|
||||
|
||||
# ldgen_create_commands
|
||||
#
|
||||
# Create the command to generate the output scripts from templates presented.
|
||||
function(ldgen_add_dependencies executable_name)
|
||||
add_dependencies(${executable_name} ldgen)
|
||||
endfunction()
|
|
@ -27,6 +27,7 @@ include(targets)
|
|||
include(kconfig)
|
||||
include(git_submodules)
|
||||
include(idf_functions)
|
||||
include(ldgen)
|
||||
|
||||
set_default(PYTHON "python")
|
||||
|
||||
|
@ -131,6 +132,16 @@ macro(project name)
|
|||
## if project uses git, retrieve revision
|
||||
git_describe(PROJECT_VER "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
#
|
||||
# Add the app executable to the build (has name of PROJECT.elf)
|
||||
#
|
||||
idf_add_executable()
|
||||
|
||||
#
|
||||
# Setup variables for linker script generation
|
||||
#
|
||||
ldgen_set_variables()
|
||||
|
||||
# Include any top-level project_include.cmake files from components
|
||||
foreach(component ${BUILD_COMPONENT_PATHS})
|
||||
set(COMPONENT_PATH "${component}")
|
||||
|
@ -156,10 +167,9 @@ macro(project name)
|
|||
unset(COMPONENT_NAME)
|
||||
unset(COMPONENT_PATH)
|
||||
|
||||
#
|
||||
# Add the app executable to the build (has name of PROJECT.elf)
|
||||
#
|
||||
idf_add_executable()
|
||||
# At this point the fragment files have been collected, generate
|
||||
# the commands needed to generate the output linker scripts
|
||||
ldgen_add_dependencies(${PROJECT_NAME}.elf)
|
||||
|
||||
# Write project description JSON file
|
||||
make_json_list("${BUILD_COMPONENTS}" build_components_json)
|
||||
|
|
|
@ -85,6 +85,9 @@ function(require_idf_targets)
|
|||
endif()
|
||||
endfunction()
|
||||
|
||||
# Dummy call for ldgen_add_fragment_file
|
||||
function(ldgen_add_fragment_file files)
|
||||
endfunction()
|
||||
|
||||
# expand_component_requirements: Recursively expand a component's requirements,
|
||||
# setting global properties BUILD_COMPONENTS & BUILD_COMPONENT_PATHS and
|
||||
|
|
0
tools/ldgen/__init__.py
Normal file
0
tools/ldgen/__init__.py
Normal file
249
tools/ldgen/fragments.py
Normal file
249
tools/ldgen/fragments.py
Normal file
|
@ -0,0 +1,249 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import re
|
||||
import collections
|
||||
import sys
|
||||
import os
|
||||
|
||||
from sdkconfig import SDKConfig
|
||||
from pyparsing import *
|
||||
|
||||
"""
|
||||
Fragment file internal representation. Parses and stores instances of the fragment definitions
|
||||
contained within the file.
|
||||
"""
|
||||
class FragmentFileModel():
|
||||
|
||||
def __init__(self, fragment_file):
|
||||
path = os.path.realpath(fragment_file.name)
|
||||
|
||||
sections = Sections.get_fragment_grammar()
|
||||
scheme = Scheme.get_fragment_grammar()
|
||||
mapping = Mapping.get_fragment_grammar()
|
||||
|
||||
# Each fragment file is composed of sections, scheme or mapping fragments. The grammar
|
||||
# for each of those objects are defined it the respective classes
|
||||
parser = OneOrMore(sections | scheme | mapping)
|
||||
|
||||
# Set any text beginnning with # as comment
|
||||
parser.ignore("#" + restOfLine)
|
||||
|
||||
self.fragments = parser.parseFile(fragment_file, parseAll=True)
|
||||
|
||||
for fragment in self.fragments:
|
||||
fragment.path = path
|
||||
|
||||
"""
|
||||
Encapsulates a fragment as defined in the generator syntax. Sets values common to all fragment and performs processing
|
||||
such as checking the validity of the fragment name and getting the entry values.
|
||||
"""
|
||||
class Fragment:
|
||||
|
||||
IDENTIFIER = Word(alphas+"_", alphanums+"_")
|
||||
ENTITY = Word(alphanums + ".-_$")
|
||||
|
||||
def __init__(self, name, entries):
|
||||
self.path = None
|
||||
self.name = name
|
||||
self.entries = entries
|
||||
|
||||
class Sections(Fragment):
|
||||
|
||||
def __init__(self, name, entries):
|
||||
Fragment.__init__(self, name, entries)
|
||||
self._process_entries()
|
||||
|
||||
def _process_entries(self):
|
||||
# Quietly ignore duplicate entries
|
||||
self.entries = set(self.entries)
|
||||
self.entries = list(self.entries)
|
||||
|
||||
"""
|
||||
Utility function that returns a list of sections given a sections fragment entry,
|
||||
with the '+' notation and symbol concatenation handled automatically.
|
||||
"""
|
||||
@staticmethod
|
||||
def get_section_data_from_entry(sections_entry, symbol=None):
|
||||
if not symbol:
|
||||
sections = list()
|
||||
sections.append(sections_entry.replace("+", ""))
|
||||
sections.append(sections_entry.replace("+", ".*"))
|
||||
return sections
|
||||
else:
|
||||
if sections_entry.endswith("+"):
|
||||
section = sections_entry.replace("+", ".*")
|
||||
expansion = section.replace(".*", "." + symbol)
|
||||
return (section, expansion)
|
||||
else:
|
||||
return (sections_entry, None)
|
||||
|
||||
@staticmethod
|
||||
def get_fragment_grammar():
|
||||
name = Fragment.IDENTIFIER
|
||||
header = Suppress("[") + Suppress("sections") + Suppress(":") + name.setResultsName("name") + Suppress("]")
|
||||
entry = Word(alphanums + "+" + ".")
|
||||
entries = Suppress("entries") + Suppress(":") + Group(OneOrMore(entry)).setResultsName("entries")
|
||||
|
||||
sections = Group(header + entries)
|
||||
|
||||
sections.setParseAction(lambda t: Sections(t[0].name, t[0].entries))
|
||||
|
||||
sections.ignore("#" + restOfLine)
|
||||
|
||||
return sections
|
||||
|
||||
"""
|
||||
Encapsulates a scheme fragment, which defines what target input sections are placed under.
|
||||
"""
|
||||
class Scheme(Fragment):
|
||||
|
||||
def __init__(self, name, items):
|
||||
Fragment.__init__(self, name, items)
|
||||
self._process_entries()
|
||||
|
||||
def _process_entries(self):
|
||||
processed = set()
|
||||
|
||||
# Store entries as a set of tuples. Quietly ignores duplicate entries.
|
||||
for entry in self.entries:
|
||||
processed.add((entry.sections, entry.target))
|
||||
|
||||
self.entries = processed
|
||||
|
||||
@staticmethod
|
||||
def get_fragment_grammar():
|
||||
name = Fragment.IDENTIFIER
|
||||
header = Suppress("[") + Suppress("scheme") + Suppress(":") + name.setResultsName("name") + Suppress("]")
|
||||
|
||||
# Scheme entry in the form 'sections -> target'
|
||||
sections = Fragment.IDENTIFIER
|
||||
target = Fragment.IDENTIFIER
|
||||
entry = Group(sections.setResultsName("sections") + Suppress("->") + target.setResultsName("target"))
|
||||
|
||||
entries = Suppress("entries") + Suppress(":") + Group(OneOrMore(entry)).setResultsName("entries")
|
||||
|
||||
scheme = Group(header + entries)
|
||||
|
||||
scheme.setParseAction(lambda t: Scheme(t[0].name, t[0].entries))
|
||||
|
||||
scheme.ignore("#" + restOfLine)
|
||||
|
||||
return scheme
|
||||
|
||||
"""
|
||||
Encapsulates a mapping fragment, which defines what targets the input sections of mappable entties are placed under.
|
||||
"""
|
||||
class Mapping(Fragment):
|
||||
|
||||
# Name of the default condition entry
|
||||
DEFAULT_CONDITION = "default"
|
||||
MAPPING_ALL_OBJECTS = "*"
|
||||
|
||||
def __init__(self, archive, entries):
|
||||
self.archive = archive
|
||||
|
||||
# Generate name from archive value by replacing all non-alphanumeric
|
||||
# characters with underscore
|
||||
name = Mapping.get_mapping_name_from_archive(self.archive)
|
||||
Fragment.__init__(self, name, entries)
|
||||
|
||||
self._process_entries()
|
||||
|
||||
def _create_mappings_set(self, mappings):
|
||||
mapping_set = set()
|
||||
|
||||
for mapping in mappings:
|
||||
obj = mapping.object
|
||||
symbol = mapping.symbol
|
||||
scheme = mapping.scheme
|
||||
|
||||
if symbol == "":
|
||||
symbol = None
|
||||
|
||||
# Quietly handle duplicate definitions under the same condition
|
||||
mapping_set.add((obj, symbol, scheme))
|
||||
|
||||
return mapping_set
|
||||
|
||||
def _process_entries(self):
|
||||
processed = []
|
||||
|
||||
for normal_group in self.entries.normal_groups:
|
||||
# Get the original string of the condition
|
||||
condition = next(iter(normal_group.condition.asList())).strip()
|
||||
mappings = self._create_mappings_set(normal_group.mappings)
|
||||
|
||||
processed.append((condition, mappings))
|
||||
|
||||
default_group = self.entries.default_group
|
||||
mappings = self._create_mappings_set(default_group.mappings)
|
||||
processed.append(("default", mappings))
|
||||
|
||||
self.entries = processed
|
||||
|
||||
@staticmethod
|
||||
def get_mapping_name_from_archive(archive):
|
||||
return re.sub(r"[^0-9a-zA-Z]+", "_", archive)
|
||||
|
||||
@staticmethod
|
||||
def get_fragment_grammar():
|
||||
|
||||
# Match header [mapping]
|
||||
header = Suppress("[") + Suppress("mapping") + Suppress("]")
|
||||
|
||||
# Define possbile values for input archive and object file
|
||||
filename = Word(alphanums + "-" + "_")
|
||||
|
||||
# There are three possible patterns for mapping entries:
|
||||
# obj:symbol (scheme)
|
||||
# obj (scheme)
|
||||
# * (scheme)
|
||||
obj = Fragment.ENTITY.setResultsName("object")
|
||||
symbol = Suppress(":") + Fragment.IDENTIFIER.setResultsName("symbol")
|
||||
scheme = Suppress("(") + Fragment.IDENTIFIER.setResultsName("scheme") + Suppress(")")
|
||||
|
||||
pattern1 = Group(obj + symbol + scheme)
|
||||
pattern2 = Group(obj + scheme)
|
||||
pattern3 = Group(Literal(Mapping.MAPPING_ALL_OBJECTS).setResultsName("object") + scheme)
|
||||
|
||||
mapping_entry = pattern1 | pattern2 | pattern3
|
||||
|
||||
# To simplify parsing, classify groups of condition-mapping entry into two types: normal and default
|
||||
# A normal grouping is one with a non-default condition. The default grouping is one which contains the
|
||||
# default condition
|
||||
mapping_entries = Group(ZeroOrMore(mapping_entry)).setResultsName("mappings")
|
||||
|
||||
normal_condition = Suppress(":") + originalTextFor(SDKConfig.get_expression_grammar())
|
||||
default_condition = Optional(Suppress(":") + Literal(Mapping.DEFAULT_CONDITION))
|
||||
|
||||
normal_group = Group(normal_condition.setResultsName("condition") + mapping_entries)
|
||||
default_group = Group(default_condition + mapping_entries).setResultsName("default_group")
|
||||
|
||||
normal_groups = Group(ZeroOrMore(normal_group)).setResultsName("normal_groups")
|
||||
|
||||
# Any mapping fragment definition can have zero or more normal group and only one default group as a last entry.
|
||||
archive = Suppress("archive") + Suppress(":") + Fragment.ENTITY.setResultsName("archive")
|
||||
entries = Suppress("entries") + Suppress(":") + (normal_groups + default_group).setResultsName("entries")
|
||||
|
||||
mapping = Group(header + archive + entries)
|
||||
|
||||
mapping.setParseAction(lambda t: Mapping(t[0].archive, t[0].entries))
|
||||
|
||||
mapping.ignore("#" + restOfLine)
|
||||
|
||||
return mapping
|
643
tools/ldgen/generation.py
Normal file
643
tools/ldgen/generation.py
Normal file
|
@ -0,0 +1,643 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import re
|
||||
import collections
|
||||
import itertools
|
||||
import os
|
||||
import subprocess
|
||||
import fnmatch
|
||||
|
||||
from sdkconfig import SDKConfig
|
||||
from fragments import FragmentFileModel, Sections, Scheme, Mapping, Fragment
|
||||
from pyparsing import *
|
||||
|
||||
"""
|
||||
Encapsulates a generated placement rule placed under a target
|
||||
"""
|
||||
class PlacementRule():
|
||||
|
||||
DEFAULT_SPECIFICITY = 0
|
||||
ARCHIVE_SPECIFICITY = 1
|
||||
OBJECT_SPECIFICITY = 2
|
||||
SYMBOL_SPECIFICITY = 3
|
||||
|
||||
class __container():
|
||||
def __init__(self, content):
|
||||
self.content = content
|
||||
|
||||
__metadata = collections.namedtuple("__metadata", "excludes expansions expanded")
|
||||
|
||||
def __init__(self, archive, obj, symbol, sections, target):
|
||||
if archive == "*":
|
||||
archive = None
|
||||
|
||||
if obj == "*":
|
||||
obj = None
|
||||
|
||||
self.archive = archive
|
||||
self.obj = obj
|
||||
self.symbol = symbol
|
||||
self.target = target
|
||||
self.sections = dict()
|
||||
|
||||
self.specificity = 0
|
||||
self.specificity += 1 if self.archive else 0
|
||||
self.specificity += 1 if (self.obj and not self.obj == '*') else 0
|
||||
self.specificity += 1 if self.symbol else 0
|
||||
|
||||
for section in sections:
|
||||
section_data = Sections.get_section_data_from_entry(section, self.symbol)
|
||||
|
||||
if not self.symbol:
|
||||
for s in section_data:
|
||||
metadata = self.__metadata(self.__container([]), self.__container([]), self.__container(False))
|
||||
self.sections[s] = metadata
|
||||
else:
|
||||
(section, expansion) = section_data
|
||||
if expansion:
|
||||
metadata = self.__metadata(self.__container([]), self.__container([expansion]), self.__container(True))
|
||||
self.sections[section] = metadata
|
||||
|
||||
def get_section_names(self):
|
||||
return self.sections.keys()
|
||||
|
||||
def add_exclusion(self, other, sections_infos = None):
|
||||
# Utility functions for this method
|
||||
def do_section_expansion(rule, section):
|
||||
if section in rule.get_section_names():
|
||||
sections_in_obj = sections_infos.get_obj_sections(rule.archive, rule.obj)
|
||||
|
||||
expansions = fnmatch.filter(sections_in_obj, section)
|
||||
return expansions
|
||||
|
||||
def remove_section_expansions(rule, section, expansions):
|
||||
existing_expansions = self.sections[section].expansions.content
|
||||
self.sections[section].expansions.content = [e for e in existing_expansions if e not in expansions]
|
||||
|
||||
# Exit immediately if the exclusion to be added is more general than this rule.
|
||||
if not other.is_more_specific_rule_of(self):
|
||||
return
|
||||
|
||||
for section in self.get_sections_intersection(other):
|
||||
if(other.specificity == PlacementRule.SYMBOL_SPECIFICITY):
|
||||
# If this sections has not been expanded previously, expand now and keep track.
|
||||
previously_expanded = self.sections[section].expanded.content
|
||||
if not previously_expanded:
|
||||
expansions = do_section_expansion(self, section)
|
||||
if expansions:
|
||||
self.sections[section].expansions.content = expansions
|
||||
self.sections[section].expanded.content = True
|
||||
previously_expanded = True
|
||||
|
||||
# Remove the sections corresponding to the symbol name
|
||||
remove_section_expansions(self, section, other.sections[section].expansions.content)
|
||||
|
||||
# If it has been expanded previously but now the expansions list is empty,
|
||||
# it means adding exclusions has exhausted the list. Remove the section entirely.
|
||||
if previously_expanded and not self.sections[section].expanded.content:
|
||||
del self.sections[section]
|
||||
else:
|
||||
# A rule section can have multiple rule sections excluded from it. Get the
|
||||
# most specific rule from the list, and if an even more specific rule is found,
|
||||
# replace it entirely. Otherwise, keep appending.
|
||||
exclusions = self.sections[section].excludes
|
||||
exclusions_list = exclusions.content if exclusions.content != None else []
|
||||
exclusions_to_remove = filter(lambda r: r.is_more_specific_rule_of(other), exclusions_list)
|
||||
|
||||
remaining_exclusions = [e for e in exclusions_list if e not in exclusions_to_remove]
|
||||
remaining_exclusions.append(other)
|
||||
|
||||
self.sections[section].excludes.content = remaining_exclusions
|
||||
|
||||
def get_sections_intersection(self, other):
|
||||
return set(self.sections.keys()).intersection(set(other.sections.keys()))
|
||||
|
||||
def is_more_specific_rule_of(self, other):
|
||||
if (self.specificity <= other.specificity):
|
||||
return False
|
||||
|
||||
# Compare archive, obj and target
|
||||
for entity_index in range (1, other.specificity + 1):
|
||||
if self[entity_index] != other[entity_index] and other[entity_index] != None:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def maps_same_entities_as(self, other):
|
||||
if self.specificity != other.specificity:
|
||||
return False
|
||||
|
||||
# Compare archive, obj and target
|
||||
for entity_index in range (1, other.specificity + 1):
|
||||
if self[entity_index] != other[entity_index] and other[entity_index] != None:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key == PlacementRule.ARCHIVE_SPECIFICITY:
|
||||
return self.archive
|
||||
elif key == PlacementRule.OBJECT_SPECIFICITY:
|
||||
return self.obj
|
||||
elif key == PlacementRule.SYMBOL_SPECIFICITY:
|
||||
return self.symbol
|
||||
else:
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
sorted_sections = sorted(self.get_section_names())
|
||||
|
||||
sections_string = list()
|
||||
|
||||
for section in sorted_sections:
|
||||
exclusions = self.sections[section].excludes.content
|
||||
|
||||
exclusion_string = None
|
||||
|
||||
if exclusions:
|
||||
exclusion_string = " ".join(map(lambda e: "*" + e.archive + (":" + e.obj + ".*" if e.obj else ""), exclusions))
|
||||
exclusion_string = "EXCLUDE_FILE(" + exclusion_string + ")"
|
||||
else:
|
||||
exclusion_string = ""
|
||||
|
||||
section_string = None
|
||||
exclusion_section_string = None
|
||||
|
||||
section_expansions = self.sections[section].expansions.content
|
||||
section_expanded = self.sections[section].expanded.content
|
||||
|
||||
if section_expansions and section_expanded:
|
||||
section_string = " ".join(section_expansions)
|
||||
exclusion_section_string = section_string
|
||||
else:
|
||||
section_string = section
|
||||
exclusion_section_string = exclusion_string + " " + section_string
|
||||
|
||||
sections_string.append(exclusion_section_string)
|
||||
|
||||
sections_string = " ".join(sections_string)
|
||||
|
||||
archive = str(self.archive) if self.archive else ""
|
||||
obj = (str(self.obj) + (".*" if self.obj else "")) if self.obj else ""
|
||||
|
||||
# Handle output string generation based on information available
|
||||
if self.specificity == PlacementRule.DEFAULT_SPECIFICITY:
|
||||
rule_string = "*(%s)" % (sections_string)
|
||||
elif self.specificity == PlacementRule.ARCHIVE_SPECIFICITY:
|
||||
rule_string = "*%s:(%s)" % (archive, sections_string)
|
||||
else:
|
||||
rule_string = "*%s:%s(%s)" % (archive, obj, sections_string)
|
||||
|
||||
return rule_string
|
||||
|
||||
def __eq__(self, other):
|
||||
if id(self) == id(other):
|
||||
return True
|
||||
|
||||
def exclusions_set(exclusions):
|
||||
exclusions_set = {(e.archive, e.obj, e.symbol, e.target) for e in exclusions}
|
||||
return exclusions_set
|
||||
|
||||
if self.archive != other.archive:
|
||||
return False
|
||||
|
||||
if self.obj != other.obj:
|
||||
return False
|
||||
|
||||
if self.symbol != other.symbol:
|
||||
return False
|
||||
|
||||
if set(self.sections.keys()) != set(other.sections.keys()):
|
||||
return False
|
||||
|
||||
for (section, metadata) in self.sections.items():
|
||||
|
||||
self_meta = metadata
|
||||
other_meta = other.sections[section]
|
||||
|
||||
if exclusions_set(self_meta.excludes.content) != exclusions_set(other_meta.excludes.content):
|
||||
return False
|
||||
|
||||
if set(self_meta.expansions.content) != set(other_meta.expansions.content):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
|
||||
def __iter__(self):
|
||||
yield self.archive
|
||||
yield self.obj
|
||||
yield self.symbol
|
||||
raise StopIteration
|
||||
|
||||
"""
|
||||
Implements generation of placement rules based on collected sections, scheme and mapping fragment.
|
||||
"""
|
||||
class GenerationModel:
|
||||
|
||||
DEFAULT_SCHEME = "default"
|
||||
|
||||
def __init__(self):
|
||||
self.schemes = {}
|
||||
self.sections = {}
|
||||
self.mappings = {}
|
||||
|
||||
def _add_mapping_rules(self, archive, obj, symbol, scheme_name, scheme_dict, rules):
|
||||
# Use an ordinary dictionary to raise exception on non-existing keys
|
||||
temp_dict = dict(scheme_dict)
|
||||
|
||||
sections_bucket = temp_dict[scheme_name]
|
||||
|
||||
for (target, sections) in sections_bucket.items():
|
||||
section_entries = []
|
||||
|
||||
for section in sections:
|
||||
section_entries.extend(section.entries)
|
||||
|
||||
rule = PlacementRule(archive, obj, symbol, section_entries, target)
|
||||
|
||||
if not rule in rules:
|
||||
rules.append(rule)
|
||||
|
||||
def _build_scheme_dictionary(self):
|
||||
scheme_dictionary = collections.defaultdict(dict)
|
||||
|
||||
# Collect sections into buckets based on target name
|
||||
for scheme in self.schemes.values():
|
||||
sections_bucket = collections.defaultdict(list)
|
||||
|
||||
for (sections_name, target_name) in scheme.entries:
|
||||
# Get the sections under the bucket 'target_name'. If this bucket does not exist
|
||||
# is is created automatically
|
||||
sections_in_bucket = sections_bucket[target_name]
|
||||
|
||||
try:
|
||||
sections = self.sections[sections_name]
|
||||
except KeyError:
|
||||
message = GenerationException.UNDEFINED_REFERENCE + " to sections '" + sections + "'."
|
||||
raise GenerationException(message, scheme)
|
||||
|
||||
sections_in_bucket.append(sections)
|
||||
|
||||
scheme_dictionary[scheme.name] = sections_bucket
|
||||
|
||||
# Search for and raise exception on first instance of sections mapped to multiple targets
|
||||
for (scheme_name, sections_bucket) in scheme_dictionary.items():
|
||||
for sections_a, sections_b in itertools.combinations(sections_bucket.values(), 2):
|
||||
set_a = set()
|
||||
set_b = set()
|
||||
|
||||
for sections in sections_a:
|
||||
set_a.update(sections.entries)
|
||||
|
||||
for sections in sections_b:
|
||||
set_b.update(sections.entries)
|
||||
|
||||
intersection = set_a.intersection(set_b)
|
||||
|
||||
# If the intersection is a non-empty set, it means sections are mapped to multiple
|
||||
# targets. Raise exception.
|
||||
if intersection:
|
||||
scheme = self.schemes[scheme_name]
|
||||
message = "Sections " + str(intersection) + " mapped to multiple targets."
|
||||
raise GenerationException(message, scheme)
|
||||
|
||||
return scheme_dictionary
|
||||
|
||||
def generate_rules(self, sdkconfig, sections_infos):
|
||||
placement_rules = collections.defaultdict(list)
|
||||
|
||||
scheme_dictionary = self._build_scheme_dictionary()
|
||||
|
||||
# Generate default rules
|
||||
default_rules = list()
|
||||
self._add_mapping_rules(None, None, None, GenerationModel.DEFAULT_SCHEME, scheme_dictionary, default_rules)
|
||||
|
||||
all_mapping_rules = collections.defaultdict(list)
|
||||
|
||||
# Generate rules based on mapping fragments
|
||||
for mapping in self.mappings.values():
|
||||
for (condition, entries) in mapping.entries:
|
||||
condition_true = False
|
||||
|
||||
# Only non-default condition are evaluated agains sdkconfig model
|
||||
if condition != Mapping.DEFAULT_CONDITION:
|
||||
try:
|
||||
condition_true = sdkconfig.evaluate_expression(condition)
|
||||
except Exception as e:
|
||||
raise GenerationException(e.message, mapping)
|
||||
else:
|
||||
condition_true = True
|
||||
|
||||
if condition_true:
|
||||
|
||||
mapping_rules = list()
|
||||
|
||||
archive = mapping.archive
|
||||
for (obj, symbol, scheme_name) in entries:
|
||||
try:
|
||||
self._add_mapping_rules(archive, obj, symbol, scheme_name, scheme_dictionary, mapping_rules)
|
||||
except KeyError:
|
||||
message = GenerationException.UNDEFINED_REFERENCE + " to scheme '" + scheme_name + "'."
|
||||
raise GenerationException(message, mapping)
|
||||
|
||||
all_mapping_rules[mapping.name] = mapping_rules
|
||||
|
||||
break # Exit on first condition that evaluates to true
|
||||
|
||||
# Detect rule conflicts
|
||||
for mapping_rules in all_mapping_rules.items():
|
||||
self._detect_conflicts(mapping_rules)
|
||||
|
||||
# Add exclusions
|
||||
for mapping_rules in all_mapping_rules.values():
|
||||
self._create_exclusions(mapping_rules, default_rules, sections_infos)
|
||||
|
||||
# Add the default rules grouped by target
|
||||
for default_rule in default_rules:
|
||||
existing_rules = placement_rules[default_rule.target]
|
||||
if default_rule.get_section_names():
|
||||
existing_rules.append(default_rule)
|
||||
|
||||
for mapping_rules in all_mapping_rules.values():
|
||||
# Add the mapping rules grouped by target
|
||||
for mapping_rule in mapping_rules:
|
||||
existing_rules = placement_rules[mapping_rule.target]
|
||||
if mapping_rule.get_section_names():
|
||||
existing_rules.append(mapping_rule)
|
||||
|
||||
return placement_rules
|
||||
|
||||
def _detect_conflicts(self, rules):
|
||||
(archive, rules_list) = rules
|
||||
|
||||
for specificity in range(0, PlacementRule.OBJECT_SPECIFICITY + 1):
|
||||
rules_with_specificity = filter(lambda r: r.specificity == specificity, rules_list)
|
||||
|
||||
for rule_a, rule_b in itertools.combinations(rules_with_specificity, 2):
|
||||
intersections = rule_a.get_sections_intersection(rule_b)
|
||||
|
||||
if intersections and rule_a.maps_same_entities_as(rule_b):
|
||||
rules_string = str([str(rule_a), str(rule_b)])
|
||||
message = "Rules " + rules_string + " map sections " + str(list(intersections)) + " into multiple targets."
|
||||
mapping = self.mappings[Mapping.get_mapping_name_from_archive(archive)]
|
||||
raise GenerationException(message, mapping)
|
||||
|
||||
def _create_extra_rules(self, rules):
|
||||
# This function generates extra rules for symbol specific rules. The reason for generating extra rules is to isolate,
|
||||
# as much as possible, rules that require expansion. Particularly, object specific extra rules are generated.
|
||||
rules_to_process = sorted(rules, key = lambda r: r.specificity)
|
||||
symbol_specific_rules = list(filter(lambda r: r.specificity == PlacementRule.SYMBOL_SPECIFICITY, rules_to_process))
|
||||
|
||||
extra_rules = dict()
|
||||
|
||||
for symbol_specific_rule in symbol_specific_rules:
|
||||
extra_rule_candidate = {s: None for s in symbol_specific_rule.get_section_names()}
|
||||
|
||||
super_rules = filter(lambda r: symbol_specific_rule.is_more_specific_rule_of(r), rules_to_process)
|
||||
|
||||
# Take a look at the existing rules that are more general than the current symbol-specific rule.
|
||||
# Only generate an extra rule if there is no existing object specific rule for that section
|
||||
for super_rule in super_rules:
|
||||
intersections = symbol_specific_rule.get_sections_intersection(super_rule)
|
||||
for intersection in intersections:
|
||||
if super_rule.specificity != PlacementRule.OBJECT_SPECIFICITY:
|
||||
extra_rule_candidate[intersection] = super_rule
|
||||
else:
|
||||
extra_rule_candidate[intersection] = None
|
||||
|
||||
# Generate the extra rules for the symbol specific rule section, keeping track of the generated extra rules
|
||||
for (section, section_rule) in extra_rule_candidate.items():
|
||||
if section_rule:
|
||||
extra_rule = None
|
||||
extra_rules_key = (symbol_specific_rule.archive, symbol_specific_rule.obj, section_rule.target)
|
||||
|
||||
try:
|
||||
extra_rule = extra_rules[extra_rules_key]
|
||||
|
||||
if section not in extra_rule.get_section_names():
|
||||
new_rule = PlacementRule(extra_rule.archive, extra_rule.obj, extra_rule.symbol, list(extra_rule.get_section_names()) + [section] , extra_rule.target)
|
||||
extra_rules[extra_rules_key] = new_rule
|
||||
except KeyError:
|
||||
extra_rule = PlacementRule(symbol_specific_rule.archive, symbol_specific_rule.obj, None, [section], section_rule.target)
|
||||
extra_rules[extra_rules_key] = extra_rule
|
||||
|
||||
return extra_rules.values()
|
||||
|
||||
def _create_exclusions(self, mapping_rules, default_rules, sections_info):
|
||||
rules = list(default_rules)
|
||||
rules.extend(mapping_rules)
|
||||
|
||||
extra_rules = self._create_extra_rules(rules)
|
||||
|
||||
mapping_rules.extend(extra_rules)
|
||||
rules.extend(extra_rules)
|
||||
|
||||
# Sort the rules by means of how specific they are. Sort by specificity from lowest to highest
|
||||
# * -> lib:* -> lib:obj -> lib:obj:symbol
|
||||
sorted_rules = sorted(rules, key = lambda r: r.specificity)
|
||||
|
||||
# Now that the rules have been sorted, loop through each rule, and then loop
|
||||
# through rules below it (higher indeces), adding exclusions whenever appropriate.
|
||||
for general_rule in sorted_rules:
|
||||
for specific_rule in reversed(sorted_rules):
|
||||
if (specific_rule.specificity > general_rule.specificity and \
|
||||
specific_rule.specificity != PlacementRule.SYMBOL_SPECIFICITY) or \
|
||||
(specific_rule.specificity == PlacementRule.SYMBOL_SPECIFICITY and \
|
||||
general_rule.specificity == PlacementRule.OBJECT_SPECIFICITY):
|
||||
general_rule.add_exclusion(specific_rule, sections_info)
|
||||
|
||||
def add_fragments_from_file(self, fragment_file):
|
||||
for fragment in fragment_file.fragments:
|
||||
dict_to_append_to = None
|
||||
|
||||
if isinstance(fragment, Scheme):
|
||||
dict_to_append_to = self.schemes
|
||||
elif isinstance(fragment, Sections):
|
||||
dict_to_append_to = self.sections
|
||||
else:
|
||||
dict_to_append_to = self.mappings
|
||||
|
||||
# Raise exception when the fragment of the same type is already in the stored fragments
|
||||
if fragment.name in dict_to_append_to.keys():
|
||||
stored = dict_to_append_to[fragment.name].path
|
||||
new = fragment.path
|
||||
message = "Duplicate definition of fragment '%s' found in %s and %s." % (fragment.name, stored, new)
|
||||
raise GenerationException(message)
|
||||
|
||||
dict_to_append_to[fragment.name] = fragment
|
||||
|
||||
"""
|
||||
Encapsulates a linker script template file. Finds marker syntax and handles replacement to generate the
|
||||
final output.
|
||||
"""
|
||||
class TemplateModel:
|
||||
|
||||
Marker = collections.namedtuple("Marker", "target indent rules")
|
||||
|
||||
def __init__(self, template_file):
|
||||
self.members = []
|
||||
self.file = os.path.realpath(template_file.name)
|
||||
|
||||
self._generate_members(template_file)
|
||||
|
||||
def _generate_members(self, template_file):
|
||||
lines = template_file.readlines()
|
||||
|
||||
target = Fragment.IDENTIFIER
|
||||
reference = Suppress("mapping") + Suppress("[") + target.setResultsName("target") + Suppress("]")
|
||||
pattern = White(" \t").setResultsName("indent") + reference
|
||||
|
||||
# Find the markers in the template file line by line. If line does not match marker grammar,
|
||||
# set it as a literal to be copied as is to the output file.
|
||||
for line in lines:
|
||||
try:
|
||||
parsed = pattern.parseString(line)
|
||||
|
||||
indent = parsed.indent
|
||||
target = parsed.target
|
||||
|
||||
marker = TemplateModel.Marker(target, indent, [])
|
||||
|
||||
self.members.append(marker)
|
||||
except ParseException:
|
||||
# Does not match marker syntax
|
||||
self.members.append(line)
|
||||
|
||||
def fill(self, mapping_rules, sdkconfig):
|
||||
for member in self.members:
|
||||
target = None
|
||||
try:
|
||||
target = member.target
|
||||
indent = member.indent
|
||||
rules = member.rules
|
||||
|
||||
del rules[:]
|
||||
|
||||
rules.extend(mapping_rules[target])
|
||||
except KeyError:
|
||||
message = GenerationException.UNDEFINED_REFERENCE + " to target '" + target + "'."
|
||||
raise GenerationException(message)
|
||||
except AttributeError as a:
|
||||
pass
|
||||
|
||||
def write(self, output_file):
|
||||
# Add information that this is a generated file.
|
||||
output_file.write("/* Automatically generated file; DO NOT EDIT */\n")
|
||||
output_file.write("/* Espressif IoT Development Framework Linker Script */\n")
|
||||
output_file.write("/* Generated from: %s */\n" % self.file)
|
||||
output_file.write("\n")
|
||||
|
||||
# Do the text replacement
|
||||
for member in self.members:
|
||||
try:
|
||||
indent = member.indent
|
||||
rules = member.rules
|
||||
|
||||
for rule in rules:
|
||||
generated_line = "".join([indent, str(rule), '\n'])
|
||||
output_file.write(generated_line)
|
||||
except AttributeError:
|
||||
output_file.write(member)
|
||||
|
||||
"""
|
||||
Exception for linker script generation failures such as undefined references/ failure to
|
||||
evaluate conditions, duplicate mappings, etc.
|
||||
"""
|
||||
class GenerationException(Exception):
|
||||
|
||||
UNDEFINED_REFERENCE = "Undefined reference"
|
||||
|
||||
def __init__(self, message, fragment=None):
|
||||
self.fragment = fragment
|
||||
self.message = message
|
||||
|
||||
def __str__(self):
|
||||
if self.fragment:
|
||||
return "%s\nIn fragment '%s' defined in '%s'." % (self.message, self.fragment.name, self.fragment.path)
|
||||
else:
|
||||
return self.message
|
||||
|
||||
"""
|
||||
Encapsulates an output of objdump. Contains information about the static library sections
|
||||
and names
|
||||
"""
|
||||
class SectionsInfo(dict):
|
||||
|
||||
PATH = Optional("/") + ZeroOrMore(Regex(r"[^/.]+") + Literal("/"))
|
||||
|
||||
__info = collections.namedtuple("__info", "filename content")
|
||||
|
||||
def __init__(self):
|
||||
self.sections = dict()
|
||||
|
||||
def add_sections_info(self, sections_info_file):
|
||||
first_line = sections_info_file.readline()
|
||||
|
||||
archive = Literal("In archive").suppress() + SectionsInfo.PATH.suppress() + Fragment.ENTITY.setResultsName("archive") + Literal(":").suppress()
|
||||
parser = archive
|
||||
|
||||
results = None
|
||||
|
||||
try:
|
||||
results = parser.parseString(first_line)
|
||||
except ParseException as p:
|
||||
raise ParseException("File " + sections_info_file.name + " is not a valid sections info file. " + p.message)
|
||||
|
||||
self.sections[results.archive] = SectionsInfo.__info(sections_info_file.name, sections_info_file.read())
|
||||
|
||||
def _get_infos_from_file(self, info):
|
||||
# Object file line: '{object}: file format elf32-xtensa-le'
|
||||
object = Fragment.ENTITY.setResultsName("object") + Literal(":").suppress() + Literal("file format elf32-xtensa-le").suppress()
|
||||
|
||||
# Sections table
|
||||
header = Suppress(Literal("Sections:") + Literal("Idx") + Literal("Name") + Literal("Size") + Literal("VMA") + Literal("LMA") + Literal("File off") + Literal("Algn"))
|
||||
entry = Word(nums).suppress() + Fragment.ENTITY + Suppress(OneOrMore(Word(alphanums, exact=8)) + Word(nums + "*") + ZeroOrMore(Word(alphas.upper()) + Optional(Literal(","))))
|
||||
|
||||
# Content is object file line + sections table
|
||||
content = Group(object + header + Group(ZeroOrMore(entry)).setResultsName("sections"))
|
||||
|
||||
parser = Group(ZeroOrMore(content)).setResultsName("contents")
|
||||
|
||||
sections_info_text = info.content
|
||||
results = None
|
||||
|
||||
try:
|
||||
results = parser.parseString(sections_info_text)
|
||||
except ParseException as p:
|
||||
raise ParseException("Unable to parse section info file " + info.filename + ". " + p.message)
|
||||
|
||||
return results
|
||||
|
||||
def get_obj_sections(self, archive, obj):
|
||||
stored = self.sections[archive]
|
||||
|
||||
# Parse the contents of the sections file
|
||||
if not isinstance(stored, dict):
|
||||
parsed = self._get_infos_from_file(stored)
|
||||
stored = dict()
|
||||
for content in parsed.contents:
|
||||
sections = list(map(lambda s: s, content.sections))
|
||||
stored[content.object] = sections
|
||||
self.sections[archive] = stored
|
||||
|
||||
for obj_key in stored.keys():
|
||||
if obj_key == obj + ".o" or obj_key == obj + ".c.obj":
|
||||
return stored[obj_key]
|
101
tools/ldgen/ldgen.py
Executable file
101
tools/ldgen/ldgen.py
Executable file
|
@ -0,0 +1,101 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from fragments import FragmentFileModel
|
||||
from sdkconfig import SDKConfig
|
||||
from generation import GenerationModel, TemplateModel, SectionsInfo
|
||||
|
||||
def main():
|
||||
|
||||
argparser = argparse.ArgumentParser(description = "ESP-IDF linker script generator")
|
||||
|
||||
argparser.add_argument(
|
||||
"--input", "-i",
|
||||
help = "Linker template file",
|
||||
type = argparse.FileType("r"))
|
||||
|
||||
argparser.add_argument(
|
||||
"--fragments", "-f",
|
||||
type = argparse.FileType("r"),
|
||||
help = "Input fragment files",
|
||||
nargs = "+")
|
||||
|
||||
argparser.add_argument(
|
||||
"--sections", "-s",
|
||||
type = argparse.FileType("r"),
|
||||
help = "Library sections info",
|
||||
nargs = "+")
|
||||
|
||||
argparser.add_argument(
|
||||
"--output", "-o",
|
||||
help = "Output linker script",
|
||||
type = argparse.FileType("w"))
|
||||
|
||||
argparser.add_argument(
|
||||
"--config", "-c",
|
||||
help = "Project configuration",
|
||||
type = argparse.FileType("r"))
|
||||
|
||||
argparser.add_argument(
|
||||
"--kconfig", "-k",
|
||||
help = "IDF Kconfig file",
|
||||
type = argparse.FileType("r"))
|
||||
|
||||
argparser.add_argument(
|
||||
"--env", "-e",
|
||||
action='append', default=[],
|
||||
help='Environment to set when evaluating the config file', metavar='NAME=VAL')
|
||||
|
||||
args = argparser.parse_args()
|
||||
|
||||
input_file = args.input
|
||||
fragment_files = [] if not args.fragments else args.fragments
|
||||
config_file = args.config
|
||||
output_file = args.output
|
||||
sections_info_files = [] if not args.sections else args.sections
|
||||
kconfig_file = args.kconfig
|
||||
|
||||
try:
|
||||
sections_infos = SectionsInfo()
|
||||
|
||||
for sections_info_file in sections_info_files:
|
||||
sections_infos.add_sections_info(sections_info_file)
|
||||
|
||||
generation_model = GenerationModel()
|
||||
|
||||
for fragment_file in fragment_files:
|
||||
fragment_file = FragmentFileModel(fragment_file)
|
||||
generation_model.add_fragments_from_file(fragment_file)
|
||||
|
||||
sdkconfig = SDKConfig(kconfig_file, config_file, args.env)
|
||||
mapping_rules = generation_model.generate_rules(sdkconfig, sections_infos)
|
||||
|
||||
script_model = TemplateModel(input_file)
|
||||
script_model.fill(mapping_rules, sdkconfig)
|
||||
|
||||
script_model.write(output_file)
|
||||
|
||||
except Exception, e:
|
||||
print("linker script generation failed for %s\nERROR: %s" % (input_file.name, e.message))
|
||||
# Delete the file so the entire build will fail; and not use an outdated script.
|
||||
os.remove(output_file.name)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
5720
tools/ldgen/pyparsing.py
Normal file
5720
tools/ldgen/pyparsing.py
Normal file
File diff suppressed because it is too large
Load diff
80
tools/ldgen/samples/esp32.lf
Normal file
80
tools/ldgen/samples/esp32.lf
Normal file
|
@ -0,0 +1,80 @@
|
|||
[sections:text]
|
||||
entries:
|
||||
.text+
|
||||
.literal+
|
||||
|
||||
[sections:data]
|
||||
entries:
|
||||
.data+
|
||||
|
||||
[sections:bss]
|
||||
entries:
|
||||
.bss+
|
||||
|
||||
[sections:common]
|
||||
entries:
|
||||
COMMON
|
||||
|
||||
[sections:rodata]
|
||||
entries:
|
||||
.rodata+
|
||||
|
||||
[sections:rtc_text]
|
||||
entries:
|
||||
.rtc.text
|
||||
.rtc.literal
|
||||
|
||||
[sections:rtc_data]
|
||||
entries:
|
||||
.rtc.data
|
||||
|
||||
[sections:rtc_rodata]
|
||||
entries:
|
||||
.rtc.rodata
|
||||
|
||||
[sections:rtc_bss]
|
||||
entries:
|
||||
.rtc.bss
|
||||
|
||||
[sections:extram_bss]
|
||||
entries:
|
||||
.exram.bss
|
||||
|
||||
[sections:iram]
|
||||
entries:
|
||||
.iram1+
|
||||
|
||||
[sections:dram]
|
||||
entries:
|
||||
.dram1+
|
||||
|
||||
[scheme:default]
|
||||
entries:
|
||||
text -> flash_text
|
||||
rodata -> flash_rodata
|
||||
data -> dram0_data
|
||||
bss -> dram0_bss
|
||||
common -> dram0_bss
|
||||
iram -> iram0_text
|
||||
dram -> dram0_data
|
||||
rtc_text -> rtc_text
|
||||
rtc_data -> rtc_data
|
||||
rtc_rodata -> rtc_data
|
||||
rtc_bss -> rtc_bss
|
||||
|
||||
[scheme:rtc]
|
||||
entries:
|
||||
text -> rtc_text
|
||||
data -> rtc_data
|
||||
rodata -> rtc_data
|
||||
bss -> rtc_bss
|
||||
common -> rtc_bss
|
||||
|
||||
[scheme:noflash]
|
||||
entries:
|
||||
text -> iram0_text
|
||||
rodata -> dram0_data
|
||||
|
||||
[scheme:noflash_data]
|
||||
entries:
|
||||
rodata -> dram0_data
|
62
tools/ldgen/samples/mappings.lf
Normal file
62
tools/ldgen/samples/mappings.lf
Normal file
|
@ -0,0 +1,62 @@
|
|||
|
||||
[mapping]
|
||||
archive: libheap.a
|
||||
entries:
|
||||
multi_heap (noflash)
|
||||
multi_heap_poisoning (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libsoc.a
|
||||
entries:
|
||||
* (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libfreertos.a
|
||||
entries:
|
||||
* (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libesp32.a
|
||||
entries:
|
||||
core_dump (noflash)
|
||||
panic (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libapp_trace.a
|
||||
entries:
|
||||
* (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libxtensa-debug-module.a
|
||||
entries:
|
||||
eri (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libphy.a
|
||||
entries:
|
||||
* (noflash_data)
|
||||
|
||||
[mapping]
|
||||
archive: librtc.a
|
||||
entries:
|
||||
* (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libhal.a
|
||||
entries:
|
||||
* (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libgcc.a
|
||||
entries:
|
||||
lib2funcs (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libspi_flash.a
|
||||
entries:
|
||||
spi_flash_rom_patch (noflash)
|
||||
|
||||
[mapping]
|
||||
archive: libgcov.a
|
||||
entries:
|
||||
* (noflash)
|
542
tools/ldgen/samples/sdkconfig
Normal file
542
tools/ldgen/samples/sdkconfig
Normal file
|
@ -0,0 +1,542 @@
|
|||
#
|
||||
# SDK tool configuration
|
||||
#
|
||||
CONFIG_TOOLPREFIX="xtensa-esp32-elf-"
|
||||
CONFIG_PYTHON="python"
|
||||
CONFIG_MAKE_WARN_UNDEFINED_VARIABLES=y
|
||||
|
||||
#
|
||||
# Bootloader config
|
||||
#
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL_NONE=
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL_ERROR=
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL_INFO=
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG=
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE=
|
||||
CONFIG_LOG_BOOTLOADER_LEVEL=2
|
||||
CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_8V=
|
||||
CONFIG_BOOTLOADER_VDDSDIO_BOOST_1_9V=y
|
||||
|
||||
#
|
||||
# Security features
|
||||
#
|
||||
CONFIG_SECURE_BOOT_ENABLED=
|
||||
CONFIG_FLASH_ENCRYPTION_ENABLED=
|
||||
|
||||
#
|
||||
# Serial flasher config
|
||||
#
|
||||
CONFIG_ESPTOOLPY_PORT="/dev/ttyUSB0"
|
||||
CONFIG_ESPTOOLPY_BAUD_115200B=y
|
||||
CONFIG_ESPTOOLPY_BAUD_230400B=
|
||||
CONFIG_ESPTOOLPY_BAUD_921600B=
|
||||
CONFIG_ESPTOOLPY_BAUD_2MB=
|
||||
CONFIG_ESPTOOLPY_BAUD_OTHER=
|
||||
CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200
|
||||
CONFIG_ESPTOOLPY_BAUD=115200
|
||||
CONFIG_ESPTOOLPY_COMPRESSED=
|
||||
CONFIG_FLASHMODE_QIO=
|
||||
CONFIG_FLASHMODE_QOUT=
|
||||
CONFIG_FLASHMODE_DIO=y
|
||||
CONFIG_FLASHMODE_DOUT=
|
||||
CONFIG_ESPTOOLPY_FLASHMODE="dio"
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_80M=
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_40M=y
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_26M=
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ_20M=
|
||||
CONFIG_ESPTOOLPY_FLASHFREQ="40m"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_1MB=
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="2MB"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y
|
||||
CONFIG_ESPTOOLPY_BEFORE_RESET=y
|
||||
CONFIG_ESPTOOLPY_BEFORE_NORESET=
|
||||
CONFIG_ESPTOOLPY_BEFORE="default_reset"
|
||||
CONFIG_ESPTOOLPY_AFTER_RESET=y
|
||||
CONFIG_ESPTOOLPY_AFTER_NORESET=
|
||||
CONFIG_ESPTOOLPY_AFTER="hard_reset"
|
||||
CONFIG_MONITOR_BAUD_9600B=
|
||||
CONFIG_MONITOR_BAUD_57600B=
|
||||
CONFIG_MONITOR_BAUD_115200B=y
|
||||
CONFIG_MONITOR_BAUD_230400B=
|
||||
CONFIG_MONITOR_BAUD_921600B=
|
||||
CONFIG_MONITOR_BAUD_2MB=
|
||||
CONFIG_MONITOR_BAUD_OTHER=
|
||||
CONFIG_MONITOR_BAUD_OTHER_VAL=115200
|
||||
CONFIG_MONITOR_BAUD=115200
|
||||
|
||||
#
|
||||
# Partition Table
|
||||
#
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP=y
|
||||
CONFIG_PARTITION_TABLE_TWO_OTA=
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv"
|
||||
CONFIG_APP_OFFSET=0x10000
|
||||
CONFIG_PARTITION_TABLE_MD5=y
|
||||
|
||||
#
|
||||
# Compiler options
|
||||
#
|
||||
CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
|
||||
CONFIG_OPTIMIZATION_LEVEL_RELEASE=
|
||||
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
|
||||
CONFIG_OPTIMIZATION_ASSERTIONS_SILENT=
|
||||
CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED=
|
||||
CONFIG_CXX_EXCEPTIONS=
|
||||
CONFIG_STACK_CHECK_NONE=y
|
||||
CONFIG_STACK_CHECK_NORM=
|
||||
CONFIG_STACK_CHECK_STRONG=
|
||||
CONFIG_STACK_CHECK_ALL=
|
||||
CONFIG_STACK_CHECK=
|
||||
CONFIG_WARN_WRITE_STRINGS=
|
||||
|
||||
#
|
||||
# Component config
|
||||
#
|
||||
|
||||
#
|
||||
# Application Level Tracing
|
||||
#
|
||||
CONFIG_ESP32_APPTRACE_DEST_TRAX=
|
||||
CONFIG_ESP32_APPTRACE_DEST_NONE=y
|
||||
CONFIG_ESP32_APPTRACE_ENABLE=
|
||||
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
|
||||
|
||||
#
|
||||
# FreeRTOS SystemView Tracing
|
||||
#
|
||||
CONFIG_AWS_IOT_SDK=
|
||||
|
||||
#
|
||||
# Bluetooth
|
||||
#
|
||||
CONFIG_BT_ENABLED=
|
||||
CONFIG_BTDM_CONTROLLER_PINNED_TO_CORE=0
|
||||
CONFIG_BT_RESERVE_DRAM=0
|
||||
|
||||
#
|
||||
# ADC configuration
|
||||
#
|
||||
CONFIG_ADC_FORCE_XPD_FSM=
|
||||
CONFIG_ADC2_DISABLE_DAC=y
|
||||
|
||||
#
|
||||
# ESP32-specific
|
||||
#
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_80=
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_160=
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
|
||||
CONFIG_SPIRAM_SUPPORT=
|
||||
CONFIG_MEMMAP_TRACEMEM=
|
||||
CONFIG_MEMMAP_TRACEMEM_TWOBANKS=
|
||||
CONFIG_ESP32_TRAX=
|
||||
CONFIG_TRACEMEM_RESERVE_DRAM=0x0
|
||||
CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH=
|
||||
CONFIG_ESP32_ENABLE_COREDUMP_TO_UART=
|
||||
CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y
|
||||
CONFIG_ESP32_ENABLE_COREDUMP=
|
||||
CONFIG_TWO_UNIVERSAL_MAC_ADDRESS=
|
||||
CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y
|
||||
CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4
|
||||
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
|
||||
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048
|
||||
CONFIG_MAIN_TASK_STACK_SIZE=4096
|
||||
CONFIG_IPC_TASK_STACK_SIZE=1024
|
||||
CONFIG_TIMER_TASK_STACK_SIZE=3584
|
||||
CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y
|
||||
CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF=
|
||||
CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR=
|
||||
CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF=
|
||||
CONFIG_NEWLIB_STDIN_LINE_ENDING_LF=
|
||||
CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y
|
||||
CONFIG_NEWLIB_NANO_FORMAT=
|
||||
CONFIG_CONSOLE_UART_DEFAULT=y
|
||||
CONFIG_CONSOLE_UART_CUSTOM=
|
||||
CONFIG_CONSOLE_UART_NONE=
|
||||
CONFIG_CONSOLE_UART_NUM=0
|
||||
CONFIG_CONSOLE_UART_BAUDRATE=115200
|
||||
CONFIG_ULP_COPROC_ENABLED=
|
||||
CONFIG_ULP_COPROC_RESERVE_MEM=0
|
||||
CONFIG_ESP32_PANIC_PRINT_HALT=
|
||||
CONFIG_ESP32_PANIC_PRINT_REBOOT=y
|
||||
CONFIG_ESP32_PANIC_SILENT_REBOOT=
|
||||
CONFIG_ESP32_PANIC_GDBSTUB=
|
||||
CONFIG_ESP32_DEBUG_OCDAWARE=y
|
||||
CONFIG_INT_WDT=y
|
||||
CONFIG_INT_WDT_TIMEOUT_MS=300
|
||||
CONFIG_TASK_WDT=y
|
||||
CONFIG_TASK_WDT_PANIC=
|
||||
CONFIG_TASK_WDT_TIMEOUT_S=5
|
||||
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
|
||||
CONFIG_BROWNOUT_DET=y
|
||||
CONFIG_BROWNOUT_DET_LVL_SEL_0=y
|
||||
CONFIG_BROWNOUT_DET_LVL_SEL_1=
|
||||
CONFIG_BROWNOUT_DET_LVL_SEL_2=
|
||||
CONFIG_BROWNOUT_DET_LVL_SEL_3=
|
||||
CONFIG_BROWNOUT_DET_LVL_SEL_4=
|
||||
CONFIG_BROWNOUT_DET_LVL_SEL_5=
|
||||
CONFIG_BROWNOUT_DET_LVL_SEL_6=
|
||||
CONFIG_BROWNOUT_DET_LVL_SEL_7=
|
||||
CONFIG_BROWNOUT_DET_LVL=0
|
||||
CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y
|
||||
CONFIG_ESP32_TIME_SYSCALL_USE_RTC=
|
||||
CONFIG_ESP32_TIME_SYSCALL_USE_FRC1=
|
||||
CONFIG_ESP32_TIME_SYSCALL_USE_NONE=
|
||||
CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y
|
||||
CONFIG_ESP32_RTC_CLOCK_SOURCE_EXTERNAL_CRYSTAL=
|
||||
CONFIG_ESP32_RTC_CLK_CAL_CYCLES=1024
|
||||
CONFIG_ESP32_RTC_XTAL_BOOTSTRAP_CYCLES=100
|
||||
CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=2000
|
||||
CONFIG_ESP32_XTAL_FREQ_40=y
|
||||
CONFIG_ESP32_XTAL_FREQ_26=
|
||||
CONFIG_ESP32_XTAL_FREQ_AUTO=
|
||||
CONFIG_ESP32_XTAL_FREQ=40
|
||||
CONFIG_DISABLE_BASIC_ROM_CONSOLE=
|
||||
CONFIG_NO_BLOBS=
|
||||
CONFIG_ESP_TIMER_PROFILING=
|
||||
CONFIG_COMPATIBLE_PRE_V2_1_BOOTLOADERS=
|
||||
CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
|
||||
|
||||
#
|
||||
# Wi-Fi
|
||||
#
|
||||
CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10
|
||||
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32
|
||||
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=
|
||||
CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y
|
||||
CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1
|
||||
CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32
|
||||
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_TX_BA_WIN=6
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_RX_BA_WIN=6
|
||||
CONFIG_ESP32_WIFI_NVS_ENABLED=y
|
||||
|
||||
#
|
||||
# PHY
|
||||
#
|
||||
CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y
|
||||
CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION=
|
||||
CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20
|
||||
CONFIG_ESP32_PHY_MAX_TX_POWER=20
|
||||
|
||||
#
|
||||
# Power Management
|
||||
#
|
||||
CONFIG_PM_ENABLE=
|
||||
|
||||
#
|
||||
# ADC-Calibration
|
||||
#
|
||||
CONFIG_ADC_CAL_EFUSE_TP_ENABLE=y
|
||||
CONFIG_ADC_CAL_EFUSE_VREF_ENABLE=y
|
||||
CONFIG_ADC_CAL_LUT_ENABLE=y
|
||||
|
||||
#
|
||||
# Ethernet
|
||||
#
|
||||
CONFIG_DMA_RX_BUF_NUM=10
|
||||
CONFIG_DMA_TX_BUF_NUM=10
|
||||
CONFIG_EMAC_L2_TO_L3_RX_BUF_MODE=
|
||||
CONFIG_EMAC_TASK_PRIORITY=20
|
||||
|
||||
#
|
||||
# FAT Filesystem support
|
||||
#
|
||||
CONFIG_FATFS_CODEPAGE_DYNAMIC=
|
||||
CONFIG_FATFS_CODEPAGE_437=y
|
||||
CONFIG_FATFS_CODEPAGE_720=
|
||||
CONFIG_FATFS_CODEPAGE_737=
|
||||
CONFIG_FATFS_CODEPAGE_771=
|
||||
CONFIG_FATFS_CODEPAGE_775=
|
||||
CONFIG_FATFS_CODEPAGE_850=
|
||||
CONFIG_FATFS_CODEPAGE_852=
|
||||
CONFIG_FATFS_CODEPAGE_855=
|
||||
CONFIG_FATFS_CODEPAGE_857=
|
||||
CONFIG_FATFS_CODEPAGE_860=
|
||||
CONFIG_FATFS_CODEPAGE_861=
|
||||
CONFIG_FATFS_CODEPAGE_862=
|
||||
CONFIG_FATFS_CODEPAGE_863=
|
||||
CONFIG_FATFS_CODEPAGE_864=
|
||||
CONFIG_FATFS_CODEPAGE_865=
|
||||
CONFIG_FATFS_CODEPAGE_866=
|
||||
CONFIG_FATFS_CODEPAGE_869=
|
||||
CONFIG_FATFS_CODEPAGE_932=
|
||||
CONFIG_FATFS_CODEPAGE_936=
|
||||
CONFIG_FATFS_CODEPAGE_949=
|
||||
CONFIG_FATFS_CODEPAGE_950=
|
||||
CONFIG_FATFS_CODEPAGE=437
|
||||
CONFIG_FATFS_LFN_NONE=y
|
||||
CONFIG_FATFS_LFN_HEAP=
|
||||
CONFIG_FATFS_LFN_STACK=
|
||||
CONFIG_FATFS_FS_LOCK=0
|
||||
CONFIG_FATFS_TIMEOUT_MS=10000
|
||||
CONFIG_FATFS_PER_FILE_CACHE=y
|
||||
|
||||
#
|
||||
# FreeRTOS
|
||||
#
|
||||
CONFIG_FREERTOS_UNICORE=y
|
||||
CONFIG_FREERTOS_CORETIMER_0=y
|
||||
CONFIG_FREERTOS_CORETIMER_1=
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y
|
||||
CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE=
|
||||
CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL=y
|
||||
CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=
|
||||
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=
|
||||
CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y
|
||||
CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3
|
||||
CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y
|
||||
CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE=
|
||||
CONFIG_FREERTOS_ASSERT_DISABLE=
|
||||
CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1024
|
||||
CONFIG_FREERTOS_ISR_STACKSIZE=1536
|
||||
CONFIG_FREERTOS_LEGACY_HOOKS=
|
||||
CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16
|
||||
CONFIG_SUPPORT_STATIC_ALLOCATION=
|
||||
CONFIG_TIMER_TASK_PRIORITY=1
|
||||
CONFIG_TIMER_TASK_STACK_DEPTH=2048
|
||||
CONFIG_TIMER_QUEUE_LENGTH=10
|
||||
CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0
|
||||
CONFIG_FREERTOS_USE_TRACE_FACILITY=
|
||||
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=
|
||||
CONFIG_FREERTOS_DEBUG_INTERNALS=
|
||||
|
||||
#
|
||||
# Heap memory debugging
|
||||
#
|
||||
CONFIG_HEAP_POISONING_DISABLED=y
|
||||
CONFIG_HEAP_POISONING_LIGHT=
|
||||
CONFIG_HEAP_POISONING_COMPREHENSIVE=
|
||||
CONFIG_HEAP_TRACING=
|
||||
|
||||
#
|
||||
# libsodium
|
||||
#
|
||||
CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y
|
||||
|
||||
#
|
||||
# Log output
|
||||
#
|
||||
CONFIG_LOG_DEFAULT_LEVEL_NONE=
|
||||
CONFIG_LOG_DEFAULT_LEVEL_ERROR=
|
||||
CONFIG_LOG_DEFAULT_LEVEL_WARN=
|
||||
CONFIG_LOG_DEFAULT_LEVEL_INFO=y
|
||||
CONFIG_LOG_DEFAULT_LEVEL_DEBUG=
|
||||
CONFIG_LOG_DEFAULT_LEVEL_VERBOSE=
|
||||
CONFIG_LOG_DEFAULT_LEVEL=3
|
||||
CONFIG_LOG_COLORS=y
|
||||
|
||||
#
|
||||
# LWIP
|
||||
#
|
||||
CONFIG_L2_TO_L3_COPY=
|
||||
CONFIG_LWIP_IRAM_OPTIMIZATION=
|
||||
CONFIG_LWIP_MAX_SOCKETS=4
|
||||
CONFIG_LWIP_SO_REUSE=
|
||||
CONFIG_LWIP_SO_RCVBUF=
|
||||
CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1
|
||||
CONFIG_LWIP_IP_FRAG=
|
||||
CONFIG_LWIP_IP_REASSEMBLY=
|
||||
CONFIG_LWIP_STATS=
|
||||
CONFIG_LWIP_ETHARP_TRUST_IP_MAC=y
|
||||
CONFIG_TCPIP_RECVMBOX_SIZE=32
|
||||
CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y
|
||||
|
||||
#
|
||||
# DHCP server
|
||||
#
|
||||
CONFIG_LWIP_DHCPS_LEASE_UNIT=60
|
||||
CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8
|
||||
CONFIG_LWIP_AUTOIP=
|
||||
CONFIG_LWIP_NETIF_LOOPBACK=y
|
||||
CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8
|
||||
|
||||
#
|
||||
# TCP
|
||||
#
|
||||
CONFIG_LWIP_MAX_ACTIVE_TCP=16
|
||||
CONFIG_LWIP_MAX_LISTENING_TCP=16
|
||||
CONFIG_TCP_MAXRTX=12
|
||||
CONFIG_TCP_SYNMAXRTX=6
|
||||
CONFIG_TCP_MSS=1436
|
||||
CONFIG_TCP_MSL=60000
|
||||
CONFIG_TCP_SND_BUF_DEFAULT=5744
|
||||
CONFIG_TCP_WND_DEFAULT=5744
|
||||
CONFIG_TCP_RECVMBOX_SIZE=6
|
||||
CONFIG_TCP_QUEUE_OOSEQ=y
|
||||
CONFIG_TCP_OVERSIZE_MSS=y
|
||||
CONFIG_TCP_OVERSIZE_QUARTER_MSS=
|
||||
CONFIG_TCP_OVERSIZE_DISABLE=
|
||||
|
||||
#
|
||||
# UDP
|
||||
#
|
||||
CONFIG_LWIP_MAX_UDP_PCBS=16
|
||||
CONFIG_UDP_RECVMBOX_SIZE=6
|
||||
CONFIG_TCPIP_TASK_STACK_SIZE=2048
|
||||
CONFIG_PPP_SUPPORT=
|
||||
|
||||
#
|
||||
# ICMP
|
||||
#
|
||||
CONFIG_LWIP_MULTICAST_PING=
|
||||
CONFIG_LWIP_BROADCAST_PING=
|
||||
|
||||
#
|
||||
# LWIP RAW API
|
||||
#
|
||||
CONFIG_LWIP_MAX_RAW_PCBS=16
|
||||
|
||||
#
|
||||
# mbedTLS
|
||||
#
|
||||
CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384
|
||||
CONFIG_MBEDTLS_DEBUG=
|
||||
CONFIG_MBEDTLS_HARDWARE_AES=y
|
||||
CONFIG_MBEDTLS_HARDWARE_MPI=y
|
||||
CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y
|
||||
CONFIG_MBEDTLS_HARDWARE_SHA=
|
||||
CONFIG_MBEDTLS_HAVE_TIME=y
|
||||
CONFIG_MBEDTLS_HAVE_TIME_DATE=
|
||||
CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y
|
||||
CONFIG_MBEDTLS_TLS_SERVER_ONLY=
|
||||
CONFIG_MBEDTLS_TLS_CLIENT_ONLY=
|
||||
CONFIG_MBEDTLS_TLS_DISABLED=
|
||||
CONFIG_MBEDTLS_TLS_SERVER=y
|
||||
CONFIG_MBEDTLS_TLS_CLIENT=y
|
||||
CONFIG_MBEDTLS_TLS_ENABLED=y
|
||||
|
||||
#
|
||||
# TLS Key Exchange Methods
|
||||
#
|
||||
CONFIG_MBEDTLS_PSK_MODES=
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA=y
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y
|
||||
CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y
|
||||
CONFIG_MBEDTLS_SSL_RENEGOTIATION=y
|
||||
CONFIG_MBEDTLS_SSL_PROTO_SSL3=
|
||||
CONFIG_MBEDTLS_SSL_PROTO_TLS1=y
|
||||
CONFIG_MBEDTLS_SSL_PROTO_TLS1_1=y
|
||||
CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y
|
||||
CONFIG_MBEDTLS_SSL_PROTO_DTLS=
|
||||
CONFIG_MBEDTLS_SSL_ALPN=y
|
||||
CONFIG_MBEDTLS_SSL_SESSION_TICKETS=y
|
||||
|
||||
#
|
||||
# Symmetric Ciphers
|
||||
#
|
||||
CONFIG_MBEDTLS_AES_C=y
|
||||
CONFIG_MBEDTLS_CAMELLIA_C=
|
||||
CONFIG_MBEDTLS_DES_C=
|
||||
CONFIG_MBEDTLS_RC4_DISABLED=y
|
||||
CONFIG_MBEDTLS_RC4_ENABLED_NO_DEFAULT=
|
||||
CONFIG_MBEDTLS_RC4_ENABLED=
|
||||
CONFIG_MBEDTLS_BLOWFISH_C=
|
||||
CONFIG_MBEDTLS_XTEA_C=
|
||||
CONFIG_MBEDTLS_CCM_C=y
|
||||
CONFIG_MBEDTLS_GCM_C=y
|
||||
CONFIG_MBEDTLS_RIPEMD160_C=
|
||||
|
||||
#
|
||||
# Certificates
|
||||
#
|
||||
CONFIG_MBEDTLS_PEM_PARSE_C=y
|
||||
CONFIG_MBEDTLS_PEM_WRITE_C=y
|
||||
CONFIG_MBEDTLS_X509_CRL_PARSE_C=y
|
||||
CONFIG_MBEDTLS_X509_CSR_PARSE_C=y
|
||||
CONFIG_MBEDTLS_ECP_C=y
|
||||
CONFIG_MBEDTLS_ECDH_C=y
|
||||
CONFIG_MBEDTLS_ECDSA_C=y
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y
|
||||
CONFIG_MBEDTLS_ECP_NIST_OPTIM=y
|
||||
|
||||
#
|
||||
# OpenSSL
|
||||
#
|
||||
CONFIG_OPENSSL_DEBUG=
|
||||
CONFIG_OPENSSL_ASSERT_DO_NOTHING=y
|
||||
CONFIG_OPENSSL_ASSERT_EXIT=
|
||||
|
||||
#
|
||||
# PThreads
|
||||
#
|
||||
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5
|
||||
CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072
|
||||
|
||||
#
|
||||
# SPI Flash driver
|
||||
#
|
||||
CONFIG_SPI_FLASH_VERIFY_WRITE=
|
||||
CONFIG_SPI_FLASH_ENABLE_COUNTERS=
|
||||
CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y
|
||||
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y
|
||||
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS=
|
||||
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED=
|
||||
|
||||
#
|
||||
# SPIFFS Configuration
|
||||
#
|
||||
CONFIG_SPIFFS_MAX_PARTITIONS=3
|
||||
|
||||
#
|
||||
# SPIFFS Cache Configuration
|
||||
#
|
||||
CONFIG_SPIFFS_CACHE=y
|
||||
CONFIG_SPIFFS_CACHE_WR=y
|
||||
CONFIG_SPIFFS_CACHE_STATS=
|
||||
CONFIG_SPIFFS_PAGE_CHECK=y
|
||||
CONFIG_SPIFFS_GC_MAX_RUNS=10
|
||||
CONFIG_SPIFFS_GC_STATS=
|
||||
CONFIG_SPIFFS_PAGE_SIZE=256
|
||||
CONFIG_SPIFFS_OBJ_NAME_LEN=32
|
||||
CONFIG_SPIFFS_USE_MAGIC=y
|
||||
CONFIG_SPIFFS_USE_MAGIC_LENGTH=y
|
||||
CONFIG_SPIFFS_META_LENGTH=4
|
||||
CONFIG_SPIFFS_USE_MTIME=y
|
||||
|
||||
#
|
||||
# Debug Configuration
|
||||
#
|
||||
CONFIG_SPIFFS_DBG=
|
||||
CONFIG_SPIFFS_API_DBG=
|
||||
CONFIG_SPIFFS_GC_DBG=
|
||||
CONFIG_SPIFFS_CACHE_DBG=
|
||||
CONFIG_SPIFFS_CHECK_DBG=
|
||||
CONFIG_SPIFFS_TEST_VISUALISATION=
|
||||
|
||||
#
|
||||
# tcpip adapter
|
||||
#
|
||||
CONFIG_IP_LOST_TIMER_INTERVAL=120
|
||||
|
||||
#
|
||||
# Wear Levelling
|
||||
#
|
||||
CONFIG_WL_SECTOR_SIZE_512=
|
||||
CONFIG_WL_SECTOR_SIZE_4096=y
|
||||
CONFIG_WL_SECTOR_SIZE=4096
|
1626
tools/ldgen/samples/sections.info
Normal file
1626
tools/ldgen/samples/sections.info
Normal file
File diff suppressed because it is too large
Load diff
217
tools/ldgen/samples/template.ld
Normal file
217
tools/ldgen/samples/template.ld
Normal file
|
@ -0,0 +1,217 @@
|
|||
/* Default entry point: */
|
||||
ENTRY(call_start_cpu0);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* RTC fast memory holds RTC wake stub code,
|
||||
including from any source file named rtc_wake_stub*.c
|
||||
*/
|
||||
.rtc.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
|
||||
mapping[rtc_text]
|
||||
|
||||
*rtc_wake_stub*.o(.literal .text .literal.* .text.*)
|
||||
} >rtc_iram_seg
|
||||
|
||||
/* RTC slow memory holds RTC wake stub
|
||||
data/rodata, including from any source file
|
||||
named rtc_wake_stub*.c
|
||||
*/
|
||||
.rtc.data :
|
||||
{
|
||||
_rtc_data_start = ABSOLUTE(.);
|
||||
|
||||
mapping[rtc_data]
|
||||
|
||||
*rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*)
|
||||
_rtc_data_end = ABSOLUTE(.);
|
||||
} > rtc_slow_seg
|
||||
|
||||
/* RTC bss, from any source file named rtc_wake_stub*.c */
|
||||
.rtc.bss (NOLOAD) :
|
||||
{
|
||||
_rtc_bss_start = ABSOLUTE(.);
|
||||
|
||||
mapping[rtc_bss]
|
||||
|
||||
*rtc_wake_stub*.o(.bss .bss.*)
|
||||
*rtc_wake_stub*.o(COMMON)
|
||||
_rtc_bss_end = ABSOLUTE(.);
|
||||
} > rtc_slow_seg
|
||||
|
||||
/* Send .iram0 code to iram */
|
||||
.iram0.vectors :
|
||||
{
|
||||
/* Vectors go to IRAM */
|
||||
_init_start = ABSOLUTE(.);
|
||||
/* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */
|
||||
. = 0x0;
|
||||
KEEP(*(.WindowVectors.text));
|
||||
. = 0x180;
|
||||
KEEP(*(.Level2InterruptVector.text));
|
||||
. = 0x1c0;
|
||||
KEEP(*(.Level3InterruptVector.text));
|
||||
. = 0x200;
|
||||
KEEP(*(.Level4InterruptVector.text));
|
||||
. = 0x240;
|
||||
KEEP(*(.Level5InterruptVector.text));
|
||||
. = 0x280;
|
||||
KEEP(*(.DebugExceptionVector.text));
|
||||
. = 0x2c0;
|
||||
KEEP(*(.NMIExceptionVector.text));
|
||||
. = 0x300;
|
||||
KEEP(*(.KernelExceptionVector.text));
|
||||
. = 0x340;
|
||||
KEEP(*(.UserExceptionVector.text));
|
||||
. = 0x3C0;
|
||||
KEEP(*(.DoubleExceptionVector.text));
|
||||
. = 0x400;
|
||||
*(.*Vector.literal)
|
||||
|
||||
*(.UserEnter.literal);
|
||||
*(.UserEnter.text);
|
||||
. = ALIGN (16);
|
||||
*(.entry.text)
|
||||
*(.init.literal)
|
||||
*(.init)
|
||||
_init_end = ABSOLUTE(.);
|
||||
|
||||
/* This goes here, not at top of linker script, so addr2line finds it last,
|
||||
and uses it in preference to the first symbol in IRAM */
|
||||
_iram_start = ABSOLUTE(0);
|
||||
} > iram0_0_seg
|
||||
|
||||
.iram0.text :
|
||||
{
|
||||
/* Code marked as runnning out of IRAM */
|
||||
_iram_text_start = ABSOLUTE(.);
|
||||
|
||||
mapping[iram0_text]
|
||||
|
||||
INCLUDE esp32.spiram.rom-functions-iram.ld
|
||||
_iram_text_end = ABSOLUTE(.);
|
||||
} > iram0_0_seg
|
||||
|
||||
.dram0.data :
|
||||
{
|
||||
_data_start = ABSOLUTE(.);
|
||||
|
||||
mapping[dram0_data]
|
||||
|
||||
*(.gnu.linkonce.d.*)
|
||||
*(.data1)
|
||||
*(.sdata)
|
||||
*(.sdata.*)
|
||||
*(.gnu.linkonce.s.*)
|
||||
*(.sdata2)
|
||||
*(.sdata2.*)
|
||||
*(.gnu.linkonce.s2.*)
|
||||
*(.jcr)
|
||||
INCLUDE esp32.spiram.rom-functions-dram.ld
|
||||
_data_end = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
} >dram0_0_seg
|
||||
|
||||
/* Shared RAM */
|
||||
.dram0.bss (NOLOAD) :
|
||||
{
|
||||
. = ALIGN (8);
|
||||
_bss_start = ABSOLUTE(.);
|
||||
|
||||
mapping[dram0_bss]
|
||||
|
||||
*(.dynsbss)
|
||||
*(.sbss)
|
||||
*(.sbss.*)
|
||||
*(.gnu.linkonce.sb.*)
|
||||
*(.scommon)
|
||||
*(.sbss2)
|
||||
*(.sbss2.*)
|
||||
*(.gnu.linkonce.sb2.*)
|
||||
*(.dynbss)
|
||||
*(.share.mem)
|
||||
*(.gnu.linkonce.b.*)
|
||||
|
||||
. = ALIGN (8);
|
||||
_bss_end = ABSOLUTE(.);
|
||||
_heap_start = ABSOLUTE(.);
|
||||
} >dram0_0_seg
|
||||
|
||||
.flash.rodata :
|
||||
{
|
||||
_rodata_start = ABSOLUTE(.);
|
||||
|
||||
mapping[flash_rodata]
|
||||
|
||||
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
|
||||
*(.gnu.linkonce.r.*)
|
||||
*(.rodata1)
|
||||
__XT_EXCEPTION_TABLE_ = ABSOLUTE(.);
|
||||
*(.xt_except_table)
|
||||
*(.gcc_except_table .gcc_except_table.*)
|
||||
*(.gnu.linkonce.e.*)
|
||||
*(.gnu.version_r)
|
||||
. = (. + 3) & ~ 3;
|
||||
__eh_frame = ABSOLUTE(.);
|
||||
KEEP(*(.eh_frame))
|
||||
. = (. + 7) & ~ 3;
|
||||
/* C++ constructor and destructor tables, properly ordered: */
|
||||
__init_array_start = ABSOLUTE(.);
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
__init_array_end = ABSOLUTE(.);
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
/* C++ exception handlers table: */
|
||||
__XT_EXCEPTION_DESCS_ = ABSOLUTE(.);
|
||||
*(.xt_except_desc)
|
||||
*(.gnu.linkonce.h.*)
|
||||
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
|
||||
*(.xt_except_desc_end)
|
||||
*(.dynamic)
|
||||
*(.gnu.version_d)
|
||||
_rodata_end = ABSOLUTE(.);
|
||||
/* Literals are also RO data. */
|
||||
_lit4_start = ABSOLUTE(.);
|
||||
*(*.lit4)
|
||||
*(.lit4.*)
|
||||
*(.gnu.linkonce.lit4.*)
|
||||
_lit4_end = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
_thread_local_start = ABSOLUTE(.);
|
||||
*(.tdata)
|
||||
*(.tdata.*)
|
||||
*(.tbss)
|
||||
*(.tbss.*)
|
||||
_thread_local_end = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
} >drom0_0_seg
|
||||
|
||||
.flash.text :
|
||||
{
|
||||
_stext = .;
|
||||
_text_start = ABSOLUTE(.);
|
||||
|
||||
mapping[flash_text]
|
||||
|
||||
*(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
|
||||
*(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */
|
||||
*(.fini.literal)
|
||||
*(.fini)
|
||||
*(.gnu.version)
|
||||
_text_end = ABSOLUTE(.);
|
||||
_etext = .;
|
||||
|
||||
/* Similar to _iram_start, this symbol goes here so it is
|
||||
resolved by addr2line in preference to the first symbol in
|
||||
the flash.text segment.
|
||||
*/
|
||||
_flash_cache_start = ABSOLUTE(0);
|
||||
} >iram0_2_seg
|
||||
}
|
87
tools/ldgen/sdkconfig.py
Normal file
87
tools/ldgen/sdkconfig.py
Normal file
|
@ -0,0 +1,87 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import os
|
||||
from pyparsing import *
|
||||
|
||||
import sys
|
||||
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
kconfig_new_dir = os.path.abspath(parent_dir_name + "/kconfig_new")
|
||||
sys.path.append(kconfig_new_dir)
|
||||
import kconfiglib
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Encapsulates an sdkconfig file. Defines grammar of a configuration entry, and enables
|
||||
evaluation of logical expressions involving those entries.
|
||||
"""
|
||||
class SDKConfig:
|
||||
|
||||
# A configuration entry is in the form CONFIG=VALUE. Definitions of components of that grammar
|
||||
IDENTIFIER = Word(printables.upper())
|
||||
|
||||
HEX = Combine("0x" + Word(hexnums)).setParseAction(lambda t:int(t[0], 16))
|
||||
DECIMAL = Combine(Optional(Literal("+") | Literal("-")) + Word(nums)).setParseAction(lambda t:int(t[0]))
|
||||
LITERAL = Word(printables)
|
||||
QUOTED_LITERAL = quotedString.setParseAction(removeQuotes)
|
||||
|
||||
VALUE = HEX | DECIMAL | LITERAL | QUOTED_LITERAL
|
||||
|
||||
# Operators supported by the expression evaluation
|
||||
OPERATOR = oneOf(["=", "!=", ">", "<", "<=", ">="])
|
||||
|
||||
def __init__(self, kconfig_file, sdkconfig_file, env = []):
|
||||
env = [ (name, value) for (name,value) in ( e.split("=",1) for e in env) ]
|
||||
|
||||
for name, value in env:
|
||||
value = " ".join(value.split())
|
||||
os.environ[name] = value
|
||||
|
||||
self.config = kconfiglib.Kconfig(kconfig_file.name)
|
||||
self.config.load_config(sdkconfig_file.name)
|
||||
|
||||
def evaluate_expression(self, expression):
|
||||
result = self.config.eval_string(expression)
|
||||
|
||||
if result == 0: # n
|
||||
return False
|
||||
elif result == 2: # y
|
||||
return True
|
||||
else: # m
|
||||
raise Exception("Unsupported config expression result.")
|
||||
|
||||
@staticmethod
|
||||
def get_expression_grammar():
|
||||
identifier = SDKConfig.IDENTIFIER.setResultsName("identifier")
|
||||
operator = SDKConfig.OPERATOR.setResultsName("operator")
|
||||
value = SDKConfig.VALUE.setResultsName("value")
|
||||
|
||||
test_binary = identifier + operator + value
|
||||
test_single = identifier
|
||||
|
||||
test = test_binary | test_single
|
||||
|
||||
condition = Group(Optional("(").suppress() + test + Optional(")").suppress())
|
||||
|
||||
grammar = infixNotation(
|
||||
condition, [
|
||||
("!", 1, opAssoc.RIGHT),
|
||||
("&&", 2, opAssoc.LEFT),
|
||||
("||", 2, opAssoc.LEFT)])
|
||||
|
||||
return grammar
|
7
tools/ldgen/test/data/Kconfig
Normal file
7
tools/ldgen/test/data/Kconfig
Normal file
|
@ -0,0 +1,7 @@
|
|||
menu "Test config"
|
||||
|
||||
config PERFORMANCE_LEVEL
|
||||
int "Performance level"
|
||||
range 0 3
|
||||
|
||||
endmenu
|
84
tools/ldgen/test/data/sample.lf
Normal file
84
tools/ldgen/test/data/sample.lf
Normal file
|
@ -0,0 +1,84 @@
|
|||
[sections:text]
|
||||
entries:
|
||||
.text+
|
||||
.literal+
|
||||
|
||||
[sections:data]
|
||||
entries:
|
||||
.data+
|
||||
|
||||
[sections:bss]
|
||||
entries:
|
||||
.bss+
|
||||
|
||||
[sections:common]
|
||||
entries:
|
||||
COMMON
|
||||
|
||||
[sections:rodata]
|
||||
entries:
|
||||
.rodata+
|
||||
|
||||
[sections:rtc_text]
|
||||
entries:
|
||||
.rtc.text
|
||||
.rtc.literal
|
||||
|
||||
[sections:rtc_data]
|
||||
entries:
|
||||
.rtc.data
|
||||
|
||||
[sections:rtc_rodata]
|
||||
entries:
|
||||
.rtc.rodata
|
||||
|
||||
[sections:rtc_bss]
|
||||
entries:
|
||||
.rtc.bss
|
||||
|
||||
[sections:extram_bss]
|
||||
entries:
|
||||
.exram.bss
|
||||
|
||||
[sections:iram]
|
||||
entries:
|
||||
.iram+
|
||||
|
||||
[sections:dram]
|
||||
entries:
|
||||
.dram+
|
||||
|
||||
[scheme:default]
|
||||
entries:
|
||||
text -> flash_text
|
||||
rodata -> flash_rodata
|
||||
data -> dram0_data
|
||||
bss -> dram0_bss
|
||||
common -> dram0_bss
|
||||
iram -> iram0_text
|
||||
dram -> dram0_data
|
||||
rtc_text -> rtc_text
|
||||
rtc_data -> rtc_data
|
||||
rtc_rodata -> rtc_data
|
||||
rtc_bss -> rtc_bss
|
||||
|
||||
[scheme:rtc]
|
||||
entries:
|
||||
text -> rtc_text
|
||||
data -> rtc_data
|
||||
rodata -> rtc_data
|
||||
bss -> rtc_bss
|
||||
common -> rtc_bss
|
||||
|
||||
[scheme:noflash]
|
||||
entries:
|
||||
text -> iram0_text
|
||||
rodata -> dram0_data
|
||||
|
||||
[scheme:noflash_text]
|
||||
entries:
|
||||
text -> iram0_text
|
||||
|
||||
[scheme:noflash_data]
|
||||
entries:
|
||||
rodata -> dram0_data
|
8
tools/ldgen/test/data/sdkconfig
Normal file
8
tools/ldgen/test/data/sdkconfig
Normal file
|
@ -0,0 +1,8 @@
|
|||
CONFIG_TEST_STRING="This Is~AString#$^#$&^(*&^#(*&^)(*@_)(#*_)(*_(*}Value"
|
||||
CONFIG_TEST_NON_STRING=y
|
||||
CONFIG_TEST_WHITESPACE= y
|
||||
CONFIG_TEST_EMPTY=
|
||||
CONFIG_TEST_POSITIVE_INT=110
|
||||
CONFIG_TEST_HEX_INT=0x8000
|
||||
CONFIG_TEST_NEGATIVE_INT=-9
|
||||
CONFIG_PERFORMANCE_LEVEL=0
|
1626
tools/ldgen/test/data/sections.info
Normal file
1626
tools/ldgen/test/data/sections.info
Normal file
File diff suppressed because it is too large
Load diff
217
tools/ldgen/test/data/template.ld
Normal file
217
tools/ldgen/test/data/template.ld
Normal file
|
@ -0,0 +1,217 @@
|
|||
/* Default entry point: */
|
||||
ENTRY(call_start_cpu0);
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* RTC fast memory holds RTC wake stub code,
|
||||
including from any source file named rtc_wake_stub*.c
|
||||
*/
|
||||
.rtc.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
|
||||
mapping[rtc_text]
|
||||
|
||||
*rtc_wake_stub*.o(.literal .text .literal.* .text.*)
|
||||
} >rtc_iram_seg
|
||||
|
||||
/* RTC slow memory holds RTC wake stub
|
||||
data/rodata, including from any source file
|
||||
named rtc_wake_stub*.c
|
||||
*/
|
||||
.rtc.data :
|
||||
{
|
||||
_rtc_data_start = ABSOLUTE(.);
|
||||
|
||||
mapping[rtc_data]
|
||||
|
||||
*rtc_wake_stub*.o(.data .rodata .data.* .rodata.* .bss .bss.*)
|
||||
_rtc_data_end = ABSOLUTE(.);
|
||||
} > rtc_slow_seg
|
||||
|
||||
/* RTC bss, from any source file named rtc_wake_stub*.c */
|
||||
.rtc.bss (NOLOAD) :
|
||||
{
|
||||
_rtc_bss_start = ABSOLUTE(.);
|
||||
|
||||
mapping[rtc_bss]
|
||||
|
||||
*rtc_wake_stub*.o(.bss .bss.*)
|
||||
*rtc_wake_stub*.o(COMMON)
|
||||
_rtc_bss_end = ABSOLUTE(.);
|
||||
} > rtc_slow_seg
|
||||
|
||||
/* Send .iram0 code to iram */
|
||||
.iram0.vectors :
|
||||
{
|
||||
/* Vectors go to IRAM */
|
||||
_init_start = ABSOLUTE(.);
|
||||
/* Vectors according to builds/RF-2015.2-win32/esp108_v1_2_s5_512int_2/config.html */
|
||||
. = 0x0;
|
||||
KEEP(*(.WindowVectors.text));
|
||||
. = 0x180;
|
||||
KEEP(*(.Level2InterruptVector.text));
|
||||
. = 0x1c0;
|
||||
KEEP(*(.Level3InterruptVector.text));
|
||||
. = 0x200;
|
||||
KEEP(*(.Level4InterruptVector.text));
|
||||
. = 0x240;
|
||||
KEEP(*(.Level5InterruptVector.text));
|
||||
. = 0x280;
|
||||
KEEP(*(.DebugExceptionVector.text));
|
||||
. = 0x2c0;
|
||||
KEEP(*(.NMIExceptionVector.text));
|
||||
. = 0x300;
|
||||
KEEP(*(.KernelExceptionVector.text));
|
||||
. = 0x340;
|
||||
KEEP(*(.UserExceptionVector.text));
|
||||
. = 0x3C0;
|
||||
KEEP(*(.DoubleExceptionVector.text));
|
||||
. = 0x400;
|
||||
*(.*Vector.literal)
|
||||
|
||||
*(.UserEnter.literal);
|
||||
*(.UserEnter.text);
|
||||
. = ALIGN (16);
|
||||
*(.entry.text)
|
||||
*(.init.literal)
|
||||
*(.init)
|
||||
_init_end = ABSOLUTE(.);
|
||||
|
||||
/* This goes here, not at top of linker script, so addr2line finds it last,
|
||||
and uses it in preference to the first symbol in IRAM */
|
||||
_iram_start = ABSOLUTE(0);
|
||||
} > iram0_0_seg
|
||||
|
||||
.iram0.text :
|
||||
{
|
||||
/* Code marked as runnning out of IRAM */
|
||||
_iram_text_start = ABSOLUTE(.);
|
||||
|
||||
mapping[iram0_text]
|
||||
|
||||
INCLUDE esp32.spiram.rom-functions-iram.ld
|
||||
_iram_text_end = ABSOLUTE(.);
|
||||
} > iram0_0_seg
|
||||
|
||||
.dram0.data :
|
||||
{
|
||||
_data_start = ABSOLUTE(.);
|
||||
|
||||
mapping[dram0_data]
|
||||
|
||||
*(.gnu.linkonce.d.*)
|
||||
*(.data1)
|
||||
*(.sdata)
|
||||
*(.sdata.*)
|
||||
*(.gnu.linkonce.s.*)
|
||||
*(.sdata2)
|
||||
*(.sdata2.*)
|
||||
*(.gnu.linkonce.s2.*)
|
||||
*(.jcr)
|
||||
INCLUDE esp32.spiram.rom-functions-dram.ld
|
||||
_data_end = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
} >dram0_0_seg
|
||||
|
||||
/* Shared RAM */
|
||||
.dram0.bss (NOLOAD) :
|
||||
{
|
||||
. = ALIGN (8);
|
||||
_bss_start = ABSOLUTE(.);
|
||||
|
||||
mapping[dram0_bss]
|
||||
|
||||
*(.dynsbss)
|
||||
*(.sbss)
|
||||
*(.sbss.*)
|
||||
*(.gnu.linkonce.sb.*)
|
||||
*(.scommon)
|
||||
*(.sbss2)
|
||||
*(.sbss2.*)
|
||||
*(.gnu.linkonce.sb2.*)
|
||||
*(.dynbss)
|
||||
*(.share.mem)
|
||||
*(.gnu.linkonce.b.*)
|
||||
|
||||
. = ALIGN (8);
|
||||
_bss_end = ABSOLUTE(.);
|
||||
_heap_start = ABSOLUTE(.);
|
||||
} >dram0_0_seg
|
||||
|
||||
.flash.rodata :
|
||||
{
|
||||
_rodata_start = ABSOLUTE(.);
|
||||
|
||||
mapping[flash_rodata]
|
||||
|
||||
*(.irom1.text) /* catch stray ICACHE_RODATA_ATTR */
|
||||
*(.gnu.linkonce.r.*)
|
||||
*(.rodata1)
|
||||
__XT_EXCEPTION_TABLE_ = ABSOLUTE(.);
|
||||
*(.xt_except_table)
|
||||
*(.gcc_except_table .gcc_except_table.*)
|
||||
*(.gnu.linkonce.e.*)
|
||||
*(.gnu.version_r)
|
||||
. = (. + 3) & ~ 3;
|
||||
__eh_frame = ABSOLUTE(.);
|
||||
KEEP(*(.eh_frame))
|
||||
. = (. + 7) & ~ 3;
|
||||
/* C++ constructor and destructor tables, properly ordered: */
|
||||
__init_array_start = ABSOLUTE(.);
|
||||
KEEP (*crtbegin.o(.ctors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
|
||||
KEEP (*(SORT(.ctors.*)))
|
||||
KEEP (*(.ctors))
|
||||
__init_array_end = ABSOLUTE(.);
|
||||
KEEP (*crtbegin.o(.dtors))
|
||||
KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
|
||||
KEEP (*(SORT(.dtors.*)))
|
||||
KEEP (*(.dtors))
|
||||
/* C++ exception handlers table: */
|
||||
__XT_EXCEPTION_DESCS_ = ABSOLUTE(.);
|
||||
*(.xt_except_desc)
|
||||
*(.gnu.linkonce.h.*)
|
||||
__XT_EXCEPTION_DESCS_END__ = ABSOLUTE(.);
|
||||
*(.xt_except_desc_end)
|
||||
*(.dynamic)
|
||||
*(.gnu.version_d)
|
||||
_rodata_end = ABSOLUTE(.);
|
||||
/* Literals are also RO data. */
|
||||
_lit4_start = ABSOLUTE(.);
|
||||
*(*.lit4)
|
||||
*(.lit4.*)
|
||||
*(.gnu.linkonce.lit4.*)
|
||||
_lit4_end = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
_thread_local_start = ABSOLUTE(.);
|
||||
*(.tdata)
|
||||
*(.tdata.*)
|
||||
*(.tbss)
|
||||
*(.tbss.*)
|
||||
_thread_local_end = ABSOLUTE(.);
|
||||
. = ALIGN(4);
|
||||
} >drom0_0_seg
|
||||
|
||||
.flash.text :
|
||||
{
|
||||
_stext = .;
|
||||
_text_start = ABSOLUTE(.);
|
||||
|
||||
mapping[flash_text]
|
||||
|
||||
*(.stub .gnu.warning .gnu.linkonce.literal.* .gnu.linkonce.t.*.literal .gnu.linkonce.t.*)
|
||||
*(.irom0.text) /* catch stray ICACHE_RODATA_ATTR */
|
||||
*(.fini.literal)
|
||||
*(.fini)
|
||||
*(.gnu.version)
|
||||
_text_end = ABSOLUTE(.);
|
||||
_etext = .;
|
||||
|
||||
/* Similar to _iram_start, this symbol goes here so it is
|
||||
resolved by addr2line in preference to the first symbol in
|
||||
the flash.text segment.
|
||||
*/
|
||||
_flash_cache_start = ABSOLUTE(0);
|
||||
} >iram0_2_seg
|
||||
}
|
632
tools/ldgen/test/test_fragments.py
Executable file
632
tools/ldgen/test/test_fragments.py
Executable file
|
@ -0,0 +1,632 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.append('../')
|
||||
from fragments import *
|
||||
from pyparsing import *
|
||||
from sdkconfig import *
|
||||
|
||||
class FragmentTest(unittest.TestCase):
|
||||
|
||||
def parse(self, text):
|
||||
self.parser.ignore("#" + restOfLine)
|
||||
fragment = self.parser.parseString(text, parseAll=True)
|
||||
return fragment[0]
|
||||
|
||||
class SectionsTest(FragmentTest):
|
||||
|
||||
def setUp(self):
|
||||
self.parser = Sections.get_fragment_grammar()
|
||||
|
||||
def test_valid_entries(self):
|
||||
valid_entries = """
|
||||
[sections:test]
|
||||
entries:
|
||||
.section1
|
||||
.section2
|
||||
|
||||
# Section 3 should not exist
|
||||
# section3
|
||||
.section4
|
||||
|
||||
# This is a comment
|
||||
|
||||
.section5
|
||||
"""
|
||||
|
||||
sections = self.parse(valid_entries)
|
||||
|
||||
self.assertEqual("test", sections.name)
|
||||
|
||||
entries = sections.entries
|
||||
|
||||
expected = {
|
||||
".section1",
|
||||
".section2",
|
||||
".section4",
|
||||
".section5"
|
||||
}
|
||||
|
||||
self.assertEqual(set(entries), expected)
|
||||
|
||||
def test_blank_entries(self):
|
||||
blank_entries = """
|
||||
[sections:test]
|
||||
entries:
|
||||
"""
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(blank_entries)
|
||||
|
||||
def test_invalid_names(self):
|
||||
with_spaces = """
|
||||
[sections:invalid name 1]
|
||||
entries:
|
||||
"""
|
||||
|
||||
begins_with_number = """
|
||||
[sections:2invalid_name]
|
||||
entries:
|
||||
"""
|
||||
|
||||
with_special_character = """
|
||||
[sections:invalid_name~]
|
||||
entries:
|
||||
"""
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(with_spaces)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(begins_with_number)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(with_special_character)
|
||||
|
||||
def test_non_existent_entries(self):
|
||||
misspelled_entries_field = """
|
||||
[sections:test]
|
||||
entrie:
|
||||
.section1
|
||||
"""
|
||||
|
||||
missing_entries_field = """
|
||||
[sections:test]
|
||||
"""
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(misspelled_entries_field)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(missing_entries_field)
|
||||
|
||||
def test_duplicate_entries(self):
|
||||
duplicate_entries = """
|
||||
[sections:test]
|
||||
entries:
|
||||
.section1
|
||||
.section3
|
||||
.section1
|
||||
.section1
|
||||
.section2
|
||||
.section3
|
||||
.section1
|
||||
"""
|
||||
|
||||
sections = self.parse(duplicate_entries)
|
||||
|
||||
entries = sections.entries
|
||||
|
||||
expected = {
|
||||
".section1",
|
||||
".section2",
|
||||
".section3",
|
||||
}
|
||||
|
||||
self.assertEqual(set(entries), expected)
|
||||
|
||||
class SchemeTest(FragmentTest):
|
||||
|
||||
def setUp(self):
|
||||
self.parser = Scheme.get_fragment_grammar()
|
||||
|
||||
def test_valid_entries(self):
|
||||
valid_entries = """
|
||||
[scheme:test]
|
||||
entries:
|
||||
sections1 -> target1
|
||||
sections2 -> target2
|
||||
"""
|
||||
|
||||
scheme = self.parse(valid_entries)
|
||||
|
||||
entries = scheme.entries
|
||||
|
||||
expected = {
|
||||
("sections1", "target1"),
|
||||
("sections2", "target2")
|
||||
}
|
||||
|
||||
self.assertEqual(entries, expected)
|
||||
|
||||
def test_duplicate_same_mapping(self):
|
||||
duplicate_entries = """
|
||||
[scheme:duplicate_same_mapping]
|
||||
entries:
|
||||
sections1 -> target1
|
||||
sections2 -> target2
|
||||
sections1 -> target1
|
||||
"""
|
||||
|
||||
scheme = self.parse(duplicate_entries)
|
||||
|
||||
entries = scheme.entries
|
||||
|
||||
expected = {
|
||||
("sections1", "target1"),
|
||||
("sections2", "target2")
|
||||
}
|
||||
|
||||
self.assertEqual(len(entries), 2)
|
||||
self.assertEqual(entries, expected)
|
||||
|
||||
def test_invalid_separator(self):
|
||||
wrong_character = """
|
||||
[scheme:test]
|
||||
entries:
|
||||
sections1, target1
|
||||
"""
|
||||
|
||||
single_word = """
|
||||
[scheme:test]
|
||||
entries:
|
||||
sections1
|
||||
"""
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
scheme = self.parse(wrong_character)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
scheme = self.parse(single_word)
|
||||
|
||||
def test_blank_entries(self):
|
||||
blank_entries = """
|
||||
[scheme:test]
|
||||
entries:
|
||||
"""
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(blank_entries)
|
||||
|
||||
def test_non_existent_entries(self):
|
||||
misspelled_entries_field = """
|
||||
[scheme:test]
|
||||
entrie:
|
||||
section -> target
|
||||
"""
|
||||
|
||||
missing_entries_field = """
|
||||
[scheme:test]
|
||||
"""
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(misspelled_entries_field)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(missing_entries_field)
|
||||
|
||||
class MappingTest(FragmentTest):
|
||||
|
||||
def setUp(self):
|
||||
self.parser = Mapping.get_fragment_grammar()
|
||||
|
||||
def parse_expression(self, expression):
|
||||
parser = SDKConfig.get_expression_grammar()
|
||||
return parser.parseString(expression, parseAll=True)
|
||||
|
||||
def test_valid_grammar(self):
|
||||
valid_entries = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
obj:symbol (noflash)
|
||||
# Comments should not matter
|
||||
obj (noflash)
|
||||
# Nor should whitespace
|
||||
obj : symbol_2 ( noflash )
|
||||
obj_2 ( noflash )
|
||||
* (noflash)
|
||||
"""
|
||||
|
||||
mapping = self.parse(valid_entries)
|
||||
|
||||
self.assertEqual("lib.a", mapping.archive)
|
||||
self.assertEqual("lib_a", mapping.name)
|
||||
|
||||
entries = mapping.entries
|
||||
|
||||
expected = [("default", {
|
||||
("obj", "symbol", "noflash"),
|
||||
("obj", None, "noflash"),
|
||||
("obj", "symbol_2", "noflash"),
|
||||
("obj_2", None, "noflash"),
|
||||
("*", None, "noflash")
|
||||
} ) ]
|
||||
|
||||
self.assertEqual(entries, expected)
|
||||
|
||||
def test_invalid_grammar(self):
|
||||
with_fragment_name = """
|
||||
[mapping:name]
|
||||
archive: lib.a
|
||||
entries:
|
||||
obj:symbol (noflash)
|
||||
"""
|
||||
|
||||
missing_archive = """
|
||||
[mapping:name]
|
||||
entries:
|
||||
obj:symbol (noflash)
|
||||
"""
|
||||
|
||||
misspelled_archive = """
|
||||
[mapping:name]
|
||||
archi: lib.a
|
||||
entries:
|
||||
obj:symbol (noflash)
|
||||
"""
|
||||
|
||||
missing_entries = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
"""
|
||||
|
||||
misspelled_entries = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entrie:
|
||||
obj:symbol (noflash)
|
||||
"""
|
||||
|
||||
missing_symbols = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
obj: (noflash)
|
||||
"""
|
||||
|
||||
missing_scheme_1 = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
obj: ()
|
||||
"""
|
||||
|
||||
missing_scheme_2 = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
obj:symbol
|
||||
"""
|
||||
|
||||
missing_entity = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
(noflash)
|
||||
"""
|
||||
|
||||
wilcard_symbol = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
obj:* (noflash)
|
||||
"""
|
||||
|
||||
empty_object_with_symbol = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
:symbol (noflash)
|
||||
"""
|
||||
|
||||
wildcard_object_with_symbol = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
*:symbol (noflash)
|
||||
"""
|
||||
|
||||
empty_definition = """
|
||||
[mapping]
|
||||
"""
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(with_fragment_name)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(missing_archive)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(misspelled_archive)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(missing_entries)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(misspelled_entries)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(missing_symbols)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(missing_scheme_1)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(missing_scheme_2)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(missing_entity)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(wilcard_symbol)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(empty_object_with_symbol)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(wildcard_object_with_symbol)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
sections = self.parse(empty_definition)
|
||||
|
||||
def test_explicit_blank_default_w_others(self):
|
||||
expl_blnk_w_oth = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: CONFIG_A = y
|
||||
obj_a (noflash)
|
||||
: default
|
||||
"""
|
||||
|
||||
mapping = self.parse(expl_blnk_w_oth)
|
||||
|
||||
entries = mapping.entries
|
||||
|
||||
expected = [ ( entries[0][0] , {
|
||||
("obj_a", None, "noflash"),
|
||||
} ),
|
||||
("default", set() ) ]
|
||||
|
||||
self.assertEqual(entries, expected)
|
||||
|
||||
|
||||
def test_implicit_blank_default_w_others(self):
|
||||
impl_blnk_w_oth = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: CONFIG_A = y
|
||||
obj_a (noflash)
|
||||
"""
|
||||
|
||||
mapping = self.parse(impl_blnk_w_oth)
|
||||
|
||||
entries = mapping.entries
|
||||
|
||||
expected = [ ( entries[0][0] , {
|
||||
("obj_a", None, "noflash"),
|
||||
} ),
|
||||
("default", set() ) ]
|
||||
|
||||
self.assertEqual(entries, expected)
|
||||
|
||||
def test_explicit_blank_default(self):
|
||||
expl_blnk_def = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: default
|
||||
"""
|
||||
mapping = self.parse(expl_blnk_def)
|
||||
entries = mapping.entries
|
||||
expected = [ ("default", set() ) ]
|
||||
|
||||
self.assertEqual(entries, expected)
|
||||
|
||||
def test_implicit_blank_default(self):
|
||||
impl_blnk_def = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: default
|
||||
"""
|
||||
mapping = self.parse(impl_blnk_def)
|
||||
entries = mapping.entries
|
||||
expected = [ ("default", set() ) ]
|
||||
|
||||
self.assertEqual(entries, expected)
|
||||
|
||||
def test_multiple_entries(self):
|
||||
multiple_entries = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: CONFIG_A = y
|
||||
obj_a1 (noflash)
|
||||
obj_a2 (noflash)
|
||||
: CONFIG_B = y
|
||||
obj_b1 (noflash)
|
||||
obj_b2 (noflash)
|
||||
obj_b3 (noflash)
|
||||
: CONFIG_C = y
|
||||
obj_c1 (noflash)
|
||||
"""
|
||||
|
||||
mapping = self.parse(multiple_entries)
|
||||
|
||||
entries = mapping.entries
|
||||
|
||||
expected = [ ( entries[0][0] , {
|
||||
("obj_a1", None, "noflash"),
|
||||
("obj_a2", None, "noflash"),
|
||||
} ),
|
||||
( entries[1][0] , {
|
||||
("obj_b1", None, "noflash"),
|
||||
("obj_b2", None, "noflash"),
|
||||
("obj_b3", None, "noflash"),
|
||||
} ),
|
||||
( entries[2][0] , {
|
||||
("obj_c1", None, "noflash"),
|
||||
} ),
|
||||
("default", set() ) ]
|
||||
|
||||
self.assertEqual(entries, expected)
|
||||
|
||||
def test_blank_entries(self):
|
||||
blank_entries = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: CONFIG_A = y
|
||||
obj_a (noflash)
|
||||
: CONFIG_B = y
|
||||
: CONFIG_C = y
|
||||
obj_c (noflash)
|
||||
: CONFIG_D = y
|
||||
: CONFIG_E = y
|
||||
: default
|
||||
obj (noflash)
|
||||
"""
|
||||
|
||||
mapping = self.parse(blank_entries)
|
||||
|
||||
entries = mapping.entries
|
||||
|
||||
expected = [ ( entries[0][0] , {
|
||||
("obj_a", None, "noflash")
|
||||
} ),
|
||||
( entries[1][0] , set()),
|
||||
( entries[2][0] , {
|
||||
("obj_c", None, "noflash")
|
||||
} ),
|
||||
( entries[3][0] , set()),
|
||||
( entries[4][0] , set()),
|
||||
( "default" , {
|
||||
("obj", None, "noflash")
|
||||
} ) ]
|
||||
|
||||
self.assertEqual(entries, expected)
|
||||
|
||||
def test_blank_first_condition(self):
|
||||
blank_first_condition = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
obj_a (noflash)
|
||||
: CONFIG_B = y
|
||||
obj_b (noflash)
|
||||
"""
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
mapping = self.parse(blank_first_condition)
|
||||
|
||||
|
||||
def test_nonlast_default(self):
|
||||
nonlast_default_1 = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: default
|
||||
obj_a (noflash)
|
||||
: CONFIG_A = y
|
||||
obj_A (noflash)
|
||||
"""
|
||||
|
||||
nonlast_default_2 = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: CONFIG_A = y
|
||||
obj_A (noflash)
|
||||
: default
|
||||
obj_a (noflash)
|
||||
: CONFIG_B = y
|
||||
obj_B (noflash)
|
||||
"""
|
||||
|
||||
nonlast_default_3 = """
|
||||
[mapping]
|
||||
archive: lib.a
|
||||
entries:
|
||||
: CONFIG_A = y
|
||||
obj_A (noflash)
|
||||
:
|
||||
obj_a (noflash)
|
||||
: CONFIG_B = y
|
||||
obj_B (noflash)
|
||||
"""
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
mapping = self.parse(nonlast_default_1)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
mapping = self.parse(nonlast_default_2)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
mapping = self.parse(nonlast_default_3)
|
||||
|
||||
def test_duplicate_default(self):
|
||||
duplicate_default_1 = """
|
||||
archive: lib.a
|
||||
entries:
|
||||
: CONFIG_A = y
|
||||
obj_A (noflash)
|
||||
: default
|
||||
obj_a (noflash)
|
||||
: CONFIG_B = y
|
||||
obj_B (noflash)
|
||||
: default
|
||||
obj_a (noflash)
|
||||
"""
|
||||
|
||||
duplicate_default_2 = """
|
||||
archive: lib.a
|
||||
entries:
|
||||
: CONFIG_A = y
|
||||
obj_A (noflash)
|
||||
: CONFIG_B = y
|
||||
obj_a (noflash)
|
||||
: default
|
||||
obj_B (noflash)
|
||||
:
|
||||
obj_a (noflash)
|
||||
"""
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
mapping = self.parse(duplicate_default_1)
|
||||
|
||||
with self.assertRaises(ParseException):
|
||||
mapping = self.parse(duplicate_default_2)
|
||||
|
||||
if __name__ =="__main__":
|
||||
unittest.main()
|
1099
tools/ldgen/test/test_generation.py
Executable file
1099
tools/ldgen/test/test_generation.py
Executable file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue