EBIKE-FreeRTOS/Common/Minimal/MessageBufferAMP.c

329 lines
13 KiB
C
Raw Normal View History

2024-04-14 18:38:39 +08:00
/*
* FreeRTOS V202212.00
* 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.
*
* https://www.FreeRTOS.org
* https://github.com/FreeRTOS
*
*/
/*
* 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:
* https://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 re-implementation 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 initialized 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;
}