//###########################################################################
//
// FILE:   F2837xD_Ipc_Driver.c
//
// TITLE:  F2837xD Inter-Processor Communication (IPC) API Driver Functions.
//
// DESCRIPTION:
//         28x API functions for inter-processor communications between the
//         two CPUs. The IPC functions require the usage of the CPU1 to CPU2
//         and CPU2 to CPU1 MSG RAM's to store the circular ring
//         buffer and indexes. Commands can be queued up in order on a single
//         IPC interrupt channel.  For those IPC commands which are not
//         interdependent, multiple IPC interrupt channels may be used.
//         The driver functions in this file are available only as
//         sample functions for application development.  Due to the generic
//         nature of these functions and the cycle overhead inherent to a
//         function call, the code is not intended to be used in cases where
//         maximum efficiency is required in a system.
// NOTE:   This source code is used by both CPUs. That is both CPU1 and CPU2
//         Cores use this code.
//         The active debug CPU will be referred to as Local CPU.
//         When using this source code in CPU1, the term "local"
//         will mean CPU1 and the term "remote" CPU will be mean CPU2.
//         When using this source code in CPU2, the term "local"
//         will mean CPU2 and the term "remote" CPU will be mean CPU1.
//
//         The abbreviations LtoR and RtoL  within the function names mean
//         Local to Remote and Remote to Local respectively.
//
//###########################################################################
//
// $Release Date: $
// $Copyright:
// Copyright (C) 2013-2023 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without 
// modification, are permitted provided that the following conditions 
// are met:
// 
//   Redistributions of source code must retain the above copyright 
//   notice, this list of conditions and the following disclaimer.
// 
//   Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the 
//   documentation and/or other materials provided with the   
//   distribution.
// 
//   Neither the name of Texas Instruments Incorporated nor the names of
//   its contributors may be used to endorse or promote products derived
//   from this software without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// $
//###########################################################################

//*****************************************************************************
//! \addtogroup ipc_driver_api
//! @{
//*****************************************************************************
#include "F2837xD_device.h"
#include "F2837xD_Ipc_drivers.h"

#if defined(CPU1)
#pragma DATA_SECTION(g_asIPCCPU1toCPU2Buffers, "PUTBUFFER");
#pragma DATA_SECTION(g_usPutWriteIndexes, "PUTWRITEIDX");
#pragma DATA_SECTION(g_usGetReadIndexes, "GETREADIDX");

#pragma DATA_SECTION(g_asIPCCPU2toCPU1Buffers, "GETBUFFER");
#pragma DATA_SECTION(g_usGetWriteIndexes, "GETWRITEIDX");
#pragma DATA_SECTION(g_usPutReadIndexes, "PUTREADIDX");

#elif defined(CPU2)

#pragma DATA_SECTION(g_asIPCCPU2toCPU1Buffers, "PUTBUFFER");
#pragma DATA_SECTION(g_usPutWriteIndexes, "PUTWRITEIDX");
#pragma DATA_SECTION(g_usGetReadIndexes, "GETREADIDX");

#pragma DATA_SECTION(g_asIPCCPU1toCPU2Buffers, "GETBUFFER");
#pragma DATA_SECTION(g_usGetWriteIndexes, "GETWRITEIDX");
#pragma DATA_SECTION(g_usPutReadIndexes, "PUTREADIDX");

#endif

//
// Global Circular Buffer Definitions
//
tIpcMessage g_asIPCCPU1toCPU2Buffers[NUM_IPC_INTERRUPTS][IPC_BUFFER_SIZE];
tIpcMessage g_asIPCCPU2toCPU1Buffers[NUM_IPC_INTERRUPTS][IPC_BUFFER_SIZE];

//
// Global Circular Buffer Index Definitions
//
uint16_t g_usPutWriteIndexes[NUM_IPC_INTERRUPTS];
uint16_t g_usPutReadIndexes[NUM_IPC_INTERRUPTS];
uint16_t g_usGetWriteIndexes[NUM_IPC_INTERRUPTS];
uint16_t g_usGetReadIndexes[NUM_IPC_INTERRUPTS];

//*****************************************************************************
//
//! Initializes System IPC driver controller
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param usCPU2IpcInterrupt specifies the CPU2 IPC interrupt number used by
//! psController.
//! \param usCPU1IpcInterrupt specifies the CPU1 IPC interrupt number used by
//! psController.
//!
//! This function initializes the IPC driver controller with circular buffer
//! and index addresses for an IPC interrupt pair. The
//! \e usCPU2IpcInterrupt and \e usCPU1IpcInterrupt parameters can be one of
//! the following values:
//! \b IPC_INT0, \b IPC_INT1, \b IPC_INT2, \b IPC_INT3.
//!
//! \note If an interrupt is currently in use by an \e tIpcController instance,
//! that particular interrupt should not be tied to a second \e tIpcController
//! instance.
//!
//! \note For a particular usCPU2IpcInterrupt - usCPU1IpcInterrupt pair, there
//! must be an instance of tIpcController defined and initialized on both the
//! CPU1 and CPU2 systems.
//!
//! \return None.
//
//*****************************************************************************
void
IPCInitialize (volatile tIpcController *psController,
               uint16_t usCPU2IpcInterrupt, uint16_t usCPU1IpcInterrupt)
{
#if defined(CPU1)
    // CPU1toCPU2PutBuffer and Index Initialization
    psController->psPutBuffer = &g_asIPCCPU1toCPU2Buffers[usCPU2IpcInterrupt-1][0];
    psController->pusPutWriteIndex = &g_usPutWriteIndexes[usCPU2IpcInterrupt-1];
    psController->pusGetReadIndex = &g_usGetReadIndexes[usCPU1IpcInterrupt-1];
    psController->ulPutFlag = (uint32_t)(1 << (usCPU2IpcInterrupt - 1));

    // CPU1toCPU2GetBuffer and Index Initialization
    psController->psGetBuffer = &g_asIPCCPU2toCPU1Buffers[usCPU1IpcInterrupt-1][0];
    psController->pusGetWriteIndex = &g_usGetWriteIndexes[usCPU1IpcInterrupt-1];
    psController->pusPutReadIndex = &g_usPutReadIndexes[usCPU2IpcInterrupt-1];
#elif defined(CPU2)
    // CPU2toCPU1PutBuffer and Index Initialization
    psController->psPutBuffer = &g_asIPCCPU2toCPU1Buffers[usCPU1IpcInterrupt-1][0];
    psController->pusPutWriteIndex = &g_usPutWriteIndexes[usCPU1IpcInterrupt-1];
    psController->pusGetReadIndex = &g_usGetReadIndexes[usCPU2IpcInterrupt-1];
    psController->ulPutFlag = (uint32_t)(1 << (usCPU1IpcInterrupt - 1));

    // CPU1toCPU2GetBuffer and Index Initialization
    psController->psGetBuffer = &g_asIPCCPU1toCPU2Buffers[usCPU2IpcInterrupt-1][0];
    psController->pusGetWriteIndex = &g_usGetWriteIndexes[usCPU2IpcInterrupt-1];
    psController->pusPutReadIndex = &g_usPutReadIndexes[usCPU1IpcInterrupt-1];
#endif
    // Initialize PutBuffer WriteIndex = 0 and GetBuffer ReadIndex = 0
    *(psController->pusPutWriteIndex) = 0;
    *(psController->pusGetReadIndex) = 0;
}

