diff --git a/components/ulp/cmake/CMakeLists.txt b/components/ulp/cmake/CMakeLists.txt index 9d42579931c6..816a4006ab71 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 fd90cfd87ffb..e9c60da22e2d 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,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 ${prefix} DEPENDS ${ulp_app_name}.sym WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/components/ulp/esp32ulp_mapgen.py b/components/ulp/esp32ulp_mapgen.py index 382752269c3c..5bc1551aa50a 100755 --- a/components/ulp/esp32ulp_mapgen.py +++ b/components/ulp/esp32ulp_mapgen.py @@ -2,71 +2,105 @@ # esp32ulp_mapgen utility converts a symbol list provided by nm into an export script # for the linker and a header file. # -# SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD +# SPDX-FileCopyrightText: 2016-2024 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 - -from __future__ import print_function - 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. * 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 - """ + """ # 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('namespace {0} {{\n'.format(namespace)) + 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 + """ # noqa: E222 + )) + expr = re.compile('^\\s*\\d+: ([a-f0-9]{8})\\s+(\\d+) OBJECT\\s+GLOBAL\\s+DEFAULT\\s+[^ ]+ (.*)$') + already_defined = 'this_symbol_is_already_defined_please_use_prefix_in_ulp_embed_binary' 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 = var_prefix + groups.group(3) + f_h.write('extern uint32_t {0}{1};\n'.format(name, '[{0}]'.format(int(size / 4)) if size > 4 else '')) + f_ld.write('{0} = DEFINED({0}) ? {2} : 0x{1:08x};\n'.format( + name_mangling(namespace + '::' + name) if cpp_mode else name, addr, already_defined)) - 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 > ') + 'The input symbols file can be generated using readelf utility like this: ' + '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)) + gen_ld_h_from_sym(args.symfile, f_ld, 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 47b2af53c404..35b5d3953193 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} @@ -92,9 +93,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() diff --git a/docs/en/api-reference/system/ulp-lp-core.rst b/docs/en/api-reference/system/ulp-lp-core.rst index 4492527c2e9a..0c8cc97ebee6 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 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 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``. 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. @@ -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`` to avoid name collisions for multiple ULP programs. 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 926ca9c2e0a7..059e6dc1a669 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 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 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``. 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. @@ -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`` to avoid name collisions for multiple ULP programs. Mutual Exclusion ^^^^^^^^^^^^^^^^ diff --git a/docs/en/api-reference/system/ulp.rst b/docs/en/api-reference/system/ulp.rst index 63337b693de3..5ca31c84d09d 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 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 argument can be a C style prefix (like ``ulp2_``). 3. Build the application as usual (e.g., ``idf.py app``).