diff --git a/components/ulp/ld/lp_core_riscv.ld.in b/components/ulp/ld/lp_core_riscv.ld.in index 315faefb5bc..1c9c95cc405 100644 --- a/components/ulp/ld/lp_core_riscv.ld.in +++ b/components/ulp/ld/lp_core_riscv.ld.in @@ -1,120 +1,16 @@ /* * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD - * * SPDX-License-Identifier: Apache-2.0 + * + * LP-core RISC-V linker script, assembled from base + layout + checks. + * A custom layout replaces the default via LP_CORE_LINKER_INCLUDE. */ -#include "sdkconfig.h" -#include "soc/soc.h" -#include "ld.common" -#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM -#include "ld.hp_mem_defs" -#endif +#include "lp_core_riscv_base.ld.in" -#if CONFIG_ESP_ROM_HAS_LP_ROM -/* With LP-ROM memory layout is different due to LP ROM stack/data */ -/* For P4 ECO5 we also reserve some RTC MEM at the first for MSPI workaround */ -#define ULP_MEM_START_ADDRESS SOC_RTC_DRAM_LOW + RESERVE_RTC_MEM + MSPI_WORKAROUND_SIZE +#ifdef LP_CORE_LINKER_INCLUDE +#include LP_CORE_LINKER_INCLUDE #else -#define ULP_MEM_START_ADDRESS (SOC_RTC_DRAM_LOW) +#include "lp_core_riscv_default_layout.ld.in" #endif -#define ALIGN_DOWN(SIZE, AL) (SIZE & ~(AL - 1)) -/* Ensure the end where the shared memory starts is aligned to 8 bytes - if updating this also update the same in ulp_lp_core_memory_shared.c - */ -#define ALIGNED_COPROC_MEM ALIGN_DOWN(CONFIG_ULP_COPROC_RESERVE_MEM, 0x8) - -ENTRY(reset_vector) - -MEMORY -{ - /* First 128 bytes for exception/interrupt vectors */ - vector_table(RX) : ORIGIN = ULP_MEM_START_ADDRESS, LENGTH = 0x80 - lp_ram(RWX) : ORIGIN = ULP_MEM_START_ADDRESS + 0x80, LENGTH = ALIGNED_COPROC_MEM - 0x80 - CONFIG_ULP_SHARED_MEM - shared_mem_ram(RW) : ORIGIN = ULP_MEM_START_ADDRESS + ALIGNED_COPROC_MEM - CONFIG_ULP_SHARED_MEM, LENGTH = CONFIG_ULP_SHARED_MEM -#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM - hp_ram(RWX) : ORIGIN = ULP_HP_MEM_START, LENGTH = ULP_HP_MEM_SIZE -#endif -} - -#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM -REGION_ALIAS("default_app_seg", hp_ram); -#else -REGION_ALIAS("default_app_seg", lp_ram); -#endif - -SECTIONS -{ - .vector.text : - { - /* Exception/interrupt vectors */ - __mtvec_base = .; - KEEP (*(.init.vector .init.vector.*)) - } > vector_table - - . = ORIGIN(lp_ram); - - /* Interrupt/exception handlers stay in LP RAM (reachable on wakeup before HP SRAM is up). */ - .rtc_text ALIGN(4): - { - _lp_text_start = .; - *(.text.vectors) /* Default reset vector must link to offset 0x80 */ - *(.text.handlers) - *(.text.handlers.*) - } > lp_ram - -#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM - /* End of LP-resident code (handlers only); align to 128 bytes. */ - . = ALIGN(128); - _lp_text_end = .; - . = ORIGIN(hp_ram); -#endif - - .text ALIGN(4): - { - *(.text) - *(.text*) - } > default_app_seg - - .rodata ALIGN(4): - { - *(.rodata) - *(.rodata*) - } > default_app_seg - - /* 128-byte alignment required for PMP TOR granularity (SOC_CPU_PMP_REGION_GRANULARITY) */ - . = ALIGN(128); -#if !CONFIG_ULP_COPROC_RUN_FROM_HP_MEM - _lp_text_end = .; -#endif - _lp_data_start = .; - - .data ALIGN(4): - { - _data_start = .; - *(.data) - *(.data*) - *(.sdata) - *(.sdata*) - _data_end = .; - } > default_app_seg - - .bss ALIGN(4) : - { - _bss_start = .; - *(.bss) - *(.bss*) - *(.sbss) - *(.sbss*) - PROVIDE(end = .); - _bss_end = .; - } > default_app_seg - - __stack_top = ORIGIN(lp_ram) + LENGTH(lp_ram); - - . = ORIGIN(shared_mem_ram); - .shared_mem (ALIGN(4)) : - { - KEEP(*(.shared_mem)) - } > shared_mem_ram -} +#include "lp_core_riscv_checks.ld.in" diff --git a/components/ulp/ld/lp_core_riscv_base.ld.in b/components/ulp/ld/lp_core_riscv_base.ld.in new file mode 100644 index 00000000000..cad4307dce4 --- /dev/null +++ b/components/ulp/ld/lp_core_riscv_base.ld.in @@ -0,0 +1,120 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD + * SPDX-License-Identifier: Apache-2.0 + * + * Base part of the LP-core linker script (base + layout + checks). It defines: + * - the usable LP-RAM window bounds LP_CORE_USER_MEMORY_REGION_START and + * LP_CORE_USER_MEMORY_REGION_END; + * - ENTRY, the interrupt vector table, the HP/LP shared-memory section and a + * default __stack_top; + * - the section-boundary macros a custom layout composes: LP_CORE_TEXT_START, + * LP_CORE_TEXT_END, LP_CORE_DATA_START and LP_CORE_DATA_END, plus the + * LP_CORE_DEFAULT_TEXT / LP_CORE_DEFAULT_DATA convenience macros used by the + * default layout. + */ +#include "sdkconfig.h" +#include "soc/soc.h" +#include "ld.common" +#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM +#include "ld.hp_mem_defs" +#endif + +#if CONFIG_ESP_ROM_HAS_LP_ROM +/* With LP-ROM memory layout is different due to LP ROM stack/data */ +/* For P4 ECO5 we also reserve some RTC MEM at the first for MSPI workaround */ +#define ULP_MEM_START_ADDRESS SOC_RTC_DRAM_LOW + RESERVE_RTC_MEM + MSPI_WORKAROUND_SIZE +#else +#define ULP_MEM_START_ADDRESS (SOC_RTC_DRAM_LOW) +#endif + +#define ALIGN_DOWN(SIZE, AL) (SIZE & ~(AL - 1)) +/* Ensure the end where the shared memory starts is aligned to 8 bytes + if updating this also update the same in ulp_lp_core_memory_shared.c + */ +#define ALIGNED_COPROC_MEM ALIGN_DOWN(CONFIG_ULP_COPROC_RESERVE_MEM, 0x8) +#define ULP_VECTOR_TABLE_LENGTH 0x80 + +/* Free LP-RAM window a layout may use, between the vector table and shared memory. */ +#define LP_CORE_USER_MEMORY_REGION_START (ULP_MEM_START_ADDRESS + ULP_VECTOR_TABLE_LENGTH) +#define LP_CORE_USER_MEMORY_REGION_END (ULP_MEM_START_ADDRESS + ALIGNED_COPROC_MEM - CONFIG_ULP_SHARED_MEM) + +/* + * Section-boundary macros a custom layout composes at each region boundary. + * ESP-IDF injects any config-dependent handling here, so the layout composes + * them the same way regardless of the active configuration. + */ + +/* Reset vector and early handlers at the boot offset, placed first in `region` + (which must start at LP_CORE_USER_MEMORY_REGION_START). */ +#define LP_CORE_TEXT_START(region) \ + . = ORIGIN(region) ; \ + .rtc_text ALIGN(4) : { \ + *(.text.vectors) /* Reset vector must link to offset 0x80 */ \ + *(.text.handlers) \ + *(.text.handlers.*) \ + } > region + +/* End of the executable region in LP RAM: the RX/RW boundary the PMP setup uses + under memory protection. (Memory protection and running from HP RAM are + mutually exclusive, so the boundary always follows the LP-RAM .text here.) */ +#if CONFIG_ULP_LP_CORE_MEMPROT +#define LP_CORE_TEXT_END() . = ALIGN(128) ; _lp_text_end = . ; +#else +#define LP_CORE_TEXT_END() +#endif + +/* Start of the writable data region. */ +#define LP_CORE_DATA_START() . = ALIGN(128) ; _lp_data_start = . ; + +/* End of the writable data region, before the stack. Marks the lowest address + the stack may descend to: the framework derives the default __stack_size from + it and bounds-checks the stack against it. A layout must compose this (or + assign _lp_data_end directly). */ +#define LP_CORE_DATA_END() . = ALIGN(4) ; _lp_data_end = . ; + +/* Convenience: standard executable and data section placement into `region`. */ +#define LP_CORE_DEFAULT_TEXT(region) \ + .text ALIGN(4) : { *(.text) *(.text*) } > region \ + .rodata ALIGN(4) : { *(.rodata) *(.rodata*) } > region +#define LP_CORE_DEFAULT_DATA(region) \ + .data ALIGN(4) : { _data_start = . ; *(.data) *(.data*) *(.sdata) *(.sdata*) _data_end = . ; } > region \ + .bss ALIGN(4) : { _bss_start = . ; *(.bss) *(.bss*) *(.sbss) *(.sbss*) PROVIDE(end = .) ; _bss_end = . ; } > region + +/* Default stack top, at the top of the usable window. A layout whose data does + not reach the window end may override it to the top of its own data region. + The companion __stack_size defaults to __stack_top - _lp_data_end (computed in + the checks part, once the layout has placed its data); a layout may override + either. Both are weak defaults, so a custom layout can take ownership. */ +PROVIDE(__stack_top = LP_CORE_USER_MEMORY_REGION_END); + +ENTRY(reset_vector) + +MEMORY +{ + /* First 128 bytes for exception/interrupt vectors */ + vector_table(RX) : ORIGIN = ULP_MEM_START_ADDRESS, LENGTH = ULP_VECTOR_TABLE_LENGTH + shared_mem_ram(RW) : ORIGIN = LP_CORE_USER_MEMORY_REGION_END, LENGTH = CONFIG_ULP_SHARED_MEM +#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM + hp_ram(RWX) : ORIGIN = ULP_HP_MEM_START, LENGTH = ULP_HP_MEM_SIZE +#endif +} + +SECTIONS +{ + .vector.text : + { + /* Exception/interrupt vectors */ + __mtvec_base = .; + KEEP (*(.init.vector .init.vector.*)) + } > vector_table + + /* Shared memory, agreed with ulp_lp_core_memory_shared.c. */ + . = ORIGIN(shared_mem_ram); + _lp_shared_start = ORIGIN(shared_mem_ram); + .shared_mem (ALIGN(4)) : { KEEP(*(.shared_mem)) } > shared_mem_ram + + /* Position the location counter at the window start so the layout part's + sections flow into their region. A layout that omits LP_CORE_TEXT_START + is then caught by the checks part instead of an obscure overflow. */ + . = LP_CORE_USER_MEMORY_REGION_START; +} diff --git a/components/ulp/ld/lp_core_riscv_checks.ld.in b/components/ulp/ld/lp_core_riscv_checks.ld.in new file mode 100644 index 00000000000..bd50c619a6b --- /dev/null +++ b/components/ulp/ld/lp_core_riscv_checks.ld.in @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD + * SPDX-License-Identifier: Apache-2.0 + * + * Checks part (base + layout + checks). Link-time ASSERTs that catch + * misplacement, overflow, and missing symbols in the layout part. + */ +ASSERT(DEFINED(reset_vector), + "LP core linker: reset_vector is not defined") + +ASSERT(__mtvec_base == ULP_MEM_START_ADDRESS, + "LP core linker: vector table is not at the reserved LP base") + +ASSERT(reset_vector == LP_CORE_USER_MEMORY_REGION_START, + "LP core linker: reset vector is not at the required offset") + +/* The layout must mark the end of writable data so the framework can size and + bounds-check the stack. */ +ASSERT(DEFINED(_lp_data_end), + "LP core linker: end of writable data is not marked; compose LP_CORE_DATA_END() (or assign _lp_data_end) in the layout") + +/* Default stack size: all free space between the data end and the stack top. + A layout that assigns __stack_size wins (PROVIDE yields and does not evaluate + the expression). */ +PROVIDE(__stack_size = __stack_top - _lp_data_end); + +ASSERT(__stack_top <= _lp_shared_start, + "LP core linker: application overruns into the reserved shared-memory region") + +ASSERT(__stack_top <= ULP_MEM_START_ADDRESS + ALIGNED_COPROC_MEM, + "LP core linker: image exceeds the reserved LP coprocessor memory") + +/* The stack grows down from __stack_top for __stack_size bytes; its lowest + address must not descend into the data region. */ +ASSERT(__stack_top - __stack_size >= _lp_data_end, + "LP core linker: stack region overlaps the data region (__stack_size too large or __stack_top too low)") + +#if CONFIG_ULP_LP_CORE_MEMPROT +ASSERT(DEFINED(_lp_text_end), + "LP core linker: _lp_text_end must be defined when CONFIG_ULP_LP_CORE_MEMPROT is enabled (it marks the RX/RW boundary used by the PMP setup)") +#endif diff --git a/components/ulp/ld/lp_core_riscv_default_layout.ld.in b/components/ulp/ld/lp_core_riscv_default_layout.ld.in new file mode 100644 index 00000000000..624a4429f42 --- /dev/null +++ b/components/ulp/ld/lp_core_riscv_default_layout.ld.in @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD + * SPDX-License-Identifier: Apache-2.0 + * + * Default layout part (base + layout + checks). Replaced by a custom layout + * when LP_CORE_LINKER_INCLUDE is set via the LINKER_LAYOUT option. Routes the + * application to HP RAM when CONFIG_ULP_COPROC_RUN_FROM_HP_MEM is enabled. + */ +MEMORY +{ + lp_ram(RWX) : ORIGIN = LP_CORE_USER_MEMORY_REGION_START, LENGTH = LP_CORE_USER_MEMORY_REGION_END - LP_CORE_USER_MEMORY_REGION_START +} + +#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM +REGION_ALIAS("default_app_seg", hp_ram); +#else +REGION_ALIAS("default_app_seg", lp_ram); +#endif + +SECTIONS +{ + LP_CORE_TEXT_START(lp_ram) +#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM + . = ORIGIN(hp_ram); +#endif + + LP_CORE_DEFAULT_TEXT(default_app_seg) + + LP_CORE_TEXT_END() + LP_CORE_DATA_START() + + LP_CORE_DEFAULT_DATA(default_app_seg) + + LP_CORE_DATA_END() +}