//*****************************************************************************
//
//! Writes a message into the PutBuffer.
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param psMessage specifies the address of the \e tIpcMessage instance to be
//! written to PutBuffer.
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a free slot (1= wait until free spot available, 0 = exit with
//! STATUS_FAIL if no free slot).
//!
//! This function checks if there is a free slot in the PutBuffer. If so, it
//! puts the message pointed to by \e psMessage into the free slot and
//! increments the WriteIndex. Then it sets the appropriate IPC interrupt flag
//! specified by \e psController->usPutFlag.  The \e bBlock parameter can be
//! one of the following values: \b ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//!
//! \return \b STATUS_FAIL if PutBuffer is full. \b STATUS_PASS if Put occurs
//! successfully.
//
//*****************************************************************************
uint16_t
IpcPut (volatile tIpcController *psController, tIpcMessage *psMessage,
        uint16_t bBlock)
{
    uint16_t writeIndex;
    uint16_t readIndex;
    uint16_t returnStatus = STATUS_PASS;

    writeIndex = *(psController->pusPutWriteIndex);
    readIndex = *(psController->pusPutReadIndex);

    //
    // Wait until Put Buffer slot is free
    //
    while (((writeIndex + 1) & MAX_BUFFER_INDEX) == readIndex)
    {
        //
        // If designated as a "Blocking" function, and Put buffer is full,
        // return immediately with fail status.
        //
        if (!bBlock)
        {
            returnStatus = STATUS_FAIL;
            break;
        }

        readIndex = *(psController->pusPutReadIndex);
    }

    if (returnStatus != STATUS_FAIL)
    {
        //
        // When slot is free, Write Message to PutBuffer, update PutWriteIndex,
        // and set the CPU IPC INT Flag
        //
        psController->psPutBuffer[writeIndex] = *psMessage;

        writeIndex = (writeIndex + 1) & MAX_BUFFER_INDEX;
        *(psController->pusPutWriteIndex) = writeIndex;

        IpcRegs.IPCSET.all = psController->ulPutFlag;
    }

    return returnStatus;
}

//*****************************************************************************
//
//! Reads a message from the GetBuffer.
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param psMessage specifies the address of the \e tIpcMessage instance where
//! the message from GetBuffer should be written to.
//! \param bBlock specifies whether to allow function to block until GetBuffer
//! has a message (1= wait until message available, 0 = exit with STATUS_FAIL
//! if no message).
//!
//! This function checks if there is a message in the GetBuffer. If so, it gets
//! the message in the GetBuffer pointed to by the ReadIndex and writes it to
//! the address pointed to by \e psMessage. The \e bBlock parameter can be one
//! of the following
//! values: \b ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//!
//! \return \b STATUS_PASS if GetBuffer is empty. \b STATUS_FAIL if Get occurs
//! successfully.
//
//*****************************************************************************
uint16_t
IpcGet (volatile tIpcController *psController, tIpcMessage *psMessage,
        uint16_t bBlock)
{
    uint16_t writeIndex;
    uint16_t readIndex;
    uint16_t returnStatus = STATUS_PASS;

    writeIndex = *(psController->pusGetWriteIndex);
    readIndex = *(psController->pusGetReadIndex);

    //
    // Loop while GetBuffer is empty
    //
    while (writeIndex == readIndex)
    {
        //
        // If designated as a "Blocking" function, and Get buffer is empty,
        // return immediately with fail status.
        //
        if (!bBlock)
        {
            returnStatus = STATUS_FAIL;
            break;
        }

        writeIndex = *(psController->pusGetWriteIndex);
    }

    if (returnStatus != STATUS_FAIL)
    {
        //
        // If there is a message in GetBuffer, Read Message and update
        // the ReadIndex
        //
        *psMessage = psController->psGetBuffer[readIndex];

        readIndex = (readIndex + 1) & MAX_BUFFER_INDEX;
        *(psController->pusGetReadIndex) = readIndex;
    }

    return returnStatus;
}

//*****************************************************************************
//
//! Sends a command to read either a 16- or 32-bit data word from the remote
//! CPU
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the remote CPU address to read from
//! \param pvData is a pointer to the 16/32-bit variable where read data will
//! be stored.
//! \param usLength designates 16- or 32-bit read (1 = 16-bit, 2 = 32-bit)
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//! \param ulResponseFlag indicates the remote CPU to the local CPU Flag
//! number mask used to report when the read data is available at pvData.
//! (\e ulResponseFlag MUST use IPC flags 17-32, and not 1-16)
//!
//! This function will allow the local CPU system to send a 16/32-bit data
//! read command to the remote CPU system and set a ResponseFlag to track the
//! status of the read.
//! The remote CPU will respond with a DataWrite command which will place
//! the data in the local CPU address pointed to by \e pvData. When the local
//! CPU receives the DataWrite command and writes the read data into \e *pvData location,
//! it will clear the ResponseFlag, indicating to the rest of the system that
//! the data is ready. The \e usLength parameter can be one of the
//! following values: \b IPC_LENGTH_16_BITS or \b IPC_LENGTH_32_BITS. The \e
//! bBlock parameter can be one of the following values: \b ENABLE_BLOCKING or
//! \b DISABLE_BLOCKING.
//! The \e ulResponseFlag parameter can be any single one of the flags \b
//! IPC_FLAG16 - \b IPC_FLAG31 or \b NO_FLAG.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRDataRead (volatile tIpcController *psController, uint32_t ulAddress,
                 void *pvData, uint16_t usLength, uint16_t bBlock,
                 uint32_t ulResponseFlag)
{

    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up read command, address, dataw1 = ResponseFlag | word length,
    // dataw2 = address where word
    // should be written to when returned.
    //
    sMessage.ulcommand = IPC_DATA_READ;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = (ulResponseFlag & 0xFFFF0000)|(uint32_t)usLength;
    sMessage.uldataw2 = (uint32_t)pvData;

    //
    // Set ResponseFlag (cleared once data is read into address at pvData)
    // Put Message into PutBuffer and set IPC INT flag
    //
    IpcRegs.IPCSET.all = (ulResponseFlag & 0xFFFF0000);
    status = IpcPut (psController, &sMessage, bBlock);

    return status;

    //
    //Note: Read Response will have sMessage.ulcommand = IPC_DATA_WRITE
    //                              sMessage.uladdress = (uint32_t) pvData
    //                              sMessage.uldataw1  = ulStatusFlag |
    //                                                   (uint32_t) usLength;
    //                              sMessage.uldataw2  = word to be read into
    //                                                   pvData address.
    //
}

