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