refactor(ulp): assemble LP-core linker scripts from base, layout and checks

This commit is contained in:
Sudeep Mohanty
2026-07-31 14:48:52 +02:00
parent 95e1386d55
commit 869f2f355f
4 changed files with 204 additions and 112 deletions
+8 -112
View File
@@ -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"
+120
View File
@@ -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;
}
@@ -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
@@ -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()
}