//*****************************************************************************
//
//! Sends the command to read either a 16- or 32-bit data word from remote
//! CPU system address to a write-protected local CPU address.
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the remote CPU address to read from
//! \param pvData is a pointer to the 16/32-bit variable where read data will
//! be stored.
//! \param usLength designates 16- or 32-bit read (1 = 16-bit, 2 = 32-bit)
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//! \param ulResponseFlag indicates the local CPU to remote CPU Flag number
//! mask used to report when the read data is available at pvData.
//! (\e ulResponseFlag MUST use IPC flags 17-32, and not 1-16)
//!
//! This function will allow the local CPU system to send a 16/32-bit data
//! read command to the remote CPU system and set a ResponseFlag to track the
//! status of the read.
//! The remote CPU system will respond with a DataWrite command which will
//! place the data in the local CPU address pointed to by \e pvData.
//! When the local CPU receives the DataWrite command and writes the read data
//! into \e *pvData location, it will clear the ResponseFlag, indicating to
//! the rest of the system that the data is ready. The \e usLength parameter
//! can be one of the following values: \b IPC_LENGTH_16_BITS or
//! \b IPC_LENGTH_32_BITS. The \e bBlock parameter can be one of the following
//! values: \b ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//! The \e ulResponseFlag parameter can be any single one of the flags \b
//! IPC_FLAG16 - \b IPC_FLAG31 or \b NO_FLAG.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRDataRead_Protected (volatile tIpcController *psController,
                           uint32_t ulAddress, void *pvData, uint16_t usLength,
                           uint16_t bBlock,
                           uint32_t ulResponseFlag)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up read command, address, dataw1 = ResponseFlag | word length, dataw2
    // = address where word should be written to when returned.
    //
    sMessage.ulcommand = IPC_DATA_READ_PROTECTED;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = (ulResponseFlag & 0xFFFF0000)|(uint32_t)usLength;
    sMessage.uldataw2 = (uint32_t)pvData;

    //
    // Set ResponseFlag (cleared once data is read into address at pvData)
    // Put Message into PutBuffer and set IPC INT flag
    //
    IpcRegs.IPCSET.all = (ulResponseFlag & 0xFFFF0000);
    status = IpcPut (psController, &sMessage, bBlock);

    return status;
    //
    // Note: Read Response will have sMessage.ulcommand = IPC_DATA_WRITE
    //                               sMessage.uladdress = (uint32_t) pvData
    //                               sMessage.uldataw1  = ulStatusFlag |
    //                                                    (uint32_t) usLength;
    //                               sMessage.uldataw2  = word to be read into
    //                                                    pvData address.
    //
}

//*****************************************************************************
//
//! Sets the designated bits in a 16-bit data word at the remote CPU system
//! address
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the remote CPU address to write to
//! \param ulMask specifies the 16/32-bit mask for bits which should be set at
//! \e ulAddress.
//! 16-bit masks should fill the lower 16-bits (upper 16-bits will be all
//! 0x0000).
//! \param usLength specifies the length of the bit mask (1=16-bits, 2=32-bits)
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the local CPU system to set bits specified by the
//! \e ulMask variable in a 16/32-bit word on the remote CPU system. The \e
//! usLength parameter can be one of the following values: \b IPC_LENGTH_16_BITS
//! or \b IPC_LENGTH_32_BITS. The \e bBlock parameter can be one of the
//! following values: \b ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRSetBits(volatile tIpcController *psController, uint32_t ulAddress,
               uint32_t ulMask, uint16_t usLength,
               uint16_t bBlock)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up set bits command, address, dataw1 = word length, dataw2 =
    // 16/32-bit mask
    //
    sMessage.ulcommand = IPC_SET_BITS;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = (uint32_t)usLength;
    sMessage.uldataw2 = ulMask;

    //
    // Put Message into PutBuffer and set IPC INT flag
    //
    status = IpcPut (psController, &sMessage, bBlock);
    return status;
}
//*****************************************************************************
//
//! Sets the designated bits in a 16-bit write-protected data word at the
//! remote CPU system address
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the remote CPU address to write to
//! \param ulMask specifies the 16/32-bit mask for bits which should be set at
//! \e ulAddress. 16-bit masks should fill the lower 16-bits (upper 16-bits
//! will be all 0x0000).
//! \param usLength specifies the length of the bit mask (1=16-bits, 2=32-bits)
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the local CPU system to set bits specified by the
//! \e ulMask variable in a write-protected 16/32-bit word on the remote CPU
//! system. The \e usLength parameter can be one of the  following values: \b
//! IPC_LENGTH_16_BITS or \b IPC_LENGTH_32_BITS. The \e bBlock parameter can be
//! one of the following values:
//! \b ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRSetBits_Protected(volatile tIpcController *psController,
                         uint32_t ulAddress, uint32_t ulMask, uint16_t usLength,
                         uint16_t bBlock)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up set bits command, address, dataw1 = word length, dataw2 =
    // 16/32-bit mask
    //
    sMessage.ulcommand = IPC_SET_BITS_PROTECTED;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = (uint32_t)usLength;
    sMessage.uldataw2 = ulMask;

    //
    // Put Message into PutBuffer and set IPC INT flag
    //
    status = IpcPut (psController, &sMessage, bBlock);
    return status;
}

//*****************************************************************************
//
//! Clears the designated bits in a 16-bit data word at the remote CPU system
//! address
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the remote CPU address to write to
//! \param ulMask specifies the 16/32-bit mask for bits which should be cleared
//! at \e ulAddress. 16-bit masks should fill the lower 16-bits (upper 16-bits
//! will be all 0x0000).
//! \param usLength specifies the length of the bit mask (1=16-bits, 2=32-bits)
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the local CPU system to clear bits specified by
//! the \e ulMask variable in a 16/32-bit word on the remote CPU system. The \e
//! usLength parameter can be one of the following values: \b
//! IPC_LENGTH_16_BITS or \b IPC_LENGTH_32_BITS. The \e bBlock parameter can be
//! one of the following values: \b ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRClearBits(volatile tIpcController *psController, uint32_t ulAddress,
                 uint32_t ulMask, uint16_t usLength,
                 uint16_t bBlock)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up clear bits command, address, dataw1 = word length, dataw2 =
    // 16/32-bit mask
    //
    sMessage.ulcommand = IPC_CLEAR_BITS;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = (uint32_t)usLength;
    sMessage.uldataw2 = ulMask;

    //
    // Put Message into PutBuffer and set IPC INT flag
    //
    status = IpcPut (psController, &sMessage, bBlock);
    return status;
}

