]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/MessageBufferAMP.c
Update version number in readiness for V10.3.0 release. Sync SVN with reviewed releas...
[freertos] / FreeRTOS / Demo / Common / Minimal / MessageBufferAMP.c
1 /*\r
2  * FreeRTOS Kernel V10.3.0\r
3  * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  *\r
22  * http://www.FreeRTOS.org\r
23  * http://aws.amazon.com/freertos\r
24  *\r
25  * 1 tab == 4 spaces!\r
26  */\r
27 \r
28 /*\r
29  * An example that mimics a message buffer being used to pass data from one core\r
30  * to another.  The core that sends the data is referred to as core A.  The core\r
31  * that receives the data is referred to as core B.  The task implemented by\r
32  * prvCoreATask() runs on core A.  Two instances of the task implemented by\r
33  * prvCoreBTasks() run on core B.  prvCoreATask() sends messages via message\r
34  * buffers to both instances of prvCoreBTasks(), one message buffer per channel.\r
35  * A third message buffer is used to pass the handle of the message buffer\r
36  * written to by core A to an interrupt service routine that is triggered by\r
37  * core A but executes on core B.\r
38  *\r
39  * The example relies on the FreeRTOS provided default implementation of\r
40  * sbSEND_COMPLETED() being overridden by an implementation in FreeRTOSConfig.h\r
41  * that writes the handle of the message buffer that contains data into the\r
42  * control message buffer, then generates an interrupt in core B.  The necessary\r
43  * implementation is provided in this file and can be enabled by adding the\r
44  * following to FreeRTOSConfig.h:\r
45  *\r
46  * #define sbSEND_COMPLETED( pxStreamBuffer ) vGenerateCoreBInterrupt( pxStreamBuffer )\r
47  *\r
48  * Core to core communication via message buffer requires the message buffers\r
49  * to be at an address known to both cores within shared memory.\r
50  *\r
51  * Note that, while this example uses three message buffers, the same\r
52  * functionality can be implemented using a single message buffer by using the\r
53  * same design pattern described on the link below for queues, but using message\r
54  * buffers instead.  It is actually simpler with a message buffer as variable\r
55  * length data can be written into the message buffer directly:\r
56  * http://www.freertos.org/Pend-on-multiple-rtos-objects.html#alternative_design_pattern\r
57  */\r
58 \r
59 /* Standard includes. */\r
60 #include "stdio.h"\r
61 #include "string.h"\r
62 \r
63 /* FreeRTOS includes. */\r
64 #include "FreeRTOS.h"\r
65 #include "task.h"\r
66 #include "message_buffer.h"\r
67 \r
68 /* Demo app includes. */\r
69 #include "MessageBufferAMP.h"\r
70 \r
71 /* Enough for 3 4 byte pointers, including the additional 4 bytes per message\r
72 overhead of message buffers. */\r
73 #define mbaCONTROL_MESSAGE_BUFFER_SIZE ( 24 )\r
74 \r
75 /* Enough four 4 8 byte strings, plus the additional 4 bytes per message\r
76 overhead of message buffers. */\r
77 #define mbaTASK_MESSAGE_BUFFER_SIZE ( 60 )\r
78 \r
79 /* The number of instances of prvCoreBTasks that are created. */\r
80 #define mbaNUMBER_OF_CORE_B_TASKS       2\r
81 \r
82 /* A block time of 0 simply means, don't block. */\r
83 #define mbaDONT_BLOCK                           0\r
84 \r
85 /* Macro that mimics an interrupt service routine executing by simply calling\r
86 the routine inline. */\r
87 #define mbaGENERATE_CORE_B_INTERRUPT() prvCoreBInterruptHandler()\r
88 \r
89 /*-----------------------------------------------------------*/\r
90 \r
91 /*\r
92  * Implementation of the task that, on a real dual core device, would run on\r
93  * core A and send message to tasks running on core B.\r
94  */\r
95 static void prvCoreATask( void *pvParameters );\r
96 \r
97 /*\r
98  * Implementation of the task that, on a real dual core device, would run on\r
99  * core B and receive message from core A.  The demo creates two instances of\r
100  * this task.\r
101  */\r
102 static void prvCoreBTasks( void *pvParameters );\r
103 \r
104 /*\r
105  * The function that, on a real dual core device, would handle inter-core\r
106  * interrupts, but in this case is just called inline.\r
107  */\r
108 static void prvCoreBInterruptHandler( void );\r
109 \r
110 /*-----------------------------------------------------------*/\r
111 \r
112 /* The message buffers used to pass data from core A to core B. */\r
113 static MessageBufferHandle_t xCoreBMessageBuffers[ mbaNUMBER_OF_CORE_B_TASKS ];\r
114 \r
115 /* The control message buffer.  This is used to pass the handle of the message\r
116 message buffer that holds application data into the core to core interrupt\r
117 service routine. */\r
118 static MessageBufferHandle_t xControlMessageBuffer;\r
119 \r
120 /* Counters used to indicate to the check that the tasks are still executing. */\r
121 static uint32_t ulCycleCounters[ mbaNUMBER_OF_CORE_B_TASKS ];\r
122 \r
123 /* Set to pdFALSE if any errors are detected.  Used to inform the check task\r
124 that something might be wrong. */\r
125 BaseType_t xDemoStatus = pdPASS;\r
126 \r
127 /*-----------------------------------------------------------*/\r
128 \r
129 void vStartMessageBufferAMPTasks( configSTACK_DEPTH_TYPE xStackSize )\r
130 {\r
131 BaseType_t x;\r
132 \r
133         xControlMessageBuffer = xMessageBufferCreate( mbaCONTROL_MESSAGE_BUFFER_SIZE );\r
134 \r
135         xTaskCreate( prvCoreATask,              /* The function that implements the task. */\r
136                                  "AMPCoreA",            /* Human readable name for the task. */\r
137                                  xStackSize,            /* Stack size (in words!). */\r
138                                  NULL,                          /* Task parameter is not used. */\r
139                                  tskIDLE_PRIORITY,      /* The priority at which the task is created. */\r
140                                  NULL );                        /* No use for the task handle. */\r
141 \r
142         for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ )\r
143         {\r
144                 xCoreBMessageBuffers[ x ] = xMessageBufferCreate( mbaTASK_MESSAGE_BUFFER_SIZE );\r
145                 configASSERT( xCoreBMessageBuffers[ x ] );\r
146 \r
147                 /* Pass the loop counter into the created task using the task's\r
148                 parameter.  The task then uses the value as an index into the\r
149                 ulCycleCounters and xCoreBMessageBuffers arrays. */\r
150                 xTaskCreate( prvCoreBTasks,\r
151                                          "AMPCoreB1",\r
152                                          xStackSize,\r
153                                          ( void * ) x,\r
154                                          tskIDLE_PRIORITY + 1,\r
155                                          NULL );\r
156         }\r
157 }\r
158 /*-----------------------------------------------------------*/\r
159 \r
160 static void prvCoreATask( void *pvParameters )\r
161 {\r
162 BaseType_t x;\r
163 uint32_t ulNextValue = 0;\r
164 const TickType_t xDelay = pdMS_TO_TICKS( 250 );\r
165 char cString[ 15 ]; /* At least large enough to hold "4294967295\0" (0xffffffff). */\r
166 \r
167         /* Remove warning about unused parameters. */\r
168         ( void ) pvParameters;\r
169 \r
170         for( ;; )\r
171         {\r
172                 /* Create the next string to send.  The value is incremented on each\r
173                 loop iteration, and the length of the string changes as the number of\r
174                 digits in the value increases. */\r
175                 sprintf( cString, "%lu", ( unsigned long ) ulNextValue );\r
176 \r
177                 /* Send the value from this (pseudo) Core A to the tasks on the (pseudo)\r
178                 Core B via the message buffers.  This will result in sbSEND_COMPLETED()\r
179                 being executed, which in turn will write the handle of the message\r
180                 buffer written to into xControlMessageBuffer then generate an interrupt\r
181                 in core B. */\r
182                 for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ )\r
183                 {\r
184                         xMessageBufferSend( /* The message buffer to write to. */\r
185                                                                 xCoreBMessageBuffers[ x ],\r
186                                                                 /* The source of the data to send. */\r
187                                                                 ( void * ) cString,\r
188                                                                 /* The length of the data to send. */\r
189                                                                 strlen( cString ),\r
190                                                                 /* The block time, should the buffer be full. */\r
191                                                                 mbaDONT_BLOCK );\r
192                 }\r
193 \r
194                 /* Delay before repeating with a different and potentially different\r
195                 length string. */\r
196                 vTaskDelay( xDelay );\r
197                 ulNextValue++;\r
198         }\r
199 }\r
200 /*-----------------------------------------------------------*/\r
201 \r
202 static void prvCoreBTasks( void *pvParameters )\r
203 {\r
204 BaseType_t x;\r
205 size_t xReceivedBytes;\r
206 uint32_t ulNextValue = 0;\r
207 char cExpectedString[ 15 ]; /* At least large enough to hold "4294967295\0" (0xffffffff). */\r
208 char cReceivedString[ 15 ];\r
209 \r
210         /* The index into the xCoreBMessageBuffers and ulLoopCounter arrays is\r
211         passed into this task using the task's parameter. */\r
212         x = ( BaseType_t ) pvParameters;\r
213         configASSERT( x < mbaNUMBER_OF_CORE_B_TASKS );\r
214 \r
215         for( ;; )\r
216         {\r
217                 /* Create the string that is expected to be received this time round. */\r
218                 sprintf( cExpectedString, "%lu", ( unsigned long ) ulNextValue );\r
219 \r
220                 /* Wait to receive the next message from core A. */\r
221                 memset( cReceivedString, 0x00, sizeof( cReceivedString ) );\r
222                 xReceivedBytes = xMessageBufferReceive( /* The message buffer to receive from. */\r
223                                                                                                 xCoreBMessageBuffers[ x ],\r
224                                                                                                 /* Location to store received data. */\r
225                                                                                                 cReceivedString,\r
226                                                                                                 /* Maximum number of bytes to receive. */\r
227                                                                                                 sizeof( cReceivedString ),\r
228                                                                                                 /* Ticks to wait if buffer is empty. */\r
229                                                                                                 portMAX_DELAY );\r
230 \r
231                 /* Check the number of bytes received was as expected. */\r
232                 configASSERT( xReceivedBytes == strlen( cExpectedString ) );\r
233                 ( void ) xReceivedBytes; /* Incase configASSERT() is not defined. */\r
234 \r
235                 /* If the received string matches that expected then increment the loop\r
236                 counter so the check task knows this task is still running. */\r
237                 if( strcmp( cReceivedString, cExpectedString ) == 0 )\r
238                 {\r
239                         ( ulCycleCounters[ x ] )++;\r
240                 }\r
241                 else\r
242                 {\r
243                         xDemoStatus = pdFAIL;\r
244                 }\r
245 \r
246                 /* Expect the next string in sequence the next time around. */\r
247                 ulNextValue++;\r
248         }\r
249 }\r
250 /*-----------------------------------------------------------*/\r
251 \r
252 /* Called by the reimplementation of sbSEND_COMPLETED(), which can be defined\r
253 as follows in FreeRTOSConfig.h:\r
254 #define sbSEND_COMPLETED( pxStreamBuffer ) vGenerateCoreBInterrupt( pxStreamBuffer )\r
255 */\r
256 void vGenerateCoreBInterrupt( void * xUpdatedMessageBuffer )\r
257 {\r
258 MessageBufferHandle_t xUpdatedBuffer = ( MessageBufferHandle_t ) xUpdatedMessageBuffer;\r
259 \r
260         /* If sbSEND_COMPLETED() has been implemented as above, then this function\r
261         is called from within xMessageBufferSend().  As this function also calls\r
262         xMessageBufferSend() itself it is necessary to guard against a recursive\r
263         call.  If the message buffer just updated is the message buffer written to\r
264         by this function, then this is a recursive call, and the function can just\r
265         exit without taking further action. */\r
266         if( xUpdatedBuffer != xControlMessageBuffer )\r
267         {\r
268                 /* Use xControlMessageBuffer to pass the handle of the message buffer\r
269                 written to by core A to the interrupt handler about to be generated in\r
270                 core B. */\r
271                 xMessageBufferSend( xControlMessageBuffer, &xUpdatedBuffer, sizeof( xUpdatedBuffer ), mbaDONT_BLOCK );\r
272 \r
273                 /* This is where the interrupt would be generated.  In this case it is\r
274                 not a genuine interrupt handler that executes, just a standard function\r
275                 call. */\r
276                 mbaGENERATE_CORE_B_INTERRUPT();\r
277         }\r
278 }\r
279 /*-----------------------------------------------------------*/\r
280 \r
281 /* Handler for the interrupts that are triggered on core A but execute on core\r
282 B. */\r
283 static void prvCoreBInterruptHandler( void )\r
284 {\r
285 MessageBufferHandle_t xUpdatedMessageBuffer;\r
286 BaseType_t xHigherPriorityTaskWoken = pdFALSE;\r
287 \r
288         /* xControlMessageBuffer contains the handle of the message buffer that\r
289         contains data. */\r
290         if( xMessageBufferReceive( xControlMessageBuffer,\r
291                                                            &xUpdatedMessageBuffer,\r
292                                                            sizeof( xUpdatedMessageBuffer ),\r
293                                                            mbaDONT_BLOCK ) == sizeof( xUpdatedMessageBuffer ) )\r
294         {\r
295                 /* Call the API function that sends a notification to any task that is\r
296                 blocked on the xUpdatedMessageBuffer message buffer waiting for data to\r
297                 arrive. */\r
298                 xMessageBufferSendCompletedFromISR( xUpdatedMessageBuffer, &xHigherPriorityTaskWoken );\r
299         }\r
300 \r
301         /* Normal FreeRTOS yield from interrupt semantics, where\r
302         xHigherPriorityTaskWoken is initialzed to pdFALSE and will then get set to\r
303         pdTRUE if the interrupt safe API unblocks a task that has a priority above\r
304         that of the currently executing task. */\r
305         portYIELD_FROM_ISR( xHigherPriorityTaskWoken );\r
306 }\r
307 /*-----------------------------------------------------------*/\r
308 \r
309 BaseType_t xAreMessageBufferAMPTasksStillRunning( void )\r
310 {\r
311 static uint32_t ulLastCycleCounters[ mbaNUMBER_OF_CORE_B_TASKS ] = { 0 };\r
312 BaseType_t x;\r
313 \r
314         /* Called by the check task to determine the health status of the tasks\r
315         implemented in this demo. */\r
316         for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ )\r
317         {\r
318                 if( ulLastCycleCounters[ x ] == ulCycleCounters[ x ] )\r
319                 {\r
320                         xDemoStatus = pdFAIL;\r
321                 }\r
322                 else\r
323                 {\r
324                         ulLastCycleCounters[ x ] = ulCycleCounters[ x ];\r
325                 }\r
326         }\r
327 \r
328         return xDemoStatus;\r
329 }\r
330 \r