ThreadX + lwIP Platform Port¶
Overview¶
OpenSOME/IP supports ThreadX + lwIP as a platform backend via the platform abstraction layer. This enables the SOME/IP stack to run on any ThreadX-based embedded system with lwIP networking, without coupling to any specific BSW framework.
CMake Flags¶
| Flag | Default | Description |
|---|---|---|
SOMEIP_USE_THREADX |
OFF |
Use ThreadX for threading primitives (Mutex, Thread, ConditionVariable, sleep_for) |
SOMEIP_USE_LWIP |
OFF |
Use lwIP socket API for networking (TCP/UDP sockets, byte-order) |
Both flags should be enabled together for a complete embedded build:
cmake -B build \
-DSOMEIP_USE_THREADX=ON \
-DSOMEIP_USE_LWIP=ON \
-DCMAKE_TOOLCHAIN_FILE=path/to/arm-none-eabi.cmake
Required ThreadX Configuration¶
ThreadX requires only a standard tx_api.h header. No special
configuration macros are needed beyond what a typical ThreadX port
provides. The backend uses:
| API | Purpose |
|---|---|
tx_mutex_create / tx_mutex_get / tx_mutex_put |
platform::Mutex with TX_INHERIT for priority inheritance |
tx_event_flags_create / tx_event_flags_set / tx_event_flags_get |
platform::ConditionVariable and Thread::join() |
tx_thread_create / tx_thread_terminate / tx_thread_delete |
platform::Thread with static stack |
tx_thread_sleep |
platform::this_thread::sleep_for |
tx_block_pool_create / tx_block_allocate / tx_block_release |
Message pool allocator |
Required lwIP Configuration¶
Same as the FreeRTOS port — see FREERTOS_PORT.md for the full lwIP configuration table. The key settings are:
| Setting | Value |
|---|---|
LWIP_SOCKET |
1 |
LWIP_COMPAT_SOCKETS |
1 (recommended) |
LWIP_TCP |
1 |
LWIP_UDP |
1 |
LWIP_IGMP |
1 |
Configurable Defines¶
| Define | Default | Description |
|---|---|---|
SOMEIP_THREADX_THREAD_STACK_SIZE |
4096 |
Stack size in bytes for SOME/IP worker threads (static allocation per Thread object) |
SOMEIP_THREADX_THREAD_PRIORITY |
16 |
ThreadX thread priority for SOME/IP threads (lower = higher priority in ThreadX) |
SOMEIP_THREADX_MESSAGE_POOL_SIZE |
16 |
Number of pre-allocated Message objects in the TX_BLOCK_POOL |
Key Differences from FreeRTOS Port¶
| Aspect | FreeRTOS | ThreadX |
|---|---|---|
| Mutex | Binary semaphore (xSemaphoreCreateMutex) |
TX_MUTEX with TX_INHERIT (native priority inheritance) |
| ConditionVariable | Counting semaphore | TX_EVENT_FLAGS_GROUP (flag bit 0) |
| Thread stack | Allocated by FreeRTOS kernel | Static UCHAR stack_[] member in Thread object |
| Trampoline signature | void(*)(void*) |
void(*)(ULONG) |
| Join mechanism | Binary semaphore | Event flags group (flag bit 0) |
| Memory pool | Manual bitmap + static buffer | TX_BLOCK_POOL (native ThreadX block allocator) |
| sleep_for | vTaskDelay(pdMS_TO_TICKS(ms)) |
tx_thread_sleep(ms * TX_TIMER_TICKS_PER_SECOND / 1000) |
Thread Lifecycle¶
- Creation:
tx_thread_create()with a static stack buffer (UCHAR stack_[SOMEIP_THREADX_THREAD_STACK_SIZE]). ATX_EVENT_FLAGS_GROUPis created forjoin()synchronization. The trampoline casts theULONGparameter back to aThread*. - join(): Blocks on the event flags group via
tx_event_flags_get()until the user function completes. Idempotent (second call is a no-op). After join, the thread control block is deleted and resources are cleaned up. - Destructor (task still running): Terminates the task via
tx_thread_terminate(), deletes it viatx_thread_delete(), then cleans up all resources.
ConditionVariable Semantics¶
ThreadX has no native condition variable. The backend uses a
TX_EVENT_FLAGS_GROUP with a single flag bit:
notify_one():tx_event_flags_set(ev, 0x1, TX_OR)— sets the flag. One waiter usingTX_OR_CLEARwill unblock and clear the flag.wait(Mutex& mtx): Unlocks the mutex, waits for flag bit 0 withTX_OR_CLEAR, re-locks the mutex.wait(Mutex& mtx, Pred pred): Predicate loop —while (!pred()) { wait(mtx); }.
Callers must use the predicate form to handle races.
Memory Pool¶
Message objects are allocated from a TX_BLOCK_POOL backed by a
static buffer:
- Initialization: Lazy, guarded by a
TX_MUTEX.tx_block_pool_create()is called once on first allocation. - Allocation:
tx_block_allocate()withTX_NO_WAIT. Returnsnullptron pool exhaustion. - Release: Calls the Message destructor, then
tx_block_release().
Integrator Example¶
cmake_minimum_required(VERSION 3.20)
project(my_embedded_app LANGUAGES CXX)
set(CMAKE_TOOLCHAIN_FILE "path/to/arm-none-eabi.cmake")
set(SOMEIP_USE_THREADX ON CACHE BOOL "")
set(SOMEIP_USE_LWIP ON CACHE BOOL "")
add_subdirectory(third_party/some-ip someip_build)
# Integrator provides ThreadX and lwIP headers + libs
target_include_directories(someip-core PUBLIC ${THREADX_INCLUDE_DIRS})
target_include_directories(someip-transport PUBLIC ${LWIP_INCLUDE_DIRS})
add_executable(my_app main.cpp)
target_link_libraries(my_app PRIVATE someip-transport someip-core threadx lwip)
CI Runtime Testing (ThreadX Linux Port)¶
Eclipse ThreadX includes an official linux port that runs the ThreadX
kernel as a native Linux process (using pthreads underneath). This
enables real ThreadX runtime testing in CI without any emulator or
hardware.
This option:
- Fetches Eclipse ThreadX v6.4.1 via FetchContent
- Builds ThreadX with THREADX_ARCH=linux, THREADX_TOOLCHAIN=gnu
- Enables SOMEIP_USE_THREADX=ON automatically
- Builds and runs test_threadx_core, which boots the real ThreadX
kernel (tx_kernel_enter()) and exercises Message, Endpoint,
Serializer, threading primitives, and the memory pool — all inside
a ThreadX thread.
The test binary calls exit() after completing, since
tx_kernel_enter() never returns.
Renode Testing (STM32F407 Cortex-M4)¶
For testing on actual ARM Cortex-M4 architecture, the project supports running tests on the Renode hardware simulator using a simulated STM32F407 Discovery board.
Running locally¶
The script handles the full workflow:
1. Cross-compiles with the threadx-cortexm4-renode CMake preset
2. Launches Renode headless with renode/stm32f4_test.resc
3. Captures USART2 output via CreateFileBackend
4. Parses [PASS]/[FAIL] results and generates JUnit XML
CMake preset¶
This preset:
- Uses the arm-none-eabi-gcc toolchain for Cortex-M4
- Enables SOMEIP_THREADX_RENODE_TESTS=ON and SOMEIP_USE_LWIP=ON
- Uses lwIP headers (fetched via FetchContent, not compiled) so the
build matches the real deployment configuration; byteorder macros
resolve to __builtin_bswap intrinsics via cmake/config/lwip/arch/cc.h
- Builds test_threadx_renode.elf linked with the shared BSP
(bsp/stm32f407_renode/) and ThreadX-specific config (tx_user.h)
Renode example¶
The examples/renode/threadx_pool_demo/ demo shows burst allocation
of SOME/IP messages using platform::allocate_message() (ThreadX block
pool), with a worker thread deserializing and hex-dumping messages on
real ARM code paths.
Supported Platform Combinations¶
| Platform | Threading Backend | Networking Backend | Status |
|---|---|---|---|
| POSIX (Linux/macOS) | std::thread |
POSIX sockets | Supported |
| Windows | std::thread |
Winsock2 | Supported |
| Zephyr | k_thread / k_mutex |
Zephyr zsock | Supported |
| FreeRTOS + lwIP | xTaskCreate / xSemaphore |
lwIP sockets | Supported |
| ThreadX + lwIP | tx_thread_create / TX_MUTEX |
lwIP sockets | Supported |