//*****************************************************************************
//
//! Clears the designated bits in a 16-bit write-protected data word at
//! remote CPU system address
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the secondary CPU address to write to
//! \param ulMask specifies the 16/32-bit mask for bits which should be cleared
//! at \e ulAddress. 16-bit masks should fill the lower 16-bits (upper 16-bits
//! will be all 0x0000).
//! \param usLength specifies the length of the bit mask (1=16-bits, 2=32-bits)
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the local CPU system to set bits specified by the
//! \e ulMask variable in a write-protected 16/32-bit word on the remote CPU
//! system. The \e usLength parameter can be  one of the following values: \b
//! IPC_LENGTH_16_BITS or \b IPC_LENGTH_32_BITS. The \e bBlock parameter can be
//! one of the following values: \b ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRClearBits_Protected(volatile tIpcController *psController,
                           uint32_t ulAddress, uint32_t ulMask,
                           uint16_t usLength, uint16_t bBlock)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up clear bits command, address, dataw1 = word length, dataw2 =
    // 16/32-bit mask
    //
    sMessage.ulcommand = IPC_CLEAR_BITS_PROTECTED;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = (uint32_t)usLength;
    sMessage.uldataw2 = ulMask;

    //
    // Put Message into PutBuffer and set IPC INT flag
    //
    status = IpcPut (psController, &sMessage, bBlock);
    return status;
}

//*****************************************************************************
//
//! Writes a 16/32-bit data word to the remote CPU system address
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the remote cpu address to write to
//! \param ulData specifies the 16/32-bit word which will be written.
//! For 16-bit words, only the lower 16-bits of ulData will be considered by
//! the master system.
//! \param usLength is the length of the word to write (1 = 16-bits, 2 =
//! 32-bits)
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//! \param ulResponseFlag is used to pass the \e ulResponseFlag back to the
//! remote cpu only when this function is called in response to \e
//! IPCMtoCDataRead(). Otherwise, set to 0.
//!
//! This function will allow the local CPU system to write a 16/32-bit word
//! via the \e ulData variable to an address on the remote CPU system.
//! The \e usLength parameter can be one of the following values:
//! \b IPC_LENGTH_16_BITS or \b IPC_LENGTH_32_BITS. The \e bBlock parameter
//! can be one of the following values: \b ENABLE_BLOCKING or \b
//! DISABLE_BLOCKING.
//! The \e ulResponseFlag parameter can be any single one of the flags \b
//! IPC_FLAG16 - \b IPC_FLAG31 or \b NO_FLAG.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRDataWrite(volatile tIpcController *psController, uint32_t ulAddress,
                 uint32_t ulData, uint16_t usLength, uint16_t bBlock,
                 uint32_t ulResponseFlag)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up write command, address, dataw1 = ResponseFlag | word length,
    // dataw2 = data to write
    //
    sMessage.ulcommand = IPC_DATA_WRITE;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = ulResponseFlag |(uint32_t)usLength;
    sMessage.uldataw2 = ulData;

    //
    // Put Message into PutBuffer and set IPC INT flag
    //
    status = IpcPut (psController, &sMessage, bBlock);
    return status;
}

//*****************************************************************************
//
//! Writes a 16/32-bit data word to a write-protected remote CPU system address
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the write-protected remote CPU address to
//! write to
//! \param ulData specifies the 16/32-bit word which will be written. For
//! 16-bit words, only the lower 16-bits of ulData will be considered by the
//! master system.
//! \param usLength is the length of the word to write (1 = 16-bits, 2 =
//! 32-bits)
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//! \param ulResponseFlag is used to pass the \e ulResponseFlag back to the
//! remote CPU only when this function is called in response to \e
//! IPCMtoCDataRead(). Otherwise, set to 0.
//!
//! This function will allow the local CPU system to write a 16/32-bit word
//! via the \e ulData variable to a write-protected address on the remote CPU
//! system. The \e usLength parameter can be one of the following values:
//!  \b IPC_LENGTH_16_BITS or \b IPC_LENGTH_32_BITS. The \e bBlock parameter
//! can be one of the following values: \b ENABLE_BLOCKING or \b
//! DISABLE_BLOCKING.
//! The \e ulResponseFlag parameter can be any single one of the flags \b
//! IPC_FLAG16 -
//! \b IPC_FLAG31 or \b NO_FLAG.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRDataWrite_Protected(volatile tIpcController *psController,
                           uint32_t ulAddress, uint32_t ulData,
                           uint16_t usLength, uint16_t bBlock,
                           uint32_t ulResponseFlag)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up write command, address, dataw1 = ResponseFlag | word length,
    // dataw2 = data to write
    //
    sMessage.ulcommand = IPC_DATA_WRITE_PROTECTED;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = ulResponseFlag |(uint32_t)usLength;
    sMessage.uldataw2 = ulData;

    //
    // Put Message into PutBuffer and set IPC INT flag
    //
    status = IpcPut (psController, &sMessage, bBlock);
    return status;
}

//*****************************************************************************
//
//! Sends the command to read a block of data from remote CPU system address
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the remote CPU memory block starting address
//! to read from.
//! \param ulShareAddress specifies the local CPU shared memory address the
//! read block will read to.
//! \param usLength designates the block size in 16-bit words.
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//! \param ulResponseFlag indicates the local CPU to remote CPU Flag number
//!  mask used to report when the read block data is available starting at
//!  /e ulShareAddress. (\e ulResponseFlag MUST use IPC flags 17-32, and not
//! 1-16)
//!
//! This function will allow the local CPU system to send a read block
//! command to the remote CPU system and set a ResponseFlag to track the status
//! of the read. The remote CPU system will process the read and place the data
//! in shared memory at the location specified in the \e ulShareAddress
//! parameter and then clear the ResponseFlag, indicating that the block is
//! ready. The \e bBlock parameter can be one of the following values: \b
//! ENABLE_BLOCKING or \b DISABLE_BLOCKING. The \e ulResponseFlag parameter can
//! be any single one of the flags \b IPC_FLAG16 - \b IPC_FLAG31 or \b NO_FLAG.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRBlockRead(volatile tIpcController *psController, uint32_t ulAddress,
                 uint32_t ulShareAddress, uint16_t usLength, uint16_t bBlock,
                 uint32_t ulResponseFlag)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up block read command, address, dataw1 = ResponseFlag | block length,
    // dataw2 = remote CPU address in shared memory
    // where block data should be read to
    // (corresponding to local CPU ulShareAddress).
    //
    sMessage.ulcommand = IPC_BLOCK_READ;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = (ulResponseFlag & 0xFFFF0000) |(uint32_t)usLength;
    sMessage.uldataw2 = ulShareAddress;

    //
    // Set ResponseFlag (cleared once data is read into Share Address location)
    // Put Message into PutBuffer and set IPC INT flag
    //
    IpcRegs.IPCSET.all = (ulResponseFlag & 0xFFFF0000);
    status = IpcPut (psController, &sMessage, bBlock);

    return status;
    //
    // Note: Read Block Response will occur in processing of ReadBlock (since
    // remote CPU has access to shared memory)
    //
}

