1 /* This is a small demo of the high-performance ThreadX kernel.  It includes examples of eight
2    threads of different priorities, using a message queue, semaphore, mutex, event flags group,
3    byte pool, and block pool.  */
4 
5 #include <stdio.h>
6 #include <xtensa/xtutil.h>
7 
8 #include "tx_api.h"
9 
10 #define DEMO_STACK_SIZE         1024
11 #define DEMO_BYTE_POOL_SIZE     9120
12 #define DEMO_BLOCK_POOL_SIZE    100
13 #define DEMO_QUEUE_SIZE         100
14 
15 
16 /* Define the ThreadX object control blocks...  */
17 
18 TX_THREAD               thread_0;
19 TX_THREAD               thread_1;
20 TX_THREAD               thread_2;
21 TX_THREAD               thread_3;
22 TX_THREAD               thread_4;
23 TX_THREAD               thread_5;
24 TX_THREAD               thread_6;
25 TX_THREAD               thread_7;
26 TX_QUEUE                queue_0;
27 TX_SEMAPHORE            semaphore_0;
28 TX_MUTEX                mutex_0;
29 TX_EVENT_FLAGS_GROUP    event_flags_0;
30 TX_BYTE_POOL            byte_pool_0;
31 TX_BLOCK_POOL           block_pool_0;
32 UCHAR                   memory_area[DEMO_BYTE_POOL_SIZE];
33 
34 
35 /* Define the counters used in the demo application...  */
36 
37 ULONG                   thread_0_counter;
38 ULONG                   thread_1_counter;
39 ULONG                   thread_1_messages_sent;
40 ULONG                   thread_2_counter;
41 ULONG                   thread_2_messages_received;
42 ULONG                   thread_3_counter;
43 ULONG                   thread_4_counter;
44 ULONG                   thread_5_counter;
45 ULONG                   thread_6_counter;
46 ULONG                   thread_7_counter;
47 
48 
49 /* Define thread prototypes.  */
50 
51 void    thread_0_entry(ULONG thread_input);
52 void    thread_1_entry(ULONG thread_input);
53 void    thread_2_entry(ULONG thread_input);
54 void    thread_3_and_4_entry(ULONG thread_input);
55 void    thread_5_entry(ULONG thread_input);
56 void    thread_6_and_7_entry(ULONG thread_input);
57 
58 
59 /* Define main entry point.  */
60 
main()61 int main()
62 {
63 
64     /* Enter the ThreadX kernel.  */
65     tx_kernel_enter();
66 }
67 
68 
69 /* Define what the initial system looks like.  */
70 
tx_application_define(void * first_unused_memory)71 void    tx_application_define(void *first_unused_memory)
72 {
73 
74 CHAR    *pointer = TX_NULL;
75 
76 
77     /* Create a byte memory pool from which to allocate the thread stacks.  */
78     tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE);
79 
80     /* Put system definition stuff in here, e.g. thread creates and other assorted
81        create information.  */
82 
83     /* Allocate the stack for thread 0.  */
84     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
85 
86     /* Create the main thread.  */
87     tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0,
88             pointer, DEMO_STACK_SIZE,
89             1, 1, TX_NO_TIME_SLICE, TX_AUTO_START);
90 
91 
92     /* Allocate the stack for thread 1.  */
93     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
94 
95     /* Create threads 1 and 2. These threads pass information through a ThreadX
96        message queue.  It is also interesting to note that these threads have a time
97        slice.  */
98     tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1,
99             pointer, DEMO_STACK_SIZE,
100             16, 16, 4, TX_AUTO_START);
101 
102     /* Allocate the stack for thread 2.  */
103     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
104 
105     tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2,
106             pointer, DEMO_STACK_SIZE,
107             16, 16, 4, TX_AUTO_START);
108 
109     /* Allocate the stack for thread 3.  */
110     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
111 
112     /* Create threads 3 and 4.  These threads compete for a ThreadX counting semaphore.
113        An interesting thing here is that both threads share the same instruction area.  */
114     tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3,
115             pointer, DEMO_STACK_SIZE,
116             8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
117 
118     /* Allocate the stack for thread 4.  */
119     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
120 
121     tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4,
122             pointer, DEMO_STACK_SIZE,
123             8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
124 
125     /* Allocate the stack for thread 5.  */
126     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
127 
128     /* Create thread 5.  This thread simply pends on an event flag which will be set
129        by thread_0.  */
130     tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5,
131             pointer, DEMO_STACK_SIZE,
132             4, 4, TX_NO_TIME_SLICE, TX_AUTO_START);
133 
134     /* Allocate the stack for thread 6.  */
135     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
136 
137     /* Create threads 6 and 7.  These threads compete for a ThreadX mutex.  */
138     tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6,
139             pointer, DEMO_STACK_SIZE,
140             8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
141 
142     /* Allocate the stack for thread 7.  */
143     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT);
144 
145     tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7,
146             pointer, DEMO_STACK_SIZE,
147             8, 8, TX_NO_TIME_SLICE, TX_AUTO_START);
148 
149     /* Allocate the message queue.  */
150     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT);
151 
152     /* Create the message queue shared by threads 1 and 2.  */
153     tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG));
154 
155     /* Create the semaphore used by threads 3 and 4.  */
156     tx_semaphore_create(&semaphore_0, "semaphore 0", 1);
157 
158     /* Create the event flags group used by threads 1 and 5.  */
159     tx_event_flags_create(&event_flags_0, "event flags 0");
160 
161     /* Create the mutex used by thread 6 and 7 without priority inheritance.  */
162     tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT);
163 
164     /* Allocate the memory for a small block pool.  */
165     tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT);
166 
167     /* Create a block memory pool to allocate a message buffer from.  */
168     tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE);
169 
170     /* Allocate a block and release the block memory.  */
171     tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT);
172 
173     /* Release the block back to the pool.  */
174     tx_block_release(pointer);
175 }
176 
177 
178 
179 /* Define the test threads.  */
180 
thread_0_entry(ULONG thread_input)181 void    thread_0_entry(ULONG thread_input)
182 {
183 
184 UINT    status;
185 
186 
187     /* This thread simply sits in while-forever-sleep loop.  */
188     while(1)
189     {
190 
191         /* Increment the thread counter.  */
192         thread_0_counter++;
193 
194         /* Sleep for 10 ticks.  */
195         tx_thread_sleep(10);
196 
197         /* Set event flag 0 to wakeup thread 5.  */
198         status =  tx_event_flags_set(&event_flags_0, 0x1, TX_OR);
199 
200         /* Check status.  */
201         if (status != TX_SUCCESS)
202             break;
203 
204         /* Print something to show we are making progress. This should use the print
205            function from libxtutil which is thread-safe.  */
206         printf("%u %u %u\n", thread_0_counter, thread_1_counter, thread_2_counter);
207     }
208 }
209 
210 
thread_1_entry(ULONG thread_input)211 void    thread_1_entry(ULONG thread_input)
212 {
213 
214 UINT    status;
215 
216 
217     /* This thread simply sends messages to a queue shared by thread 2.  */
218     while(1)
219     {
220 
221         /* Increment the thread counter.  */
222         thread_1_counter++;
223 
224         /* Send message to queue 0.  */
225         status =  tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER);
226 
227         /* Check completion status.  */
228         if (status != TX_SUCCESS)
229             break;
230 
231         /* Increment the message sent.  */
232         thread_1_messages_sent++;
233     }
234 }
235 
236 
thread_2_entry(ULONG thread_input)237 void    thread_2_entry(ULONG thread_input)
238 {
239 
240 ULONG   received_message;
241 UINT    status;
242 
243     /* This thread retrieves messages placed on the queue by thread 1.  */
244     while(1)
245     {
246 
247         /* Increment the thread counter.  */
248         thread_2_counter++;
249 
250         /* Retrieve a message from the queue.  */
251         status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER);
252 
253         /* Check completion status and make sure the message is what we
254            expected.  */
255         if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received))
256             break;
257 
258         /* Otherwise, all is okay.  Increment the received message count.  */
259         thread_2_messages_received++;
260     }
261 }
262 
263 
thread_3_and_4_entry(ULONG thread_input)264 void    thread_3_and_4_entry(ULONG thread_input)
265 {
266 
267 UINT    status;
268 
269 
270     /* This function is executed from thread 3 and thread 4.  As the loop
271        below shows, these function compete for ownership of semaphore_0.  */
272     while(1)
273     {
274 
275         /* Increment the thread counter.  */
276         if (thread_input == 3)
277             thread_3_counter++;
278         else
279             thread_4_counter++;
280 
281         /* Get the semaphore with suspension.  */
282         status =  tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER);
283 
284         /* Check status.  */
285         if (status != TX_SUCCESS)
286             break;
287 
288         /* Sleep for 2 ticks to hold the semaphore.  */
289         tx_thread_sleep(2);
290 
291         /* Release the semaphore.  */
292         status =  tx_semaphore_put(&semaphore_0);
293 
294         /* Check status.  */
295         if (status != TX_SUCCESS)
296             break;
297     }
298 }
299 
300 
thread_5_entry(ULONG thread_input)301 void    thread_5_entry(ULONG thread_input)
302 {
303 
304 UINT    status;
305 ULONG   actual_flags;
306 
307 
308     /* This thread simply waits for an event in a forever loop.  */
309     while(1)
310     {
311 
312         /* Increment the thread counter.  */
313         thread_5_counter++;
314 
315         /* Wait for event flag 0.  */
316         status =  tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR,
317                                                 &actual_flags, TX_WAIT_FOREVER);
318 
319         /* Check status.  */
320         if ((status != TX_SUCCESS) || (actual_flags != 0x1))
321             break;
322     }
323 }
324 
325 
thread_6_and_7_entry(ULONG thread_input)326 void    thread_6_and_7_entry(ULONG thread_input)
327 {
328 
329 UINT    status;
330 
331 
332     /* This function is executed from thread 6 and thread 7.  As the loop
333        below shows, these function compete for ownership of mutex_0.  */
334     while(1)
335     {
336 
337         /* Increment the thread counter.  */
338         if (thread_input == 6)
339             thread_6_counter++;
340         else
341             thread_7_counter++;
342 
343         /* Get the mutex with suspension.  */
344         status =  tx_mutex_get(&mutex_0, TX_WAIT_FOREVER);
345 
346         /* Check status.  */
347         if (status != TX_SUCCESS)
348             break;
349 
350         /* Get the mutex again with suspension.  This shows
351            that an owning thread may retrieve the mutex it
352            owns multiple times.  */
353         status =  tx_mutex_get(&mutex_0, TX_WAIT_FOREVER);
354 
355         /* Check status.  */
356         if (status != TX_SUCCESS)
357             break;
358 
359         /* Sleep for 2 ticks to hold the mutex.  */
360         tx_thread_sleep(2);
361 
362         /* Release the mutex.  */
363         status =  tx_mutex_put(&mutex_0);
364 
365         /* Check status.  */
366         if (status != TX_SUCCESS)
367             break;
368 
369         /* Release the mutex again.  This will actually
370            release ownership since it was obtained twice.  */
371         status =  tx_mutex_put(&mutex_0);
372 
373         /* Check status.  */
374         if (status != TX_SUCCESS)
375             break;
376     }
377 }
378