Skip to main content
Explorer
June 13, 2025
Solved

ThreadX Modules with CMake

  • June 13, 2025
  • 3 replies
  • 654 views

Hello community,

I have been working towards getting a prototype ThreadX modules project on my board (Nucleo U545RE-Q) using CMake from a generated STM32CubeMX project. 

I have been loosely following the documentation https://github.com/eclipse-threadx/rtos-docs/blob/main/rtos-docs/threadx-modules/

I have been able to build a ThreadX Module as a library, and a ThreadX Module Manager as a library - CMake samples below:

# Interface library for includes and symbols
add_library(module_1 INTERFACE)
target_include_directories(module_1 INTERFACE ${MX_Include_Dirs})
target_compile_definitions(module_1 INTERFACE ${MX_Defines_Syms})

# Create STM32_Drivers static library
target_link_libraries(STM32_Drivers PUBLIC module_1)


# Create ThreadX static library

add_library(ThreadXModules OBJECT)
target_sources(ThreadXModules PRIVATE ${Module_sources})
target_link_libraries(ThreadXModules PUBLIC module_1)
# Interface library for includes and symbols
add_library(module_manager INTERFACE)
target_include_directories(module_manager INTERFACE ${MX_Include_Dirs})
target_compile_definitions(module_manager INTERFACE ${MX_Defines_Syms})

# Create STM32_Drivers static library
target_link_libraries(STM32_Drivers PUBLIC module_manager)


# Create ThreadX static library
add_library(ThreadX_ModMan OBJECT)
target_sources(ThreadX_ModMan PRIVATE ${ThreadX_Src} ${ThreadX_Module_Src} ${ThreadX_Module_Manager_Src})
target_link_libraries(ThreadX_ModMan PUBLIC module_manager)

These are added as subdirectories from the top level CMake and linked:

# Add STM32CubeMX generated sources
add_subdirectory(cmake/stm32cubemx)
add_subdirectory(cmake/module_manager)
add_subdirectory(cmake/module_1)

. . .

# Add linked libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
 stm32cubemx

 # Add user defined libraries
 module_1
 module_manager
)

As I am very new to this, I seem to be missing a final step that allows my module code to not be discarded as my .map file shows:

Discarded input sections
...
 .text.demo_module_start
 0x00000000 0x8c CMakeFiles/uart_out.dir/Core/Src/app_module.c.obj
 .text.MainThread_Entry
 0x00000000 0xbc CMakeFiles/uart_out.dir/Core/Src/app_module.c.obj

 I have taken care to ensure that my module 'main' is called 'demo_module_start' which corresponds to the name within my 'txm_module_preamble.s':

 /* Define public symbols. */
 .global __txm_module_preamble

 /* Define application-specific start/stop entry points for the module. */
 .global demo_module_start

 /* Define common external refrences. */
 .global _txm_module_thread_shell_entry
 .global _txm_module_callback_request_thread_entry

__txm_module_preamble:
 .dc.l 0x4D4F4455 // Module ID
 .dc.l 0x6 // Module Major Version
 .dc.l 0x1 // Module Minor Version
 .dc.l 32 // Module Preamble Size in 32-bit words
 .dc.l 0x12345678 // Module ID (application defined)
 .dc.l 0x02000007 // Module Properties where:
 // Bits 31-24: Compiler ID
 // 0 -> IAR
 // 1 -> ARM
 // 2 -> GNU
 // Bits 23-3: Reserved
 // Bit 2: 0 -> Disable shared/external memory access
 // 1 -> Enable shared/external memory access
 // Bit 1: 0 -> No MPU protection
 // 1 -> MPU protection (must have user mode selected - bit 0 set)
 // Bit 0: 0 -> Privileged mode execution
 // 1 -> User mode execution
 .dc.l _txm_module_thread_shell_entry - . - 0 // Module Shell Entry Point
 .dc.l demo_module_start - . - 0 // Module Start Thread Entry Point

 

What final step am I missing to allow 'demo_module_start' to be given an address such that I can call it from:

txm_module_manager_in_place_load(&my_module1, "my module1", (VOID *) ADDRESS_OF_MODULE_1);

My understanding is that as the module is not being called directly it must be getting thrown out, how do I force the linker to place it within the .map file?

 

Thanks in advance.

    This topic has been closed for replies.
    Best answer by dbit

    For those following along, I have been able to resolve this by implementing a few things.

    1. I have taken the sample linker file for the module editing to the correct memorydbit_0-1751450223532.png
    2. I have added the `gcc_setup.s` to my application sourcesdbit_2-1751450266246.png
    3. I have amended the `gcc-arm-none-eabi.cmake` to not attempt to link in a main or an `.init` into the map file by adding `-nostartfiles -e _txm_module_thread_shell_entry` to the C link flagsdbit_3-1751450293914.png

    This allowed me to load into the correct part of memory with the preamble being populated at the start of the memory section

    dbit_4-1751450342169.png

    To do so I have had to split my project such that two linker files are separately used, two elf files created and flashed one after another.  

    3 replies

    ST Employee
    June 19, 2025

    Hello @dbit ,

    To prevent your ThreadX module code (e.g., 'demo_module_start') from being discarded by the linker, you can modify your linker script to keep the relevant sections using the 'KEEP()' directive. For example, add this to your linker script:

    /* Keep ThreadX module preamble and entry point */
    KEEP(*(.txm_module_preamble))
    KEEP(*(.text.demo_module_start))
    KEEP(*(.text.demo_module_*))

    Also, ensure your module entry function is placed in the '.text.demo_module_start' section by adding this attribute in your C code:

    void demo_module_start(void) __attribute__((section(".text.demo_module_start"), used));

    This forces the linker to retain your module code so it can be loaded dynamically via 'txm_module_manager_in_place_load()'. 

    Kind regards,

    dbitAuthor
    Explorer
    June 24, 2025

    Thanks Khaled,

    I have in my linker already: 

    dbit_0-1750695264711.png

    to the corresponding region:

    dbit_1-1750695295782.png

    Then above every method and global I have the relevant attribute:

    dbit_2-1750695422529.png

    This allows the Module region to be populated:

    dbit_3-1750695518811.png

    However my module does not get loaded properly by the manager as it does not read the length of the code properly. 

    dbit_0-1750771788075.png

    Do you know why this could be?

    My thinking is that the ThreadX library itself needs to be within the section?

    dbitAuthor
    Explorer
    June 27, 2025

    As an extra, this is the memory within the section that I have loaded my module into:

    dbit_0-1751017925662.png

     

    dbitAuthorAnswer
    Explorer
    July 2, 2025

    For those following along, I have been able to resolve this by implementing a few things.

    1. I have taken the sample linker file for the module editing to the correct memorydbit_0-1751450223532.png
    2. I have added the `gcc_setup.s` to my application sourcesdbit_2-1751450266246.png
    3. I have amended the `gcc-arm-none-eabi.cmake` to not attempt to link in a main or an `.init` into the map file by adding `-nostartfiles -e _txm_module_thread_shell_entry` to the C link flagsdbit_3-1751450293914.png

    This allowed me to load into the correct part of memory with the preamble being populated at the start of the memory section

    dbit_4-1751450342169.png

    To do so I have had to split my project such that two linker files are separately used, two elf files created and flashed one after another.