//*****************************************************************************
//
//! Writes a block of data to remote CPU system address
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the remote CPU memory block starting address
//!  to write to.
//! \param ulShareAddress specifies the local CPU shared memory address where
//! data to write from resides.
//! \param usLength designates the block size in 16- or 32-bit words (depends
//! on \e usWordLength).
//! \param usWordLength designates the word size (16-bits = 1 or 32-bits = 2).
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the local CPU system to write a block of data to
//! the remote CPU system starting from the location specified by the
//! \e ulAdress parameter. Prior to calling this function, the local CPU
//! system code should place the data to write in shared memory starting at /e
//! ulShareAddress.
//! The \e usWordLength parameter can be one of the following values:
//! \b IPC_LENGTH_16_BITS or \b IPC_LENGTH_32_BITS. The \e bBlock parameter
//! can be one of the following values: \b ENABLE_BLOCKING or \b
//! DISABLE_BLOCKING.
//! The \e ulResponseFlag parameter can be any single one of the flags \b
//! IPC_FLAG16 - \b IPC_FLAG31 or \b NO_FLAG.
//!
//! \note If the shared SARAM blocks are used to pass the RAM block between the
//! processors, the IPCReqMemAccess() function must be called first in order to
//! give the slave CPU write access to the shared memory block(s).
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRBlockWrite(volatile tIpcController *psController, uint32_t ulAddress,
                  uint32_t ulShareAddress, uint16_t usLength,
                  uint16_t usWordLength, uint16_t bBlock)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up block write command, address, dataw1 = block length,
    // dataw2 = remote CPU shared mem address
    // where write data resides
    //
    sMessage.ulcommand = IPC_BLOCK_WRITE;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = ((uint32_t)(usWordLength)<<16) + (uint32_t)usLength;
    sMessage.uldataw2 = ulShareAddress;

    //
    // Put Message into PutBuffer and set IPC INT flag
    //
    status = IpcPut (psController, &sMessage, bBlock);
    return status;
}

//*****************************************************************************
//
//! Writes a block of data to a write-protected remote CPU system address
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the write-protected remote CPU block starting
//! address to write to.
//! \param ulShareAddress specifies the local CPU shared memory address where
//!  data to write from resides.
//! \param usLength designates the block size in 16- or 32-bit words (depends
//! on \e usWordLength).
//! \param usWordLength designates the word size (16-bits = 1 or 32-bits = 2).
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the local CPU system to write a block of data to
//! a write-protected region on the remote CPU system starting from the
//! location specified by the \e ulAdress parameter. Prior to calling this
//! function, the local CPU system code should place the data to write in
//! shared memory starting at /e ulShareAddress.
//! The \e usWordLength parameter can be one of the following values:
//! \b IPC_LENGTH_16_BITS or \b IPC_LENGTH_32_BITS. The \e bBlock parameter
//! can be one of the following values: \b ENABLE_BLOCKING or \b
//! DISABLE_BLOCKING.
//! The \e ulResponseFlag parameter can be any single one of the flags \b
//! IPC_FLAG16 - \b IPC_FLAG31 or \b NO_FLAG.
//!
//! \note If the shared SARAM blocks are used to pass the RAM block between the
//! processors, the IPCReqMemAccess() function must be called first in order to
//! give the the slave CPU write access to the shared memory block(s).
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRBlockWrite_Protected(volatile tIpcController *psController,
                            uint32_t ulAddress, uint32_t ulShareAddress,
                            uint16_t usLength, uint16_t usWordLength,
                            uint16_t bBlock)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up block write command, address, dataw1 = block length,
    // dataw2 = remote CPU shared mem address
    // where write data resides
    //
    sMessage.ulcommand = IPC_BLOCK_WRITE_PROTECTED;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = ((uint32_t)(usWordLength)<<16) + (uint32_t)usLength;
    sMessage.uldataw2 = ulShareAddress;

    //
    // Put Message into PutBuffer and set IPC INT flag
    //
    status = IpcPut (psController, &sMessage, bBlock);
    return status;
}

//*****************************************************************************
//
//! Calls remote CPU function with 1 optional parameter .
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulAddress specifies the remote CPU function address
//! \param ulParam specifies the 32-bit optional parameter value. If not used,
//! this can be a dummy value.
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the local CPU system to call a function on the
//! remote CPU. The \e ulParam variable is a single optional 32-bit parameter
//! to pass to the function. The \e bBlock parameter can be one of the
//! following values: \b ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRFunctionCall(volatile tIpcController *psController, uint32_t ulAddress,
                    uint32_t ulParam,
                    uint16_t bBlock)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Set up function call command, address, dataw1 = 32-bit parameter
    //
    sMessage.ulcommand = IPC_FUNC_CALL;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = ulParam;

    //
    // Put Message into PutBuffer and set IPC INT flag
    //
    status = IpcPut (psController, &sMessage, bBlock);
    return status;
}

//*****************************************************************************
//
//! Sends generic message to remote CPU system
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulCommand specifies 32-bit command word to insert into message.
//! \param ulAddress specifies 32-bit address word to insert into message.
//! \param ulDataW1 specifies 1st 32-bit data word to insert into message.
//! \param ulDataW2 specifies 2nd 32-bit data word to insert into message.
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the local CPU system to send a generic message to
//! the remote CPU system. Note that the user should create a corresponding
//! function on the remote CPU side to interpret/use the message or fill
//! parameters in such a way that the existing IPC drivers can recognize the
//! command and properly process the message.
//! The \e bBlock parameter can be one of the following values: \b
//! ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCLtoRSendMessage(volatile tIpcController *psController, uint32_t ulCommand,
                   uint32_t ulAddress, uint32_t ulDataW1, uint32_t ulDataW2,
                   uint16_t bBlock)
{
    uint16_t status;
    tIpcMessage sMessage;

    //
    // Package message to send
    //
    sMessage.ulcommand = ulCommand;
    sMessage.uladdress = ulAddress;
    sMessage.uldataw1 = ulDataW1;
    sMessage.uldataw2 = ulDataW2;

    //
    // Put Message into PutBuffer and set IPC INT flag
    //
    status = IpcPut (psController, &sMessage, bBlock);
    return status;
}

