--- title: Chapter 2 - Module Requirements description: This article is a description of the requirements for build a ThreadX Module. --- # Chapter 2 - Module requirements A ThreadX Module contains a preamble, which defines the basic characteristics of the module. The preamble is followed by the instruction area of the module. Modules may be executed in place or they may be loaded into the module memory area by the Module Manager prior to execution. The only requirement is that the preamble is always located at the first address of the module. Figure 2 illustrates a basic module layout. | Module Layout | |:---:| | \[module preamble\] | | \[module instruction area\] | | \[module RAM area\] | **Figure 2** - Module Layout > **Note:** Modules must be built with the appropriate position independent code and data compiler/linker options. This enables execution of the module in any memory area. When a Module thread is created, a second stack space is allocated for use when the thread is in the memory-protected kernel. The size of the thread's kernel stack space is user-configurable using **TXM_MODULE_KERNEL_STACK_SIZE** in ***txm_module_port.h***. This allows a smaller stack size to be used when creating a Module thread, as the stack specified by the user when calling ***tx_thread_create*** is only used in the module. > **Note:** The top of a module thread stack contains the thread entry information structure (**TXM_MODULE_THREAD_ENTRY_INFO**), so the available stack size is decreased by the size of this structure. When creating a thread in a module, increase its stack size by at least this the size of this structure. The following steps are required for creating and building a ThreadX Module (each step is described in greater detail below). 1. All C files in a module must #define **TXM_MODULE** prior to including ***txm_module.h***. This can be accomplished in the source file being compiled or as part of the project settings. Doing so remaps the ThreadX API calls to the module-specific version of the API that invokes the dispatch function in the resident Module Manager to perform the call to the actual API function. 2. Each module must have a preamble at its first instruction area address which defines the characteristics and the resource needs of the module. 3. Each module must link the preamble at the beginning of the module instruction area. 4. Each module must link against a module library (***txm.a***), which contains module-specific functions used to interact with ThreadX. ## Module sources ThreadX Modules have their own set of source files that are designed to be linked and located directly with the module source code. These files provide the bridge between the separate module and resident Module Manager. The Module files are as follows. | File Name | Contents | |---|---| | **txm_module.h** | Include file that defines module information. | | **txm_module_port.h** | Include file that defines port-specific module information. | | **txm_module_user.h** | Defines and values the user can customize. | | **txm_module_initialize.s [1][3]** | Calls intrinsic functions to startup module. | | **txm_module_preamble.\{s/S/68\}** | Module preamble assembly file. This file defines various module-specific attributes and is linked with the module application code. | | **txm_module_application_request.c [1]** | Module application request function sends an application-specific request to the resident code. | | **txm_module_callback_request_thread_entry.c [1]** | Module callback thread that is responsible for processing callbacks requested by the module, including timers and notification callbacks. | | **txm_*.c [1][2]** | The standard ThreadX API services, these call the kernel dispatcher. | **txm_module_object_allocate.c [1]** | Module function to allocate memory for module objects located in the manager memory pool. | | **txm_module_object_deallocate.c [1]** | Module function to deallocate memory for module objects located in the manager memory pool. | | **txm_module_object_pointer_get.c [1]** | Module function to retrieve a pointer to a system object. | | **txm_module_object_pointer_get_extended.c [1]** | Module function to retrieve a pointer to a system object, name length safety. | | **txm_module_thread_shell_entry.c [1]** | Module thread entry function. | | **txm_module_thread_system_suspend.c [1]** | Module function to suspend a thread. | **[1]** Located in library ***txm.a***. **[2]** These files have the same name as the ThreadX API files, with **txm_** prefix instead of **tx_** prefix. **[3]** The **txm_module_initialize.s** file is only for ports using ARM tools. ## Module preamble The Module Preamble defines characteristics and resources of the module. Information such as the initial thread entry function and the initial memory area associated with the thread are defined in the preamble. Port-specific preamble examples are in the [appendix](appendix.md). Figure 3 shows an example ThreadX module preamble for a generic target (the lines starting with * are values typically modified by the application): ```c AREA Init, CODE, READONLY /* Define public symbols. */ EXPORT __txm_module_preamble /* Define application-specific start/stop entry points for the module. */ IMPORT demo_module_start /* Define common external references. */ IMPORT _txm_module_thread_shell_entry IMPORT _txm_module_callback_request_thread_entry IMPORT |Image$$ER_RO$$Length| IMPORT |Image$$ER_RW$$Length| __txm_module_preamble DCD 0x4D4F4455 ; Module ID DCD 0x6 ; Module Major Version DCD 0x1 ; Module Minor Version DCD 32 ; Module Preamble Size in 32-bit words * DCD 0x12345678 ; Module ID (application defined) * DCD 0x01000001 ; Module Properties where: ; Bits 31-24: Compiler ID ; 0 -> IAR ; 1 -> ARM ; 2 -> GNU ; Bits 23-1: Reserved ; Bit 0: 0 -> Privileged mode execution (no MMU protection) ; 1 -> User mode execution (MMU protection) DCD _txm_module_thread_shell_entry - . + . ; Module Shell Entry Point * DCD demo_module_start - . + . ; Module Start Thread Entry Point DCD 0 ; Module Stop Thread Entry Point * DCD 1 ; Module Start/Stop Thread Priority * DCD 2048 ; Module Start/Stop Thread Stack Size DCD _txm_module_callback_request_thread_entry - . + . ; Module Callback Thread Entry DCD 1 ; Module Callback Thread Priority DCD 2048 ; Module Callback Thread Stack Size DCD |Image$$ER_RO$$Length| ; Module Code Size DCD |Image$$ER_RW$$Length| ; Module Data Size DCD 0 ; Reserved 0 DCD 0 ; Reserved 1 DCD 0 ; Reserved 2 DCD 0 ; Reserved 3 DCD 0 ; Reserved 4 DCD 0 ; Reserved 5 DCD 0 ; Reserved 6 DCD 0 ; Reserved 7 DCD 0 ; Reserved 8 DCD 0 ; Reserved 9 DCD 0 ; Reserved 10 DCD 0 ; Reserved 11 DCD 0 ; Reserved 12 DCD 0 ; Reserved 13 DCD 0 ; Reserved 14 DCD 0 ; Reserved 15 END ``` **Figure 3** In most cases, the developer only needs to define the module's starting thread (offset 0x1C), module ID (offset 0x10), start/stop thread priority (offset 0x24), and start/stop thread stack size (offset 0x28). The demonstration above is set up such that the starting thread of the module is ***demo_module_start***, the module ID is ***0x12345678***, and the starting thread has a priority of ***1***, and a stack size of ***2048*** bytes. Some applications may optionally define a stopping thread, which is executed as the Module Manager stops the module. In addition, some applications might utilize the Module Properties field, defined as follows. ## Module properties bit map The table below shows an example of the properties bit map. Port-specific properties bitmaps are in the [appendix](appendix.md). | Bit | Value | Meaning | |---|---|---| | 0 | 0
1 | Privileged mode execution
User mode execution | | 1 | 0
1 | No MPU protection
MPU protection (must have user mode selected) | | 2 | 0
1 | Disable shared/external memory access
Enable shared/external memory access | | [23-3] | 0 | Reserved | [31-24] |
0x01
0x02
0x03 | **Compiler ID**
IAR
ARM
GNU | ## Module linker control file When building a module, the module preamble must be placed before any other code section. A module must be built with position-independent code and data sections. Port-specific example linker files are in the [appendix](appendix.md). ## Module ThreadX library Each module must link against a special, module-centric ThreadX library. This library provides access to ThreadX services in the resident code. Most of the access is accomplished via the ***txm_\*.c*** files. The following is an example of the module access call for the ThreadX API function ***tx_thread_relinquish*** (in ***\*txm_thread_relinquish.c\****). ```c (_txm_module_kernel_call_dispatcher)(TXM_THREAD_RELINQUISH_CALL, 0, 0, 0); ``` In this example, the function pointer supplied by the Module Manager is used to call the Module Manager dispatch function with the ID associated with the ***tx_thread_relinquish*** service. This service takes no parameters. ## Module example The following is an example of the standard ThreadX demonstration in the form of a module. The main differences between the standard ThreadX demonstration and the module demonstration are. 1. Replacement of ***tx_api.h*** with ***txm_module.h*** 2. Addition of **#define TXM_MODULE** prior to ***txm_module.h*** 3. Replacement of ***main*** and **tx_application_define** with ***demo_module_start*** 4. Declaring *pointers* to ThreadX objects rather than the objects themselves. ```c /* Specify that this is a module! */ #define TXM_MODULE /* Include the ThreadX module header. */ #include "txm_module.h" /* Define constants. */ #define DEMO_STACK_SIZE 1024 #define DEMO_BYTE_POOL_SIZE 9120 #define DEMO_BLOCK_POOL_SIZE 100 #define DEMO_QUEUE_SIZE 100 /* Define the pool space in the bss section of the module. ULONG is used to get word alignment. */ ULONG demo_module_pool_space[DEMO_BYTE_POOL_SIZE / 4]; /* Define the ThreadX object control block pointers. */ TX_THREAD *thread_0; TX_THREAD *thread_1; TX_THREAD *thread_2; TX_THREAD *thread_3; TX_THREAD *thread_4; TX_THREAD *thread_5; TX_THREAD *thread_6; TX_THREAD *thread_7; TX_QUEUE *queue_0; TX_SEMAPHORE *semaphore_0; TX_MUTEX *mutex_0; TX_EVENT_FLAGS_GROUP *event_flags_0; TX_BYTE_POOL *byte_pool_0; TX_BLOCK_POOL *block_pool_0; /* Define the counters used in the demo application. */ ULONG thread_0_counter; ULONG thread_1_counter; ULONG thread_1_messages_sent; ULONG thread_2_counter; ULONG thread_2_messages_received; ULONG thread_3_counter; ULONG thread_4_counter; ULONG thread_5_counter; ULONG thread_6_counter; ULONG thread_7_counter; ULONG semaphore_0_puts; ULONG event_0_sets; ULONG queue_0_sends; /* Define thread prototypes. */ void thread_0_entry(ULONG thread_input); void thread_1_entry(ULONG thread_input); void thread_2_entry(ULONG thread_input); void thread_3_and_4_entry(ULONG thread_input); void thread_5_entry(ULONG thread_input); void thread_6_and_7_entry(ULONG thread_input); /* Define notify functions. */ void semaphore_0_notify(TX_SEMAPHORE *semaphore_ptr) { if (semaphore_ptr == semaphore_0) semaphore_0_puts++; } void event_0_notify(TX_EVENT_FLAGS_GROUP *event_flag_group_ptr) { if (event_flag_group_ptr == event_flags_0) event_0_sets++; } void queue_0_notify(TX_QUEUE *queue_ptr) { if (queue_ptr == queue_0) queue_0_sends++; } /* Define the module start function. */ void demo_module_start(ULONG id) { CHAR *pointer; /* Allocate all the objects. In memory protection mode, modules cannot allocate control blocks within their own memory area so they cannot corrupt the resident portion of ThreadX by corrupting the control block(s). */ txm_module_object_allocate(&thread_0, sizeof(TX_THREAD)); txm_module_object_allocate(&thread_1, sizeof(TX_THREAD)); txm_module_object_allocate(&thread_2, sizeof(TX_THREAD)); txm_module_object_allocate(&thread_3, sizeof(TX_THREAD)); txm_module_object_allocate(&thread_4, sizeof(TX_THREAD)); txm_module_object_allocate(&thread_5, sizeof(TX_THREAD)); txm_module_object_allocate(&thread_6, sizeof(TX_THREAD)); txm_module_object_allocate(&thread_7, sizeof(TX_THREAD)); txm_module_object_allocate(&queue_0, sizeof(TX_QUEUE)); txm_module_object_allocate(&semaphore_0, sizeof(TX_SEMAPHORE)); txm_module_object_allocate(&mutex_0, sizeof(TX_MUTEX)); txm_module_object_allocate(&event_flags_0, sizeof(TX_EVENT_FLAGS_GROUP)); txm_module_object_allocate(&byte_pool_0, sizeof(TX_BYTE_POOL)); txm_module_object_allocate(&block_pool_0, sizeof(TX_BLOCK_POOL)); /* Create a byte memory pool from which to allocate the thread stacks. */ tx_byte_pool_create(byte_pool_0, "module byte pool 0", demo_module_pool_space, DEMO_BYTE_POOL_SIZE); /* Allocate the stack for thread 0. */ tx_byte_allocate(byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); /* Create thread 0. */ tx_thread_create(thread_0, "module thread 0", thread_0_entry, 0, pointer, DEMO_STACK_SIZE, 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); /* Allocate the stack for thread 1. */ tx_byte_allocate(byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); /* Create threads 1 and 2. These threads pass information through a ThreadX message queue. It is also interesting to note that these threads have a time slice. */ tx_thread_create(thread_1, "module thread 1", thread_1_entry, 1, pointer, DEMO_STACK_SIZE, 16, 16, 4, TX_AUTO_START); /* Allocate the stack and create thread 2. */ tx_byte_allocate(byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); tx_thread_create(thread_2, "module thread 2", thread_2_entry, 2, pointer, DEMO_STACK_SIZE, 16, 16, 4, TX_AUTO_START); /* Allocate the stack for thread 3. */ tx_byte_allocate(byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. An interesting thing here is that both threads share the same instruction area. */ tx_thread_create(thread_3, "module thread 3", thread_3_and_4_entry, 3, pointer, DEMO_STACK_SIZE, 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); /* Allocate the stack and create thread 4. */ tx_byte_allocate(byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); tx_thread_create(thread_4, "module thread 4", thread_3_and_4_entry, 4, pointer, DEMO_STACK_SIZE, 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); /* Allocate the stack for thread 5. */ tx_byte_allocate(byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); /* Create thread 5. This thread simply pends on an event flag which will be set by thread 0. */ tx_thread_create(thread_5, "module thread 5", thread_5_entry, 5, pointer, DEMO_STACK_SIZE, 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); /* Allocate the stack for thread 6. */ tx_byte_allocate(byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ tx_thread_create(thread_6, "module thread 6", thread_6_and_7_entry, 6, pointer, DEMO_STACK_SIZE, 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); /* Allocate the stack and create thread 7. */ tx_byte_allocate(byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); tx_thread_create(thread_7, "module thread 7", thread_6_and_7_entry, 7, pointer, DEMO_STACK_SIZE, 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); /* Allocate the message queue. */ tx_byte_allocate(byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); /* Create the message queue shared by threads 1 and 2. */ tx_queue_create(queue_0, "module queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); /* Register queue send callback. */ tx_queue_send_notify(queue_0, queue_0_notify); /* Create the semaphore used by threads 3 and 4. */ tx_semaphore_create(semaphore_0, "module semaphore 0", 1); /* Register semaphore put callback. */ tx_semaphore_put_notify(semaphore_0, semaphore_0_notify); /* Create the event flags group used by threads 1 and 5. */ tx_event_flags_create(event_flags_0, "module event flags 0"); /* Register event flag set callback. */ tx_event_flags_set_notify(event_flags_0, event_0_notify); /* Create the mutex used by thread 6 and 7 without priority inheritance. */ tx_mutex_create(mutex_0, "module mutex 0", TX_NO_INHERIT); /* Allocate the memory for a small block pool. */ tx_byte_allocate(byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); /* Create a block memory pool. */ tx_block_pool_create(block_pool_0, "module block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); /* Allocate a block. */ tx_block_allocate(block_pool_0, (VOID **) &pointer, TX_NO_WAIT); /* Release the block back to the pool. */ tx_block_release(pointer); } /* Define all the threads. */ void thread_0_entry(ULONG thread_input) { UINT status; /* This thread simply sits in while-forever-sleep loop. */ while(1) { /* Increment the thread counter. */ thread_0_counter++; /* Sleep for 10 ticks. */ tx_thread_sleep(10); /* Set event flag 0 to wake up thread 5. */ status = tx_event_flags_set(event_flags_0, 0x1, TX_OR); /* Check status. */ if (status != TX_SUCCESS) break; } } void thread_1_entry(ULONG thread_input) { UINT status; /* This thread simply sends messages to a queue shared by thread 2. */ while(1) { /* Increment the thread counter. */ thread_1_counter++; /* Send message to queue 0. */ status = tx_queue_send(queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); /* Check completion status. */ if (status != TX_SUCCESS) break; /* Increment the message sent. */ thread_1_messages_sent++; } } void thread_2_entry(ULONG thread_input) { ULONG received_message; UINT status; /* This thread retrieves messages placed on the queue by thread 1. */ while(1) { /* Increment the thread counter. */ thread_2_counter++; /* Retrieve a message from the queue. */ status = tx_queue_receive(queue_0, &received_message, TX_WAIT_FOREVER); /* Check completion status and make sure the message is what we expected. */ if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)) break; /* Otherwise, all is okay. Increment the received message count. */ thread_2_messages_received++; } } void thread_3_and_4_entry(ULONG thread_input) { UINT status; /* This function is executed from thread 3 and thread 4. As the loop below shows, these function compete for ownership of semaphore_0. */ while(1) { /* Increment the thread counter. */ if (thread_input == 3) thread_3_counter++; else thread_4_counter++; /* Get the semaphore with suspension. */ status = tx_semaphore_get(semaphore_0, TX_WAIT_FOREVER); /* Check status. */ if (status != TX_SUCCESS) break; /* Sleep for 2 ticks to hold the semaphore. */ tx_thread_sleep(2); /* Release the semaphore. */ status = tx_semaphore_put(semaphore_0); /* Check status. */ if (status != TX_SUCCESS) break; } } void thread_5_entry(ULONG thread_input) { UINT status; ULONG actual_flags; /* This thread simply waits for an event in a forever loop. */ while(1) { /* Increment the thread counter. */ thread_5_counter++; /* Wait for event flag 0. */ status = tx_event_flags_get(event_flags_0, 0x1, TX_OR_CLEAR, &actual_flags, TX_WAIT_FOREVER); /* Check status. */ if ((status != TX_SUCCESS) || (actual_flags != 0x1)) break; } } void thread_6_and_7_entry(ULONG thread_input) { UINT status; /* This function is executed from thread 6 and thread 7. As the loop below shows, these function compete for ownership of mutex_0. */ while(1) { /* Increment the thread counter. */ if (thread_input == 6) thread_6_counter++; else thread_7_counter++; /* Get the mutex with suspension. */ status = tx_mutex_get(mutex_0, TX_WAIT_FOREVER); /* Check status. */ if (status != TX_SUCCESS) break; /* Get the mutex again with suspension. This shows that an owning thread may retrieve the mutex it owns multiple times. */ status = tx_mutex_get(mutex_0, TX_WAIT_FOREVER); /* Check status. */ if (status != TX_SUCCESS) break; /* Sleep for 2 ticks to hold the mutex. */ tx_thread_sleep(2); /* Release the mutex. */ status = tx_mutex_put(mutex_0); /* Check status. */ if (status != TX_SUCCESS) break; /* Release the mutex again. This will actually release ownership since it was obtained twice. */ status = tx_mutex_put(mutex_0); /* Check status. */ if (status != TX_SUCCESS) break; } } ``` ## Building Modules Building a module is dependent on the tool chain being used. See [appendix](appendix.md) for port-specific examples. Common activities to all ports include the following. - Building a module library - Building the module application Each module is required to have a **txm_module_preamble** (setup specifically for the module) and the module library (for example, ***txm.a***).