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