]> begriffs open source - freertos/blob - Demo/Common/Minimal/AltBlock.c
Update to V5.4.1
[freertos] / Demo / Common / Minimal / AltBlock.c
1 /*\r
2         FreeRTOS V5.4.1 - Copyright (C) 2009 Real Time Engineers Ltd.\r
3 \r
4         This file is part of the FreeRTOS distribution.\r
5 \r
6         FreeRTOS is free software; you can redistribute it and/or modify it     under \r
7         the terms of the GNU General Public License (version 2) as published by the \r
8         Free Software Foundation and modified by the FreeRTOS exception.\r
9         **NOTE** The exception to the GPL is included to allow you to distribute a\r
10         combined work that includes FreeRTOS without being obliged to provide the \r
11         source code for proprietary components outside of the FreeRTOS kernel.  \r
12         Alternative commercial license and support terms are also available upon \r
13         request.  See the licensing section of http://www.FreeRTOS.org for full \r
14         license details.\r
15 \r
16         FreeRTOS is distributed in the hope that it will be useful,     but WITHOUT\r
17         ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or\r
18         FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for\r
19         more details.\r
20 \r
21         You should have received a copy of the GNU General Public License along\r
22         with FreeRTOS; if not, write to the Free Software Foundation, Inc., 59\r
23         Temple Place, Suite 330, Boston, MA  02111-1307  USA.\r
24 \r
25 \r
26         ***************************************************************************\r
27         *                                                                         *\r
28         * Looking for a quick start?  Then check out the FreeRTOS eBook!          *\r
29         * See http://www.FreeRTOS.org/Documentation for details                   *\r
30         *                                                                         *\r
31         ***************************************************************************\r
32 \r
33         1 tab == 4 spaces!\r
34 \r
35         Please ensure to read the configuration and relevant port sections of the\r
36         online documentation.\r
37 \r
38         http://www.FreeRTOS.org - Documentation, latest information, license and\r
39         contact details.\r
40 \r
41         http://www.SafeRTOS.com - A version that is certified for use in safety\r
42         critical systems.\r
43 \r
44         http://www.OpenRTOS.com - Commercial support, development, porting,\r
45         licensing and training services.\r
46 */\r
47 \r
48 /*\r
49  * This is a version of BlockTim.c that uses the light weight API.\r
50  *\r
51  * This file contains some test scenarios that ensure tasks do not exit queue\r
52  * send or receive functions prematurely.  A description of the tests is\r
53  * included within the code.\r
54  */\r
55 \r
56 /* Kernel includes. */\r
57 #include "FreeRTOS.h"\r
58 #include "task.h"\r
59 #include "queue.h"\r
60 \r
61 /* Demo includes. */\r
62 #include "AltBlock.h"\r
63 \r
64 /* Task priorities. */\r
65 #define bktPRIMARY_PRIORITY                     ( 3 )\r
66 #define bktSECONDARY_PRIORITY           ( 2 )\r
67 \r
68 /* Task behaviour. */\r
69 #define bktQUEUE_LENGTH                         ( 5 )\r
70 #define bktSHORT_WAIT                           ( ( ( portTickType ) 20 ) / portTICK_RATE_MS )\r
71 #define bktPRIMARY_BLOCK_TIME           ( 10 )\r
72 #define bktALLOWABLE_MARGIN                     ( 12 )\r
73 #define bktTIME_TO_BLOCK                        ( 175 )\r
74 #define bktDONT_BLOCK                           ( ( portTickType ) 0 )\r
75 #define bktRUN_INDICATOR                        ( ( unsigned portBASE_TYPE ) 0x55 )\r
76 \r
77 /* The queue on which the tasks block. */\r
78 static xQueueHandle xTestQueue;\r
79 \r
80 /* Handle to the secondary task is required by the primary task for calls\r
81 to vTaskSuspend/Resume(). */\r
82 static xTaskHandle xSecondary;\r
83 \r
84 /* Used to ensure that tasks are still executing without error. */\r
85 static portBASE_TYPE xPrimaryCycles = 0, xSecondaryCycles = 0;\r
86 static portBASE_TYPE xErrorOccurred = pdFALSE;\r
87 \r
88 /* Provides a simple mechanism for the primary task to know when the\r
89 secondary task has executed. */\r
90 static volatile unsigned portBASE_TYPE xRunIndicator;\r
91 \r
92 /* The two test tasks.  Their behaviour is commented within the files. */\r
93 static void vPrimaryBlockTimeTestTask( void *pvParameters );\r
94 static void vSecondaryBlockTimeTestTask( void *pvParameters );\r
95 \r
96 /*-----------------------------------------------------------*/\r
97 \r
98 void vCreateAltBlockTimeTasks( void )\r
99 {\r
100         /* Create the queue on which the two tasks block. */\r
101     xTestQueue = xQueueCreate( bktQUEUE_LENGTH, sizeof( portBASE_TYPE ) );\r
102 \r
103         /* vQueueAddToRegistry() adds the queue to the queue registry, if one is\r
104         in use.  The queue registry is provided as a means for kernel aware \r
105         debuggers to locate queues and has no purpose if a kernel aware debugger\r
106         is not being used.  The call to vQueueAddToRegistry() will be removed\r
107         by the pre-processor if configQUEUE_REGISTRY_SIZE is not defined or is \r
108         defined to be less than 1. */\r
109         vQueueAddToRegistry( xTestQueue, ( signed portCHAR * ) "AltBlockQueue" );\r
110 \r
111 \r
112         /* Create the two test tasks. */\r
113         xTaskCreate( vPrimaryBlockTimeTestTask, ( signed portCHAR * )"FBTest1", configMINIMAL_STACK_SIZE, NULL, bktPRIMARY_PRIORITY, NULL );\r
114         xTaskCreate( vSecondaryBlockTimeTestTask, ( signed portCHAR * )"FBTest2", configMINIMAL_STACK_SIZE, NULL, bktSECONDARY_PRIORITY, &xSecondary );\r
115 }\r
116 /*-----------------------------------------------------------*/\r
117 \r
118 static void vPrimaryBlockTimeTestTask( void *pvParameters )\r
119 {\r
120 portBASE_TYPE xItem, xData;\r
121 portTickType xTimeWhenBlocking;\r
122 portTickType xTimeToBlock, xBlockedTime;\r
123 \r
124         #ifdef USE_STDIO\r
125         void vPrintDisplayMessage( const portCHAR * const * ppcMessageToSend );\r
126         \r
127                 const portCHAR * const pcTaskStartMsg = "Alt primary block time test started.\r\n";\r
128 \r
129                 /* Queue a message for printing to say the task has started. */\r
130                 vPrintDisplayMessage( &pcTaskStartMsg );\r
131         #endif\r
132 \r
133         ( void ) pvParameters;\r
134 \r
135         for( ;; )\r
136         {\r
137                 /*********************************************************************\r
138         Test 1\r
139 \r
140         Simple block time wakeup test on queue receives. */\r
141                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
142                 {\r
143                         /* The queue is empty. Attempt to read from the queue using a block\r
144                         time.  When we wake, ensure the delta in time is as expected. */\r
145                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
146 \r
147                         /* A critical section is used to minimise the jitter in the time\r
148                         measurements. */\r
149                         portENTER_CRITICAL();\r
150                         {\r
151                                 xTimeWhenBlocking = xTaskGetTickCount();\r
152                                 \r
153                                 /* We should unblock after xTimeToBlock having not received\r
154                                 anything on the queue. */\r
155                                 if( xQueueAltReceive( xTestQueue, &xData, xTimeToBlock ) != errQUEUE_EMPTY )\r
156                                 {\r
157                                         xErrorOccurred = pdTRUE;\r
158                                 }\r
159 \r
160                                 /* How long were we blocked for? */\r
161                                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
162                         }\r
163                         portEXIT_CRITICAL();\r
164 \r
165                         if( xBlockedTime < xTimeToBlock )\r
166                         {\r
167                                 /* Should not have blocked for less than we requested. */\r
168                                 xErrorOccurred = pdTRUE;\r
169                         }\r
170 \r
171                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
172                         {\r
173                                 /* Should not have blocked for longer than we requested,\r
174                                 although we would not necessarily run as soon as we were\r
175                                 unblocked so a margin is allowed. */\r
176                                 xErrorOccurred = pdTRUE;\r
177                         }\r
178                 }\r
179 \r
180 \r
181                 #if configUSE_PREEMPTION == 0\r
182                         taskYIELD();\r
183                 #endif\r
184 \r
185 \r
186                 /*********************************************************************\r
187         Test 2\r
188 \r
189         Simple block time wakeup test on queue sends.\r
190 \r
191                 First fill the queue.  It should be empty so all sends should pass. */\r
192                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
193                 {\r
194                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
195                         {\r
196                                 xErrorOccurred = pdTRUE;\r
197                         }\r
198                 }\r
199 \r
200                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
201                 {\r
202                         /* The queue is full. Attempt to write to the queue using a block\r
203                         time.  When we wake, ensure the delta in time is as expected. */\r
204                         xTimeToBlock = bktPRIMARY_BLOCK_TIME << xItem;\r
205 \r
206                         portENTER_CRITICAL();\r
207                         {\r
208                                 xTimeWhenBlocking = xTaskGetTickCount();\r
209                                 \r
210                                 /* We should unblock after xTimeToBlock having not received\r
211                                 anything on the queue. */\r
212                                 if( xQueueAltSendToBack( xTestQueue, &xItem, xTimeToBlock ) != errQUEUE_FULL )\r
213                                 {\r
214                                         xErrorOccurred = pdTRUE;\r
215                                 }\r
216 \r
217                                 /* How long were we blocked for? */\r
218                                 xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
219                         }\r
220                         portEXIT_CRITICAL();\r
221 \r
222                         if( xBlockedTime < xTimeToBlock )\r
223                         {\r
224                                 /* Should not have blocked for less than we requested. */\r
225                                 xErrorOccurred = pdTRUE;\r
226                         }\r
227 \r
228                         if( xBlockedTime > ( xTimeToBlock + bktALLOWABLE_MARGIN ) )\r
229                         {\r
230                                 /* Should not have blocked for longer than we requested,\r
231                                 although we would not necessarily run as soon as we were\r
232                                 unblocked so a margin is allowed. */\r
233                                 xErrorOccurred = pdTRUE;\r
234                         }\r
235                 }\r
236 \r
237                 #if configUSE_PREEMPTION == 0\r
238                         taskYIELD();\r
239                 #endif\r
240 \r
241                 \r
242                 /*********************************************************************\r
243         Test 3\r
244 \r
245                 Wake the other task, it will block attempting to post to the queue.\r
246                 When we read from the queue the other task will wake, but before it\r
247                 can run we will post to the queue again.  When the other task runs it\r
248                 will find the queue still full, even though it was woken.  It should\r
249                 recognise that its block time has not expired and return to block for\r
250                 the remains of its block time.\r
251 \r
252                 Wake the other task so it blocks attempting to post to the already\r
253                 full queue. */\r
254                 xRunIndicator = 0;\r
255                 vTaskResume( xSecondary );\r
256 \r
257                 /* We need to wait a little to ensure the other task executes. */\r
258                 while( xRunIndicator != bktRUN_INDICATOR )\r
259                 {\r
260                         /* The other task has not yet executed. */\r
261                         vTaskDelay( bktSHORT_WAIT );\r
262                 }\r
263                 /* Make sure the other task is blocked on the queue. */\r
264                 vTaskDelay( bktSHORT_WAIT );\r
265                 xRunIndicator = 0;\r
266 \r
267                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
268                 {\r
269                         /* Now when we make space on the queue the other task should wake\r
270                         but not execute as this task has higher priority. */                            \r
271                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
272                         {\r
273                                 xErrorOccurred = pdTRUE;\r
274                         }\r
275 \r
276                         /* Now fill the queue again before the other task gets a chance to\r
277                         execute.  If the other task had executed we would find the queue\r
278                         full ourselves, and the other task have set xRunIndicator. */\r
279                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
280                         {\r
281                                 xErrorOccurred = pdTRUE;\r
282                         }\r
283 \r
284                         if( xRunIndicator == bktRUN_INDICATOR )\r
285                         {\r
286                                 /* The other task should not have executed. */\r
287                                 xErrorOccurred = pdTRUE;\r
288                         }\r
289 \r
290                         /* Raise the priority of the other task so it executes and blocks\r
291                         on the queue again. */\r
292                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
293 \r
294                         /* The other task should now have re-blocked without exiting the\r
295                         queue function. */\r
296                         if( xRunIndicator == bktRUN_INDICATOR )\r
297                         {\r
298                                 /* The other task should not have executed outside of the\r
299                                 queue function. */\r
300                                 xErrorOccurred = pdTRUE;\r
301                         }\r
302 \r
303                         /* Set the priority back down. */\r
304                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );                  \r
305                 }\r
306 \r
307                 /* Let the other task timeout.  When it unblockes it will check that it\r
308                 unblocked at the correct time, then suspend itself. */\r
309                 while( xRunIndicator != bktRUN_INDICATOR )\r
310                 {\r
311                         vTaskDelay( bktSHORT_WAIT );\r
312                 }\r
313                 vTaskDelay( bktSHORT_WAIT );\r
314                 xRunIndicator = 0;\r
315 \r
316                 #if configUSE_PREEMPTION == 0\r
317                         taskYIELD();\r
318                 #endif\r
319 \r
320                 /*********************************************************************\r
321         Test 4\r
322 \r
323                 As per test 3 - but with the send and receive the other way around.\r
324                 The other task blocks attempting to read from the queue.\r
325 \r
326                 Empty the queue.  We should find that it is full. */\r
327                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
328                 {\r
329                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
330                         {\r
331                                 xErrorOccurred = pdTRUE;\r
332                         }\r
333                 }\r
334                 \r
335                 /* Wake the other task so it blocks attempting to read from  the\r
336                 already empty queue. */\r
337                 vTaskResume( xSecondary );\r
338 \r
339                 /* We need to wait a little to ensure the other task executes. */\r
340                 while( xRunIndicator != bktRUN_INDICATOR )\r
341                 {\r
342                         vTaskDelay( bktSHORT_WAIT );\r
343                 }\r
344                 vTaskDelay( bktSHORT_WAIT );\r
345                 xRunIndicator = 0;\r
346 \r
347                 for( xItem = 0; xItem < bktQUEUE_LENGTH; xItem++ )\r
348                 {\r
349                         /* Now when we place an item on the queue the other task should\r
350                         wake but not execute as this task has higher priority. */                               \r
351                         if( xQueueAltSendToBack( xTestQueue, &xItem, bktDONT_BLOCK ) != pdPASS )\r
352                         {\r
353                                 xErrorOccurred = pdTRUE;\r
354                         }\r
355 \r
356                         /* Now empty the queue again before the other task gets a chance to\r
357                         execute.  If the other task had executed we would find the queue\r
358                         empty ourselves, and the other task would be suspended. */\r
359                         if( xQueueAltReceive( xTestQueue, &xData, bktDONT_BLOCK ) != pdPASS )\r
360                         {\r
361                                 xErrorOccurred = pdTRUE;\r
362                         }\r
363 \r
364                         if( xRunIndicator == bktRUN_INDICATOR )\r
365                         {\r
366                                 /* The other task should not have executed. */\r
367                                 xErrorOccurred = pdTRUE;\r
368                         }\r
369 \r
370                         /* Raise the priority of the other task so it executes and blocks\r
371                         on the queue again. */\r
372                         vTaskPrioritySet( xSecondary, bktPRIMARY_PRIORITY + 2 );\r
373 \r
374                         /* The other task should now have re-blocked without exiting the\r
375                         queue function. */\r
376                         if( xRunIndicator == bktRUN_INDICATOR )\r
377                         {\r
378                                 /* The other task should not have executed outside of the\r
379                                 queue function. */\r
380                                 xErrorOccurred = pdTRUE;\r
381                         }\r
382                         vTaskPrioritySet( xSecondary, bktSECONDARY_PRIORITY );                  \r
383                 }\r
384 \r
385                 /* Let the other task timeout.  When it unblockes it will check that it\r
386                 unblocked at the correct time, then suspend itself. */\r
387                 while( xRunIndicator != bktRUN_INDICATOR )\r
388                 {\r
389                         vTaskDelay( bktSHORT_WAIT );\r
390                 }\r
391                 vTaskDelay( bktSHORT_WAIT );\r
392 \r
393                 xPrimaryCycles++;\r
394         }\r
395 }\r
396 /*-----------------------------------------------------------*/\r
397 \r
398 static void vSecondaryBlockTimeTestTask( void *pvParameters )\r
399 {\r
400 portTickType xTimeWhenBlocking, xBlockedTime;\r
401 portBASE_TYPE xData;\r
402 \r
403         #ifdef USE_STDIO\r
404         void vPrintDisplayMessage( const portCHAR * const * ppcMessageToSend );\r
405         \r
406                 const portCHAR * const pcTaskStartMsg = "Alt secondary block time test started.\r\n";\r
407 \r
408                 /* Queue a message for printing to say the task has started. */\r
409                 vPrintDisplayMessage( &pcTaskStartMsg );\r
410         #endif\r
411 \r
412         ( void ) pvParameters;\r
413 \r
414         for( ;; )\r
415         {\r
416                 /*********************************************************************\r
417         Test 1 and 2\r
418 \r
419                 This task does does not participate in these tests. */\r
420                 vTaskSuspend( NULL );\r
421 \r
422                 /*********************************************************************\r
423         Test 3\r
424 \r
425                 The first thing we do is attempt to read from the queue.  It should be\r
426                 full so we block.  Note the time before we block so we can check the\r
427                 wake time is as per that expected. */\r
428                 portENTER_CRITICAL();\r
429                 {\r
430                         xTimeWhenBlocking = xTaskGetTickCount();\r
431                         \r
432                         /* We should unblock after bktTIME_TO_BLOCK having not received\r
433                         anything on the queue. */\r
434                         xData = 0;\r
435                         xRunIndicator = bktRUN_INDICATOR;\r
436                         if( xQueueAltSendToBack( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_FULL )\r
437                         {\r
438                                 xErrorOccurred = pdTRUE;\r
439                         }\r
440 \r
441                         /* How long were we inside the send function? */\r
442                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
443                 }\r
444                 portEXIT_CRITICAL();\r
445 \r
446                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
447                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
448                 {\r
449                         xErrorOccurred = pdTRUE;\r
450                 }\r
451 \r
452                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
453                 either.  A margin is permitted as we would not necessarily run as\r
454                 soon as we unblocked. */\r
455                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
456                 {\r
457                         xErrorOccurred = pdTRUE;\r
458                 }\r
459 \r
460                 /* Suspend ready for test 3. */\r
461                 xRunIndicator = bktRUN_INDICATOR;\r
462                 vTaskSuspend( NULL );\r
463 \r
464                 /*********************************************************************\r
465         Test 4\r
466 \r
467                 As per test three, but with the send and receive reversed. */\r
468                 portENTER_CRITICAL();\r
469                 {\r
470                         xTimeWhenBlocking = xTaskGetTickCount();\r
471                         \r
472                         /* We should unblock after bktTIME_TO_BLOCK having not received\r
473                         anything on the queue. */\r
474                         xRunIndicator = bktRUN_INDICATOR;\r
475                         if( xQueueAltReceive( xTestQueue, &xData, bktTIME_TO_BLOCK ) != errQUEUE_EMPTY )\r
476                         {\r
477                                 xErrorOccurred = pdTRUE;\r
478                         }\r
479 \r
480                         xBlockedTime = xTaskGetTickCount() - xTimeWhenBlocking;\r
481                 }\r
482                 portEXIT_CRITICAL();\r
483 \r
484                 /* We should not have blocked for less time than bktTIME_TO_BLOCK. */\r
485                 if( xBlockedTime < bktTIME_TO_BLOCK )\r
486                 {\r
487                         xErrorOccurred = pdTRUE;\r
488                 }\r
489 \r
490                 /* We should of not blocked for much longer than bktALLOWABLE_MARGIN\r
491                 either.  A margin is permitted as we would not necessarily run as soon\r
492                 as we unblocked. */\r
493                 if( xBlockedTime > ( bktTIME_TO_BLOCK + bktALLOWABLE_MARGIN ) )\r
494                 {\r
495                         xErrorOccurred = pdTRUE;\r
496                 }\r
497 \r
498                 xRunIndicator = bktRUN_INDICATOR;\r
499 \r
500                 xSecondaryCycles++;\r
501         }\r
502 }\r
503 /*-----------------------------------------------------------*/\r
504 \r
505 portBASE_TYPE xAreAltBlockTimeTestTasksStillRunning( void )\r
506 {\r
507 static portBASE_TYPE xLastPrimaryCycleCount = 0, xLastSecondaryCycleCount = 0;\r
508 portBASE_TYPE xReturn = pdPASS;\r
509 \r
510         /* Have both tasks performed at least one cycle since this function was\r
511         last called? */\r
512         if( xPrimaryCycles == xLastPrimaryCycleCount )\r
513         {\r
514                 xReturn = pdFAIL;\r
515         }\r
516 \r
517         if( xSecondaryCycles == xLastSecondaryCycleCount )\r
518         {\r
519                 xReturn = pdFAIL;\r
520         }\r
521 \r
522         if( xErrorOccurred == pdTRUE )\r
523         {\r
524                 xReturn = pdFAIL;\r
525         }\r
526 \r
527         xLastSecondaryCycleCount = xSecondaryCycles;\r
528         xLastPrimaryCycleCount = xPrimaryCycles;\r
529 \r
530         return xReturn;\r
531 }\r