forked from 0xha0/df_datalink
567 lines
15 KiB
C
567 lines
15 KiB
C
/*****************************************************************************/
|
|
/**
|
|
* Description: MPC protocol implementation file.
|
|
*
|
|
* Date: 2023-05-06 10:13
|
|
* Last Edit Time: [2023-05-06 21:45]
|
|
*
|
|
* Revisions: Rev 0.0
|
|
* History:
|
|
* [2023-05-06]: Initial version.
|
|
* [2023-09-13]: Update to 485 protocol-20220816
|
|
*
|
|
* Copyright(c) 2021 Beijing Institute of Technology.
|
|
* Lab of Communication and Network
|
|
*
|
|
******************************************************************************/
|
|
|
|
/***************************** Include Files *********************************/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include "mpc.h"
|
|
#include "util.h"
|
|
#include "common.h"
|
|
#include "error.h"
|
|
#include "circ_queue.h"
|
|
#include "xbram.h"
|
|
#include "xgpio.h"
|
|
|
|
#include "../system_monitor.h"
|
|
|
|
/************************** Constant Definitions *****************************/
|
|
// Common Field
|
|
// -- Command word
|
|
#define WD_CMD 0x00
|
|
|
|
// -- Production type
|
|
#define WD_PRD 0x01
|
|
#define PRD_DL_K 0xDD
|
|
#define PRD_DL_S 0xEE
|
|
#define PRD_ALL 0xFF
|
|
|
|
// Self-Test
|
|
#define CMD_SELF_TEST 0xE1
|
|
// -- Request
|
|
// -- -- --
|
|
// -- Response
|
|
// -- -- Self-Test Result
|
|
#define ST_RSP_LEN 16
|
|
#define WD_ST_RET 0x02
|
|
#define ST_RET_OK 0xAA
|
|
#define ST_RET_ERROR 0x55
|
|
#define ST_RET_INFO_LEN 13
|
|
// -- -- Signal processor PS voltage
|
|
#define WD_SP_FPGA_VOLT 0x03
|
|
// -- -- Signal processor PS AUX voltage
|
|
#define WD_SP_CLK_STATE 0x04
|
|
// -- -- Signal processor DDR voltage
|
|
#define WD_SP_AD_LOCK_STATE 0x05
|
|
// -- -- Signal processor PL voltage
|
|
#define WD_SP_AD_TEMP_0 0x06
|
|
// -- -- Signal processor PL AUX voltage
|
|
#define WD_SP_AD_TEMP_1 0x07
|
|
// -- -- Signal processor Bram voltage
|
|
#define WD_SP_FPGA_TEMP 0x08
|
|
|
|
// Config load
|
|
#define CMD_CONFIG 0xE3
|
|
// -- Request
|
|
#define WD_EN_CMD 0x02
|
|
#define EN_CMD_ON_HIGH_POWER 0x66
|
|
#define EN_CMD_ON_LOW_POWER 0x77
|
|
#define EN_CMD_SILENT 0x88
|
|
|
|
#define WD_MODE_CMD 0x03
|
|
#define MODE_CMD_HEAD 0x11
|
|
#define MODE_CMD_MEMBER 0x00
|
|
// -- Response
|
|
// -- -- none response
|
|
|
|
// Remote Test
|
|
#define RM_TEST_RESP_LEN 158
|
|
// -- Request
|
|
// Remote Test FLJ
|
|
#define CMD_FLJ_RM_TEST 0xE6
|
|
// Remote Test LJ
|
|
#define CMD_LJ_RM_TEST 0xF6
|
|
#define WD_RM_TEST 0x02
|
|
#define RM_TEST_CODE 0x99
|
|
// -- Response
|
|
#define WD_TIME_STAMP 0x02
|
|
#define WD_TIME_STAMP_LEN 8
|
|
#define WD_SYSBUS_LOG 0x0A
|
|
#define WD_SYSBUS_LOG_LEN 14
|
|
#define WD_LINK_LOG 0x18
|
|
#define WD_LINK_LOG_LEN 64
|
|
#define WD_PHY_LOG 0x58
|
|
#define WD_PHY_LOG_LEN 57
|
|
#define WD_HARDWARE_ST 0x91
|
|
#define WD_HARDWARE_ST_LEN 13
|
|
|
|
// My Production Type
|
|
#define MY_PRD_ID PRD_DL_K
|
|
|
|
/************************** Function Prototypes ******************************/
|
|
/**************************** Type Definitions *******************************/
|
|
/************************** Variable Definitions *****************************/
|
|
// TODO: better approach?
|
|
#include "../networking/link_layer/link_layer.h"
|
|
#include "../networking/phy_layer/phy_layer.h"
|
|
extern L2_INST *linkInstPtr;
|
|
extern L2_CONFIG linkConfig;
|
|
|
|
inline bool Mpc_IsEnable(MPC *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
return (instPtr->isEnable);
|
|
}
|
|
|
|
inline bool Mpc_IsReqPending(MPC *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
return (!isCircQueueEmpty(instPtr->rxQueue));
|
|
}
|
|
|
|
inline bool Mpc_IsRsqPending(MPC *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
return (!isCircQueueEmpty(instPtr->txQueue));
|
|
}
|
|
|
|
inline bool Mpc_IsTxBusy(MPC *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
return (!instPtr->isTxDone);
|
|
}
|
|
|
|
inline bool Mpc_IsRxBusy(MPC *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
return (!instPtr->isRxDone);
|
|
}
|
|
|
|
/**
|
|
* @brief Generate System Bus SelfTest response.
|
|
*/
|
|
static int32_t Mpc_GenSelfTestRsp(MPC *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
MPC_HW_FRAME *mpcRsp;
|
|
uint8_t *msg;
|
|
if (Mpc_IsEnable(instPtr) == false)
|
|
{
|
|
DBG_ERROR("System is not enabled!\n");
|
|
return EIGNORE;
|
|
}
|
|
mpcRsp = malloc(sizeof(MPC_HW_FRAME));
|
|
if (mpcRsp == NULL)
|
|
{
|
|
DBG_ERROR("Failed to allocate memory for LJ REMOTE TEST Response!\n");
|
|
return ENOMEM;
|
|
}
|
|
mpcRsp->len = ST_RSP_LEN;
|
|
msg = mpcRsp->data + FRAME_PAYLOAD_POFFSET;
|
|
|
|
HWSTATE *hwStatsPtr = &(instPtr->hwState);
|
|
|
|
msg[WD_CMD] = CMD_SELF_TEST;
|
|
msg[WD_PRD] = MY_PRD_ID;
|
|
SysMonitor_GetHardwareStatus(hwStatsPtr);
|
|
if (Mpc_IsEnable(instPtr))
|
|
{
|
|
msg[WD_ST_RET] = (hwStatsPtr->isSpOk && hwStatsPtr->isAntOk) ? ST_RET_OK : ST_RET_ERROR;
|
|
}
|
|
else
|
|
{
|
|
msg[WD_ST_RET] = (hwStatsPtr->isSpOk) ? ST_RET_OK : ST_RET_ERROR;
|
|
}
|
|
// apply status and omit 2 bytes of overhead.
|
|
memcpy(msg + WD_SP_FPGA_VOLT, &(hwStatsPtr->spFpgaVolt), ST_RET_INFO_LEN);
|
|
|
|
if (CircQueuePush(instPtr->txQueue, (void *)mpcRsp) == false)
|
|
{
|
|
free(mpcRsp);
|
|
DBG_ERROR("SystemBus::TX Queue overflowed!\n");
|
|
return EQUEUE;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Apply system configuration
|
|
*/
|
|
static int32_t Mpc_SystemConfig(MPC *instPtr, uint8_t *msg)
|
|
{
|
|
PHY_INST *phyRef = linkInstPtr->config.phyInstPtr;
|
|
TMC_INST *tmcRef = linkInstPtr->config.tmcInstPtr;
|
|
bool reconfig = false;
|
|
|
|
if (msg[WD_MODE_CMD] == MODE_CMD_HEAD)
|
|
{
|
|
// omit unnecessary reconfiguration
|
|
reconfig = (L2_NODE_HEAD != L2_GetNodeType(linkInstPtr));
|
|
linkConfig.myType = L2_NODE_HEAD;
|
|
linkConfig.myId = 2;
|
|
}
|
|
|
|
else if (msg[WD_MODE_CMD] == MODE_CMD_MEMBER)
|
|
{
|
|
// omit unnecessary reconfiguration
|
|
reconfig = (L2_NODE_MEMBER != L2_GetNodeType(linkInstPtr));
|
|
linkConfig.myType = L2_NODE_MEMBER;
|
|
linkConfig.myId = 2;
|
|
}
|
|
else
|
|
{
|
|
// Ignore current command
|
|
DBG_ERROR("Unexpected config command 0x%02X:0x%02X",
|
|
WD_MODE_CMD, msg[WD_MODE_CMD]);
|
|
return EIGNORE;
|
|
}
|
|
|
|
if (msg[WD_EN_CMD] == EN_CMD_ON_HIGH_POWER)
|
|
{
|
|
// high power config
|
|
Phy_AntConfigMode(phyRef, PHY_ANT_MODE_POWER_FULL);
|
|
if (reconfig == true || !Mpc_IsEnable(instPtr))
|
|
{
|
|
Linklayer_Reconfig(linkInstPtr, &linkConfig);
|
|
}
|
|
Linklayer_Start(linkInstPtr);
|
|
instPtr->isEnable = true;
|
|
}
|
|
else if (msg[WD_EN_CMD] == EN_CMD_ON_LOW_POWER)
|
|
{
|
|
// low power config
|
|
Phy_AntConfigMode(phyRef, PHY_ANT_MODE_POWER_LOW);
|
|
if (reconfig == true || !Mpc_IsEnable(instPtr))
|
|
{
|
|
Linklayer_Reconfig(linkInstPtr, &linkConfig);
|
|
}
|
|
Linklayer_Start(linkInstPtr);
|
|
instPtr->isEnable = true;
|
|
}
|
|
else if (msg[WD_EN_CMD] == EN_CMD_SILENT)
|
|
{
|
|
// silence config
|
|
// TODO: better approach?
|
|
instPtr->isEnable = false;
|
|
Linklayer_Halt(linkInstPtr);
|
|
Phy_AntConfigMode(phyRef, PHY_ANT_MODE_STANDBY);
|
|
TIME_WORD pullTime = {0, 0};
|
|
pullTime.int_part = TimeCtrl_GetIntCnt(tmcRef) + 1;
|
|
if (Phy_SetTimeWord(phyRef, &pullTime, PHY_TIME_WORD_POLL) != SUCCESS)
|
|
{
|
|
DBG_ERROR("Physical set pull time failed.\n");
|
|
}
|
|
Phy_SetupNextSlot(phyRef, PHY_TOR_RX, L1_10KBPS_10MS_FRAME, NULL);
|
|
}
|
|
else
|
|
{
|
|
// Ignore current command
|
|
DBG_ERROR("Unexpected config command 0x%02X:0x%02X",
|
|
WD_EN_CMD, msg[WD_EN_CMD]);
|
|
return EIGNORE;
|
|
}
|
|
instPtr->mpcLog.lastConfig[0] = msg[WD_MODE_CMD];
|
|
instPtr->mpcLog.lastConfig[1] = msg[WD_EN_CMD];
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief Initialize MPC instance.
|
|
*
|
|
* @param InstPtr A double pointer to MPC instance.
|
|
* @param config A pointer to MPC configurations.
|
|
*
|
|
* @return Error code.
|
|
*
|
|
******************************************************************************/
|
|
int32_t Mpc_Init(MPC **instPtr, MPC_CONFIG *config)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
ASSERT(config != NULL);
|
|
MPC_HW_CONFIG mpcHwConfig;
|
|
int32_t Status;
|
|
(*instPtr) = malloc(sizeof(MPC));
|
|
if ((*instPtr) == NULL)
|
|
{
|
|
return ENOMEM;
|
|
}
|
|
(*instPtr)->rxQueue = NULL;
|
|
(*instPtr)->txQueue = NULL;
|
|
(*instPtr)->mpchwDesc = NULL;
|
|
memcpy(&((*instPtr)->config), config, sizeof(MPC_CONFIG));
|
|
memset(&((*instPtr)->mpcLog), 0x00, sizeof(SYSBUSLOG));
|
|
memset(&((*instPtr)->linkLog), 0x00, sizeof(LINKLOG));
|
|
memset(&((*instPtr)->phyLog), 0x00, sizeof(PHYLOG));
|
|
memset(&((*instPtr)->hwState), 0x00, sizeof(HWSTATE));
|
|
|
|
XBram_Config *bramConfigPtr;
|
|
XBram mpcBram;
|
|
|
|
bramConfigPtr = XBram_LookupConfig(config->bramId);
|
|
if (bramConfigPtr == NULL)
|
|
{
|
|
Mpc_Destroy(*instPtr);
|
|
return EDCONF;
|
|
}
|
|
Status = XBram_CfgInitialize(&mpcBram, bramConfigPtr,
|
|
bramConfigPtr->CtrlBaseAddress);
|
|
if (Status != SUCCESS)
|
|
{
|
|
Mpc_Destroy(*instPtr);
|
|
return Status;
|
|
}
|
|
/*
|
|
* Execute the BRAM driver selftest.
|
|
*/
|
|
Status = XBram_SelfTest(&mpcBram, 0);
|
|
if (Status != SUCCESS)
|
|
{
|
|
Mpc_Destroy(*instPtr);
|
|
return Status;
|
|
}
|
|
|
|
mpcHwConfig.txBuffAddr = mpcBram.Config.MemBaseAddress;
|
|
mpcHwConfig.rxBuffAddr = mpcBram.Config.MemBaseAddress;
|
|
mpcHwConfig.gpioId = config->gpioId;
|
|
|
|
Status = MpcHw_Init(&((*instPtr)->mpchwDesc), &mpcHwConfig);
|
|
if (Status != SUCCESS)
|
|
{
|
|
Mpc_Destroy(*instPtr);
|
|
return ENOMEM;
|
|
}
|
|
if (CircQueueCreate(&((*instPtr)->txQueue), (*instPtr)->config.txQueueMax) == false)
|
|
{
|
|
Mpc_Destroy(*instPtr);
|
|
return ENOMEM;
|
|
}
|
|
if (CircQueueCreate(&((*instPtr)->rxQueue), (*instPtr)->config.rxQueueMax) == false)
|
|
{
|
|
Mpc_Destroy(*instPtr);
|
|
return ENOMEM;
|
|
}
|
|
(*instPtr)->isEnable = false; // nothing received.
|
|
(*instPtr)->isRxDone = false; // nothing received.
|
|
(*instPtr)->isTxDone = true; // nothing to be sent.
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief Destroy MPC instance.
|
|
*
|
|
* @param InstPtr A pointer to MPC instance.
|
|
*
|
|
* @return None.
|
|
*
|
|
******************************************************************************/
|
|
void Mpc_Destroy(MPC *instPtr)
|
|
{
|
|
if (instPtr == NULL)
|
|
{
|
|
return;
|
|
}
|
|
free(instPtr->mpchwDesc);
|
|
free(instPtr->txQueue);
|
|
free(instPtr->rxQueue);
|
|
free(instPtr);
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief Generate a remote test response message.
|
|
*
|
|
* @param instPtr A pointer to MPC instance.
|
|
*
|
|
* @return SUCCESS
|
|
* error code
|
|
*
|
|
******************************************************************************/
|
|
int32_t Mpc_PendRemoteTest(MPC *instPtr)
|
|
{
|
|
MPC_HW_FRAME *mpcRsp;
|
|
uint8_t *msg;
|
|
if (!Mpc_IsEnable(instPtr))
|
|
{
|
|
DBG_ERROR("System is not enabled!\n");
|
|
return EIGNORE;
|
|
}
|
|
mpcRsp = malloc(sizeof(MPC_HW_FRAME));
|
|
if (mpcRsp == NULL)
|
|
{
|
|
DBG_ERROR("Failed to allocate memory for LJ REMOTE TEST Response!\n");
|
|
return ENOMEM;
|
|
}
|
|
msg = mpcRsp->data + FRAME_PAYLOAD_POFFSET;
|
|
mpcRsp->len = RM_TEST_RESP_LEN;
|
|
if (L2_GetNodeType(linkInstPtr) == L2_NODE_HEAD)
|
|
{
|
|
// head node config
|
|
msg[WD_CMD] = CMD_FLJ_RM_TEST;
|
|
}
|
|
else
|
|
{
|
|
msg[WD_CMD] = CMD_LJ_RM_TEST;
|
|
}
|
|
msg[WD_PRD] = MY_PRD_ID;
|
|
|
|
TIME_WORD tmWord;
|
|
uint64_t time;
|
|
TimeCtrl_GetCurrentTime(linkInstPtr->config.tmcInstPtr, &tmWord);
|
|
time = (uint64_t)tmWord.int_part * TIME_FRAC_MAX + tmWord.frac_part;
|
|
memcpy(msg + WD_TIME_STAMP, &time, WD_TIME_STAMP_LEN);
|
|
SysMonitor_GetLinklayerLog(&(instPtr->linkLog));
|
|
SysMonitor_GetPhysicalLayerLog(&(instPtr->phyLog));
|
|
SysMonitor_GetHardwareStatus(&(instPtr->hwState));
|
|
memcpy(msg + WD_SYSBUS_LOG, &(instPtr->mpcLog), WD_SYSBUS_LOG_LEN);
|
|
memcpy(msg + WD_LINK_LOG, &(instPtr->linkLog), WD_LINK_LOG_LEN);
|
|
memcpy(msg + WD_PHY_LOG, &(instPtr->phyLog), WD_PHY_LOG_LEN);
|
|
memcpy(msg + WD_HARDWARE_ST, &(instPtr->hwState.spFpgaVolt), WD_HARDWARE_ST_LEN);
|
|
|
|
if (CircQueuePush(instPtr->txQueue, (void *)mpcRsp) == false)
|
|
{
|
|
free(mpcRsp);
|
|
DBG_WARNING("SystemBus::TX Queue overflowed!\n");
|
|
return EQUEUE;
|
|
}
|
|
instPtr->mpcLog.remoteCnt++;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief A handler function. This handler evaluate the message from system
|
|
* bus receive queue, and make appropriate response.
|
|
*
|
|
* @param instPtr A pointer to MPC instance.
|
|
*
|
|
* @return None
|
|
*
|
|
******************************************************************************/
|
|
int32_t Mpc_HandleCmd(MPC *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
int32_t Status;
|
|
MPC_HW_FRAME *mpcReq;
|
|
uint8_t *reqMsg;
|
|
if (CircQueuePop(instPtr->rxQueue, (void **)(&mpcReq)) == false)
|
|
{
|
|
DBG_WARNING("SystemBus::Nothing to be handled.\n");
|
|
return EIGNORE;
|
|
}
|
|
reqMsg = mpcReq->data + FRAME_PAYLOAD_POFFSET;
|
|
if (reqMsg[WD_PRD] != MY_PRD_ID && reqMsg[WD_PRD] != PRD_ALL)
|
|
{
|
|
DBG_WARNING("SystemBus::Production code mismatch!\n");
|
|
return EIGNORE;
|
|
}
|
|
|
|
switch (reqMsg[WD_CMD])
|
|
{
|
|
case CMD_CONFIG:
|
|
{
|
|
Status = Mpc_SystemConfig(instPtr, reqMsg);
|
|
if (Status == SUCCESS)
|
|
{
|
|
Status = Mpc_GenSelfTestRsp(instPtr);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
DBG_ERROR("SystemBus::Unknown command code!\n");
|
|
Status = FAILURE;
|
|
break;
|
|
}
|
|
}
|
|
free(mpcReq);
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief This function send a repose frame from MPC tx queue.
|
|
*
|
|
* @param instPtr A pointer to MPC instance.
|
|
*
|
|
* @return None
|
|
*
|
|
******************************************************************************/
|
|
int32_t Mpc_MakeResp(MPC *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
int32_t Status;
|
|
MPC_HW_FRAME *mpcRsp;
|
|
if (CircQueuePop(instPtr->txQueue, (void **)(&mpcRsp)) == false)
|
|
{
|
|
DBG_WARNING("SystemBus::Nothing to be sent.\n");
|
|
return EIGNORE;
|
|
}
|
|
instPtr->isTxDone = false; // A transmission procedure is started.
|
|
Status = MpcHw_Send(instPtr->mpchwDesc, mpcRsp);
|
|
free(mpcRsp);
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief This function handles the mpc tx irq events.
|
|
*
|
|
* @param instPtr A pointer to MPC instance.
|
|
*
|
|
* @return None
|
|
*
|
|
******************************************************************************/
|
|
int32_t Mpc_TxIrqHandler(MPC *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
// nothing special...
|
|
instPtr->isTxDone = true; // A transmission procedure finished.
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief This function handles the mpc rx irq events.
|
|
*
|
|
* @param instPtr A pointer to MPC instance.
|
|
*
|
|
* @return None
|
|
*
|
|
******************************************************************************/
|
|
int32_t Mpc_RxIrqHandler(MPC *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
int32_t Status;
|
|
MPC_HW_FRAME *mpcReq;
|
|
instPtr->isRxDone = true; // whether reception successes or not, receiving procedure ended.
|
|
mpcReq = malloc(sizeof(MPC_HW_FRAME));
|
|
if (mpcReq == NULL)
|
|
{
|
|
return ENOMEM;
|
|
}
|
|
Status = MpcHw_Recv(instPtr->mpchwDesc, mpcReq);
|
|
if (Status != SUCCESS)
|
|
{
|
|
DBG_ERROR("SystemBus::RX error!\n");
|
|
free(mpcReq);
|
|
return Status;
|
|
}
|
|
if (CircQueuePush(instPtr->rxQueue, (void *)mpcReq) == false)
|
|
{
|
|
DBG_ERROR("SystemBus::RX Queue overflowed!\n");
|
|
free(mpcReq);
|
|
return EQUEUE;
|
|
}
|
|
instPtr->isRxDone = false; // another reception is undergoing.
|
|
return SUCCESS;
|
|
}
|