mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-18 19:43:17 +03:00
feat(ulp): support placing ULP app in HP-RAM
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* ESP32-C5 target-specific HP memory definitions shared between HP app and LP Core linker scripts */
|
||||
|
||||
#define SRAM_SEG_END 0x4084E5A0 /* 2nd stage bootloader iram_loader_seg start address */
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
#define ULP_HP_MEM_SIZE CONFIG_ULP_COPROC_RESERVE_HP_MEM_BYTES
|
||||
#define ULP_HP_MEM_START (SRAM_SEG_END - ULP_HP_MEM_SIZE)
|
||||
#define ULP_HP_MEM_END SRAM_SEG_END
|
||||
#endif
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "ld.common"
|
||||
#include "ld.hp_mem_defs"
|
||||
|
||||
#if !CONFIG_SECURE_ENABLE_TEE
|
||||
#define SRAM_SEG_START (0x40800000)
|
||||
@@ -22,8 +23,11 @@
|
||||
#define FLASH_SEG_OFFSET (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE)
|
||||
#endif // CONFIG_SECURE_ENABLE_TEE
|
||||
|
||||
#define SRAM_SEG_END 0x4084E5A0 /* 2nd stage bootloader iram_loader_seg start address */
|
||||
#define SRAM_SEG_SIZE SRAM_SEG_END - SRAM_SEG_START
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
#define SRAM_SEG_SIZE ((ULP_HP_MEM_START) - (SRAM_SEG_START))
|
||||
#else
|
||||
#define SRAM_SEG_SIZE ((SRAM_SEG_END) - (SRAM_SEG_START))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* IDRAM0_2_SEG_SIZE_DEFAULT is used when page size is 64KB
|
||||
@@ -163,3 +167,8 @@ REGION_ALIAS("dram_seg", sram_seg);
|
||||
|
||||
ALIGN_VECTOR_TABLE = 0x100;
|
||||
OFFSET_TEE = 0x2b0;
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
_ulp_hp_mem_resv_start = ULP_HP_MEM_START;
|
||||
_ulp_hp_mem_resv_end = ULP_HP_MEM_END;
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* ESP32-C6 target-specific HP memory definitions shared between HP app and LP Core linker scripts */
|
||||
|
||||
#define SRAM_SEG_END 0x4086E610 /* 2nd stage bootloader iram_loader_seg start address */
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
#define ULP_HP_MEM_SIZE CONFIG_ULP_COPROC_RESERVE_HP_MEM_BYTES
|
||||
#define ULP_HP_MEM_START (SRAM_SEG_END - ULP_HP_MEM_SIZE)
|
||||
#define ULP_HP_MEM_END SRAM_SEG_END
|
||||
#endif
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "ld.common"
|
||||
#include "ld.hp_mem_defs"
|
||||
|
||||
#if !CONFIG_SECURE_ENABLE_TEE
|
||||
#define SRAM_SEG_START (0x40800000)
|
||||
@@ -22,8 +23,11 @@
|
||||
#define FLASH_SEG_OFFSET (CONFIG_SECURE_TEE_IROM_SIZE + CONFIG_SECURE_TEE_DROM_SIZE)
|
||||
#endif // CONFIG_SECURE_ENABLE_TEE
|
||||
|
||||
#define SRAM_SEG_END 0x4086E610 /* 2nd stage bootloader iram_loader_seg start address */
|
||||
#define SRAM_SEG_SIZE SRAM_SEG_END - SRAM_SEG_START
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
#define SRAM_SEG_SIZE ((ULP_HP_MEM_START) - (SRAM_SEG_START))
|
||||
#else
|
||||
#define SRAM_SEG_SIZE ((SRAM_SEG_END) - (SRAM_SEG_START))
|
||||
#endif
|
||||
|
||||
#if CONFIG_APP_BUILD_USE_FLASH_SECTIONS
|
||||
/*
|
||||
@@ -161,3 +165,8 @@ REGION_ALIAS("dram_seg", sram_seg);
|
||||
|
||||
ALIGN_VECTOR_TABLE = 0x100;
|
||||
OFFSET_TEE = 0x2e0;
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
_ulp_hp_mem_resv_start = ULP_HP_MEM_START;
|
||||
_ulp_hp_mem_resv_end = ULP_HP_MEM_END;
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* ESP32-P4 target-specific HP memory definitions shared between HP app and LP Core linker scripts */
|
||||
|
||||
#if CONFIG_ESP32P4_SELECTS_REV_LESS_V3
|
||||
#define SRAM_MAIN_END 0x4FF2CBD0 /* 2nd stage bootloader iram_loader_seg start address (rev < 3) */
|
||||
#else
|
||||
#define SRAM_MAIN_END 0x4FFAEFC0 /* 2nd stage bootloader iram_loader_seg start address (rev3+) */
|
||||
#endif
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
#define ULP_HP_MEM_SIZE CONFIG_ULP_COPROC_RESERVE_HP_MEM_BYTES
|
||||
/* Align down to 64-byte cache line boundary */
|
||||
#define ULP_HP_MEM_START ((SRAM_MAIN_END - ULP_HP_MEM_SIZE) & ~(64 - 1))
|
||||
/* Align down to 64-byte cache line boundary so that ULP_HP_MEM_END - ULP_HP_MEM_START
|
||||
* is always a multiple of the cache line size, satisfying esp_cache_msync alignment */
|
||||
#define ULP_HP_MEM_END (SRAM_MAIN_END & ~(64 - 1))
|
||||
#endif
|
||||
@@ -14,14 +14,23 @@
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "ld.common"
|
||||
#include "ld.hp_mem_defs"
|
||||
|
||||
#if !CONFIG_ESP32P4_SELECTS_REV_LESS_V3
|
||||
#define SRAM_START 0x4FF00000 + CONFIG_CACHE_L2_CACHE_SIZE
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
#define SRAM_END ULP_HP_MEM_START
|
||||
#else
|
||||
#define SRAM_END 0x4FFAEFC0 /* 2nd stage bootloader iram_loader_seg start address */
|
||||
#endif
|
||||
#define SRAM_SIZE SRAM_END - SRAM_START
|
||||
#else
|
||||
#define SRAM_LOW_START 0x4FF00000
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
#define SRAM_LOW_END ULP_HP_MEM_START
|
||||
#else
|
||||
#define SRAM_LOW_END 0x4FF2CBD0 /* 2nd stage bootloader iram_loader_seg start address */
|
||||
#endif
|
||||
#define SRAM_LOW_SIZE SRAM_LOW_END - SRAM_LOW_START
|
||||
|
||||
/* If the cache size is less than 512KB, then there is a region of RAM
|
||||
@@ -209,3 +218,8 @@ REGION_ALIAS("rtc_reserved_seg", lp_reserved_seg );
|
||||
#endif //#if CONFIG_SPIRAM_XIP_FROM_PSRAM
|
||||
|
||||
ALIGN_VECTOR_TABLE = 0x40;
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
_ulp_hp_mem_resv_start = ULP_HP_MEM_START;
|
||||
_ulp_hp_mem_resv_end = ULP_HP_MEM_END;
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* ESP32-S31 target-specific HP memory definitions shared between HP app and LP Core linker scripts */
|
||||
|
||||
#define SRAM_SEG_END 0x2F07AFC0 /* 2nd stage bootloader iram_loader_seg start address */
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
#define ULP_HP_MEM_SIZE CONFIG_ULP_COPROC_RESERVE_HP_MEM_BYTES
|
||||
#define ULP_HP_MEM_START (SRAM_SEG_END - ULP_HP_MEM_SIZE)
|
||||
#define ULP_HP_MEM_END SRAM_SEG_END
|
||||
#endif
|
||||
@@ -14,10 +14,14 @@
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "ld.common"
|
||||
#include "ld.hp_mem_defs"
|
||||
|
||||
#define SRAM_START 0x2F000000
|
||||
#define SRAM_END 0x2F07AFC0 /* 2nd stage bootloader iram_loader_seg start address */
|
||||
#define SRAM_SIZE SRAM_END - SRAM_START
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
#define SRAM_SIZE (ULP_HP_MEM_START - SRAM_START)
|
||||
#else
|
||||
#define SRAM_SIZE (SRAM_SEG_END - SRAM_START)
|
||||
#endif
|
||||
|
||||
#define IDROM_SEG_SIZE (CONFIG_MMU_PAGE_SIZE << 10)
|
||||
|
||||
@@ -116,3 +120,8 @@ REGION_ALIAS("rtc_reserved_seg", lp_reserved_seg);
|
||||
#endif //#if CONFIG_SPIRAM_XIP_FROM_PSRAM
|
||||
|
||||
ALIGN_VECTOR_TABLE = 0x40;
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
_ulp_hp_mem_resv_start = ULP_HP_MEM_START;
|
||||
_ulp_hp_mem_resv_end = ULP_HP_MEM_END;
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -115,10 +115,16 @@ SOC_RESERVE_MEMORY_REGION((intptr_t)&_data_start, (intptr_t)&_heap_start, dram_d
|
||||
// Target has a shared D/IRAM virtual address, no need to calculate I_D_OFFSET like previous chips
|
||||
SOC_RESERVE_MEMORY_REGION((intptr_t)&_iram_start, (intptr_t)&_iram_end, iram_code);
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
extern uint32_t _ulp_hp_mem_resv_start;
|
||||
extern uint32_t _ulp_hp_mem_resv_end;
|
||||
SOC_RESERVE_MEMORY_REGION((intptr_t)&_ulp_hp_mem_resv_start, (intptr_t)&_ulp_hp_mem_resv_end, ulp_hp_mem);
|
||||
#endif
|
||||
|
||||
/* NOTE: When ESP-TEE is enabled, the start of the internal SRAM
|
||||
* is used by the TEE and is protected from any REE access using
|
||||
* memory protection mechanisms employed by ESP-TEE.
|
||||
*/
|
||||
* is used by the TEE and is protected from any REE access using
|
||||
* memory protection mechanisms employed by ESP-TEE.
|
||||
*/
|
||||
#if CONFIG_SECURE_ENABLE_TEE
|
||||
SOC_RESERVE_MEMORY_REGION((intptr_t)SRAM_DIRAM_TEE_ORG, (intptr_t)(SRAM_DIRAM_TEE_END), tee_diram);
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -112,6 +112,12 @@ SOC_RESERVE_MEMORY_REGION((intptr_t)&_data_start, (intptr_t)&_heap_start, dram_d
|
||||
// Target has a shared D/IRAM virtual address, no need to calculate I_D_OFFSET like previous chips
|
||||
SOC_RESERVE_MEMORY_REGION((intptr_t)&_iram_start, (intptr_t)&_iram_end, iram_code);
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
extern uint32_t _ulp_hp_mem_resv_start;
|
||||
extern uint32_t _ulp_hp_mem_resv_end;
|
||||
SOC_RESERVE_MEMORY_REGION((intptr_t)&_ulp_hp_mem_resv_start, (intptr_t)&_ulp_hp_mem_resv_end, ulp_hp_mem);
|
||||
#endif
|
||||
|
||||
/* NOTE: When ESP-TEE is enabled, the start of the internal SRAM
|
||||
* is used by the TEE and is protected from any REE access using
|
||||
* memory protection mechanisms employed by ESP-TEE.
|
||||
|
||||
@@ -146,6 +146,12 @@ SOC_RESERVE_MEMORY_REGION((intptr_t)&_data_start, (intptr_t)&_heap_start, dram_d
|
||||
// Target has a shared D/IRAM virtual address, no need to calculate I_D_OFFSET like previous chips
|
||||
SOC_RESERVE_MEMORY_REGION((intptr_t)&_iram_start, (intptr_t)&_iram_end, iram_code);
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
extern uint32_t _ulp_hp_mem_resv_start;
|
||||
extern uint32_t _ulp_hp_mem_resv_end;
|
||||
SOC_RESERVE_MEMORY_REGION((intptr_t)&_ulp_hp_mem_resv_start, (intptr_t)&_ulp_hp_mem_resv_end, ulp_hp_mem);
|
||||
#endif
|
||||
|
||||
SOC_RESERVE_MEMORY_REGION((intptr_t)&_spm_text_start, (intptr_t)&_spm_data_end, spm_code_data);
|
||||
|
||||
#ifdef CONFIG_SPIRAM
|
||||
|
||||
@@ -97,6 +97,12 @@ SOC_RESERVE_MEMORY_REGION((intptr_t)&_data_start, (intptr_t)&_heap_start, dram_d
|
||||
// Target has a shared D/IRAM virtual address, no need to calculate I_D_OFFSET like previous chips
|
||||
SOC_RESERVE_MEMORY_REGION((intptr_t)&_iram_start, (intptr_t)&_iram_end, iram_code);
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
extern uint32_t _ulp_hp_mem_resv_start;
|
||||
extern uint32_t _ulp_hp_mem_resv_end;
|
||||
SOC_RESERVE_MEMORY_REGION((intptr_t)&_ulp_hp_mem_resv_start, (intptr_t)&_ulp_hp_mem_resv_end, ulp_hp_mem);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SPIRAM
|
||||
SOC_RESERVE_MEMORY_REGION( SOC_EXTRAM_LOW, SOC_EXTRAM_HIGH, extram_region);
|
||||
#endif
|
||||
|
||||
@@ -100,4 +100,5 @@ idf_component_register(
|
||||
INCLUDE_DIRS ${includes}
|
||||
REQUIRES esp_adc esp_driver_gpio esp_driver_uart esp_driver_i2s
|
||||
esp_hal_i2c esp_hal_touch_sens esp_hal_gpspi esp_hal_pmu esp_hal_rtc_timer esp_hal_clock
|
||||
PRIV_REQUIRES bootloader_support esp_mm
|
||||
)
|
||||
|
||||
@@ -81,6 +81,32 @@ menu "Ultra Low Power (ULP) Co-processor"
|
||||
Size of the shared memory defined in ulp_lp_core_memory_shared.c.
|
||||
Size should be kept in-sync with the size of the struct defined there.
|
||||
|
||||
menu "LP Core HP Memory"
|
||||
depends on ULP_COPROC_TYPE_LP_CORE
|
||||
|
||||
config ULP_COPROC_RUN_FROM_HP_MEM
|
||||
bool "Run LP Core from HP memory"
|
||||
depends on !SECURE_ENABLE_TEE
|
||||
default n
|
||||
help
|
||||
Enable this option to allow the LP Core application to use HP SRAM for
|
||||
code and data. This allows much larger LP Core applications than would
|
||||
fit in LP RAM.
|
||||
|
||||
Note: When this option is enabled, the LP Core cannot run during deep
|
||||
sleep because HP SRAM is powered off during sleep.
|
||||
|
||||
config ULP_COPROC_RESERVE_HP_MEM_BYTES
|
||||
hex "HP SRAM size reserved for LP Core (bytes)"
|
||||
depends on ULP_COPROC_RUN_FROM_HP_MEM
|
||||
default 0x10000
|
||||
range 0x1000 0x80000
|
||||
help
|
||||
Amount of HP SRAM (in bytes) to carve out from the top of the HP SRAM
|
||||
region for the LP Core application. This memory is not available to the
|
||||
HP application.
|
||||
endmenu
|
||||
|
||||
config ULP_ROM_PRINT_ENABLE
|
||||
depends on ULP_COPROC_TYPE_LP_CORE && ESP_ROM_HAS_LP_ROM
|
||||
bool
|
||||
|
||||
@@ -70,6 +70,7 @@ function(ulp_apply_default_sources ulp_app_name)
|
||||
list(APPEND ULP_PREPRO_ARGS -I${COMPONENT_DIR})
|
||||
list(APPEND ULP_PREPRO_ARGS -I${sdkconfig_dir})
|
||||
list(APPEND ULP_PREPRO_ARGS -I${IDF_PATH}/components/esp_system/ld)
|
||||
list(APPEND ULP_PREPRO_ARGS -I${IDF_PATH}/components/esp_system/ld/${IDF_TARGET})
|
||||
|
||||
target_include_directories(${ulp_app_name} PRIVATE ${COMPONENT_INCLUDES} ${sdkconfig_dir})
|
||||
|
||||
@@ -262,14 +263,23 @@ function(ulp_add_build_binary_targets ulp_app_name)
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Dump the binary for inclusion into the project
|
||||
add_custom_command(OUTPUT ${ulp_app_name}.bin
|
||||
COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${ulp_app_name}> ${ulp_app_name}.bin
|
||||
DEPENDS ${ulp_app_name}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
if(BUILD_LP_CORE AND CONFIG_ULP_COPROC_RUN_FROM_HP_MEM)
|
||||
add_custom_command(OUTPUT ${ulp_app_name}.bin
|
||||
COMMAND ${PYTHON} -m esptool --chip ${IDF_TARGET} elf2image
|
||||
--output ${ulp_app_name}.bin $<TARGET_FILE:${ulp_app_name}>
|
||||
DEPENDS ${ulp_app_name}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
else()
|
||||
add_custom_command(OUTPUT ${ulp_app_name}.bin
|
||||
COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${ulp_app_name}> ${ulp_app_name}.bin
|
||||
DEPENDS ${ulp_app_name}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
add_custom_command(OUTPUT ${ulp_app_name}.ld ${ulp_app_name}.h
|
||||
COMMAND ${ULP_MAP_GEN} -s ${ulp_app_name}.sym -o ${ulp_app_name}
|
||||
--base ${ULP_BASE_ADDR} --prefix ${ULP_PREFIX}
|
||||
--target ${IDF_TARGET}
|
||||
DEPENDS ${ulp_app_name}.sym
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
# SPDX-FileCopyrightText: 2016-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2016-2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# esp32ulp_mapgen utility converts a symbol list provided by nm into an export script
|
||||
@@ -19,8 +19,29 @@ def name_mangling(name: str) -> str:
|
||||
return f'_ZN{len(ns)}{ns}{len(n)}{n}E'
|
||||
|
||||
|
||||
# On ESP32-P4 the LP CPU accesses HP SRAM via the L2 cache at the same
|
||||
# address as the HP CPU cacheable alias. The HP application should access
|
||||
# LP Core variables through the non-cacheable alias to avoid coherency
|
||||
# issues with the LP CPU. The non-cacheable alias is obtained by adding
|
||||
# the offset 0x40000000 to addresses below 0x50000000.
|
||||
_P4_NON_CACHEABLE_OFFSET = 0x40000000
|
||||
_P4_LP_RAM_BASE = 0x50000000
|
||||
|
||||
|
||||
def map_to_hp_cpu_addr(addr: int, target: str | None) -> int:
|
||||
"""Translate an LP-CPU VMA to the address the HP CPU should use to access it."""
|
||||
if target == 'esp32p4' and addr < _P4_LP_RAM_BASE:
|
||||
return addr + _P4_NON_CACHEABLE_OFFSET
|
||||
return addr
|
||||
|
||||
|
||||
def gen_ld_h_from_sym(
|
||||
f_sym: typing.TextIO, f_ld: typing.TextIO, f_h: typing.TextIO, base_addr: int, prefix: str
|
||||
f_sym: typing.TextIO,
|
||||
f_ld: typing.TextIO,
|
||||
f_h: typing.TextIO,
|
||||
base_addr: int,
|
||||
prefix: str,
|
||||
target: str | None = None,
|
||||
) -> None:
|
||||
f_ld.write(
|
||||
textwrap.dedent(
|
||||
@@ -82,7 +103,7 @@ def gen_ld_h_from_sym(
|
||||
continue
|
||||
|
||||
# Extract the symbol information
|
||||
addr = int(groups.group('address'), 16) + base_addr
|
||||
addr = map_to_hp_cpu_addr(int(groups.group('address'), 16) + base_addr, target)
|
||||
size = int(groups.group('size'))
|
||||
sym_name = groups.group('name')
|
||||
|
||||
@@ -128,11 +149,17 @@ def main() -> None:
|
||||
)
|
||||
parser.add_argument('--base-addr', required=True, help='base address of the ULP memory, to be added to each symbol')
|
||||
parser.add_argument('-p', '--prefix', required=False, help='prefix for generated header file', default='ulp_')
|
||||
parser.add_argument(
|
||||
'--target',
|
||||
required=False,
|
||||
help='IDF target chip name (e.g. esp32p4), used for address translation',
|
||||
default=None,
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.outputfile + '.h', 'w') as f_h, open(args.outputfile + '.ld', 'w') as f_ld:
|
||||
gen_ld_h_from_sym(args.symfile, f_ld, f_h, int(args.base_addr, 0), args.prefix)
|
||||
gen_ld_h_from_sym(args.symfile, f_ld, f_h, int(args.base_addr, 0), args.prefix, args.target)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#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 */
|
||||
@@ -28,10 +31,19 @@ MEMORY
|
||||
{
|
||||
/*first 128byte for exception/interrupt vectors*/
|
||||
vector_table(RX) : ORIGIN = ULP_MEM_START_ADDRESS , LENGTH = 0x80
|
||||
ram(RWX) : ORIGIN = ULP_MEM_START_ADDRESS + 0x80, LENGTH = ALIGNED_COPROC_MEM - 0x80 - CONFIG_ULP_SHARED_MEM
|
||||
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 :
|
||||
@@ -41,39 +53,55 @@ SECTIONS
|
||||
KEEP (*(.init.vector .init.vector.*))
|
||||
} > vector_table
|
||||
|
||||
. = ORIGIN(ram);
|
||||
. = ORIGIN(lp_ram);
|
||||
|
||||
/* Interrupt/exception handlers and reset vector must remain in LP RAM so they
|
||||
* are accessible immediately on wakeup, before HP RAM is powered on. */
|
||||
.rtc_text ALIGN(4):
|
||||
{
|
||||
*(.text.vectors) /* Default reset vector must link to offset 0x80 */
|
||||
*(.text.handlers)
|
||||
*(.text.handlers.*)
|
||||
} > lp_ram
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
. = ORIGIN(default_app_seg);
|
||||
#endif
|
||||
|
||||
.text ALIGN(4):
|
||||
{
|
||||
*(.text.vectors) /* Default reset vector must link to offset 0x80 */
|
||||
*(.text)
|
||||
*(.text*)
|
||||
} >ram
|
||||
} > default_app_seg
|
||||
|
||||
.rodata ALIGN(4):
|
||||
{
|
||||
*(.rodata)
|
||||
*(.rodata*)
|
||||
} > ram
|
||||
} > default_app_seg
|
||||
|
||||
.data ALIGN(4):
|
||||
{
|
||||
_data_start = .;
|
||||
*(.data)
|
||||
*(.data*)
|
||||
*(.sdata)
|
||||
*(.sdata*)
|
||||
} > ram
|
||||
_data_end = .;
|
||||
} > default_app_seg
|
||||
|
||||
.bss ALIGN(4) :
|
||||
{
|
||||
_bss_start = .;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(.sbss)
|
||||
*(.sbss*)
|
||||
PROVIDE(end = .);
|
||||
} >ram
|
||||
_bss_end = .;
|
||||
} > default_app_seg
|
||||
|
||||
__stack_top = ORIGIN(ram) + LENGTH(ram);
|
||||
__stack_top = ORIGIN(lp_ram) + LENGTH(lp_ram);
|
||||
|
||||
. = ORIGIN(shared_mem_ram);
|
||||
.shared_mem (ALIGN(4)) :
|
||||
|
||||
@@ -30,6 +30,15 @@
|
||||
extern uint8_t _rtc_ulp_memory_start[];
|
||||
#endif //ESP_ROM_HAS_LP_ROM
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
#include "esp_image_format.h"
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
#include "esp_cache.h"
|
||||
#endif
|
||||
extern uint32_t _ulp_hp_mem_resv_start;
|
||||
extern uint32_t _ulp_hp_mem_resv_end;
|
||||
#endif //CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
|
||||
#if SOC_LP_CORE_HW_AUTO_CLRWAKEUPCAUSE
|
||||
#include "hal/lp_aon_hal.h"
|
||||
#include "rom/rtc.h"
|
||||
@@ -53,6 +62,33 @@ static uint32_t wakeup_src_sw_to_hw_flag_lookup[WAKEUP_SOURCE_MAX_NUMBER] = {
|
||||
#endif
|
||||
};
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
static uintptr_t ulp_lp_core_lp_mem_start(void)
|
||||
{
|
||||
#if ESP_ROM_HAS_LP_ROM
|
||||
return (uintptr_t)&_rtc_ulp_memory_start;
|
||||
#else
|
||||
return (uintptr_t)RTC_SLOW_MEM;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uintptr_t ulp_lp_core_lp_mem_end(void)
|
||||
{
|
||||
return ulp_lp_core_lp_mem_start() + CONFIG_ULP_COPROC_RESERVE_MEM;
|
||||
}
|
||||
|
||||
static bool ulp_lp_core_addr_range_in_region(uintptr_t start, size_t len, uintptr_t region_start, uintptr_t region_end)
|
||||
{
|
||||
uintptr_t end;
|
||||
|
||||
if (__builtin_add_overflow(start, (uintptr_t)len, &end)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return start >= region_start && end <= region_end;
|
||||
}
|
||||
#endif //CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
|
||||
/* Convert the wake-up sources defined in ulp_lp_core.h to the actual HW wake-up source values */
|
||||
static uint32_t lp_core_get_wakeup_source_hw_flags(uint32_t flags)
|
||||
{
|
||||
@@ -159,28 +195,117 @@ esp_err_t ulp_lp_core_run(ulp_lp_core_cfg_t* cfg)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
static esp_err_t ulp_lp_core_load_segments(const uint8_t *program_binary, size_t program_size_bytes)
|
||||
{
|
||||
if (program_size_bytes < sizeof(esp_image_header_t)) {
|
||||
ESP_LOGE(TAG, "Binary too small for image header");
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
const esp_image_header_t *image_header = (const esp_image_header_t *)program_binary;
|
||||
const uintptr_t lp_mem_start = ulp_lp_core_lp_mem_start();
|
||||
const uintptr_t lp_mem_end = ulp_lp_core_lp_mem_end();
|
||||
const uintptr_t hp_mem_start = (uintptr_t)&_ulp_hp_mem_resv_start;
|
||||
const uintptr_t hp_mem_end = (uintptr_t)&_ulp_hp_mem_resv_end;
|
||||
|
||||
if (image_header->magic != ESP_IMAGE_HEADER_MAGIC) {
|
||||
ESP_LOGE(TAG, "Invalid image magic 0x%02x, expected 0x%02x", image_header->magic, ESP_IMAGE_HEADER_MAGIC);
|
||||
return ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
|
||||
if (image_header->segment_count > ESP_IMAGE_MAX_SEGMENTS) {
|
||||
ESP_LOGE(TAG, "Segment count %d exceeds max %d", image_header->segment_count, ESP_IMAGE_MAX_SEGMENTS);
|
||||
return ESP_ERR_IMAGE_INVALID;
|
||||
}
|
||||
|
||||
const uint8_t *seg_ptr = program_binary + sizeof(esp_image_header_t);
|
||||
for (int i = 0; i < image_header->segment_count; i++) {
|
||||
size_t seg_offset = (size_t)(seg_ptr - program_binary);
|
||||
if (__builtin_add_overflow(seg_offset, sizeof(esp_image_segment_header_t), &seg_offset)
|
||||
|| seg_offset > program_size_bytes) {
|
||||
ESP_LOGE(TAG, "Segment %d header extends beyond binary size", i);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
const esp_image_segment_header_t *seg_hdr = (const esp_image_segment_header_t *)seg_ptr;
|
||||
seg_ptr += sizeof(esp_image_segment_header_t);
|
||||
|
||||
size_t data_offset = (size_t)(seg_ptr - program_binary);
|
||||
size_t data_end_offset;
|
||||
if (__builtin_add_overflow(data_offset, (size_t)seg_hdr->data_len, &data_end_offset)
|
||||
|| data_end_offset > program_size_bytes) {
|
||||
ESP_LOGE(TAG, "Segment %d extends beyond binary size", i);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
uintptr_t load_addr = (uintptr_t)seg_hdr->load_addr;
|
||||
uintptr_t load_end;
|
||||
if (__builtin_add_overflow(load_addr, (uintptr_t)seg_hdr->data_len, &load_end)) {
|
||||
ESP_LOGE(TAG, "Segment %d load range overflows", i);
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
bool load_in_lp_mem = ulp_lp_core_addr_range_in_region(load_addr, seg_hdr->data_len, lp_mem_start, lp_mem_end);
|
||||
bool load_in_hp_mem = ulp_lp_core_addr_range_in_region(load_addr, seg_hdr->data_len, hp_mem_start, hp_mem_end);
|
||||
|
||||
if (!load_in_lp_mem && !load_in_hp_mem) {
|
||||
ESP_LOGE(TAG, "Segment %d load range [0x%08x, 0x%08x) is outside LP/HP ULP memory", i,
|
||||
(unsigned)load_addr, (unsigned)load_end);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Loading segment %d: load_addr=0x%08x size=0x%x",
|
||||
i, (unsigned)seg_hdr->load_addr, (unsigned)seg_hdr->data_len);
|
||||
hal_memcpy((void *)(uintptr_t)seg_hdr->load_addr, seg_ptr, seg_hdr->data_len);
|
||||
seg_ptr += seg_hdr->data_len;
|
||||
}
|
||||
|
||||
#if SOC_CACHE_INTERNAL_MEM_VIA_L1CACHE
|
||||
/* Flush HP CPU write-back cache so the LP CPU sees the data written above.
|
||||
* Both _ulp_hp_mem_resv_start and _ulp_hp_mem_resv_end are aligned to the
|
||||
* L1 cache line size in the linker script, so the size is always a multiple
|
||||
* of the cache line size as required by esp_cache_msync. */
|
||||
size_t hp_mem_size = (uintptr_t)&_ulp_hp_mem_resv_end - (uintptr_t)&_ulp_hp_mem_resv_start;
|
||||
esp_cache_msync(&_ulp_hp_mem_resv_start, hp_mem_size,
|
||||
ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_INVALIDATE);
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif /* CONFIG_ULP_COPROC_RUN_FROM_HP_MEM */
|
||||
|
||||
esp_err_t ulp_lp_core_load_binary(const uint8_t* program_binary, size_t program_size_bytes)
|
||||
{
|
||||
if (program_binary == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (program_size_bytes > CONFIG_ULP_COPROC_RESERVE_MEM) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
|
||||
/* Turn off LP CPU before loading binary */
|
||||
ulp_lp_core_stop();
|
||||
|
||||
#if ESP_ROM_HAS_LP_ROM
|
||||
uint32_t* base = (uint32_t*)_rtc_ulp_memory_start;
|
||||
#else
|
||||
uint32_t* base = RTC_SLOW_MEM;
|
||||
#endif
|
||||
|
||||
//Start by clearing memory reserved with zeros, this will also will initialize the bss:
|
||||
/* Clear LP RAM — also initializes LP-side bss */
|
||||
hal_memset(base, 0, CONFIG_ULP_COPROC_RESERVE_MEM);
|
||||
|
||||
#if CONFIG_ULP_COPROC_RUN_FROM_HP_MEM
|
||||
ESP_LOGW(TAG, "Running ULP app from HP memory. ULP will not be able to run if system enters deep sleep and power down HP memory.");
|
||||
/* Clear HP RAM — initializes HP-side bss before the LP core runs */
|
||||
hal_memset(&_ulp_hp_mem_resv_start, 0, CONFIG_ULP_COPROC_RESERVE_HP_MEM_BYTES);
|
||||
return ulp_lp_core_load_segments(program_binary, program_size_bytes);
|
||||
#else
|
||||
if (program_size_bytes > CONFIG_ULP_COPROC_RESERVE_MEM) {
|
||||
return ESP_ERR_INVALID_SIZE;
|
||||
}
|
||||
hal_memcpy(base, program_binary, program_size_bytes);
|
||||
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ulp_lp_core_sleep_start(void)
|
||||
|
||||
@@ -16,7 +16,7 @@ extern "C" {
|
||||
#if SOC_LP_CORE_SINGLE_INTERRUPT_VECTOR
|
||||
#define LP_CORE_ISR_ATTR // On chips with just a single interrupt entry point registers are saved by us before calling the ISR
|
||||
#else
|
||||
#define LP_CORE_ISR_ATTR __attribute__((interrupt))
|
||||
#define LP_CORE_ISR_ATTR __attribute__((interrupt, section(".text.handlers")))
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,7 +44,7 @@ void __attribute__((weak)) ulp_lp_core_panic_handler(RvExcFrame *frame, int exc
|
||||
ulp_lp_core_abort();
|
||||
}
|
||||
|
||||
static void ulp_lp_core_default_intr_handler(void)
|
||||
static void __attribute__((section(".text.handlers"))) ulp_lp_core_default_intr_handler(void)
|
||||
{
|
||||
ulp_lp_core_abort();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,16 @@ components/ulp/test_apps/lp_core/lp_core_basic_tests:
|
||||
- esp_hw_support
|
||||
|
||||
|
||||
components/ulp/test_apps/lp_core/lp_core_hp_mem:
|
||||
disable:
|
||||
- if: SOC_LP_CORE_SUPPORTED != 1
|
||||
depends_components:
|
||||
- ulp
|
||||
- esp_hal_uart
|
||||
- esp_hal_pmu
|
||||
- esp_hw_support
|
||||
|
||||
|
||||
components/ulp/test_apps/lp_core/lp_core_hp_uart:
|
||||
disable:
|
||||
- if: SOC_LP_CORE_SUPPORTED != 1
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
list(PREPEND SDKCONFIG_DEFAULTS "$ENV{IDF_PATH}/tools/test_apps/configs/sdkconfig.debug_helpers" "sdkconfig.defaults")
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
"$ENV{IDF_PATH}/tools/test_apps/components"
|
||||
)
|
||||
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
set(COMPONENTS main)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(lp_core_hp_mem_test)
|
||||
@@ -0,0 +1,3 @@
|
||||
| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | ESP32-S31 |
|
||||
| ----------------- | -------- | -------- | -------- | --------- |
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
set(app_sources "test_app_main.c" "test_lp_core_hp_mem.c")
|
||||
set(lp_core_sources "lp_core/test_main.c")
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "lp_core"
|
||||
REQUIRES bootloader_support esp_hw_support ulp unity
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
set(lp_core_exp_dep_srcs ${app_sources})
|
||||
|
||||
ulp_embed_binary(lp_core_hp_mem_app "${lp_core_sources}" "${lp_core_exp_dep_srcs}")
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "ulp_lp_core_print.h"
|
||||
|
||||
volatile uint32_t hp_mem_data_marker = 0x12345678;
|
||||
volatile uint32_t hp_mem_bss_marker;
|
||||
volatile uint32_t hp_mem_done_marker;
|
||||
const uint32_t hp_mem_rodata_marker __attribute__((used)) = 0xA5A5F00D;
|
||||
|
||||
/* Pad rodata to push the LP Core binary above the LP RAM reservation limit
|
||||
* (CONFIG_ULP_COPROC_RESERVE_MEM), proving this binary can only run from HP RAM. */
|
||||
static const volatile uint8_t s_hp_mem_size_padding[50 * 1024] __attribute__((used)) = {[0] = 0xAA};
|
||||
|
||||
int main(void)
|
||||
{
|
||||
lp_core_printf("LP core running from HP memory\n");
|
||||
hp_mem_data_marker = 0x89ABCDEF;
|
||||
hp_mem_bss_marker = 0x13579BDF;
|
||||
hp_mem_done_marker = 0x2468ACE0;
|
||||
|
||||
lp_core_printf("s_hp_mem_size_padding[0] = 0x%02X\n", s_hp_mem_size_padding[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// Some resources are lazy allocated in the sleep code, the threshold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-500)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "esp_memory_utils.h"
|
||||
#include "soc/soc.h"
|
||||
#include "lp_core_hp_mem_app.h"
|
||||
#include "ulp_lp_core.h"
|
||||
|
||||
#define HP_MEM_DATA_INIT UINT32_C(0x12345678)
|
||||
#define HP_MEM_DATA_DONE UINT32_C(0x89ABCDEF)
|
||||
#define HP_MEM_BSS_DONE UINT32_C(0x13579BDF)
|
||||
#define HP_MEM_DONE_MARKER UINT32_C(0x2468ACE0)
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
extern uint32_t _ulp_hp_mem_resv_end;
|
||||
#endif
|
||||
|
||||
extern uint32_t _ulp_hp_mem_resv_start;
|
||||
|
||||
extern const uint8_t lp_core_hp_mem_bin_start[] asm("_binary_lp_core_hp_mem_app_bin_start");
|
||||
extern const uint8_t lp_core_hp_mem_bin_end[] asm("_binary_lp_core_hp_mem_app_bin_end");
|
||||
|
||||
static uintptr_t hp_mem_raw_start(void)
|
||||
{
|
||||
return (uintptr_t)&_ulp_hp_mem_resv_start;
|
||||
}
|
||||
|
||||
static uintptr_t hp_mem_raw_end(void)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
return (uintptr_t)&_ulp_hp_mem_resv_end;
|
||||
#else
|
||||
return hp_mem_raw_start() + CONFIG_ULP_COPROC_RESERVE_HP_MEM_BYTES;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uintptr_t hp_mem_access_start(void)
|
||||
{
|
||||
uintptr_t start = hp_mem_raw_start();
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
start += SOC_NON_CACHEABLE_OFFSET_SRAM;
|
||||
#endif
|
||||
return start;
|
||||
}
|
||||
|
||||
static uintptr_t hp_mem_access_end(void)
|
||||
{
|
||||
uintptr_t end = hp_mem_raw_end();
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
end += SOC_NON_CACHEABLE_OFFSET_SRAM;
|
||||
#endif
|
||||
return end;
|
||||
}
|
||||
|
||||
static bool addr_in_range(uintptr_t addr, uintptr_t start, uintptr_t end)
|
||||
{
|
||||
return addr >= start && addr < end;
|
||||
}
|
||||
|
||||
/* Verify that an LP Core variable pointer is in HP SRAM, not LP SRAM.
|
||||
* On P4, mapgen applies the non-cacheable alias offset (+0x40000000) to HP SRAM
|
||||
* symbols so the HP CPU can access them without L1 cache coherency issues.
|
||||
* Strip that offset before calling esp_ptr_in_dram(), which checks the HP SRAM range. */
|
||||
static bool lp_var_in_hp_sram(const void *ptr)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32P4
|
||||
ptr = (const void *)((uintptr_t)ptr - SOC_NON_CACHEABLE_OFFSET_SRAM);
|
||||
#endif
|
||||
return esp_ptr_in_dram(ptr);
|
||||
}
|
||||
|
||||
static uint32_t read_ulp_word(const uint32_t *symbol)
|
||||
{
|
||||
return *(volatile const uint32_t *)symbol;
|
||||
}
|
||||
|
||||
static void assert_segment_layout(const uint8_t *program_binary, size_t program_size_bytes)
|
||||
{
|
||||
const esp_image_header_t *image_header = (const esp_image_header_t *)program_binary;
|
||||
const uintptr_t hp_start = hp_mem_raw_start();
|
||||
const uintptr_t hp_end = hp_mem_raw_end();
|
||||
const uintptr_t lp_start = SOC_RTC_DRAM_LOW;
|
||||
const uintptr_t lp_end = lp_start + CONFIG_ULP_COPROC_RESERVE_MEM;
|
||||
const uint8_t *seg_ptr = program_binary + sizeof(esp_image_header_t);
|
||||
bool saw_lp_segment = false;
|
||||
bool saw_hp_segment = false;
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX8(ESP_IMAGE_HEADER_MAGIC, image_header->magic);
|
||||
|
||||
for (int i = 0; i < image_header->segment_count; i++) {
|
||||
const esp_image_segment_header_t *seg_hdr = (const esp_image_segment_header_t *)seg_ptr;
|
||||
seg_ptr += sizeof(esp_image_segment_header_t);
|
||||
|
||||
TEST_ASSERT_TRUE(((seg_ptr - program_binary) + seg_hdr->data_len) <= program_size_bytes);
|
||||
|
||||
printf("LP segment %d load_addr=0x%08" PRIx32 " size=0x%08" PRIx32 "\n",
|
||||
i, seg_hdr->load_addr, seg_hdr->data_len);
|
||||
|
||||
if (addr_in_range(seg_hdr->load_addr, hp_start, hp_end)) {
|
||||
saw_hp_segment = true;
|
||||
} else if (addr_in_range(seg_hdr->load_addr, lp_start, lp_end)) {
|
||||
saw_lp_segment = true;
|
||||
}
|
||||
|
||||
seg_ptr += seg_hdr->data_len;
|
||||
}
|
||||
|
||||
TEST_ASSERT_TRUE(saw_hp_segment);
|
||||
TEST_ASSERT_TRUE(saw_lp_segment);
|
||||
}
|
||||
|
||||
TEST_CASE("LP-Core can run from HP memory", "[lp_core][hp_mem]")
|
||||
{
|
||||
const uint8_t *program_binary = lp_core_hp_mem_bin_start;
|
||||
const size_t program_size_bytes = lp_core_hp_mem_bin_end - lp_core_hp_mem_bin_start;
|
||||
const uintptr_t hp_access_start = hp_mem_access_start();
|
||||
const uintptr_t hp_access_end = hp_mem_access_end();
|
||||
ulp_lp_core_cfg_t cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||
};
|
||||
|
||||
/* The LP binary must exceed the LP RAM reservation — it can only run from HP RAM */
|
||||
TEST_ASSERT_GREATER_THAN(CONFIG_ULP_COPROC_RESERVE_MEM, program_size_bytes);
|
||||
printf("LP Core binary size: 0x%08" PRIx32 " bytes\n", (uint32_t)program_size_bytes);
|
||||
|
||||
assert_segment_layout(program_binary, program_size_bytes);
|
||||
|
||||
printf("HP raw memory range: 0x%08" PRIxPTR " - 0x%08" PRIxPTR "\n", hp_mem_raw_start(), hp_mem_raw_end());
|
||||
printf("HP access memory range: 0x%08" PRIxPTR " - 0x%08" PRIxPTR "\n", hp_access_start, hp_access_end);
|
||||
printf("ULP reset vector: %p\n", &ulp_reset_vector);
|
||||
printf("ULP data marker: %p\n", &ulp_hp_mem_data_marker);
|
||||
printf("ULP bss marker: %p\n", &ulp_hp_mem_bss_marker);
|
||||
printf("ULP done marker: %p\n", &ulp_hp_mem_done_marker);
|
||||
|
||||
/* Address range checks using the HP access alias */
|
||||
TEST_ASSERT_FALSE(addr_in_range((uintptr_t)&ulp_reset_vector, hp_access_start, hp_access_end));
|
||||
TEST_ASSERT_TRUE(addr_in_range((uintptr_t)&ulp_hp_mem_data_marker, hp_access_start, hp_access_end));
|
||||
TEST_ASSERT_TRUE(addr_in_range((uintptr_t)&ulp_hp_mem_bss_marker, hp_access_start, hp_access_end));
|
||||
TEST_ASSERT_TRUE(addr_in_range((uintptr_t)&ulp_hp_mem_done_marker, hp_access_start, hp_access_end));
|
||||
|
||||
/* Verify HP mem variables are in HP SRAM (DRAM), not LP SRAM */
|
||||
TEST_ASSERT_TRUE(lp_var_in_hp_sram(&ulp_hp_mem_data_marker));
|
||||
TEST_ASSERT_TRUE(lp_var_in_hp_sram(&ulp_hp_mem_bss_marker));
|
||||
TEST_ASSERT_TRUE(lp_var_in_hp_sram(&ulp_hp_mem_done_marker));
|
||||
TEST_ASSERT_FALSE(lp_var_in_hp_sram(&ulp_reset_vector));
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, ulp_lp_core_load_binary(program_binary, program_size_bytes));
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX32(HP_MEM_DATA_INIT, read_ulp_word(&ulp_hp_mem_data_marker));
|
||||
TEST_ASSERT_EQUAL_HEX32(0, read_ulp_word(&ulp_hp_mem_bss_marker));
|
||||
TEST_ASSERT_EQUAL_HEX32(0, read_ulp_word(&ulp_hp_mem_done_marker));
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, ulp_lp_core_run(&cfg));
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
|
||||
TEST_ASSERT_EQUAL_HEX32(HP_MEM_DATA_DONE, read_ulp_word(&ulp_hp_mem_data_marker));
|
||||
TEST_ASSERT_EQUAL_HEX32(HP_MEM_BSS_DONE, read_ulp_word(&ulp_hp_mem_bss_marker));
|
||||
TEST_ASSERT_EQUAL_HEX32(HP_MEM_DONE_MARKER, read_ulp_word(&ulp_hp_mem_done_marker));
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
from pytest_embedded_idf.utils import idf_parametrize
|
||||
|
||||
|
||||
@pytest.mark.generic
|
||||
@idf_parametrize('target', ['esp32c5', 'esp32c6', 'esp32p4', 'esp32s31'], indirect=['target'])
|
||||
def test_lp_core_hp_mem(dut: Dut) -> None:
|
||||
dut.run_all_single_board_cases()
|
||||
@@ -0,0 +1,8 @@
|
||||
CONFIG_ESP_TASK_WDT_INIT=n
|
||||
|
||||
CONFIG_ULP_COPROC_ENABLED=y
|
||||
CONFIG_ULP_COPROC_TYPE_LP_CORE=y
|
||||
CONFIG_ULP_COPROC_RESERVE_MEM=12000
|
||||
CONFIG_ULP_COPROC_RUN_FROM_HP_MEM=y
|
||||
CONFIG_ULP_COPROC_RESERVE_HP_MEM_BYTES=0x10000
|
||||
CONFIG_ULP_HP_UART_CONSOLE_PRINT=y
|
||||
@@ -202,6 +202,15 @@ Once the program is loaded into LP memory, the application can be configured and
|
||||
|
||||
ESP_ERROR_CHECK( ulp_lp_core_run(&cfg) );
|
||||
|
||||
Running the LP Core from HP Memory
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:ref:`CONFIG_ULP_COPROC_RUN_FROM_HP_MEM` allows placing most of the LP core application in reserved HP SRAM instead of LP RAM. This can be useful when the application is too large to fit in LP RAM, while still keeping the LP reset and handler code in LP memory.
|
||||
|
||||
When this option is enabled, :ref:`CONFIG_ULP_COPROC_RESERVE_HP_MEM_BYTES` reserves a window at the top of HP SRAM for the LP core binary. During :cpp:func:`ulp_lp_core_load_binary`, LP-memory segments are still loaded into the reserved LP region, while data and code segments mapped to the HP-memory window are copied into the reserved HP SRAM region.
|
||||
|
||||
This mode has an important limitation: the LP core cannot keep running while the chip is in Deep-sleep, because HP SRAM is powered down in that sleep mode. Use this mode for cases where the LP core only needs to run while the HP system remains powered, and keep the default LP-memory-only mode for Deep-sleep use cases.
|
||||
|
||||
ULP LP Core Program Flow
|
||||
------------------------
|
||||
|
||||
|
||||
@@ -202,6 +202,15 @@ ULP LP 内核代码会与 ESP-IDF 项目共同编译,生成一个单独的二
|
||||
|
||||
ESP_ERROR_CHECK( ulp_lp_core_run(&cfg) );
|
||||
|
||||
从 HP 内存运行 LP 内核
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:ref:`CONFIG_ULP_COPROC_RUN_FROM_HP_MEM` 允许将 LP 内核应用的大部分代码和数据放到预留的 HP SRAM 中,而不是仅放在 LP RAM 中。当应用程序过大、无法完全放入 LP RAM 时,这种方式会很有用,同时仍然会将 LP 复位和处理程序代码保留在 LP 内存中。
|
||||
|
||||
启用该选项后,:ref:`CONFIG_ULP_COPROC_RESERVE_HP_MEM_BYTES` 会在 HP SRAM 顶部预留一段专供 LP 内核使用的内存窗口。在调用 :cpp:func:`ulp_lp_core_load_binary` 时,位于 LP 内存的段仍会加载到预留的 LP 区域,而映射到 HP 内存窗口的代码段和数据段则会复制到预留的 HP SRAM 区域。
|
||||
|
||||
该模式有一个重要限制:芯片进入 Deep-sleep 后,LP 内核无法继续运行,因为该睡眠模式下 HP SRAM 会被断电。因此,这种模式适用于 LP 内核只需要在 HP 系统保持上电时运行的场景;如果应用需要在 Deep-sleep 期间继续运行,则应继续使用默认的纯 LP 内存模式。
|
||||
|
||||
ULP LP 内核程序流程
|
||||
-------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user