#if defined (CPU2)
//*****************************************************************************
//
//! Slave CPU Configures master R/W/Exe Access to Shared SARAM.
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param ulMask specifies the 32-bit mask for the GSxMSEL RAM control
//! register to indicate which GSx SARAM blocks the slave CPU is requesting
//! master access to.
//! \param usMaster specifies whether the CPU1 or CPU2 are given
//!  master access to the GSx blocks.
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the slave CPU system to configure master R/W/Exe
//! access to the GSx SARAM blocks specified by the /e ulMask parameter.  The
//! function calls the \e IPCSetBits_Protected() or \e
//! IPCClearBits_Protected() functions, and therefore in the master CPU
//! application code, the corresponding functions should be called.
//! The \e bBlock parameter can be one of the following values: \b
//! ENABLE_BLOCKING or \b DISABLE_BLOCKING. The \e usMaster parameter can be
//! either: \b IPC_GSX_CPU2_MASTER or \b IPC_GSX_CPU1_MASTER. The \e ulMask
//! parameter can be any of the options: \b S0_ACCESS - \b S7_ACCESS.
//!
//! \return status of command (\b STATUS_PASS =success, \b STATUS_FAIL = error
//! because PutBuffer was full, command was not sent)
//
//*****************************************************************************
uint16_t
IPCReqMemAccess (volatile tIpcController *psController, uint32_t ulMask,
                     uint16_t usMaster, uint16_t bBlock)
{
    uint16_t status = STATUS_PASS;
    uint32_t GSxMSEL_REGaddress = (uint32_t)(&MemCfgRegs.GSxMSEL.all);

    if (usMaster == IPC_GSX_CPU2_MASTER)
    {
        if ((MemCfgRegs.GSxMSEL.all & ulMask) != ulMask)
        {
            status =
                IPCLtoRSetBits_Protected (psController, GSxMSEL_REGaddress,
                                          ulMask, IPC_LENGTH_32_BITS,
                                          bBlock);
        }
    }
    else if (usMaster == IPC_GSX_CPU1_MASTER)
    {
        if ((MemCfgRegs.GSxMSEL.all & ulMask) != 0)
        {
            status =
                IPCLtoRClearBits_Protected (psController, GSxMSEL_REGaddress,
                                            ulMask, IPC_LENGTH_32_BITS,
                                            bBlock);
        }
    }

    return status;
}
#endif

//*****************************************************************************
//
//! Responds to 16/32-bit data word write command the remote CPU system
//!
//! \param psMessage specifies the pointer to the message received from remote
//! CPU system which includes the 16/32-bit data word to write to the local CPU
//! address.
//!
//! This function will allow the local CPU system to write a 16/32-bit word
//! received from the remote CPU system to the address indicated in \e
//! *psMessage. In the event that the IPC_DATA_WRITE command was received as a
//! result of an IPC_DATA_READ command, this function will also clear the IPC
//! response flag tracking the read so other threads in the local CPU system
//! will be aware that the data is ready.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLDataWrite(tIpcMessage *psMessage)
{
    //
    // Data word length = dataw1 (15:0), responseFlag = valid only for IPC
    // flags 17-32
    //
    uint16_t length = (uint16_t) psMessage->uldataw1;
    uint32_t responseFlag = (psMessage->uldataw1) & 0xFFFF0000;

    //
    // Write 16/32-bit word to address
    //
    if (length == IPC_LENGTH_16_BITS)
    {
        *(uint16_t *)(psMessage->uladdress) = (uint16_t)psMessage->uldataw2;
    }
    else if (length == IPC_LENGTH_32_BITS)
    {
        *(uint32_t *)(psMessage->uladdress) = psMessage->uldataw2;
    }

    //
    // If data write command is in response to a data read command from remote
    // CPU to local CPU clear ResponseFlag, indicating read data from remote
    // CPU is ready.
    //
    IpcRegs.IPCCLR.all = responseFlag;
}

//*****************************************************************************
//
//! Responds to 16/32-bit write-protected data word write command from
//! secondary CPU system
//!
//! \param psMessage specifies the pointer to the message received from the
//! secondary CPU system which includes the 16/32-bit data word to write to the
//! local CPU address.
//!
//! This function will allow the local CPU system to write a 16/32-bit word
//! received from the secondary CPU system to the write-protected address
//! indicated in \e *psMessage.
//! In the event that the IPC_DATA_WRITE_PROTECTED command was received as a
//! result of an IPC_DATA_READ_PROTECTED command, this function will also clear
//! the IPC response flag tracking the read so other threads in the local CPU
//! will be aware that the data is ready.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLDataWrite_Protected(tIpcMessage *psMessage)
{
    //
    // Data word length = dataw1 (15:0), responseFlag = valid only for IPC
    // flags 17-32
    //
    uint16_t length = (uint16_t) psMessage->uldataw1;
    uint32_t responseFlag = (psMessage->uldataw1) & 0xFFFF0000;

    //
    // Allow access to EALLOW-protected registers.
    //
    EALLOW;

    //
    // Write 16/32-bit word to EALLOW-protected address
    //
    if (length == IPC_LENGTH_16_BITS)
    {
        *(uint16_t *)(psMessage->uladdress) = (uint16_t)psMessage->uldataw2;
    }
    else if (length == IPC_LENGTH_32_BITS)
    {
        *(uint32_t *)(psMessage->uladdress) = psMessage->uldataw2;
    }

    //
    // Disable access to EALLOW-protected registers.
    //
    EDIS;

    //
    // If data write command is in response to a data read command from local
    // CPU to remote CPU, clear ResponseFlag, indicating read data from
    // secondary CPU is ready
    //
    IpcRegs.IPCCLR.all = responseFlag;
}

//*****************************************************************************
//
//! Responds to 16/32-bit data word read command from remote CPU system.
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param psMessage specifies the pointer to the message received from the
//! remote CPU system.
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the remote CPU system to read a 16/32-bit data
//! word at the local CPU address specified in /e psMessage, and send a Write
//! command with the read data back to the local CPU system. It will also send
//! the Response Flag used to track the read back to the remote CPU.
//! The \e bBlock parameter can be one of the following values: \b
//! ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLDataRead(volatile tIpcController *psController, tIpcMessage *psMessage,
                uint16_t bBlock)
{
    unsigned long ulReaddata;
    uint16_t usLength;

    //
    // If data word length = 16-bits, read the 16-bit value at the given
    // address and cast as 32-bit word to send back to remote CPU.
    // If data word length = 32-bits, read the 32-bit value at the given
    // address.
    //
    usLength = (uint16_t)psMessage->uldataw1;

    if (usLength == IPC_LENGTH_16_BITS)
    {
        ulReaddata = (unsigned long)(*(volatile uint16_t *)psMessage->uladdress);
    }
    else if (usLength == IPC_LENGTH_32_BITS)
    {
        ulReaddata = *(unsigned long *)psMessage->uladdress;
    }

    //
    // Send a Write command to write the requested data to the remote CPU read
    // into address.
    // psMessage->uldataw2 contains remote CPU address where readdata will be
    // written.
    // psMessage->uldataw1 contains the read response flag in IPC flag 17-32.
    //
    IPCLtoRDataWrite(psController, psMessage->uldataw2, ulReaddata, usLength,
                     bBlock,(psMessage->uldataw1 & 0xFFFF0000));
}

