mirror of
https://github.com/eclipse-threadx/rtos-docs.git
synced 2026-02-06 19:32:12 +08:00
540 lines
17 KiB
Markdown
540 lines
17 KiB
Markdown
---
|
|
title: Chapter 6 - Demonstration System for ThreadX
|
|
description: This chapter contains a description of the demonstration system that is delivered with all ThreadX processor support packages.
|
|
---
|
|
# Chapter 6 - Demonstration System for ThreadX
|
|
|
|
This chapter contains a description of the demonstration system that
|
|
is delivered with all ThreadX processor support packages.
|
|
|
|
## Overview
|
|
|
|
Each ThreadX product distribution contains a demonstration system that
|
|
runs on all supported microprocessors.
|
|
|
|
This example system is defined in the distribution file
|
|
***demo_threadx.c*** and is designed to illustrate how ThreadX is
|
|
used in an embedded multithread environment. The demonstration
|
|
consists of initialization, eight threads, one byte pool, one block
|
|
pool, one queue, one semaphore, one mutex, and one event flags group.
|
|
|
|
> **Note:** *Except for the thread's stack size, the demonstration application is
|
|
identical on all ThreadX supported processors.*
|
|
|
|
The complete listing of
|
|
***demo_threadx.c***, including the line numbers referenced
|
|
throughout the remainder of this chapter.
|
|
|
|
## Application Define
|
|
|
|
The ***tx_application_define*** function executes after the basic
|
|
ThreadX initialization is complete. It is responsible for setting up
|
|
all of the initial system resources, including threads, queues,
|
|
semaphores, mutexes, event flags, and memory pools.
|
|
|
|
The demonstration system's ***tx_application_define*** (*line
|
|
numbers 60-164*) creates the demonstration objects in the following
|
|
order:
|
|
|
|
- byte_pool_0
|
|
- thread_0
|
|
- thread_1
|
|
- thread_2
|
|
- thread_3
|
|
- thread_4
|
|
- thread_5
|
|
- thread_6
|
|
- thread_7
|
|
- queue_0
|
|
- semaphore_0
|
|
- event_flags_0
|
|
- mutex_0
|
|
- block_pool_0
|
|
|
|
The demonstration system does not create any other additional ThreadX
|
|
objects. However, an actual application may create system objects
|
|
during runtime inside of executing threads.
|
|
|
|
### Initial Execution
|
|
|
|
All threads are created with the **TX_AUTO_START**
|
|
option. This makes them initially ready for execution. After
|
|
***tx_application_define*** completes, control is transferred to the
|
|
thread scheduler and from there to each individual thread.
|
|
|
|
The order in which the threads execute is determined by their priority
|
|
and the order that they were created. In the demonstration system,
|
|
***thread_0*** executes first because it has the highest priority
|
|
(*it was created with a priority of 1*). After ***thread_0***
|
|
suspends, ***thread_5*** is executed, followed by the execution of
|
|
***thread_3***, ***thread_4***, ***thread_6***, ***thread_7***,
|
|
***thread_1***, and finally ***thread_2***.
|
|
|
|
> **Note:** *Even though **thread_3** and **thread_4** have the same priority
|
|
(both created with a priority of 8), **thread_3** executes first.
|
|
This is because **thread_3** was created and became ready before
|
|
**thread_4**. Threads of equal priority execute in a FIFO fashion.*
|
|
|
|
## Thread 0
|
|
|
|
The function ***thread_0_entry*** marks the entry point of the
|
|
thread *(lines 167-190*). ***Thread_0*** is the first thread in the
|
|
demonstration system to execute. Its processing is simple: it
|
|
increments its counter, sleeps for 10 timer ticks, sets an event flag
|
|
to wake up ***thread_5***, then repeats the sequence.
|
|
|
|
***Thread_0*** is the highest priority thread in the system. When its
|
|
requested sleep expires, it will preempt any other executing thread in
|
|
the demonstration.
|
|
|
|
## Thread 1
|
|
|
|
The function
|
|
***thread_1_entry*** marks the entry point of the thread *(lines
|
|
193-216*). ***Thread_1*** is the second-to-last thread in the
|
|
demonstration system to execute. Its processing consists of
|
|
incrementing its counter, sending a message to ***thread_2***
|
|
(*through* ***queue_0***), and repeating the sequence. Notice that
|
|
***thread_1*** suspends whenever ***queue_0*** becomes full (*line
|
|
207*).
|
|
|
|
## Thread 2
|
|
|
|
The function ***thread_2_entry*** marks the entry point of the
|
|
thread *(lines 219-243*). ***Thread_2*** is the last thread in the
|
|
demonstration system to execute. Its processing consists of
|
|
incrementing its counter, getting a message from ***thread_1***
|
|
(through ***queue_0***), and repeating the sequence. Notice that
|
|
***thread_2*** suspends whenever ***queue_0*** becomes empty (*line
|
|
233*).
|
|
|
|
Although ***thread_1*** and ***thread_2*** share the lowest priority
|
|
in the demonstration system (*priority 16*), they *Threads 3 and 4*
|
|
|
|
are also the only threads that are ready for execution most of the
|
|
time. They are also the only threads created with time-slicing (*lines
|
|
87 and 93*). Each thread is allowed to execute for a maximum of 4
|
|
timer ticks before the other thread is executed.
|
|
|
|
## Threads 3 and 4
|
|
|
|
The function
|
|
***thread_3_and_4_entry*** marks the entry point of both
|
|
***thread_3*** and ***thread_4*** (lines 246-280). Both threads
|
|
have a priority of 8, which makes them the third and fourth threads in
|
|
the demonstration system to execute. The processing for each thread is
|
|
the same: incrementing its counter, getting ***semaphore_0***,
|
|
sleeping for 2 timer ticks, releasing ***semaphore_0***, and
|
|
repeating the sequence. Notice that each thread suspends whenever
|
|
***semaphore_0*** is unavailable (line 264).
|
|
|
|
Also both threads use the same function for their main processing.
|
|
This presents no problems because they both have their own unique
|
|
stack, and C is naturally reentrant. Each thread determines which one
|
|
it is by examination of the thread input parameter (line 258), which
|
|
is setup when they are created (lines 102 and 109).
|
|
|
|
> **Note:** *It is also reasonable to obtain the current thread point during
|
|
thread execution and compare it with the control block's address to
|
|
determine thread identity.*
|
|
|
|
## Thread 5
|
|
|
|
The function ***thread_5_entry*** marks the entry point of the
|
|
thread (lines 283-305). ***Thread_5*** is the second thread in the
|
|
demonstration system to execute. Its processing consists of
|
|
incrementing its counter, getting an event flag from ***thread_0***
|
|
(through ***event_flags_0***), and repeating the sequence. Notice
|
|
that ***thread_5*** suspends whenever the event flag in
|
|
***event_flags_0*** is not available (line 298).
|
|
|
|
## Threads 6 and 7
|
|
|
|
The function ***thread_6_and_7_entry*** marks the entry point of
|
|
both ***thread_6*** and ***thread_7*** (lines 307-358). Both
|
|
threads have a priority of 8, which makes them the fifth and sixth
|
|
threads in the demonstration system to execute. The processing for
|
|
each thread is the same: incrementing its counter, getting
|
|
***mutex_0*** twice, sleeping for 2 timer ticks, releasing
|
|
***mutex_0*** twice, and repeating the sequence. Notice that each
|
|
thread suspends whenever ***mutex_0*** is unavailable (line 325).
|
|
|
|
Also both threads use the same
|
|
function for their main processing. This presents no problems because
|
|
they both have their own unique stack, and C is naturally reentrant.
|
|
Each thread determines which one it is by examination of the thread
|
|
input parameter (line 319), which is setup when they are created
|
|
(lines 126 and 133).
|
|
|
|
## Observing the Demonstration
|
|
|
|
Each of the demonstration threads increments its own unique counter.
|
|
The following counters may be examined to check on the demo's
|
|
operation:
|
|
|
|
- thread_0_counter
|
|
- thread_1_counter
|
|
- thread_2_counter
|
|
- thread_3_counter
|
|
- thread_4_counter
|
|
- thread_5_counter
|
|
- thread_6_counter
|
|
- thread_7_counter
|
|
|
|
Each of these counters should continue to increase as the demonstration executes, with
|
|
***thread_1_counter*** and ***thread_2_counter*** increasing at the fastest rate.
|
|
|
|
## Distribution file: demo_threadx.c
|
|
|
|
This section displays the complete listing of ***demo_threadx.c***,
|
|
including the line numbers referenced throughout this chapter.
|
|
|
|
```c
|
|
/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight
|
|
threads of different priorities, using a message queue, semaphore, mutex, event flags group,
|
|
byte pool, and block pool. */
|
|
|
|
#include "tx_api.h"
|
|
|
|
#define DEMO_STACK_SIZE 1024
|
|
#define DEMO_BYTE_POOL_SIZE 9120
|
|
#define DEMO_BLOCK_POOL_SIZE 100
|
|
#define DEMO_QUEUE_SIZE 100
|
|
|
|
/* Define the ThreadX object control blocks... */
|
|
|
|
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;
|
|
|
|
/* 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 main entry point. */
|
|
|
|
int main()
|
|
{
|
|
/* Enter the ThreadX kernel. */
|
|
tx_kernel_enter();
|
|
}
|
|
|
|
/* Define what the initial system looks like. */
|
|
void tx_application_define(void *first_unused_memory)
|
|
{
|
|
|
|
CHAR *pointer;
|
|
|
|
/* Create a byte memory pool from which to allocate the thread stacks. */
|
|
tx_byte_pool_create(&byte_pool_0, "byte pool 0", first_unused_memory,
|
|
DEMO_BYTE_POOL_SIZE);
|
|
|
|
/* Put system definition stuff in here, e.g., thread creates and other assorted
|
|
create information. */
|
|
|
|
/* Allocate the stack for thread 0. */
|
|
tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
|
|
|
|
/* Create the main thread. */
|
|
tx_thread_create(&thread_0, "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, &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, "thread 1", thread_1_entry, 1,
|
|
pointer, DEMO_STACK_SIZE,
|
|
16, 16, 4, TX_AUTO_START);
|
|
|
|
/* Allocate the stack for thread 2. */
|
|
tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
|
|
tx_thread_create(&thread_2, "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, &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, "thread 3", thread_3_and_4_entry, 3,
|
|
pointer, DEMO_STACK_SIZE,
|
|
8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
|
|
|
|
/* Allocate the stack for thread 4. */
|
|
tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
|
|
|
|
tx_thread_create(&thread_4, "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, &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, "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, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
|
|
|
|
/* Create threads 6 and 7. These threads compete for a ThreadX mutex. */
|
|
tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6,
|
|
pointer, DEMO_STACK_SIZE,
|
|
8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
|
|
|
|
/* Allocate the stack for thread 7. */
|
|
tx_byte_allocate(&byte_pool_0, &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
|
|
|
|
tx_thread_create(&thread_7, "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, &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT);
|
|
|
|
/* Create the message queue shared by threads 1 and 2. */
|
|
tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG));
|
|
|
|
/* Create the semaphore used by threads 3 and 4. */
|
|
tx_semaphore_create(&semaphore_0, "semaphore 0", 1);
|
|
|
|
/* Create the event flags group used by threads 1 and 5. */
|
|
tx_event_flags_create(&event_flags_0, "event flags 0");
|
|
|
|
/* Create the mutex used by thread 6 and 7 without priority inheritance. */
|
|
tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT);
|
|
|
|
/* Allocate the memory for a small block pool. */
|
|
tx_byte_allocate(&byte_pool_0, &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT);
|
|
|
|
/* Create a block memory pool to allocate a message buffer from. */
|
|
tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer,
|
|
DEMO_BLOCK_POOL_SIZE);
|
|
|
|
/* Allocate a block and release the block memory. */
|
|
tx_block_allocate(&block_pool_0, &pointer, TX_NO_WAIT);
|
|
|
|
/* Release the block back to the pool. */
|
|
tx_block_release(pointer);
|
|
}
|
|
|
|
/* Define the test 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 wakeup 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;
|
|
}
|
|
}
|
|
```
|