From c8b6b986b0b64da1cec7265bead57516c28ba5bb Mon Sep 17 00:00:00 2001 From: X-Ryl669 Date: Thu, 28 Nov 2024 14:03:46 +0100 Subject: [PATCH 1/2] fix(esp_ulp): Add support for multiple ULP program embedding without name collision d --- components/ulp/cmake/CMakeLists.txt | 2 +- components/ulp/cmake/IDFULPProject.cmake | 7 +- components/ulp/esp32ulp_mapgen.py | 97 +++++++++++++++--------- components/ulp/project_include.cmake | 12 ++- 4 files changed, 73 insertions(+), 45 deletions(-) diff --git a/components/ulp/cmake/CMakeLists.txt b/components/ulp/cmake/CMakeLists.txt index 9d42579931c..816a4006ab7 100644 --- a/components/ulp/cmake/CMakeLists.txt +++ b/components/ulp/cmake/CMakeLists.txt @@ -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} ${ULP_VAR_PREFIX}) diff --git a/components/ulp/cmake/IDFULPProject.cmake b/components/ulp/cmake/IDFULPProject.cmake index fd90cfd87ff..b1804e1e9e1 100644 --- a/components/ulp/cmake/IDFULPProject.cmake +++ b/components/ulp/cmake/IDFULPProject.cmake @@ -178,7 +178,7 @@ function(ulp_apply_default_sources ulp_app_name) endif() endfunction() -function(ulp_add_build_binary_targets ulp_app_name) +function(ulp_add_build_binary_targets ulp_app_name prefix) if(CONFIG_ULP_COPROC_TYPE_LP_CORE) set(ULP_BASE_ADDR "0x0") @@ -190,7 +190,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 $ > ${ulp_app_name}.sym + COMMAND ${CMAKE_READELF} -sW $ > ${ulp_app_name}.sym DEPENDS ${ulp_app_name} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) @@ -201,7 +201,7 @@ 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 ${prefix} DEPENDS ${ulp_app_name}.sym WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) @@ -209,7 +209,6 @@ function(ulp_add_build_binary_targets ulp_app_name) # ULP files being built. add_custom_target(build DEPENDS ${ulp_app_name} ${ulp_app_name}.bin ${ulp_app_name}.sym - ${CMAKE_CURRENT_BINARY_DIR}/${ulp_app_name}.ld ${CMAKE_CURRENT_BINARY_DIR}/${ulp_app_name}.h WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/components/ulp/esp32ulp_mapgen.py b/components/ulp/esp32ulp_mapgen.py index 382752269c3..cb59ea9d498 100755 --- a/components/ulp/esp32ulp_mapgen.py +++ b/components/ulp/esp32ulp_mapgen.py @@ -11,62 +11,87 @@ import os import textwrap import typing +import re 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: - f_ld.write(textwrap.dedent( - f""" - /* ULP variable definitions for the linker. - * This file is generated automatically by {UTIL} utility. - */ - """ - )) - 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 - """ - )) +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_h_from_sym(f_sym: typing.TextIO, f_h: typing.TextIO, base_addr: int, prefix: str) -> None: + cpp_mode = False + 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 + """ + )) + f_h.write("namespace {0} {{\n".format(prefix.split('::')[0])) + 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 + """ + )) + + expr = re.compile("^\\s*\\d+: ([a-f0-9]{8})\\s+(\\d+) OBJECT\\s+GLOBAL\\s+DEFAULT\\s+[^ ]+ (.*)$") 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: + # index: addr_hex size TYPE SCOPE DEFAULT junk symbol_name + # So match the line with a regular expression to parse it first + groups = expr.match(line) + if groups is None: # Ignore non global or non object + continue + addr = int(groups.group(1), 16) + base_addr + size = int(groups.group(2)) + name = groups.group(3) + if not cpp_mode: + name = prefix + name + f_h.write('extern uint32_t {0}{1};'.format(name, '[{0}]'.format(int(size/4)) if size > 4 else '')) + # Then set the address to find the variable + f_h.write(' asm(".equ {0}, 0x{1:08X}");\n'.format(name_mangling(prefix+name) if cpp_mode else name, addr)) - f_h.write(textwrap.dedent( - """ - #ifdef __cplusplus - } - #endif - """ - )) + 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: ' - 'nm -g -f posix > ') + 'readelf -sW > ') 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') 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)) + with open(args.outputfile + '.h', 'w') as f_h: + gen_h_from_sym(args.symfile, f_h, int(args.base_addr, 0), args.prefix) if __name__ == '__main__': diff --git a/components/ulp/project_include.cmake b/components/ulp/project_include.cmake index 47b2af53c40..1939f166f18 100644 --- a/components/ulp/project_include.cmake +++ b/components/ulp/project_include.cmake @@ -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) @@ -60,6 +60,7 @@ function(__setup_ulp_project app_name project_path s_sources exp_dep_srcs) -DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN_FLAG} -DULP_S_SOURCES=$ -DULP_APP_NAME=${app_name} + -DULP_VAR_PREFIX=${prefix} -DCOMPONENT_DIR=${COMPONENT_DIR} -DCOMPONENT_INCLUDES=$ -DIDF_TARGET=${idf_target} @@ -86,15 +87,18 @@ function(__setup_ulp_project app_name project_path s_sources exp_dep_srcs) add_dependencies(${COMPONENT_LIB} ${app_name}_artifacts) - target_linker_script(${COMPONENT_LIB} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}.ld) target_add_binary_data(${COMPONENT_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}.bin BINARY) endif() 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}") + __setup_ulp_project("${app_name}" "${idf_path}/components/ulp/cmake" "ulp_" "${s_sources}" "${exp_dep_srcs}") +endfunction() + +function(ulp_embed_binary_prefix app_name prefix s_sources exp_dep_srcs) + __setup_ulp_project("${app_name}" "${idf_path}/components/ulp/cmake" "${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() From 3903f11a23bb023c5487c87ddf9c7436e1183fd4 Mon Sep 17 00:00:00 2001 From: X-Ryl669 Date: Thu, 28 Nov 2024 14:35:38 +0100 Subject: [PATCH 2/2] (docs) Improve documentation for ulp_embed_binary changes --- docs/en/api-reference/system/ulp-lp-core.rst | 24 +++++++++++++------- docs/en/api-reference/system/ulp-risc-v.rst | 22 +++++++++++++----- docs/en/api-reference/system/ulp.rst | 15 ++++++++++++ 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/docs/en/api-reference/system/ulp-lp-core.rst b/docs/en/api-reference/system/ulp-lp-core.rst index 0e8a7ca2fec..92501bf19db 100644 --- a/docs/en/api-reference/system/ulp-lp-core.rst +++ b/docs/en/api-reference/system/ulp-lp-core.rst @@ -36,7 +36,21 @@ Using ``ulp_embed_binary`` ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}") 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 program, in order to avoid conflicting variable names, it's possible to modify the prefix 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_prefix(${ulp_app_name} "ULP::" "${ulp_sources}" "${ulp_exp_dep_srcs}") + +The additional argument can be a C style prefix (like ``ulp2_``) or a C++ style prefix (like ``ULP::``). Using a Custom CMake Project ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -140,13 +154,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``. Array 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. @@ -161,7 +169,7 @@ 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. - + The ``ulp_`` prefix is the default value. You can specify the prefix to use with ``ulp_embed_binary_prefix`` to avoid name collusions for multiple ULP program. Starting the ULP LP Core Program -------------------------------- diff --git a/docs/en/api-reference/system/ulp-risc-v.rst b/docs/en/api-reference/system/ulp-risc-v.rst index 926ca9c2e0a..3e98d0d0f8e 100644 --- a/docs/en/api-reference/system/ulp-risc-v.rst +++ b/docs/en/api-reference/system/ulp-risc-v.rst @@ -39,7 +39,21 @@ Using ``ulp_embed_binary`` ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}") 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 program, in order to avoid conflicting variable names, it's possible to modify the prefix 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_prefix(${ulp_app_name} "ULP::" "${ulp_sources}" "${ulp_exp_dep_srcs}") + +The additional argument can be a C style prefix (like ``ulp2_``) or a C++ style prefix (like ``ULP::``). Using a Custom CMake Project ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -143,11 +157,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``. Array 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. @@ -162,7 +172,7 @@ 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. - + The ``ulp_`` prefix is the default value. You can specify the prefix to use with ``ulp_embed_binary_prefix`` to avoid name collusions for multiple ULP program. Mutual Exclusion ^^^^^^^^^^^^^^^^ diff --git a/docs/en/api-reference/system/ulp.rst b/docs/en/api-reference/system/ulp.rst index 63337b693de..ef23d21f227 100644 --- a/docs/en/api-reference/system/ulp.rst +++ b/docs/en/api-reference/system/ulp.rst @@ -50,6 +50,21 @@ To compile the ULP FSM code as part of the component, the following steps must b ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}") 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 program, in order to avoid conflicting variable names, it's possible to modify the prefix 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_prefix(${ulp_app_name} "ULP::" "${ulp_sources}" "${ulp_exp_dep_srcs}") + +The additional 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``).