//*****************************************************************************
//
//! Responds to 16/32-bit data word read command from remote CPU system.
//! to read into a write-protected word on the remote CPU system.
//!
//! \param psController specifies the address of a \e tIpcController instance
//! used to store information about the "Put" and "Get" circular buffers and
//! their respective indexes.
//! \param psMessage specifies the pointer to the message received from the
//! remote CPU system.
//! \param bBlock specifies whether to allow function to block until PutBuffer
//! has a slot (1= wait until slot free, 0 = exit with STATUS_FAIL if no slot).
//!
//! This function will allow the remote CPU system to read a 16/32-bit data
//! word at the local CPU address specified in /e psMessage, and send a Write
//! Protected command with the read data back to the remote CPU system at a
//! write protected address. It will also send the Response Flag used to track
//! the read back to the remote CPU. The \e bBlock parameter can be one of the
//! following values: \b ENABLE_BLOCKING or \b DISABLE_BLOCKING.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLDataRead_Protected(volatile tIpcController *psController,
                          tIpcMessage *psMessage, uint16_t bBlock)
{
    unsigned long ulReaddata;
    uint16_t usLength;

    //
    // If data word length = 16-bits, read the 16-bit value at the given
    // address and cast as 32-bit word to send back to remote CPU.
    // If data word length = 32-bits, read the 32-bit value at the given
    // address.
    //
    usLength = (uint16_t)psMessage->uldataw1;

    if (usLength == IPC_LENGTH_16_BITS)
    {
        ulReaddata = (unsigned long)(*(volatile uint16_t *)psMessage->uladdress);
    }
    else if (usLength == IPC_LENGTH_32_BITS)
    {
        ulReaddata = *(unsigned long *)psMessage->uladdress;
    }

    //
    // Send a Write command to write the requested data to the remote CPU read
    // into address.
    // psMessage->uldataw2 contains remote CPU address where readdata will be
    // written.
    // psMessage->uldataw1 contains the read response flag in IPC flag 17-32.
    //
    IPCLtoRDataWrite_Protected(psController, psMessage->uldataw2, ulReaddata,
                               usLength, bBlock,
                               (psMessage->uldataw1 & 0xFFFF0000));
}

//*****************************************************************************
//
//! Sets the designated bits in a 16/32-bit data word at a local CPU system
//! address
//!
//! \param psMessage specifies the pointer to the message received from the
//! remote CPU system.
//!
//! This function will allow the remote CPU system to set the bits in a
//! 16/32-bit word on the local CPU system via a local CPU address and mask
//! passed in via the \e psMessage.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLSetBits(tIpcMessage *psMessage)
{
    uint16_t usLength;

    //
    // Determine length of word at psMessage->uladdress and then set bits based
    // on either the 16-bit or 32-bit bit-mask in psMessage->uldataw2.
    // (16-bit length ignores upper 16-bits of psMessage->uldataw2)
    //
    usLength = (uint16_t)psMessage->uldataw1;

    if (usLength == IPC_LENGTH_16_BITS)
    {
        *(volatile uint16_t*)psMessage->uladdress |=
                                                (uint16_t) psMessage->uldataw2;
    }
    else if (usLength == IPC_LENGTH_32_BITS)
    {
        *(volatile unsigned long *)psMessage->uladdress |=  psMessage->uldataw2;
    }
}

//*****************************************************************************
//
//! Sets the designated bits in a 16/32-bit write-protected data word at a
//! local CPU system address
//!
//! \param psMessage specifies the pointer to the message received from the
//! remote CPU system.
//!
//! This function will allow the remote CPU system to set the bits in a write-
//! protected 16/32-bit word on the local CPU system via a local CPU address
//! and mask passed in via the \e psMessage.
//!
//! \return None
//
//*****************************************************************************
void
IPCRtoLSetBits_Protected(tIpcMessage *psMessage)
{
    uint16_t usLength;

    //
    // Allow access to EALLOW-protected registers.
    //
    EALLOW;

    //
    // Determine length of word at psMessage->uladdress and then set bits based
    // on either the 16-bit or 32-bit bit-mask in psMessage->uldataw2.
    // (16-bit length ignores upper 16-bits of psMessage->uldataw2)
    //
    usLength = (uint16_t)psMessage->uldataw1;

    if (usLength == IPC_LENGTH_16_BITS)
    {
        *(volatile uint16_t*)psMessage->uladdress |=
                                                (uint16_t) psMessage->uldataw2;
    }
    else if (usLength == IPC_LENGTH_32_BITS)
    {
        *(volatile unsigned long *)psMessage->uladdress |= psMessage->uldataw2;
    }

    //
    // Disable access to EALLOW-protected registers.
    //
    EDIS;
}

//*****************************************************************************
//
//! Clears the designated bits in a 32-bit data word at a local CPU system
//! address
//!
//! \param psMessage specifies the pointer to the message received from the
//! remote CPU system.
//!
//! This function will allow the remote CPU system to clear the bits in a
//! 16/32-bit word on the local CPU system via a local CPU address and mask
//! passed in via the \e psMessage.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLClearBits(tIpcMessage *psMessage)
{
    uint16_t usLength;

    //
    // Determine length of word at psMessage->uladdress and then clear bits
    // based on
    // either the 16-bit or 32-bit bit-mask in psMessage->uldataw2.
    // (16-bit length ignores upper 16-bits of psMessage->uldataw2)
    //
    usLength = (uint16_t)psMessage->uldataw1;

    if (usLength == IPC_LENGTH_16_BITS)
    {
        *(volatile uint16_t*)psMessage->uladdress &=
            ~((uint16_t) psMessage->uldataw2);
    }
    else if (usLength == IPC_LENGTH_32_BITS)
    {
        *(volatile unsigned long *)psMessage->uladdress &=
            ~(psMessage->uldataw2);
    }
}

