title: Chapter 2 - Module Requirements description: This article is a description of the requirements for build a ThreadX Module.
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).
-
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.
-
Each module must have a preamble at its first instruction area address which defines the characteristics and the resource needs of the module.
-
Each module must link the preamble at the beginning of the module instruction area.
-
Each module must link against a module library (txm.a), which contains module-specific functions used to interact with ThreadX.
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.
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. Figure 3 shows an example ThreadX module preamble for a generic target (the lines starting with * are values typically modified by the application):
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.
The table below shows an example of the properties bit map. Port-specific properties bitmaps are in the appendix.
Bit | Value | Meaning |
---|---|---|
0 |
0 |
Privileged mode execution |
1 |
0 |
No MPU protection |
2 |
0 |
Disable shared/external memory access |
[23-3] |
0 |
Reserved |
[31-24] |
|
Compiler ID |
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.
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*).
(_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.
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.
-
Replacement of tx_api.h with txm_module.h
-
Addition of #define TXM_MODULE prior to txm_module.h
-
Replacement of main and tx_application_define with demo_module_start
-
Declaring pointers to ThreadX objects rather than the objects themselves.
/* 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 a module is dependent on the tool chain being used. See appendix 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).