/* | |
* FreeRTOS Kernel V10.3.0 | |
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy of | |
* this software and associated documentation files (the "Software"), to deal in | |
* the Software without restriction, including without limitation the rights to | |
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of | |
* the Software, and to permit persons to whom the Software is furnished to do so, | |
* subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in all | |
* copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS | |
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
* | |
* http://www.FreeRTOS.org | |
* http://aws.amazon.com/freertos | |
* | |
* 1 tab == 4 spaces! | |
*/ | |
/* | |
* An example that mimics a message buffer being used to pass data from one core | |
* to another. The core that sends the data is referred to as core A. The core | |
* that receives the data is referred to as core B. The task implemented by | |
* prvCoreATask() runs on core A. Two instances of the task implemented by | |
* prvCoreBTasks() run on core B. prvCoreATask() sends messages via message | |
* buffers to both instances of prvCoreBTasks(), one message buffer per channel. | |
* A third message buffer is used to pass the handle of the message buffer | |
* written to by core A to an interrupt service routine that is triggered by | |
* core A but executes on core B. | |
* | |
* The example relies on the FreeRTOS provided default implementation of | |
* sbSEND_COMPLETED() being overridden by an implementation in FreeRTOSConfig.h | |
* that writes the handle of the message buffer that contains data into the | |
* control message buffer, then generates an interrupt in core B. The necessary | |
* implementation is provided in this file and can be enabled by adding the | |
* following to FreeRTOSConfig.h: | |
* | |
* #define sbSEND_COMPLETED( pxStreamBuffer ) vGenerateCoreBInterrupt( pxStreamBuffer ) | |
* | |
* Core to core communication via message buffer requires the message buffers | |
* to be at an address known to both cores within shared memory. | |
* | |
* Note that, while this example uses three message buffers, the same | |
* functionality can be implemented using a single message buffer by using the | |
* same design pattern described on the link below for queues, but using message | |
* buffers instead. It is actually simpler with a message buffer as variable | |
* length data can be written into the message buffer directly: | |
* http://www.freertos.org/Pend-on-multiple-rtos-objects.html#alternative_design_pattern | |
*/ | |
/* Standard includes. */ | |
#include "stdio.h" | |
#include "string.h" | |
/* FreeRTOS includes. */ | |
#include "FreeRTOS.h" | |
#include "task.h" | |
#include "message_buffer.h" | |
/* Demo app includes. */ | |
#include "MessageBufferAMP.h" | |
/* Enough for 3 4 byte pointers, including the additional 4 bytes per message | |
overhead of message buffers. */ | |
#define mbaCONTROL_MESSAGE_BUFFER_SIZE ( 24 ) | |
/* Enough four 4 8 byte strings, plus the additional 4 bytes per message | |
overhead of message buffers. */ | |
#define mbaTASK_MESSAGE_BUFFER_SIZE ( 60 ) | |
/* The number of instances of prvCoreBTasks that are created. */ | |
#define mbaNUMBER_OF_CORE_B_TASKS 2 | |
/* A block time of 0 simply means, don't block. */ | |
#define mbaDONT_BLOCK 0 | |
/* Macro that mimics an interrupt service routine executing by simply calling | |
the routine inline. */ | |
#define mbaGENERATE_CORE_B_INTERRUPT() prvCoreBInterruptHandler() | |
/*-----------------------------------------------------------*/ | |
/* | |
* Implementation of the task that, on a real dual core device, would run on | |
* core A and send message to tasks running on core B. | |
*/ | |
static void prvCoreATask( void *pvParameters ); | |
/* | |
* Implementation of the task that, on a real dual core device, would run on | |
* core B and receive message from core A. The demo creates two instances of | |
* this task. | |
*/ | |
static void prvCoreBTasks( void *pvParameters ); | |
/* | |
* The function that, on a real dual core device, would handle inter-core | |
* interrupts, but in this case is just called inline. | |
*/ | |
static void prvCoreBInterruptHandler( void ); | |
/*-----------------------------------------------------------*/ | |
/* The message buffers used to pass data from core A to core B. */ | |
static MessageBufferHandle_t xCoreBMessageBuffers[ mbaNUMBER_OF_CORE_B_TASKS ]; | |
/* The control message buffer. This is used to pass the handle of the message | |
message buffer that holds application data into the core to core interrupt | |
service routine. */ | |
static MessageBufferHandle_t xControlMessageBuffer; | |
/* Counters used to indicate to the check that the tasks are still executing. */ | |
static uint32_t ulCycleCounters[ mbaNUMBER_OF_CORE_B_TASKS ]; | |
/* Set to pdFALSE if any errors are detected. Used to inform the check task | |
that something might be wrong. */ | |
BaseType_t xDemoStatus = pdPASS; | |
/*-----------------------------------------------------------*/ | |
void vStartMessageBufferAMPTasks( configSTACK_DEPTH_TYPE xStackSize ) | |
{ | |
BaseType_t x; | |
xControlMessageBuffer = xMessageBufferCreate( mbaCONTROL_MESSAGE_BUFFER_SIZE ); | |
xTaskCreate( prvCoreATask, /* The function that implements the task. */ | |
"AMPCoreA", /* Human readable name for the task. */ | |
xStackSize, /* Stack size (in words!). */ | |
NULL, /* Task parameter is not used. */ | |
tskIDLE_PRIORITY, /* The priority at which the task is created. */ | |
NULL ); /* No use for the task handle. */ | |
for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ ) | |
{ | |
xCoreBMessageBuffers[ x ] = xMessageBufferCreate( mbaTASK_MESSAGE_BUFFER_SIZE ); | |
configASSERT( xCoreBMessageBuffers[ x ] ); | |
/* Pass the loop counter into the created task using the task's | |
parameter. The task then uses the value as an index into the | |
ulCycleCounters and xCoreBMessageBuffers arrays. */ | |
xTaskCreate( prvCoreBTasks, | |
"AMPCoreB1", | |
xStackSize, | |
( void * ) x, | |
tskIDLE_PRIORITY + 1, | |
NULL ); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvCoreATask( void *pvParameters ) | |
{ | |
BaseType_t x; | |
uint32_t ulNextValue = 0; | |
const TickType_t xDelay = pdMS_TO_TICKS( 250 ); | |
char cString[ 15 ]; /* At least large enough to hold "4294967295\0" (0xffffffff). */ | |
/* Remove warning about unused parameters. */ | |
( void ) pvParameters; | |
for( ;; ) | |
{ | |
/* Create the next string to send. The value is incremented on each | |
loop iteration, and the length of the string changes as the number of | |
digits in the value increases. */ | |
sprintf( cString, "%lu", ( unsigned long ) ulNextValue ); | |
/* Send the value from this (pseudo) Core A to the tasks on the (pseudo) | |
Core B via the message buffers. This will result in sbSEND_COMPLETED() | |
being executed, which in turn will write the handle of the message | |
buffer written to into xControlMessageBuffer then generate an interrupt | |
in core B. */ | |
for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ ) | |
{ | |
xMessageBufferSend( /* The message buffer to write to. */ | |
xCoreBMessageBuffers[ x ], | |
/* The source of the data to send. */ | |
( void * ) cString, | |
/* The length of the data to send. */ | |
strlen( cString ), | |
/* The block time, should the buffer be full. */ | |
mbaDONT_BLOCK ); | |
} | |
/* Delay before repeating with a different and potentially different | |
length string. */ | |
vTaskDelay( xDelay ); | |
ulNextValue++; | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
static void prvCoreBTasks( void *pvParameters ) | |
{ | |
BaseType_t x; | |
size_t xReceivedBytes; | |
uint32_t ulNextValue = 0; | |
char cExpectedString[ 15 ]; /* At least large enough to hold "4294967295\0" (0xffffffff). */ | |
char cReceivedString[ 15 ]; | |
/* The index into the xCoreBMessageBuffers and ulLoopCounter arrays is | |
passed into this task using the task's parameter. */ | |
x = ( BaseType_t ) pvParameters; | |
configASSERT( x < mbaNUMBER_OF_CORE_B_TASKS ); | |
for( ;; ) | |
{ | |
/* Create the string that is expected to be received this time round. */ | |
sprintf( cExpectedString, "%lu", ( unsigned long ) ulNextValue ); | |
/* Wait to receive the next message from core A. */ | |
memset( cReceivedString, 0x00, sizeof( cReceivedString ) ); | |
xReceivedBytes = xMessageBufferReceive( /* The message buffer to receive from. */ | |
xCoreBMessageBuffers[ x ], | |
/* Location to store received data. */ | |
cReceivedString, | |
/* Maximum number of bytes to receive. */ | |
sizeof( cReceivedString ), | |
/* Ticks to wait if buffer is empty. */ | |
portMAX_DELAY ); | |
/* Check the number of bytes received was as expected. */ | |
configASSERT( xReceivedBytes == strlen( cExpectedString ) ); | |
( void ) xReceivedBytes; /* Incase configASSERT() is not defined. */ | |
/* If the received string matches that expected then increment the loop | |
counter so the check task knows this task is still running. */ | |
if( strcmp( cReceivedString, cExpectedString ) == 0 ) | |
{ | |
( ulCycleCounters[ x ] )++; | |
} | |
else | |
{ | |
xDemoStatus = pdFAIL; | |
} | |
/* Expect the next string in sequence the next time around. */ | |
ulNextValue++; | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
/* Called by the reimplementation of sbSEND_COMPLETED(), which can be defined | |
as follows in FreeRTOSConfig.h: | |
#define sbSEND_COMPLETED( pxStreamBuffer ) vGenerateCoreBInterrupt( pxStreamBuffer ) | |
*/ | |
void vGenerateCoreBInterrupt( void * xUpdatedMessageBuffer ) | |
{ | |
MessageBufferHandle_t xUpdatedBuffer = ( MessageBufferHandle_t ) xUpdatedMessageBuffer; | |
/* If sbSEND_COMPLETED() has been implemented as above, then this function | |
is called from within xMessageBufferSend(). As this function also calls | |
xMessageBufferSend() itself it is necessary to guard against a recursive | |
call. If the message buffer just updated is the message buffer written to | |
by this function, then this is a recursive call, and the function can just | |
exit without taking further action. */ | |
if( xUpdatedBuffer != xControlMessageBuffer ) | |
{ | |
/* Use xControlMessageBuffer to pass the handle of the message buffer | |
written to by core A to the interrupt handler about to be generated in | |
core B. */ | |
xMessageBufferSend( xControlMessageBuffer, &xUpdatedBuffer, sizeof( xUpdatedBuffer ), mbaDONT_BLOCK ); | |
/* This is where the interrupt would be generated. In this case it is | |
not a genuine interrupt handler that executes, just a standard function | |
call. */ | |
mbaGENERATE_CORE_B_INTERRUPT(); | |
} | |
} | |
/*-----------------------------------------------------------*/ | |
/* Handler for the interrupts that are triggered on core A but execute on core | |
B. */ | |
static void prvCoreBInterruptHandler( void ) | |
{ | |
MessageBufferHandle_t xUpdatedMessageBuffer; | |
BaseType_t xHigherPriorityTaskWoken = pdFALSE; | |
/* xControlMessageBuffer contains the handle of the message buffer that | |
contains data. */ | |
if( xMessageBufferReceive( xControlMessageBuffer, | |
&xUpdatedMessageBuffer, | |
sizeof( xUpdatedMessageBuffer ), | |
mbaDONT_BLOCK ) == sizeof( xUpdatedMessageBuffer ) ) | |
{ | |
/* Call the API function that sends a notification to any task that is | |
blocked on the xUpdatedMessageBuffer message buffer waiting for data to | |
arrive. */ | |
xMessageBufferSendCompletedFromISR( xUpdatedMessageBuffer, &xHigherPriorityTaskWoken ); | |
} | |
/* Normal FreeRTOS yield from interrupt semantics, where | |
xHigherPriorityTaskWoken is initialzed to pdFALSE and will then get set to | |
pdTRUE if the interrupt safe API unblocks a task that has a priority above | |
that of the currently executing task. */ | |
portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); | |
} | |
/*-----------------------------------------------------------*/ | |
BaseType_t xAreMessageBufferAMPTasksStillRunning( void ) | |
{ | |
static uint32_t ulLastCycleCounters[ mbaNUMBER_OF_CORE_B_TASKS ] = { 0 }; | |
BaseType_t x; | |
/* Called by the check task to determine the health status of the tasks | |
implemented in this demo. */ | |
for( x = 0; x < mbaNUMBER_OF_CORE_B_TASKS; x++ ) | |
{ | |
if( ulLastCycleCounters[ x ] == ulCycleCounters[ x ] ) | |
{ | |
xDemoStatus = pdFAIL; | |
} | |
else | |
{ | |
ulLastCycleCounters[ x ] = ulCycleCounters[ x ]; | |
} | |
} | |
return xDemoStatus; | |
} | |