//*****************************************************************************
//
//! Clears the designated bits in a write-protected 16/32-bit data word at a
//! local CPU system address
//!
//! \param psMessage specifies the pointer to the message received from the
//! remote CPU system.
//!
//! This function will allow the secondary CPU system to clear the bits in a
//! 16/32-bit write-protected  word on the local CPU system via a local
//! CPU address and mask passed in via the \e psMessage.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLClearBits_Protected(tIpcMessage *psMessage)
{
    uint16_t usLength;

    //
    // Allow access to EALLOW-protected registers.
    //
    EALLOW;

    //
    // Determine length of word at psMessage->uladdress and then clear bits
    // based on
    // either the 16-bit or 32-bit bit-mask in psMessage->uldataw2.
    // (16-bit length ignores upper 16-bits of psMessage->uldataw2)
    //
    usLength = (uint16_t)psMessage->uldataw1;

    if (usLength == IPC_LENGTH_16_BITS)
    {
        *(volatile uint16_t*)psMessage->uladdress &=
            ~((uint16_t) psMessage->uldataw2);
    }
    else if (usLength == IPC_LENGTH_32_BITS)
    {
        *(volatile unsigned long *)psMessage->uladdress &=
            ~(psMessage->uldataw2);
    }

    //
    // Disable access to EALLOW-protected registers.
    //
    EDIS;
}

//*****************************************************************************
//
//! Reads a block of data from a remote CPU system address and stores into
//! shared RAM
//!
//! \param psMessage specifies the pointer to the message received from the
//! remote CPU system.
//!
//! This function will respond to the remote CPU system request to read a block
//! of data from the local control system, by reading the data and placing that
//! data into the shared RAM location specified in \e psMessage.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLBlockRead(tIpcMessage *psMessage)
{

    uint16_t usLength;
    volatile uint16_t* pusRAddress;
    volatile uint16_t* pusWAddress;
    uint16_t usIndex;

    pusRAddress = (volatile uint16_t *)psMessage->uladdress;
    pusWAddress = (volatile uint16_t *)psMessage->uldataw2;
    usLength = (uint16_t)psMessage->uldataw1;

    for (usIndex=0; usIndex<usLength; usIndex++)
    {
        *pusWAddress = *pusRAddress;
        pusWAddress += 1;
        pusRAddress += 1;
    }

    IpcRegs.IPCACK.all = (psMessage->uldataw1 & 0xFFFF0000);
}

//*****************************************************************************
//
//! Writes a block of data to a local CPU system address from shared RAM
//!
//! \param psMessage specifies the pointer to the message received from the
//! remote CPU system.
//!
//! This function will write a block of data to an address on the local CPU
//! system.
//! The data is first written by the remote CPU to shared RAM. This function
//! reads the shared RAM location, word size (16- or 32-bit), and block size
//! from \e psMessage and writes the block to the local address specified
//! in \e psMessage.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLBlockWrite(tIpcMessage *psMessage)
{
    uint16_t usLength;
    uint16_t usWLength;
    uint16_t usIndex;

    usLength = (uint16_t)psMessage->uldataw1;
    usWLength = (uint16_t)((psMessage->uldataw1)>>16);

    //
    // Determine data word access size to write to data block.
    //
    if (usWLength == IPC_LENGTH_16_BITS)
    {
        volatile uint16_t *pusWAddress = (volatile uint16_t *)psMessage->uladdress;
        volatile uint16_t *pusRAddress = (volatile uint16_t *)psMessage->uldataw2;
        for (usIndex=0; usIndex<usLength; usIndex++)
        {
            *pusWAddress = *pusRAddress;
            pusWAddress += 1;
            pusRAddress += 1;
        }
    } else if (usWLength == IPC_LENGTH_32_BITS)
    {
        volatile unsigned long *pulWAddress =
            (volatile unsigned long *)psMessage->uladdress;
        volatile unsigned long *pulRAddress =
            (volatile unsigned long *)psMessage->uldataw2;

        for (usIndex=0; usIndex<usLength; usIndex++)
        {
            *pulWAddress = *pulRAddress;
            pulWAddress += 1;
            pulRAddress += 1;
        }
    }
}

//*****************************************************************************
//
//! Writes a block of data to a local CPU system write-protected address from
//! shared RAM
//!
//! \param psMessage specifies the pointer to the message received from the
//! remote CPU system.
//!
//! This function will write a block of data to a write-protected group of
//! addresses on the local CPU system. The data is first written by the
//! remote CPU to shared RAM. This function reads the shared RAM location,
//! word size (16- or 32-bit), and block size from \e psMessage  and writes the
//! block to the local address specified in \e psMessage.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLBlockWrite_Protected(tIpcMessage *psMessage)
{
    uint16_t usLength;
    uint16_t usWLength;
    uint16_t usIndex;

    //
    // Allow access to EALLOW-protected registers.
    //
    EALLOW;

    usLength = (uint16_t)psMessage->uldataw1;
    usWLength = (uint16_t)((psMessage->uldataw1)>>16);

    //
    // Determine data word access size to write to data block.
    // (Writes registers accessible via APB bus must be 32-bits wide)
    //
    if (usWLength == IPC_LENGTH_16_BITS)
    {
        volatile uint16_t *pusWAddress = (volatile uint16_t *)psMessage->uladdress;
        volatile uint16_t *pusRAddress = (volatile uint16_t *)psMessage->uldataw2;
        for (usIndex=0; usIndex<usLength; usIndex++)
        {
            *pusWAddress = *pusRAddress;
            pusWAddress += 1;
            pusRAddress += 1;
        }
    } else if (usWLength == IPC_LENGTH_32_BITS)
    {
        volatile unsigned long *pulWAddress =
            (volatile unsigned long *)psMessage->uladdress;
        volatile unsigned long *pulRAddress =
            (volatile unsigned long *)psMessage->uldataw2;

        for (usIndex=0; usIndex<usLength; usIndex++)
        {
            *pulWAddress = *pulRAddress;
            pulWAddress += 1;
            pulRAddress += 1;
        }
    }

    //
    // Disable access to EALLOW-protected registers.
    //
    EDIS;
}

//*****************************************************************************
//
//! Calls a local function with a single optional parameter.
//!
//! \param psMessage specifies the pointer to the message received from the
//! remote CPU system.
//!
//! This function will allow the remote CPU system to call a local CPU function
//! with a single optional parameter. There is no return value from the
//! executed function.
//!
//! \return None.
//
//*****************************************************************************
void
IPCRtoLFunctionCall(tIpcMessage *psMessage)
{
    //
    // Executes function call with parameter at given address.
    //
    tfIpcFuncCall func_call = (tfIpcFuncCall)psMessage->uladdress;
    func_call(psMessage->uldataw1);
}

//*****************************************************************************
// Close the Doxygen group.
//! @}
//*****************************************************************************


