Files
Linux_Drivers/freertos/cvitek/task/demo/Common/Minimal/MessageBufferAMP.c
sam.xiang cbb030f19f [freertos] add freertos firmware
Change-Id: I4158d66d9b5fc444e28287f55e79ac24e0a1666f
2023-03-10 20:35:49 +08:00

321 lines
12 KiB
C

/*
* 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;
}