text: /*
   * FreeRTOS V202107.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:
   * 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 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;
  }
  