/*****************************************************************************/ /** * 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 #include #include #include #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 None. * ******************************************************************************/ 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 FAILURE; } (*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) { Status = EDCONF; goto mpc_init_error; } Status = XBram_CfgInitialize(&mpcBram, bramConfigPtr, bramConfigPtr->CtrlBaseAddress); if (Status != SUCCESS) { goto mpc_init_error; } /* * Execute the BRAM driver selftest. */ Status = XBram_SelfTest(&mpcBram, 0); if (Status != SUCCESS) { goto mpc_init_error; } mpcHwConfig.txBuffAddr = mpcBram.Config.MemBaseAddress; mpcHwConfig.rxBuffAddr = mpcBram.Config.MemBaseAddress; mpcHwConfig.gpioId = config->gpioId; Status = MpcHw_Init(&((*instPtr)->mpchwDesc), &mpcHwConfig); if (Status != SUCCESS) { Status = ENOMEM; goto mpc_init_error; } if (CircQueueCreate(&((*instPtr)->txQueue), (*instPtr)->config.txQueueMax) == false) { Status = ENOMEM; goto mpc_init_error; } if (CircQueueCreate(&((*instPtr)->rxQueue), (*instPtr)->config.rxQueueMax) == false) { Status = ENOMEM; goto mpc_init_error; } (*instPtr)->isEnable = false; // nothing received. (*instPtr)->isRxDone = false; // nothing received. (*instPtr)->isTxDone = true; // nothing to be sent. return SUCCESS; mpc_init_error: free((*instPtr)->mpchwDesc); free((*instPtr)->txQueue); free((*instPtr)->rxQueue); free(*instPtr); return Status; } /*****************************************************************************/ /** * @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) { goto recv_error; } if (CircQueuePush(instPtr->rxQueue, (void *)mpcReq) == false) { DBG_ERROR("SystemBus::RX Queue overflowed!\n"); Status = EQUEUE; goto recv_error; } instPtr->isRxDone = false; // another reception is undergoing. return SUCCESS; recv_error: free(mpcReq); return Status; }