444 lines
13 KiB
C
444 lines
13 KiB
C
|
//###########################################################################
|
||
|
//
|
||
|
// FILE: ipc.c
|
||
|
//
|
||
|
// TITLE: C28x IPC driver.
|
||
|
//
|
||
|
//###########################################################################
|
||
|
// $Copyright:
|
||
|
// Copyright (C) 2022 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.
|
||
|
// $
|
||
|
//###########################################################################
|
||
|
|
||
|
#include "ipc.h"
|
||
|
|
||
|
//
|
||
|
// Macros internal to the IPC driver
|
||
|
//
|
||
|
|
||
|
#define IPC_ADDR_OFFSET_NOCHANGE 2U
|
||
|
#define IPC_ADDR_OFFSET_MUL2 4U
|
||
|
#define IPC_ADDR_OFFSET_DIV2 1U
|
||
|
|
||
|
#define IPC_ADDR_OFFSET_CORR(addr, corr) (((addr) * (corr)) / 2U)
|
||
|
|
||
|
#if IPC_MSGQ_SUPPORT == 1U
|
||
|
|
||
|
//
|
||
|
// Global Circular Buffer Definitions
|
||
|
//
|
||
|
|
||
|
|
||
|
#pragma DATA_SECTION(IPC_CPU1_To_CPU2_PutBuffer, "MSGRAM_CPU1_TO_CPU2")
|
||
|
#pragma DATA_SECTION(IPC_CPU1_To_CPU2_GetBuffer, "MSGRAM_CPU2_TO_CPU1")
|
||
|
|
||
|
//
|
||
|
// IPC_CPU1_To_CPU2_PutBuffer acts as IPC_CPU2_To_CPU1_GetBuffer and
|
||
|
// IPC_CPU1_To_CPU2_GetBuffer acts as IPC_CPU2_To_CPU1_PutBuffer
|
||
|
//
|
||
|
IPC_PutBuffer_t IPC_CPU1_To_CPU2_PutBuffer;
|
||
|
IPC_GetBuffer_t IPC_CPU1_To_CPU2_GetBuffer;
|
||
|
#endif
|
||
|
|
||
|
const IPC_Instance_t IPC_Instance[IPC_TOTAL_NUM] = {
|
||
|
|
||
|
/* IPC_CPU1_L_CPU2_R */
|
||
|
{
|
||
|
.IPC_Flag_Ctr_Reg = (volatile IPC_Flag_Ctr_Reg_t *) IPC_BASE,
|
||
|
.IPC_SendCmd_Reg = (volatile IPC_SendCmd_Reg_t *)
|
||
|
(IPC_BASE + 0x10U),
|
||
|
.IPC_RecvCmd_Reg = (volatile IPC_RecvCmd_Reg_t *)
|
||
|
(IPC_BASE + 0x18U),
|
||
|
.IPC_Boot_Pump_Reg = (volatile IPC_Boot_Pump_Reg_t *)
|
||
|
(IPC_BASE + 0x20U),
|
||
|
.IPC_IntNum = {INT_IPC_0, INT_IPC_1, INT_IPC_2, INT_IPC_3,
|
||
|
0U, 0U, 0U, 0U},
|
||
|
.IPC_MsgRam_LtoR = CPU1_TO_CPU2_MSG_RAM_BASE,
|
||
|
.IPC_MsgRam_RtoL = CPU2_TO_CPU1_MSG_RAM_BASE,
|
||
|
.IPC_Offset_Corr = IPC_ADDR_OFFSET_NOCHANGE
|
||
|
#if IPC_MSGQ_SUPPORT == 1U
|
||
|
,
|
||
|
.IPC_PutBuffer = &IPC_CPU1_To_CPU2_PutBuffer,
|
||
|
.IPC_GetBuffer = &IPC_CPU1_To_CPU2_GetBuffer
|
||
|
#endif
|
||
|
},
|
||
|
|
||
|
/* IPC_CPU2_L_CPU1_R */
|
||
|
{
|
||
|
.IPC_Flag_Ctr_Reg = (volatile IPC_Flag_Ctr_Reg_t *) IPC_BASE,
|
||
|
.IPC_SendCmd_Reg = (volatile IPC_SendCmd_Reg_t *)
|
||
|
(IPC_BASE + 0x18U),
|
||
|
.IPC_RecvCmd_Reg = (volatile IPC_RecvCmd_Reg_t *)
|
||
|
(IPC_BASE + 0x10U),
|
||
|
.IPC_Boot_Pump_Reg = (volatile IPC_Boot_Pump_Reg_t *)
|
||
|
(IPC_BASE + 0x20U),
|
||
|
.IPC_IntNum = {INT_IPC_0, INT_IPC_1, INT_IPC_2, INT_IPC_3,
|
||
|
0U, 0U, 0U, 0U},
|
||
|
.IPC_MsgRam_LtoR = CPU2_TO_CPU1_MSG_RAM_BASE,
|
||
|
.IPC_MsgRam_RtoL = CPU1_TO_CPU2_MSG_RAM_BASE,
|
||
|
.IPC_Offset_Corr = IPC_ADDR_OFFSET_NOCHANGE
|
||
|
#if IPC_MSGQ_SUPPORT == 1U
|
||
|
,
|
||
|
.IPC_PutBuffer = (IPC_PutBuffer_t *)&IPC_CPU1_To_CPU2_GetBuffer,
|
||
|
.IPC_GetBuffer = (IPC_GetBuffer_t *)&IPC_CPU1_To_CPU2_PutBuffer
|
||
|
#endif
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// IPC_sendCommand
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
bool IPC_sendCommand(IPC_Type_t ipcType, uint32_t flags, bool addrCorrEnable,
|
||
|
uint32_t command, uint32_t addr, uint32_t data)
|
||
|
{
|
||
|
bool ret;
|
||
|
|
||
|
//
|
||
|
// Check whether the flags are not busy
|
||
|
//
|
||
|
if((IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_FLG & flags) == 0U)
|
||
|
{
|
||
|
ret = true;
|
||
|
|
||
|
if(addrCorrEnable)
|
||
|
{
|
||
|
//
|
||
|
// Update the command registers. ADDR register holds the offset
|
||
|
// from the base address of the MSG RAM
|
||
|
//
|
||
|
IPC_Instance[ipcType].IPC_SendCmd_Reg->IPC_SENDCOM = command;
|
||
|
IPC_Instance[ipcType].IPC_SendCmd_Reg->IPC_SENDDATA = data;
|
||
|
IPC_Instance[ipcType].IPC_SendCmd_Reg->IPC_SENDADDR =
|
||
|
addr - IPC_Instance[ipcType].IPC_MsgRam_LtoR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Update the command registers. addr param remains as is.
|
||
|
//
|
||
|
IPC_Instance[ipcType].IPC_SendCmd_Reg->IPC_SENDCOM = command;
|
||
|
IPC_Instance[ipcType].IPC_SendCmd_Reg->IPC_SENDDATA = data;
|
||
|
IPC_Instance[ipcType].IPC_SendCmd_Reg->IPC_SENDADDR = addr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the flags to indicate the remote core
|
||
|
//
|
||
|
IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_SET = flags;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ret = false;
|
||
|
}
|
||
|
|
||
|
return(ret);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// IPC_readCommand
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
bool IPC_readCommand(IPC_Type_t ipcType, uint32_t flags, bool addrCorrEnable,
|
||
|
uint32_t *command, uint32_t *addr, uint32_t *data)
|
||
|
{
|
||
|
bool ret;
|
||
|
uint32_t addrReg;
|
||
|
|
||
|
//
|
||
|
// Check whether the flags are not empty
|
||
|
//
|
||
|
if((IPC_Instance[ipcType].IPC_Flag_Ctr_Reg->IPC_STS & flags) != 0U)
|
||
|
{
|
||
|
ret = true;
|
||
|
|
||
|
//
|
||
|
// Read the command registers
|
||
|
//
|
||
|
*command = IPC_Instance[ipcType].IPC_RecvCmd_Reg->IPC_RECVCOM;
|
||
|
addrReg = IPC_Instance[ipcType].IPC_RecvCmd_Reg->IPC_RECVADDR;
|
||
|
*data = IPC_Instance[ipcType].IPC_RecvCmd_Reg->IPC_RECVDATA;
|
||
|
|
||
|
if(addrCorrEnable)
|
||
|
{
|
||
|
//
|
||
|
// Calculate the address form the offset
|
||
|
//
|
||
|
*addr = IPC_Instance[ipcType].IPC_MsgRam_RtoL +
|
||
|
IPC_ADDR_OFFSET_CORR(addrReg,
|
||
|
IPC_Instance[ipcType].IPC_Offset_Corr);
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*addr = addrReg;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ret = false;
|
||
|
}
|
||
|
|
||
|
return(ret);
|
||
|
}
|
||
|
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// IPC_registerInterrupt
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
void IPC_registerInterrupt(IPC_Type_t ipcType, uint32_t ipcInt,
|
||
|
void (*pfnHandler)(void))
|
||
|
{
|
||
|
//
|
||
|
// Check for arguments
|
||
|
//
|
||
|
|
||
|
ASSERT(ipcInt <= IPC_INT3);
|
||
|
|
||
|
//
|
||
|
// Get the corresponding interrupt number
|
||
|
//
|
||
|
uint32_t intNum = IPC_Instance[ipcType].IPC_IntNum[ipcInt];
|
||
|
|
||
|
//
|
||
|
// Register the interrupt handler
|
||
|
//
|
||
|
|
||
|
Interrupt_register(intNum, pfnHandler);
|
||
|
|
||
|
//
|
||
|
// Enable the interrupt
|
||
|
//
|
||
|
Interrupt_enable(intNum);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// IPC_unregisterInterrupt
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
void IPC_unregisterInterrupt(IPC_Type_t ipcType, uint32_t ipcInt)
|
||
|
{
|
||
|
//
|
||
|
// Check for arguments
|
||
|
//
|
||
|
|
||
|
ASSERT(ipcInt <= IPC_INT3);
|
||
|
|
||
|
//
|
||
|
// Get the corresponding interrupt number
|
||
|
//
|
||
|
uint32_t intNum = IPC_Instance[ipcType].IPC_IntNum[ipcInt];
|
||
|
|
||
|
//
|
||
|
// Disable the interrupt.
|
||
|
//
|
||
|
Interrupt_disable(intNum);
|
||
|
|
||
|
//
|
||
|
// Unregister the interrupt handler.
|
||
|
//
|
||
|
|
||
|
Interrupt_unregister(intNum);
|
||
|
}
|
||
|
|
||
|
#if IPC_MSGQ_SUPPORT == 1U
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// IPCinitMessageQueue
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
void IPC_initMessageQueue(IPC_Type_t ipcType,
|
||
|
volatile IPC_MessageQueue_t *msgQueue,
|
||
|
uint32_t ipcInt_L, uint32_t ipcInt_R)
|
||
|
{
|
||
|
//
|
||
|
// Check for arguments
|
||
|
//
|
||
|
ASSERT(msgQueue != NULL);
|
||
|
ASSERT(ipcInt_L < IPC_NUM_OF_INTERRUPTS);
|
||
|
ASSERT(ipcInt_R < IPC_NUM_OF_INTERRUPTS);
|
||
|
|
||
|
IPC_PutBuffer_t *putBuffer = IPC_Instance[ipcType].IPC_PutBuffer;
|
||
|
IPC_GetBuffer_t *getBuffer = IPC_Instance[ipcType].IPC_GetBuffer;
|
||
|
|
||
|
//
|
||
|
// L->R Put Buffer and Index Initialization
|
||
|
//
|
||
|
msgQueue->PutBuffer = putBuffer->Buffer[ipcInt_R];
|
||
|
msgQueue->PutWriteIndex = &(putBuffer->PutWriteIndex[ipcInt_R]);
|
||
|
msgQueue->GetReadIndex = &(putBuffer->GetReadIndex[ipcInt_L]);
|
||
|
msgQueue->PutFlag = (uint32_t)1U << ipcInt_R;
|
||
|
|
||
|
//
|
||
|
// L->R Get Buffer and Index Initialization
|
||
|
//
|
||
|
msgQueue->GetBuffer = getBuffer->Buffer[ipcInt_L];
|
||
|
msgQueue->GetWriteIndex = &(getBuffer->GetWriteIndex[ipcInt_L]);
|
||
|
msgQueue->PutReadIndex = &(getBuffer->PutReadIndex[ipcInt_R]);
|
||
|
|
||
|
//
|
||
|
// Initialize PutBuffer WriteIndex = 0 and GetBuffer ReadIndex = 0
|
||
|
//
|
||
|
*(msgQueue->PutWriteIndex) = 0U;
|
||
|
*(msgQueue->GetReadIndex) = 0U;
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// IPC_sendMessageToQueue
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
bool IPC_sendMessageToQueue(IPC_Type_t ipcType,
|
||
|
volatile IPC_MessageQueue_t *msgQueue,
|
||
|
bool addrCorrEnable, IPC_Message_t *msg, bool block)
|
||
|
{
|
||
|
//
|
||
|
// Check for arguments
|
||
|
//
|
||
|
ASSERT(msgQueue != NULL);
|
||
|
ASSERT(msg != NULL);
|
||
|
|
||
|
uint16_t writeIndex;
|
||
|
uint16_t readIndex;
|
||
|
uint16_t ret = true;
|
||
|
|
||
|
writeIndex = *(msgQueue->PutWriteIndex);
|
||
|
readIndex = *(msgQueue->PutReadIndex);
|
||
|
|
||
|
//
|
||
|
// Wait until Put Buffer slot is free
|
||
|
//
|
||
|
while(((writeIndex + 1U) & IPC_MAX_BUFFER_INDEX) == readIndex)
|
||
|
{
|
||
|
//
|
||
|
// If designated as a "Blocking" function, and Put buffer is full,
|
||
|
// return immediately with fail status.
|
||
|
//
|
||
|
if(!block)
|
||
|
{
|
||
|
ret = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
readIndex = *(msgQueue->PutReadIndex);
|
||
|
}
|
||
|
|
||
|
if(ret != false)
|
||
|
{
|
||
|
//
|
||
|
// When slot is free, Write Message to PutBuffer, update PutWriteIndex,
|
||
|
// and set the CPU IPC INT Flag
|
||
|
//
|
||
|
msgQueue->PutBuffer[writeIndex] = *msg;
|
||
|
|
||
|
if(addrCorrEnable)
|
||
|
{
|
||
|
msgQueue->PutBuffer[writeIndex].address -=
|
||
|
IPC_Instance[ipcType].IPC_MsgRam_LtoR;
|
||
|
}
|
||
|
|
||
|
writeIndex = (writeIndex + 1U) & IPC_MAX_BUFFER_INDEX;
|
||
|
*(msgQueue->PutWriteIndex) = writeIndex;
|
||
|
|
||
|
IPC_setFlagLtoR(ipcType, msgQueue->PutFlag);
|
||
|
}
|
||
|
|
||
|
return(ret);
|
||
|
}
|
||
|
|
||
|
//*****************************************************************************
|
||
|
//
|
||
|
// IPC_readMessageFromQueue
|
||
|
//
|
||
|
//*****************************************************************************
|
||
|
bool IPC_readMessageFromQueue(IPC_Type_t ipcType,
|
||
|
volatile IPC_MessageQueue_t *msgQueue,
|
||
|
bool addrCorrEnable, IPC_Message_t *msg, bool block)
|
||
|
{
|
||
|
//
|
||
|
// Check for arguments
|
||
|
//
|
||
|
ASSERT(msgQueue != NULL);
|
||
|
ASSERT(msg != NULL);
|
||
|
|
||
|
uint16_t writeIndex;
|
||
|
uint16_t readIndex;
|
||
|
uint16_t ret = true;
|
||
|
|
||
|
writeIndex = *(msgQueue->GetWriteIndex);
|
||
|
readIndex = *(msgQueue->GetReadIndex);
|
||
|
|
||
|
//
|
||
|
// 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(!block)
|
||
|
{
|
||
|
ret = false;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
writeIndex = *(msgQueue->GetWriteIndex);
|
||
|
}
|
||
|
|
||
|
if(ret != false)
|
||
|
{
|
||
|
//
|
||
|
// If there is a message in GetBuffer, Read Message and update
|
||
|
// the ReadIndex
|
||
|
//
|
||
|
*msg = msgQueue->GetBuffer[readIndex];
|
||
|
if(addrCorrEnable)
|
||
|
{
|
||
|
msg->address = IPC_Instance[ipcType].IPC_MsgRam_RtoL +
|
||
|
IPC_ADDR_OFFSET_CORR(msg->address,
|
||
|
IPC_Instance[ipcType].IPC_Offset_Corr);
|
||
|
}
|
||
|
|
||
|
readIndex = (readIndex + 1U) & IPC_MAX_BUFFER_INDEX;
|
||
|
*(msgQueue->GetReadIndex) = readIndex;
|
||
|
}
|
||
|
|
||
|
return(ret);
|
||
|
}
|
||
|
#endif
|