]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/Common/Minimal/MessageBufferAMP.c
a73e482b378df4f3d82bc997b66fa47187170461
[freertos] / FreeRTOS / Demo / Common / Minimal / MessageBufferAMP.c
1 /*\r
2  * FreeRTOS Kernel V10.0.0\r
3  * Copyright (C) 2017 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. If you wish to use our Amazon\r
14  * FreeRTOS name, please do so in a fair use way that does not cause confusion.\r
15  *\r
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
18  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
22  *\r
23  * http://www.FreeRTOS.org\r
24  * http://aws.amazon.com/freertos\r
25  *\r
26  * 1 tab == 4 spaces!\r
27  */\r
28 \r
29 /*\r
30  * An example that mimics a message buffer being used to pass data from one core\r
31  * to another.  The core that sends the data is referred to as core A.  The core\r
32  * that receives the data is referred to as core B.  The task implemented by\r
33  * prvCoreATask() runs on core A.  Two instances of the task implemented by\r
34  * prvCoreBTasks() run on core B.  prvCoreATask() sends messages via message\r
35  * buffers to both instances of prvCoreBTasks(), one message buffer per channel.\r
36  * A third message buffer is used to pass the handle of the message buffer\r
37  * written to by core A to an interrupt service routine that is triggered by\r
38  * core A but executes on core B.\r
39  *\r
40  * The example relies on the FreeRTOS provided default implementation of\r
41  * sbSEND_COMPLETED() being overridden by an implementation in FreeRTOSConfig.h\r
42  * that writes the handle of the message buffer that contains data into the\r
43  * control message buffer, then generates an interrupt in core B.  The necessary\r
44  * implementation is provided in this file and can be enabled by adding the\r
45  * following to FreeRTOSConfig.h:\r
46  *\r
47  * #define sbSEND_COMPLETED( pxStreamBuffer ) vGenerateCoreBInterrupt( pxStreamBuffer )\r
48  *\r
49  * Core to core communication via message buffer requires the message buffers\r
50  * to be at an address known to both cores within shared memory.\r
51  *\r
52  * Note that, while this example uses three message buffers, the same\r
53  * functionality can be implemented using a single message buffer by using the\r
54  * same design pattern described on the link below for queues, but using message\r
55  * buffers instead.  It is actually simpler with a message buffer as variable\r
56  * length data can be written into the message buffer directly:\r
57  * http://www.freertos.org/Pend-on-multiple-rtos-objects.html#alternative_design_pattern\r
58  */\r
59 \r
60 /* Standard includes. */\r
61 #include "stdio.h"\r
62 #include "string.h"\r
63 \r
64 /* FreeRTOS includes. */\r
65 #include "FreeRTOS.h"\r
66 #include "task.h"\r
67 #include "message_buffer.h"\r
68 \r
69 /* Demo app includes. */\r
70 #include "MessageBufferAMP.h"\r
71 \r
72 /* Enough for 3 4 byte pointers, including the additional 4 bytes per message\r
73 overhead of message buffers. */\r
74 #define mbaCONTROL_MESSAGE_BUFFER_SIZE ( 24 )\r
75 \r
76 /* Enough four 4 8 byte strings, plus the additional 4 bytes per message\r
77 overhead of message buffers. */\r
78 #define mbaTASK_MESSAGE_BUFFER_SIZE ( 60 )\r
79 \r
80 /* The number of instances of prvCoreBTasks that are created. */\r
81 #define mbaNUMBER_OF_CORE_B_TASKS       2\r
82 \r
83 /* A block time of 0 simply means, don't block. */\r
84 #define mbaDONT_BLOCK                           0\r
85 \r
86 /* Macro that mimics an interrupt service routine executing by simply calling\r
87 the routine inline. */\r
88 #define mbaGENERATE_CORE_B_INTERRUPT() prvCoreBInterruptHandler()\r
89 \r
90 /*-----------------------------------------------------------*/\r
91 \r
92 /*\r
93  * Implementation of the task that, on a real dual core device, would run on\r
94  * core A and send message to tasks running on core B.\r
95  */\r
96 static void prvCoreATask( void *pvParameters );\r
97 \r
98 /*\r
99  * Implementation of the task that, on a real dual core device, would run on\r
100  * core B and receive message from core A.  The demo creates two instances of\r
101  * this task.\r
102  */\r
103 static void prvCoreBTasks( void *pvParameters );\r
104 \r
105 /*\r
106  * The function that, on a real dual core device, would handle inter-core\r
107  * interrupts, but in this case is just called inline.\r
108  */\r
109 static void prvCoreBInterruptHandler( void );\r
110 \r
111 /*-----------------------------------------------------------*/\r
112 \r
113 /* The message buffers used to pass data from core A to core B. */\r
114 static MessageBufferHandle_t xCoreBMessageBuffers[ mbaNUMBER_OF_CORE_B_TASKS ];\r
115 \r
116 /* The control message buffer.  This is used to pass the handle of the message\r
117 message buffer that holds application data into the core to core interrupt\r
118 service routine. */\r
119 static MessageBufferHandle_t xControlMessageBuffer;\r
120 \r
121 /* Counters used to indicate to the check that the tasks are still executing. */\r
122 static uint32_t ulCycleCounters[ mbaNUMBER_OF_CORE_B_TASKS ];\r
123 \r
124 /* Set to pdFALSE if any errors are detected.  Used to inform the check task\r
125 that something might be wrong. */\r
126 BaseType_t xDemoStatus = pdPASS;\r
127 \r
128 /*-----------------------------------------------------------*/\r
129 \r
130 void vStartMessageBufferAMPTasks( void )\r
131 {\r
132 BaseType_t x;\r
133 \r
134         xControlMessageBuffer = xMessageBufferCreate( mbaCONTROL_MESSAGE_BUFFER_SIZE );\r
135 \r
136         xTaskCreate( prvCoreATask,                              /* The function that implements the task. */\r
137                                  "AMPCoreA",                            /* Human readable name for the task. */\r
138                                  configMINIMAL_STACK_SIZE,      /* Stack size (in words!). */\r
139                                  NULL,                                          /* Task parameter is not used. */\r
140                                  tskIDLE_PRIORITY,                      /* The priority at which the task is created. */\r
141                                  NULL );                                        /* No use for the task handle. */\r
142 \r
143         for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ )\r
144         {\r
145                 xCoreBMessageBuffers[ x ] = xMessageBufferCreate( mbaTASK_MESSAGE_BUFFER_SIZE );\r
146                 configASSERT( xCoreBMessageBuffers[ x ] );\r
147 \r
148                 /* Pass the loop counter into the created task using the task's\r
149                 parameter.  The task then uses the value as an index into the\r
150                 ulCycleCounters and xCoreBMessageBuffers arrays. */\r
151                 xTaskCreate( prvCoreBTasks,\r
152                                          "AMPCoreB1",\r
153                                          configMINIMAL_STACK_SIZE,\r
154                                          ( void * ) x,\r
155                                          tskIDLE_PRIORITY + 1,\r
156                                          NULL );\r
157         }\r
158 }\r
159 /*-----------------------------------------------------------*/\r
160 \r
161 static void prvCoreATask( void *pvParameters )\r
162 {\r
163 BaseType_t x;\r
164 uint32_t ulNextValue = 0;\r
165 const TickType_t xDelay = pdMS_TO_TICKS( 250 );\r
166 char cString[ 15 ]; /* At least large enough to hold "4294967295\0" (0xffffffff). */\r
167 \r
168         /* Remove warning about unused parameters. */\r
169         ( void ) pvParameters;\r
170 \r
171         for( ;; )\r
172         {\r
173                 /* Create the next string to send.  The value is incremented on each\r
174                 loop iteration, and the length of the string changes as the number of\r
175                 digits in the value increases. */\r
176                 sprintf( cString, "%lu", ( unsigned long ) ulNextValue );\r
177 \r
178                 /* Send the value from this (pseudo) Core A to the tasks on the (pseudo)\r
179                 Core B via the message buffers.  This will result in sbSEND_COMPLETED()\r
180                 being executed, which in turn will write the handle of the message\r
181                 buffer written to into xControlMessageBuffer then generate an interrupt\r
182                 in core B. */\r
183                 for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ )\r
184                 {\r
185                         xMessageBufferSend( /* The message buffer to write to. */\r
186                                                                 xCoreBMessageBuffers[ x ],\r
187                                                                 /* The source of the data to send. */\r
188                                                                 ( void * ) cString,\r
189                                                                 /* The length of the data to send. */\r
190                                                                 strlen( cString ),\r
191                                                                 /* The block time, should the buffer be full. */\r
192                                                                 mbaDONT_BLOCK );\r
193                 }\r
194 \r
195                 /* Delay before repeating with a different and potentially different\r
196                 length string. */\r
197                 vTaskDelay( xDelay );\r
198                 ulNextValue++;\r
199         }\r
200 }\r
201 /*-----------------------------------------------------------*/\r
202 \r
203 static void prvCoreBTasks( void *pvParameters )\r
204 {\r
205 BaseType_t x;\r
206 size_t xReceivedBytes;\r
207 uint32_t ulNextValue = 0;\r
208 char cExpectedString[ 15 ]; /* At least large enough to hold "4294967295\0" (0xffffffff). */\r
209 char cReceivedString[ 15 ];\r
210 \r
211         /* The index into the xCoreBMessageBuffers and ulLoopCounter arrays is\r
212         passed into this task using the task's parameter. */\r
213         x = ( BaseType_t ) pvParameters;\r
214         configASSERT( x < mbaNUMBER_OF_CORE_B_TASKS );\r
215 \r
216         for( ;; )\r
217         {\r
218                 /* Create the string that is expected to be received this time round. */\r
219                 sprintf( cExpectedString, "%lu", ( unsigned long ) ulNextValue );\r
220 \r
221                 /* Wait to receive the next message from core A. */\r
222                 memset( cReceivedString, 0x00, sizeof( cReceivedString ) );\r
223                 xReceivedBytes = xMessageBufferReceive( /* The message buffer to receive from. */\r
224                                                                                                 xCoreBMessageBuffers[ x ],\r
225                                                                                                 /* Location to store received data. */\r
226                                                                                                 cReceivedString,\r
227                                                                                                 /* Maximum number of bytes to receive. */\r
228                                                                                                 sizeof( cReceivedString ),\r
229                                                                                                 /* Ticks to wait if buffer is empty. */\r
230                                                                                                 portMAX_DELAY );\r
231 \r
232                 /* Check the number of bytes received was as expected. */\r
233                 configASSERT( xReceivedBytes == strlen( cExpectedString ) );\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