forked from espressif/esp-idf
Merge branch 'contrib/github_pr_14954' into 'master'
fix(esp_ulp): Add support for multiple ULP program embedding without name collision (GitHub PR) Closes IDFGH-14152 and DOC-9881 See merge request espressif/esp-idf!35483
This commit is contained in:
@@ -8,4 +8,4 @@ include(IDFULPProject)
|
||||
|
||||
ulp_apply_default_options(${ULP_APP_NAME})
|
||||
ulp_apply_default_sources(${ULP_APP_NAME})
|
||||
ulp_add_build_binary_targets(${ULP_APP_NAME})
|
||||
ulp_add_build_binary_targets(${ULP_APP_NAME} PREFIX ${ULP_VAR_PREFIX})
|
||||
|
@@ -80,6 +80,7 @@ function(ulp_apply_default_sources ulp_app_name)
|
||||
|
||||
# To avoid warning "Manually-specified variables were not used by the project"
|
||||
set(bypassWarning "${IDF_TARGET}")
|
||||
set(bypassWarning "${ULP_VAR_PREFIX}")
|
||||
|
||||
if(CONFIG_ULP_COPROC_TYPE_RISCV)
|
||||
#risc-v ulp uses extra files for building:
|
||||
@@ -179,6 +180,10 @@ function(ulp_apply_default_sources ulp_app_name)
|
||||
endfunction()
|
||||
|
||||
function(ulp_add_build_binary_targets ulp_app_name)
|
||||
cmake_parse_arguments(ULP "" "PREFIX" "" ${ARGN})
|
||||
if(NOT ULP_PREFIX)
|
||||
set(ULP_PREFIX "ulp_")
|
||||
endif()
|
||||
|
||||
if(ADD_PICOLIBC_SPECS)
|
||||
target_compile_options(${ulp_app_name} PRIVATE $<$<COMPILE_LANG_AND_ID:C,GNU>:-specs=picolibc.specs>)
|
||||
@@ -195,7 +200,7 @@ function(ulp_add_build_binary_targets ulp_app_name)
|
||||
|
||||
# Dump the list of global symbols in a convenient format
|
||||
add_custom_command(OUTPUT ${ULP_APP_NAME}.sym
|
||||
COMMAND ${CMAKE_NM} -f posix -g $<TARGET_FILE:${ulp_app_name}> > ${ulp_app_name}.sym
|
||||
COMMAND ${CMAKE_READELF} -sW $<TARGET_FILE:${ulp_app_name}> > ${ulp_app_name}.sym
|
||||
DEPENDS ${ulp_app_name}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
@@ -206,7 +211,8 @@ function(ulp_add_build_binary_targets ulp_app_name)
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
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}
|
||||
COMMAND ${ULP_MAP_GEN} -s ${ulp_app_name}.sym -o ${ulp_app_name}
|
||||
--base ${ULP_BASE_ADDR} --prefix ${ULP_PREFIX}
|
||||
DEPENDS ${ulp_app_name}.sym
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
|
@@ -6,13 +6,20 @@
|
||||
# for the linker and a header file.
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import textwrap
|
||||
import typing
|
||||
|
||||
UTIL = os.path.basename(__file__)
|
||||
|
||||
|
||||
def gen_ld_h_from_sym(f_sym: typing.TextIO, f_ld: typing.TextIO, f_h: typing.TextIO, base_addr: int) -> None:
|
||||
def name_mangling(name: str) -> str:
|
||||
# Simple and dumb name mangling for namespaced name following GCC algorithm
|
||||
ns, n = name.split('::')
|
||||
return '_ZN{0}{1}{2}{3}E'.format(len(ns), ns, len(n), n)
|
||||
|
||||
|
||||
def gen_ld_h_from_sym(f_sym: typing.TextIO, f_ld: typing.TextIO, f_h: typing.TextIO, base_addr: int, prefix: str) -> None:
|
||||
f_ld.write(textwrap.dedent(
|
||||
f"""
|
||||
/* ULP variable definitions for the linker.
|
||||
@@ -20,52 +27,93 @@ def gen_ld_h_from_sym(f_sym: typing.TextIO, f_ld: typing.TextIO, f_h: typing.Tex
|
||||
*/
|
||||
""" # noqa: E222
|
||||
))
|
||||
f_h.write(textwrap.dedent(
|
||||
f"""
|
||||
/* ULP variable definitions for the compiler.
|
||||
* This file is generated automatically by {UTIL} utility.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
|
||||
#pragma once
|
||||
#ifdef __cplusplus
|
||||
extern "C" {{
|
||||
#endif
|
||||
""" # noqa: E222
|
||||
))
|
||||
cpp_mode = False
|
||||
var_prefix = prefix
|
||||
namespace = ''
|
||||
if '::' in prefix:
|
||||
# C++ mode, let's avoid the extern "C" type and instead use namespace
|
||||
f_h.write(textwrap.dedent(
|
||||
f"""
|
||||
/* ULP variable definitions for the compiler.
|
||||
* This file is generated automatically by {UTIL} utility.
|
||||
*/
|
||||
#pragma once
|
||||
""" # noqa: E222
|
||||
))
|
||||
tmp = prefix.split('::')
|
||||
namespace = tmp[0]
|
||||
var_prefix = '_'.join(tmp[1:]) # Limit to a single namespace here to avoid complex mangling rules
|
||||
f_h.write(f'namespace {namespace} {{\n')
|
||||
cpp_mode = True
|
||||
else:
|
||||
f_h.write(textwrap.dedent(
|
||||
f"""
|
||||
/* ULP variable definitions for the compiler.
|
||||
* This file is generated automatically by {UTIL} utility.
|
||||
*/
|
||||
#pragma once
|
||||
#ifdef __cplusplus
|
||||
extern "C" {{
|
||||
#endif\n
|
||||
""" # noqa: E222
|
||||
))
|
||||
|
||||
# Format the regular expression to match the readelf output
|
||||
expr = re.compile(r'^.*(?P<address>[a-f0-9]{8})\s+(?P<size>\d+) (OBJECT|NOTYPE)\s+GLOBAL\s+DEFAULT\s+[^ ]+ (?P<name>.*)$')
|
||||
for line in f_sym:
|
||||
# NM "posix" format output has the following structure:
|
||||
# symbol_name symbol_type addr_hex [size_hex]
|
||||
parts = line.split()
|
||||
name = parts[0]
|
||||
addr = int(parts[2], 16) + base_addr
|
||||
f_h.write('extern uint32_t ulp_{0};\n'.format(name))
|
||||
f_ld.write('PROVIDE ( ulp_{0} = 0x{1:08x} );\n'.format(name, addr))
|
||||
# readelf format output has the following structure:
|
||||
# Num: Value Size Type Bind Vis Ndx Name
|
||||
# So match the line with a regular expression to parse it first
|
||||
groups = expr.match(line)
|
||||
|
||||
f_h.write(textwrap.dedent(
|
||||
"""
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
))
|
||||
# Ignore lines that do not match the expected format such as non global symbols
|
||||
if groups is None:
|
||||
continue
|
||||
|
||||
# Extract the symbol information
|
||||
addr = int(groups.group('address'), 16) + base_addr
|
||||
size = int(groups.group('size'))
|
||||
sym_name = groups.group('name')
|
||||
|
||||
# Add the variable prefix if provided
|
||||
var_name = var_prefix + sym_name
|
||||
|
||||
# Get the dimension of the variable if it is an array
|
||||
var_dimension = f'[{size // 4}]' if size > 4 else '' # Use // to automatically perform an integer division
|
||||
|
||||
# Write the variable definition to the header file and the address to the linker script
|
||||
f_h.write(f'extern uint32_t {var_name}{var_dimension};\n')
|
||||
|
||||
name_in_ld = name_mangling(namespace + '::' + var_name) if cpp_mode else var_name
|
||||
f_ld.write(f'{name_in_ld} = 0x{addr:08x};\n')
|
||||
|
||||
if cpp_mode:
|
||||
f_h.write('}\n')
|
||||
else:
|
||||
f_h.write(textwrap.dedent(
|
||||
"""
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
))
|
||||
|
||||
|
||||
def main() -> None:
|
||||
description = ('This application generates .h and .ld files for symbols defined in input file. '
|
||||
'The input symbols file can be generated using nm utility like this: '
|
||||
'<PREFIX>nm -g -f posix <elf_file> > <symbols_file>')
|
||||
'The input symbols file can be generated using readelf utility like this: '
|
||||
'<PREFIX>readelf -sW <elf_file> > <symbols_file>')
|
||||
|
||||
parser = argparse.ArgumentParser(description=description)
|
||||
parser.add_argument('-s', '--symfile', required=True, help='symbols file name', metavar='SYMFILE', type=argparse.FileType('r'))
|
||||
parser.add_argument('-o', '--outputfile', required=True, help='destination .h and .ld files name prefix', metavar='OUTFILE')
|
||||
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_')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.outputfile + '.h', 'w', encoding='utf-8') as f_h, open(args.outputfile + '.ld', 'w', encoding='utf-8') as f_ld:
|
||||
gen_ld_h_from_sym(args.symfile, f_ld, f_h, int(args.base_addr, 0))
|
||||
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)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@@ -2,7 +2,7 @@
|
||||
#
|
||||
# Create ULP binary and embed into the application.
|
||||
|
||||
function(__setup_ulp_project app_name project_path s_sources exp_dep_srcs)
|
||||
function(__setup_ulp_project app_name project_path prefix s_sources exp_dep_srcs)
|
||||
|
||||
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
|
||||
spaces2list(s_sources)
|
||||
@@ -61,6 +61,7 @@ function(__setup_ulp_project app_name project_path s_sources exp_dep_srcs)
|
||||
-DULP_S_SOURCES=$<TARGET_PROPERTY:${app_name},ULP_SOURCES>
|
||||
-DULP_APP_NAME=${app_name}
|
||||
-DADD_PICOLIBC_SPECS=${CONFIG_LIBC_PICOLIBC}
|
||||
-DULP_VAR_PREFIX=${prefix}
|
||||
-DCOMPONENT_DIR=${COMPONENT_DIR}
|
||||
-DCOMPONENT_INCLUDES=$<TARGET_PROPERTY:${COMPONENT_TARGET},INTERFACE_INCLUDE_DIRECTORIES>
|
||||
-DIDF_TARGET=${idf_target}
|
||||
@@ -93,9 +94,14 @@ function(__setup_ulp_project app_name project_path s_sources exp_dep_srcs)
|
||||
endfunction()
|
||||
|
||||
function(ulp_embed_binary app_name s_sources exp_dep_srcs)
|
||||
__setup_ulp_project("${app_name}" "${idf_path}/components/ulp/cmake" "${s_sources}" "${exp_dep_srcs}")
|
||||
cmake_parse_arguments(ULP "" "PREFIX" "" ${ARGN})
|
||||
if(NOT ULP_PREFIX)
|
||||
set(ULP_PREFIX "ulp_")
|
||||
endif()
|
||||
__setup_ulp_project("${app_name}" "${idf_path}/components/ulp/cmake"
|
||||
"${ULP_PREFIX}" "${s_sources}" "${exp_dep_srcs}")
|
||||
endfunction()
|
||||
|
||||
function(ulp_add_project app_name project_path)
|
||||
__setup_ulp_project("${app_name}" "${project_path}" "" "")
|
||||
__setup_ulp_project("${app_name}" "${project_path}" "ulp_" "" "")
|
||||
endfunction()
|
||||
|
@@ -24,6 +24,8 @@ if(CONFIG_SOC_LP_VAD_SUPPORTED)
|
||||
list(APPEND app_sources "test_lp_core_vad.c")
|
||||
endif()
|
||||
|
||||
list(APPEND app_sources "test_lp_core_prefix.c")
|
||||
|
||||
set(lp_core_sources "lp_core/test_main.c")
|
||||
set(lp_core_sources_counter "lp_core/test_main_counter.c")
|
||||
|
||||
@@ -92,3 +94,6 @@ endif()
|
||||
if(CONFIG_SOC_LP_VAD_SUPPORTED)
|
||||
ulp_embed_binary(lp_core_test_app_vad "${lp_core_sources_vad}" "${lp_core_exp_dep_srcs}")
|
||||
endif()
|
||||
|
||||
ulp_embed_binary(lp_core_test_app_prefix1 "lp_core/test_main_prefix1.c" "${lp_core_exp_dep_srcs}" PREFIX "ulp1_")
|
||||
ulp_embed_binary(lp_core_test_app_prefix2 "lp_core/test_main_prefix2.c" "${lp_core_exp_dep_srcs}" PREFIX "ulp2_")
|
||||
|
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* Global variable with the same name across different ULP binaries */
|
||||
volatile int g_var;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
g_var = 1;
|
||||
|
||||
while (1)
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* A global variable to make sure that g_var doesn't have the same address across different ULP binaries */
|
||||
volatile int g_array[10];
|
||||
|
||||
/* Global variable with the same name across different ULP binaries */
|
||||
volatile int g_var;
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 10; i++) {
|
||||
g_array[i] = i;
|
||||
}
|
||||
g_var = 2;
|
||||
|
||||
while (1)
|
||||
;
|
||||
|
||||
return 0;
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "lp_core_test_app_prefix1.h"
|
||||
#include "lp_core_test_app_prefix2.h"
|
||||
#include "ulp_lp_core.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "test_shared.h"
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
|
||||
extern const uint8_t lp_core_main_prefix1_bin_start[] asm("_binary_lp_core_test_app_prefix1_bin_start");
|
||||
extern const uint8_t lp_core_main_prefix1_bin_end[] asm("_binary_lp_core_test_app_prefix1_bin_end");
|
||||
extern const uint8_t lp_core_main_prefix2_bin_start[] asm("_binary_lp_core_test_app_prefix2_bin_start");
|
||||
extern const uint8_t lp_core_main_prefix2_bin_end[] asm("_binary_lp_core_test_app_prefix2_bin_end");
|
||||
|
||||
static void load_and_start_lp_core_firmware(ulp_lp_core_cfg_t* cfg, const uint8_t* firmware_start, const uint8_t* firmware_end)
|
||||
{
|
||||
TEST_ASSERT(ulp_lp_core_load_binary(firmware_start,
|
||||
(firmware_end - firmware_start)) == ESP_OK);
|
||||
|
||||
TEST_ASSERT(ulp_lp_core_run(cfg) == ESP_OK);
|
||||
}
|
||||
|
||||
TEST_CASE("LP-Core header prefix test", "[lp_core]")
|
||||
{
|
||||
/* Load ULP firmware and start the coprocessor */
|
||||
ulp_lp_core_cfg_t cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||
#if ESP_ROM_HAS_LP_ROM
|
||||
.skip_lp_rom_boot = true,
|
||||
#endif //ESP_ROM_HAS_LP_ROM
|
||||
};
|
||||
|
||||
/* Load and run the first LP core firmware */
|
||||
load_and_start_lp_core_firmware(&cfg, lp_core_main_prefix1_bin_start, lp_core_main_prefix1_bin_end);
|
||||
vTaskDelay(10);
|
||||
|
||||
/* Verify the shared variable value */
|
||||
TEST_ASSERT_EQUAL(1, ulp1_g_var);
|
||||
|
||||
/* Load and run the second LP core firmware */
|
||||
load_and_start_lp_core_firmware(&cfg, lp_core_main_prefix2_bin_start, lp_core_main_prefix2_bin_end);
|
||||
vTaskDelay(10);
|
||||
|
||||
/* Verify that a global array can be accessed as an array on the HP CPU */
|
||||
for (int i = 0; i < 10; i++) {
|
||||
TEST_ASSERT_EQUAL(i, ulp2_g_array[i]);
|
||||
}
|
||||
|
||||
/* Verify that the shared variable with the same name is updated once the second LP core binary runs */
|
||||
TEST_ASSERT_EQUAL(2, ulp2_g_var);
|
||||
}
|
@@ -7,7 +7,7 @@ The ULP LP core (Low-power core) coprocessor is a variant of the ULP present in
|
||||
|
||||
The ULP LP core coprocessor has the following features:
|
||||
|
||||
* A RV32I (32-bit RISC-V ISA) processor, with the multiplication/division (M), atomic (A), and compressed (C) extensions.
|
||||
* An RV32I (32-bit RISC-V ISA) processor, with the multiplication/division (M), atomic (A), and compressed (C) extensions.
|
||||
* Interrupt controller.
|
||||
* Includes a debug module that supports external debugging via JTAG.
|
||||
* Can access all of the High-power (HP) SRAM and peripherals when the entire system is active.
|
||||
@@ -37,6 +37,21 @@ Using ``ulp_embed_binary``
|
||||
|
||||
The first argument to ``ulp_embed_binary`` specifies the ULP binary name. The name specified here is also used by other generated artifacts such as the ELF file, map file, header file, and linker export file. The second argument specifies the ULP source files. Finally, the third argument specifies the list of component source files which include the header file to be generated. This list is needed to build the dependencies correctly and ensure that the generated header file is created before any of these files are compiled. See the section below for the concept of generated header files for ULP applications.
|
||||
|
||||
Variables in the ULP code will be prefixed with ``ulp_`` (default value) in this generated header file.
|
||||
|
||||
If you need to embed multiple ULP programs, you may add a custom prefix in order to avoid conflicting variable names like this:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_register()
|
||||
|
||||
set(ulp_app_name ulp_${COMPONENT_NAME})
|
||||
set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S")
|
||||
set(ulp_exp_dep_srcs "ulp_c_source_file.c")
|
||||
|
||||
ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}" PREFIX "ULP::")
|
||||
|
||||
The additional PREFIX argument can be a C style prefix (like ``ulp2_``) or a C++ style prefix (like ``ULP::``).
|
||||
|
||||
Using a Custom CMake Project
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -140,13 +155,7 @@ The header file contains the declaration of the symbol:
|
||||
|
||||
extern uint32_t ulp_measurement_count;
|
||||
|
||||
Note that all symbols (variables, arrays, functions) are declared as ``uint32_t``. For functions and arrays, take the address of the symbol and cast it to the appropriate type.
|
||||
|
||||
The generated linker script file defines the locations of symbols in LP_MEM:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
||||
Note that all symbols (variables, functions) are declared as ``uint32_t``. Arrays are declared as ``uint32_t [SIZE]``. For functions, take the address of the symbol and cast it to the appropriate type.
|
||||
|
||||
To access the ULP LP core program variables from the main program, the generated header file should be included using an ``include`` statement. This allows the ULP LP core program variables to be accessed as regular variables.
|
||||
|
||||
@@ -160,7 +169,9 @@ To access the ULP LP core program variables from the main program, the generated
|
||||
|
||||
.. note::
|
||||
|
||||
Variables declared in the global scope of the LP core program reside in either the ``.bss`` or ``.data`` section of the binary. These sections are initialized when the LP core binary is loaded and executed. Accessing these variables from the main program on the HP-Core before the first LP core run may result in undefined behavior.
|
||||
- Variables declared in the global scope of the LP core program reside in either the ``.bss`` or ``.data`` section of the binary. These sections are initialized when the LP core binary is loaded and executed. Accessing these variables from the main program on the HP-Core before the first LP core run may result in undefined behavior.
|
||||
|
||||
- The ``ulp_`` prefix is the default value. You can specify the prefix to use with ``ulp_embed_binary`` to avoid name collisions for multiple ULP programs.
|
||||
|
||||
|
||||
Starting the ULP LP Core Program
|
||||
|
@@ -40,6 +40,21 @@ Using ``ulp_embed_binary``
|
||||
|
||||
The first argument to ``ulp_embed_binary`` specifies the ULP binary name. The name specified here is also used by other generated artifacts such as the ELF file, map file, header file, and linker export file. The second argument specifies the ULP source files. Finally, the third argument specifies the list of component source files which include the header file to be generated. This list is needed to build the dependencies correctly and ensure that the generated header file is created before any of these files are compiled. See the section below for the concept of generated header files for ULP applications.
|
||||
|
||||
Variables in the ULP code will be prefixed with ``ulp_`` (default value) in this generated header file.
|
||||
|
||||
If you need to embed multiple ULP programs, you may add a custom prefix in order to avoid conflicting variable names like this:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_register()
|
||||
|
||||
set(ulp_app_name ulp_${COMPONENT_NAME})
|
||||
set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S")
|
||||
set(ulp_exp_dep_srcs "ulp_c_source_file.c")
|
||||
|
||||
ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}" PREFIX "ULP::")
|
||||
|
||||
The additional PREFIX argument can be a C style prefix (like ``ulp2_``) or a C++ style prefix (like ``ULP::``).
|
||||
|
||||
Using a Custom CMake Project
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -143,11 +158,7 @@ The header file contains the declaration of the symbol:
|
||||
|
||||
extern uint32_t ulp_measurement_count;
|
||||
|
||||
Note that all symbols (variables, arrays, functions) are declared as ``uint32_t``. For functions and arrays, take the address of the symbol and cast it to the appropriate type.
|
||||
|
||||
The generated linker script file defines the locations of symbols in RTC_SLOW_MEM::
|
||||
|
||||
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
||||
Note that all symbols (variables, functions) are declared as ``uint32_t``. Arrays are declared as ``uint32_t [SIZE]``. For functions, take the address of the symbol and cast it to the appropriate type.
|
||||
|
||||
To access the ULP RISC-V program variables from the main program, the generated header file should be included using an ``include`` statement. This will allow the ULP RISC-V program variables to be accessed as regular variables.
|
||||
|
||||
@@ -161,8 +172,9 @@ To access the ULP RISC-V program variables from the main program, the generated
|
||||
|
||||
.. note::
|
||||
|
||||
Variables declared in the global scope of the ULP RISC-V program reside in either the ``.bss`` or ``.data`` section of the binary. These sections are initialized when the ULP RISC-V binary is loaded and executed. Accessing these variables from the main program on the main CPU before the first ULP RISC-V run may result in undefined behavior.
|
||||
- Variables declared in the global scope of the ULP RISC-V program reside in either the ``.bss`` or ``.data`` section of the binary. These sections are initialized when the ULP RISC-V binary is loaded and executed. Accessing these variables from the main program on the main CPU before the first ULP RISC-V run may result in undefined behavior.
|
||||
|
||||
- The ``ulp_`` prefix is the default value. You can specify the prefix to use with ``ulp_embed_binary`` to avoid name collisions for multiple ULP programs.
|
||||
|
||||
Mutual Exclusion
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
@@ -51,6 +51,22 @@ To compile the ULP FSM code as part of the component, the following steps must b
|
||||
|
||||
The first argument to ``ulp_embed_binary`` specifies the ULP FSM binary name. The name specified here will also be used by other generated artifacts such as the ELF file, map file, header file and linker export file. The second argument specifies the ULP FSM assembly source files. Finally, the third argument specifies the list of component source files which include the header file to be generated. This list is needed to build the dependencies correctly and ensure that the generated header file will be created before any of these files are compiled. See the section below for the concept of generated header files for ULP applications.
|
||||
|
||||
Variables in the ULP code will be prefixed with ``ulp_`` (default value) in this generated header file.
|
||||
|
||||
If you need to embed multiple ULP programs, you may add a custom prefix in order to avoid conflicting variable names like this:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_register()
|
||||
|
||||
set(ulp_app_name ulp_${COMPONENT_NAME})
|
||||
set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S")
|
||||
set(ulp_exp_dep_srcs "ulp_c_source_file.c")
|
||||
|
||||
ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}" PREFIX "ULP::")
|
||||
|
||||
The additional PREFIX argument can be a C style prefix (like ``ulp2_``) or a C++ style prefix (like ``ULP::``).
|
||||
|
||||
3. Build the application as usual (e.g., ``idf.py app``).
|
||||
|
||||
Inside, the build system will take the following steps to build ULP FSM program:
|
||||
|
@@ -37,6 +37,21 @@ ULP LP 内核代码会与 ESP-IDF 项目共同编译,生成一个单独的二
|
||||
|
||||
``ulp_embed_binary`` 的第一个参数指定生成的 ULP 二进制文件名。该文件名也用于其他生成的文件,如 ELF 文件、映射文件、头文件和链接器导出文件。第二个参数指定 ULP 源文件。第三个参数指定组件源文件列表,其中包括生成的头文件。此列表用以正确构建依赖,并确保在编译这些文件前创建要生成的头文件。有关 ULP 应用程序生成头文件的概念,请参阅本文档后续章节。
|
||||
|
||||
在这个生成的头文件中,ULP 代码中的变量默认以 ``ulp_`` 作为前缀。
|
||||
|
||||
如果需要嵌入多个 ULP 程序,可以添加自定义前缀,以避免变量名冲突,如下所示:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_register()
|
||||
|
||||
set(ulp_app_name ulp_${COMPONENT_NAME})
|
||||
set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S")
|
||||
set(ulp_exp_dep_srcs "ulp_c_source_file.c")
|
||||
|
||||
ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}" PREFIX "ULP::")
|
||||
|
||||
最后的 PREFIX 参数可以是 C 语言风格命名的前缀(如 ``ulp2_``)或 C++ 风格命名的前缀(如 ``ULP::``)。
|
||||
|
||||
使用自定义的 CMake 项目
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -140,13 +155,7 @@ ULP LP 内核代码会与 ESP-IDF 项目共同编译,生成一个单独的二
|
||||
|
||||
extern uint32_t ulp_measurement_count;
|
||||
|
||||
注意,所有的符号(变量、数组、函数)都被声明为 ``uint32_t`` 类型。对于函数和数组,获取符号的地址并将其转换为合适的类型。
|
||||
|
||||
生成的链接器脚本文件定义了 LP_MEM 中符号的位置:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
||||
注意,所有的符号(变量、函数)都被声明为 ``uint32_t`` 类型。数组被声明为 ``uint32_t [SIZE]`` 类型。函数需要先获取符号地址,再转换为适当的类型。
|
||||
|
||||
要从主程序访问 ULP LP 内核程序变量,应使用 ``include`` 语句将生成的头文件包含在主程序中,这样就可以像访问常规变量一样访问 ULP LP 内核程序变量。
|
||||
|
||||
@@ -160,7 +169,9 @@ ULP LP 内核代码会与 ESP-IDF 项目共同编译,生成一个单独的二
|
||||
|
||||
.. note::
|
||||
|
||||
LP 内核程序全局变量存储在二进制文件的 ``.bss`` 或者 ``.data`` 部分。这些部分在加载和执行 LP 内核二进制文件时被初始化。在首次运行 LP 内核之前,从 HP-Core 主程序访问这些变量可能会导致未定义行为。
|
||||
- LP 内核程序全局变量存储在二进制文件的 ``.bss`` 或者 ``.data`` 部分。这些部分在加载和执行 LP 内核二进制文件时被初始化。在首次运行 LP 内核之前,从 HP-Core 主程序访问这些变量可能会导致未定义行为。
|
||||
|
||||
- 默认以 ``ulp_`` 作为前缀。你可以在使用 ``ulp_embed_binary`` 时指定前缀,以避免多个 ULP 程序之间的命名冲突。
|
||||
|
||||
|
||||
启动 ULP LP 内核程序
|
||||
|
@@ -40,6 +40,21 @@ ULP RISC-V 协处理器代码以 C 语言(或汇编语言)编写,使用基
|
||||
|
||||
``ulp_embed_binary`` 的第一个参数指定生成的 ULP 二进制文件名。该文件名也用于其他生成的文件,如 ELF 文件、映射文件、头文件和链接器导出文件。第二个参数指定 ULP 源文件。第三个参数指定组件源文件列表,其中包括生成的头文件。此列表用以正确构建依赖,并确保在编译这些文件前创建要生成的头文件。有关 ULP 应用程序生成头文件的概念,请参阅本文档后续章节。
|
||||
|
||||
在这个生成的头文件中,ULP 代码中的变量默认以 ``ulp_`` 作为前缀。
|
||||
|
||||
如果需要嵌入多个 ULP 程序,可以添加自定义前缀,以避免变量名冲突,如下所示:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_register()
|
||||
|
||||
set(ulp_app_name ulp_${COMPONENT_NAME})
|
||||
set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S")
|
||||
set(ulp_exp_dep_srcs "ulp_c_source_file.c")
|
||||
|
||||
ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}" PREFIX "ULP::")
|
||||
|
||||
最后的 PREFIX 参数可以是 C 语言风格命名的前缀(如 ``ulp2_``)或 C++ 风格命名的前缀(如 ``ULP::``)。
|
||||
|
||||
使用自定义的 CMake 项目
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
@@ -143,11 +158,7 @@ ULP RISC-V 协处理器代码以 C 语言(或汇编语言)编写,使用基
|
||||
|
||||
extern uint32_t ulp_measurement_count;
|
||||
|
||||
注意,所有符号(包括变量、数组、函数)均被声明为 ``uint32_t``。函数和数组需要先获取符号地址,再转换为适当的类型。
|
||||
|
||||
生成的链接器文本定义了符号在 RTC_SLOW_MEM 中的位置::
|
||||
|
||||
PROVIDE ( ulp_measurement_count = 0x50000060 );
|
||||
注意,所有的符号(变量、函数)都被声明为 ``uint32_t`` 类型。数组被声明为 ``uint32_t [SIZE]`` 类型。函数需要先获取符号地址,再转换为适当的类型。
|
||||
|
||||
要从主程序访问 ULP RISC-V 程序变量,需使用 ``include`` 语句包含生成的头文件。这样,就可以像访问常规变量一样访问 ULP RISC-V 程序变量。
|
||||
|
||||
@@ -161,8 +172,9 @@ ULP RISC-V 协处理器代码以 C 语言(或汇编语言)编写,使用基
|
||||
|
||||
.. note::
|
||||
|
||||
ULP RISC-V 程序全局变量存储在二进制文件的 ``.bss`` 或者 ``.data`` 部分。这些部分在加载和执行 ULP RISC-V 二进制文件时被初始化。在首次运行 ULP RISC-V 之前,从主 CPU 上的主程序访问这些变量可能会导致未定义行为。
|
||||
- ULP RISC-V 程序全局变量存储在二进制文件的 ``.bss`` 或者 ``.data`` 部分。这些部分在加载和执行 ULP RISC-V 二进制文件时被初始化。在首次运行 ULP RISC-V 之前,从主 CPU 上的主程序访问这些变量可能会导致未定义行为。
|
||||
|
||||
- 默认以 ``ulp_`` 作为前缀。你可以在使用 ``ulp_embed_binary`` 时指定前缀,以避免多个 ULP 程序之间的命名冲突。
|
||||
|
||||
互斥
|
||||
^^^^^^^
|
||||
|
@@ -51,6 +51,22 @@ ULP FSM 协处理器代码由汇编语言编写,使用 `binutils-esp32ulp 工
|
||||
|
||||
``ulp_embed_binary`` 的第一个参数为 ULP 二进制文件命名。指定的此名称也用于生成的其他文件,如:ELF 文件、.map 文件、头文件和链接器导出文件。第二个参数指定 ULP FSM 程序集源文件。最后,第三个参数指定组件源文件列表,其中包括被生成的头文件。此列表用以建立正确的依赖项,并确保在编译这些文件之前先创建生成的头文件。有关 ULP FSM 应用程序生成的头文件等相关概念,请参考下文。
|
||||
|
||||
在这个生成的头文件中,ULP 代码中的变量默认以 ``ulp_`` 作为前缀。
|
||||
|
||||
如果需要嵌入多个 ULP 程序,可以添加自定义前缀,以避免变量名冲突,如下所示:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
idf_component_register()
|
||||
|
||||
set(ulp_app_name ulp_${COMPONENT_NAME})
|
||||
set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S")
|
||||
set(ulp_exp_dep_srcs "ulp_c_source_file.c")
|
||||
|
||||
ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}" PREFIX "ULP::")
|
||||
|
||||
最后的 PREFIX 参数可以是 C 语言风格命名的前缀(如 ``ulp2_``)或 C++ 风格命名的前缀(如 ``ULP::``)。
|
||||
|
||||
3. 使用常规方法(例如 ``idf.py app``)编译应用程序。
|
||||
|
||||
在内部,构建系统将按照以下步骤编译 ULP FSM 程序:
|
||||
|
Reference in New Issue
Block a user