/*
 * Copyright (c) 2022, Shenzhen CVA Innovation CO.,LTD
 * All rights reserved.
 *
 * Shenzhen CVA Innovation CO.,LTD (CVA chip) is supplying this file for use
 * exclusively with CVA's microcontroller products. This file can be freely
 * distributed within development tools that are supporting such microcontroller
 * products.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS".  NO WARRANTIES, WHETHER EXPRESS, IMPLIED
 * OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE.
 * CVA SHALL NOT, IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL,
 * OR CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 */

/*******************************************************************************
 * the includes
 ******************************************************************************/

#include <math.h>
#include <stddef.h>
#include "flexcan_drv.h"
#include "flexcan_reg.h"
#include "pcc0_reg.h"
#include "pcc1_reg.h"
#include "scg0_reg.h"
#include "scg2_reg.h"
#include "sim_reg.h"
#include "../CVM012x_features.h"

/*******************************************************************************
 * the defines
 ******************************************************************************/

#define FLEXCAN_DRV_CAN_RAM_SIZE      (512U)

#define RX_FIFO_FILT_TABLE_ADD_OFFSET (0xE0U)

#define TX_PENDING_ABORT_SUCCESS      1
#define TX_PENDING_ABORT_NO_TRANSFER  0
#define TX_PENDING_ABORT_FAILED       (0xFFU)

/*******************************************************************************
 * the typedefs
 ******************************************************************************/

/*******************************************************************************
 * the globals
 ******************************************************************************/

/*******************************************************************************
 * the functions
 ******************************************************************************/

static inline uint8_t FlexCanDrv_ConvertMsgBufSizeSel(uint8_t MBSizeCode)
{
    uint8_t result = 0;

    switch(MBSizeCode)
    {
    case FLEXCANDRV_MB_SIZE_BYTE_8:
        result = 16;
        break;
    case FLEXCANDRV_MB_SIZE_BYTE_16:
        result = 24;
        break;
    case FLEXCANDRV_MB_SIZE_BYTE_32:
        result = 40;
        break;
    case FLEXCANDRV_MB_SIZE_BYTE_64:
        result = 72;
        break;
    }

    return result;
}

static inline uint32_t FlexCanDrv_GetMsgBufStartAddr(FlexCanDrvType *obj, uint16_t msgBufId)
{
    /* get the first message buffer start address */
    uint32_t addr       = FlexCanReg_GetMsgBufferStartAddr(obj->flexCanReg);
    uint8_t  msgBufSize = 0;

    /* get the message buffer size */
#if FEATURE_CAN_FD_INSTANCE_NUM == 0 && FEATURE_CAN_2_0_INSTANCE_NUM == 1
    uint8_t msgBufSizeSel = FLEXCANDRV_MB_SIZE_BYTE_8;
#elif FEATURE_CAN_FD_INSTANCE_NUM == 1 && FEATURE_CAN_2_0_INSTANCE_NUM == 0
    uint8_t msgBufSizeSel = FlexCanReg_GetRegionDataSizeSel(obj->flexCanReg, 0);
#else
#error "Not matching CVM012x chip definition."
#endif
    msgBufSize = FlexCanDrv_ConvertMsgBufSizeSel(msgBufSizeSel) / 4;

    if(msgBufSizeSel == FLEXCANDRV_MB_SIZE_BYTE_8)
    {
        addr += (msgBufSize * msgBufId) * 4;
    }
    else if(msgBufSizeSel == FLEXCANDRV_MB_SIZE_BYTE_16)
    {
        addr += (msgBufSize * msgBufId + (2 * (msgBufId / 21))) * 4;
    }
    else if(msgBufSizeSel == FLEXCANDRV_MB_SIZE_BYTE_32)
    {
        addr += (msgBufSize * msgBufId + (8 * (msgBufId / 12))) * 4;
    }
    else
    {
        addr += (msgBufSize * msgBufId + (2 * (msgBufId / 7))) * 4;
    }

    return addr;
}

static inline uint8_t FlexCanDrv_DLC2DataLen(FlexCanDrv_DLCType dlc)
{
    uint8_t dataLen = 0;

    switch(dlc)
    {
    case DLC_BYTE_0:
    case DLC_BYTE_1:
    case DLC_BYTE_2:
    case DLC_BYTE_3:
    case DLC_BYTE_4:
    case DLC_BYTE_5:
    case DLC_BYTE_6:
    case DLC_BYTE_7:
    case DLC_BYTE_8:
        dataLen = (uint8_t)dlc;
        break;
    case DLC_BYTE_12:
        dataLen = 12;
        break;
    case DLC_BYTE_16:
        dataLen = 16;
        break;
    case DLC_BYTE_20:
        dataLen = 20;
        break;
    case DLC_BYTE_24:
        dataLen = 24;
        break;
    case DLC_BYTE_32:
        dataLen = 32;
        break;
    case DLC_BYTE_48:
        dataLen = 48;
        break;
    case DLC_BYTE_64:
        dataLen = 64;
        break;
    default:
        break;
    }

    return dataLen;
}

static inline uint8_t FlexCanDrv_GetMsgBufDataLen(FlexCanDrvType *obj, uint16_t msgBufId)
{
    uint32_t *msgBufPtr;
    uint8_t   dlc     = 0;
    uint8_t   dataLen = 0;

    /* get current message buffer start address */
    msgBufPtr = (uint32_t *)FlexCanDrv_GetMsgBufStartAddr(obj, msgBufId);

    /* get DLC code from message buffer */
    dlc = (*msgBufPtr & MESSAGE_BUFFER_CS_DLC_MASK) >> MESSAGE_BUFFER_CS_DLC_SHIFT;

    /* convert DLC code to data length */
    dataLen = FlexCanDrv_DLC2DataLen((FlexCanDrv_DLCType)dlc);

    return dataLen;
}

static inline void FlexCanDrv_SetMsgId(uint32_t *msgBufPtr, uint32_t msgId, bool isExt)
{
    uint32_t tempVar = 0;

    if(isExt)
    {
        /* set extended message ID bit (IDE) */
        msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] |= (MESSAGE_BUFFER_CS_IDE_MASK | MESSAGE_BUFFER_CS_SRR_MASK);

        /* set extended message ID */
        tempVar = msgBufPtr[MESSAGE_BUFFER_ID_WORD_NUM];
        tempVar &= ~(MESSAGE_BUFFER_ID_EXT_ID_MASK);
        tempVar |= ((msgId << MESSAGE_BUFFER_ID_EXT_ID_SHIFT) & MESSAGE_BUFFER_ID_EXT_ID_MASK);
        msgBufPtr[MESSAGE_BUFFER_ID_WORD_NUM] = tempVar;
    }
    else
    {
        /* clear extended message ID bit (IDE) */
        msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] &= ~(MESSAGE_BUFFER_CS_IDE_MASK | MESSAGE_BUFFER_CS_SRR_MASK);

        /* load standard message ID */
        tempVar = msgBufPtr[MESSAGE_BUFFER_ID_WORD_NUM];
        tempVar &= ~(MESSAGE_BUFFER_ID_STD_ID_MASK);
        tempVar |= ((msgId << MESSAGE_BUFFER_ID_STD_ID_SHIFT) & MESSAGE_BUFFER_ID_STD_ID_MASK);
        msgBufPtr[MESSAGE_BUFFER_ID_WORD_NUM] = tempVar;
    }
}

static inline void FlexCanDrv_SetMsgDLC(uint32_t *msgBufPtr, FlexCanDrv_DLCType dlc)
{
    msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] &= (~MESSAGE_BUFFER_CS_DLC_MASK);
    msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] |= (((uint32_t)dlc) << MESSAGE_BUFFER_CS_DLC_SHIFT & MESSAGE_BUFFER_CS_DLC_MASK);
}

static inline void FlexCanDrv_SetFDEnable(uint32_t *msgBufPtr, bool fdEnable)
{
    /* clear EDL bit */
    msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] &= (~MESSAGE_BUFFER_CS_EDL_MASK);
    /* clear BRS bit */
    msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] &= (~MESSAGE_BUFFER_CS_BRS_MASK);

    if(fdEnable)
    {
        /* set EDL bit */
        msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] |= MESSAGE_BUFFER_CS_EDL_MASK;
        /* set BRS bit */
        msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] |= MESSAGE_BUFFER_CS_BRS_MASK;
    }
    else
    {
        /* non CAN-FD keep EDL, BRS bit clear */
    }
}

static inline void FlexCanDrv_SetMsgType(uint32_t *msgBufPtr, FlexCanDrv_MsgType_t msgType)
{
    if(msgType == FLEXCANDRV_MSGTYPE_TX)
    {
        /* tx message set CODE as INACTIVE */
        msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] &= (~MESSAGE_BUFFER_CS_CODE_MASK);
        msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] |= ((FLEXCANDRV_MSG_BUF_CODE_INACTIVE << MESSAGE_BUFFER_CS_CODE_SHIFT) & MESSAGE_BUFFER_CS_CODE_MASK);
    }
    else if(msgType == FLEXCANDRV_MSGTYPE_RX)
    {
        /* rx message set CODE as RX_ACTIVE */
        msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] &= (~MESSAGE_BUFFER_CS_CODE_MASK);
        msgBufPtr[MESSAGE_BUFFER_CS_WORD_NUM] |= ((FLEXCANDRV_MSG_BUF_CODE_RX_ACTIVE << MESSAGE_BUFFER_CS_CODE_SHIFT) & MESSAGE_BUFFER_CS_CODE_MASK);
    }
    else
    {
    }
}

static inline void FlexCanDrv_InitCanRAM(FlexCanDrvType *obj)
{
    uint32_t *u32Ptr = (uint32_t *)FlexCanReg_GetMsgBufferStartAddr(obj->flexCanReg);
    uint32_t *tempPtr;
    uint32_t  i = 0;

    /* 0x80 - 0x480: message buffer 512 byte * 2 region */
    for(i = 0; i < 128 * obj->initHwPar.canRamNum; i++)
    {
        tempPtr  = (u32Ptr + i);
        *tempPtr = 0;
    }
}

static inline void FlexCanDrv_EnableCanCfgClk(FlexCanDrvType *obj)
{
    /* unlock PCC key */
    Pcc0Reg_SetKey(obj->pcc0Reg, 0x55534552);
    /* enable CAN CFG clock */
    Pcc1Reg_SetCanCfgCkEn(obj->pcc1Reg, 1);
    /* lock PCC key again */
    Pcc0Reg_SetKey(obj->pcc0Reg, 0);
}

static inline void FlexCanDrv_EnableCanOperationClk(FlexCanDrvType *obj)
{
    /* unlock PCC key */
    Pcc0Reg_SetKey(obj->pcc0Reg, 0x55534552);
    /* enable CAN operation clock */
    Pcc1Reg_SetCanCkEn(obj->pcc1Reg, 1);
    /* lock PCC key again */
    Pcc0Reg_SetKey(obj->pcc0Reg, 0);
}

static inline void FlexCanDrv_EnableCanSoscClkSrc(FlexCanDrvType *obj)
{
    /* unlock scg */
    Scg0Reg_SetKey(obj->scg0Reg, 0x19962020);
    /* enable SOSC_DIV */
    Scg2Reg_SetSoscCfgEn(obj->scg2Reg, 1);
    /* lock scg again */
    Scg0Reg_SetKey(obj->scg0Reg, 0);
}

void FlexCanDrv_Init(FlexCanDrvType           *obj,
                     struct _FlexCanRegType_  *flexCanReg,
                     struct _Pcc1RegType_     *pcc1Reg,
                     struct _Pcc0RegType_     *pcc0Reg,
                     struct _Scg0RegType_     *scg0Reg,
                     struct _Scg2RegType_     *scg2Reg,
                     struct _SimRegType_      *simReg,
                     FlexCanDrv_InitHwParType *initHwPar)
{
    obj->initHwPar.canRamNum = initHwPar->canRamNum;
    obj->flexCanReg          = flexCanReg;
    obj->pcc1Reg             = pcc1Reg;
    obj->pcc0Reg             = pcc0Reg;
    obj->scg0Reg             = scg0Reg;
    obj->scg2Reg             = scg2Reg;
    obj->simReg              = simReg;
}

void FlexCanDrv_GetDefaultCfg(FlexCanDrv_ControllerCfgType *controllerCfg)
{
    uint8_t i = 0;

    controllerCfg->bitTiming.prescalerDiv      = 0;
    controllerCfg->bitTiming.propSeg           = 0;
    controllerCfg->bitTiming.phaseSeg1         = 0;
    controllerCfg->bitTiming.phaseSeg2         = 0;
    controllerCfg->bitTiming.resyncJumpWidth   = 0;
    controllerCfg->fdBitTiming.prescalerDiv    = 0;
    controllerCfg->fdBitTiming.propSeg         = 0;
    controllerCfg->fdBitTiming.phaseSeg1       = 0;
    controllerCfg->fdBitTiming.phaseSeg2       = 0;
    controllerCfg->fdBitTiming.resyncJumpWidth = 0;
    controllerCfg->clkSrc                      = FLEXCANDRV_CLKSRC_CHICLK;
    controllerCfg->fifoEnable                  = 0;
    controllerCfg->dmaEnable                   = 0;
    controllerCfg->rxMBGlobalMask              = 0xFFFFFFFF;
    controllerCfg->rxMB14Mask                  = 0xFFFFFFFF;
    controllerCfg->rxMB15Mask                  = 0xFFFFFFFF;
    controllerCfg->rxFifoGlobalMask            = 0xFFFFFFFF;
    controllerCfg->rxFifoFiltFormat            = FLEXCANDRV_RXFIFO_FILTFORMAT_A;
    controllerCfg->rxFifoFilterNum             = 0;      
    controllerCfg->individualMaskEnable        = 0;
    controllerCfg->rxFifoIndivMskNum           = 8;
    for(i = 0; i < 32; i ++)
    {
        controllerCfg->rxFifoIndivMsk[i]       = 0xFFFFFFFF;
    }
    controllerCfg->fdEnable                    = 0;
    controllerCfg->fdISOEnable                 = 0;
    controllerCfg->msgBufDataLenSel            = FLEXCANDRV_MB_SIZE_BYTE_8;
    controllerCfg->loopbackEnable              = 0;
    controllerCfg->tdcEnable                   = 0;
    controllerCfg->tdcOffset                   = 0;
    controllerCfg->txAbortEnable               = 0;
    controllerCfg->edgeWkupEnable              = 0;
    controllerCfg->busoffIntEnable             = 0;
    controllerCfg->errorIntEnable              = 0;
    controllerCfg->msgNum                      = 0;
    controllerCfg->msgCfg                      = (void *)0;
}

void FlexCanDrv_Start(FlexCanDrvType *obj)
{
    /* set freeze mode disable */
    FlexCanReg_SetFreezeMode(obj->flexCanReg, false);
    /* set halt mode disable */
    FlexCanReg_SetHaltMode(obj->flexCanReg, false);

    /* wait for synchronization */
    while(FlexCanReg_GetFreezeAck(obj->flexCanReg))
    {
    }
}

void FlexCanDrv_SoftReset(FlexCanDrvType *obj)
{
    /* check for low power mode */
    if(FlexCanReg_GetLowPowerAck(obj->flexCanReg))
    {
        /* Enable clock */
        FlexCanReg_SetModuleDisable(obj->flexCanReg, false);
        /* wait until enabled */
        while(FlexCanReg_GetLowPowerAck(obj->flexCanReg))
        {
            /* enable CAN operation clock */
            FlexCanDrv_EnableCanOperationClk(obj);
        }
    }

    /* Reset the CAN module */
    FlexCanReg_SetSoftResetRequest(obj->flexCanReg, true);
    /* Wait for reset cycle to complete */
    while(FlexCanReg_GetSoftResetRequest(obj->flexCanReg))
    {
    }

    /* set freeze mode enable */
    FlexCanReg_SetFreezeMode(obj->flexCanReg, true);
    /* set halt mode enable */
    FlexCanReg_SetHaltMode(obj->flexCanReg, true);

    /* check for freeze Ack */
    while((FlexCanReg_GetFreezeAck(obj->flexCanReg) == 0) || (FlexCanReg_GetNotReadyFlag(obj->flexCanReg) == 0))
    {
        /* soft reset failed */
    }
}

void FlexCanDrv_CfgMsgObj(FlexCanDrvType *obj, const FlexCanDrv_MsgCfgType *msgCfg, uint8_t msgBufId)
{
    FlexCanDrv_MsgObjType msgObj;
    uint32_t             *msgBufPtr;

    /* get current message buffer start address */
    msgBufPtr = (uint32_t *)FlexCanDrv_GetMsgBufStartAddr(obj, msgBufId);

    /* set message ID */
    FlexCanDrv_SetMsgId(msgBufPtr, msgCfg->msgId, msgCfg->isExtMsgId);

    /* set message DLC */
    FlexCanDrv_SetMsgDLC(msgBufPtr, msgCfg->dlc);

    /* set FD configures */
    FlexCanDrv_SetFDEnable(msgBufPtr, msgCfg->isFd);

    /* set message type */
    FlexCanDrv_SetMsgType(msgBufPtr, msgCfg->msgType);

    if(msgCfg->intEnable)
    {
        msgObj.msgBufId = msgBufId;
        /* set message buffer interrupt enable */
        FlexCanDrv_EnableMsgObjInterrupt(obj, &msgObj);
    }
    else
    {
        /* message buffer interrupt disable, do nothing */
    }

    if(msgCfg->msgType == FLEXCANDRV_MSGTYPE_RX)
    {
        /* set message buffer individual mask */
        FlexCanReg_SetRxIndividualMask(obj->flexCanReg, msgBufId, msgCfg->individualMask);
    }
    else
    {
        /* current message buffer is Tx message no need to configure individual mask */
    }
}

void FlexCanDrv_Configure(FlexCanDrvType *obj, const FlexCanDrv_ControllerCfgType *controllerCfg)
{
    uint8_t msgBufSize;
    uint8_t msgBufNum = 0;
    uint8_t i = 0;

    /* enable CAN CFG clock */
    FlexCanDrv_EnableCanCfgClk(obj);

    if(controllerCfg->clkSrc == FLEXCANDRV_CLKSRC_OSC)
    {
        /* use oscillator clock, enable SOSC DIV2 */
        FlexCanDrv_EnableCanSoscClkSrc(obj);
    }
    else
    {
        /* use peripheral clock, do nothing */
    }
    /* disable FlexCan module */
    FlexCanReg_SetModuleDisable(obj->flexCanReg, true);

    /* make sure that the module has enter into low power mode successfully */
    while(FlexCanReg_GetLowPowerAck(obj->flexCanReg) == 0)
    {

    }

    /* configure FlexCan clock source */
    FlexCanReg_SetClockSource(obj->flexCanReg, controllerCfg->clkSrc);

    /* reset CAN internal status */
    FlexCanDrv_SoftReset(obj);

    /* switch to supervisor mode */
    FlexCanReg_SetAccessMode(obj->flexCanReg, FLEXCANDRV_CAN_ACCESS_MODE_SUP);

    /* enable write access in freeze mode */
    FlexCanReg_SetFreezeModeWriteAcc(obj->flexCanReg, true);

    /* initial can ram */
    FlexCanDrv_InitCanRAM(obj);

    /* calculate the total message number according to the message
     * buffer size per region */
    msgBufSize = FlexCanDrv_ConvertMsgBufSizeSel(controllerCfg->msgBufDataLenSel);
    if(msgBufSize != 0)
    {
        msgBufNum  = (FLEXCAN_DRV_CAN_RAM_SIZE / msgBufSize) * obj->initHwPar.canRamNum;
    }
    /* set message buffer maximum number */
    FlexCanReg_SetMaxMsgBufferNum(obj->flexCanReg, msgBufNum);

    /*
     *  set standard CAN bit timing
     */
    /* enable CAN extended bit timing */
    FlexCanReg_SetExtendedBitTimingEnable(obj->flexCanReg, true);
    /* set prescaler division */
    FlexCanReg_SetExtPresalerDivision(obj->flexCanReg, controllerCfg->bitTiming.prescalerDiv - 1);
    /* set resync jump width */
    FlexCanReg_SetExtResyncJumpWidth(obj->flexCanReg, controllerCfg->bitTiming.resyncJumpWidth - 1);
    /* set propagation segment */
    FlexCanReg_SetExtPorpSeg(obj->flexCanReg, controllerCfg->bitTiming.propSeg - 1);
    /* set phase segment 1 */
    FlexCanReg_SetExtPhaseSeg1(obj->flexCanReg, controllerCfg->bitTiming.phaseSeg1 - 1);
    /* set phase segment 2 */
    FlexCanReg_SetExtPhaseSeg2(obj->flexCanReg, controllerCfg->bitTiming.phaseSeg2 - 1);

    if(controllerCfg->fifoEnable)
    {
        /* set Rx FIFO enable */
        FlexCanReg_SetRxFIFOEnalbe(obj->flexCanReg, true);
        /* set Rx FIFO DMA enable */
        FlexCanReg_SetRxFIFODmaEnable(obj->flexCanReg, controllerCfg->dmaEnable);
        /* set Rx FIFO global mask */
        FlexCanReg_SetRxFifoGlobalMask(obj->flexCanReg, controllerCfg->rxFifoGlobalMask);
        /* set Rx FIFO ID filter format */
        FlexCanReg_SetRxFifoFiltMode(obj->flexCanReg, controllerCfg->rxFifoFiltFormat);
        /* set Rx FIFO filter number code */
        FlexCanReg_SetRxFifoFilterNum(obj->flexCanReg, controllerCfg->rxFifoFilterNum);
        /* set Rx Individual mask dedicated for Rx FIFO */
        if(controllerCfg->individualMaskEnable == true)
        {
            for(i = 0; i < controllerCfg->rxFifoIndivMskNum; i ++)
            {
                FlexCanReg_SetRxIndividualMask(obj->flexCanReg, i, controllerCfg->rxFifoIndivMsk[i]);
            }
        }
    }
    else
    {
        /* set Rx FIFO disable */
        FlexCanReg_SetRxFIFOEnalbe(obj->flexCanReg, false);
        /* disable Rx FIFO DMA */
        FlexCanReg_SetRxFIFODmaEnable(obj->flexCanReg, false);
    }

#if FEATURE_CAN_FD_INSTANCE_NUM == 1 && FEATURE_CAN_2_0_INSTANCE_NUM == 0
    if(controllerCfg->fdEnable)
    {
        /* set CAN FD operation enable */
        FlexCanReg_SetFdEnable(obj->flexCanReg, controllerCfg->fdEnable);
        /* enable FD rate switch */
        FlexCanReg_SetFdRateSwitch(obj->flexCanReg, true);

        /* set FD prescaler division */
        FlexCanReg_SetFdPrescalerDivision(obj->flexCanReg, controllerCfg->fdBitTiming.prescalerDiv - 1);
        /* set FD resync jump width */
        FlexCanReg_SetFdResyncJumpWidth(obj->flexCanReg, controllerCfg->fdBitTiming.resyncJumpWidth - 1);
        /* set FD propagation segment */
        FlexCanReg_SetFdPorpSeg(obj->flexCanReg, controllerCfg->fdBitTiming.propSeg);
        /* set FD phase segment 1 */
        FlexCanReg_SetFdPhaseSeg1(obj->flexCanReg, controllerCfg->fdBitTiming.phaseSeg1 - 1);
        /* set FD phase segment 2 */
        FlexCanReg_SetFdPhaseSeg2(obj->flexCanReg, controllerCfg->fdBitTiming.phaseSeg2 - 1);

        /* set region 0 data size selection */
        FlexCanReg_SetRegionDataSizeSel(obj->flexCanReg, 0, controllerCfg->msgBufDataLenSel);
        /* set region 1 data size selection */
        FlexCanReg_SetRegionDataSizeSel(obj->flexCanReg, 1, controllerCfg->msgBufDataLenSel);
        /* set region 2 data size selection */
        FlexCanReg_SetRegionDataSizeSel(obj->flexCanReg, 2, controllerCfg->msgBufDataLenSel);
        /* set region 3 data size selection */
        FlexCanReg_SetRegionDataSizeSel(obj->flexCanReg, 3, controllerCfg->msgBufDataLenSel);

        /* set TDC enable */
        FlexCanReg_SetTdcEnable(obj->flexCanReg, controllerCfg->tdcEnable);
        /* set TDC offset */
        FlexCanReg_SetTdcOffset(obj->flexCanReg, controllerCfg->tdcOffset);

        /* set CAN-FD ISO enable */
        FlexCanReg_SetIsoFdEnalbe(obj->flexCanReg, controllerCfg->fdISOEnable);
    }
#endif

    /* set rx mailbox global mask */
    FlexCanReg_SetRxMailboxGlobalMask(obj->flexCanReg, controllerCfg->rxMBGlobalMask);
    /* set rx mailbox 14 mask */
    FlexCanReg_SetRxMailbox14Mask(obj->flexCanReg, controllerCfg->rxMB14Mask);
    /* set rx mailbox 15 mask */
    FlexCanReg_SetRxMailbox15Mask(obj->flexCanReg, controllerCfg->rxMB15Mask);
    /* set individual Rx masking and queue enable */
    FlexCanReg_SetIndividualMaskEnable(obj->flexCanReg, controllerCfg->individualMaskEnable);

    /* loopback mode configuration */
    if(controllerCfg->loopbackEnable)
    {
        FlexCanReg_SetSelfReceptionDisable(obj->flexCanReg, false); /* enable self reception (0) */
        FlexCanReg_SetLoopbackMode(obj->flexCanReg, true);          /* enable loop-back */
    }
    else
    {
        FlexCanReg_SetSelfReceptionDisable(obj->flexCanReg, true); /* disable self reception (1) */
        FlexCanReg_SetLoopbackMode(obj->flexCanReg, false);        /* disable loop-back */
    }

    /* edge wakeup configuration */
    if(controllerCfg->edgeWkupEnable)
    {
        /* enable edge(self) wakeup */
        FlexCanReg_SetSelfWakeupEnable(obj->flexCanReg, 1U);
        /* enable edge(self) wakeup interrupt */
        FlexCanReg_SetWakeupIntMask(obj->flexCanReg, 1U);
    }
    else
    {
        /* disable edge(self) wakeup */
        FlexCanReg_SetSelfWakeupEnable(obj->flexCanReg, 0U);
        /* disable edge(self) wakeup interrupt */
        FlexCanReg_SetWakeupIntMask(obj->flexCanReg, 0U);
    }

    /* set Tx message abort enable */
    FlexCanReg_SetTxAbortEnable(obj->flexCanReg, controllerCfg->txAbortEnable);

    /* set busoff interrupt enable mask */
    FlexCanReg_SetBusOffIntEnable(obj->flexCanReg, controllerCfg->busoffIntEnable);
    /* set error interrupt enable mask */
    FlexCanReg_SetErrorIntEnable(obj->flexCanReg, controllerCfg->errorIntEnable);

    /* configure messages */
    for(i = 0; i < controllerCfg->msgNum; i++)
    {
        uint16_t j = 0;
        /* configure messages according to the message buffer Id and message buffer length parameters */
        for(j = controllerCfg->msgCfg[i].msgBufId; j < controllerCfg->msgCfg[i].msgBufId + controllerCfg->msgCfg[i].msgBufLen; j++)
        {
            FlexCanDrv_CfgMsgObj(obj, &controllerCfg->msgCfg[i], j);
        }
    }

    /* clear all message buffer flags */
    FlexCanDrv_ClearAllMsgObjFlags(obj);

    /* enable FlexCan module */
    FlexCanDrv_Start(obj);

    /* switch to user mode */
    FlexCanReg_SetAccessMode(obj->flexCanReg, FLEXCANDRV_CAN_ACCESS_MODE_USER);
}

void FlexCanDrv_TransmitMsg(FlexCanDrvType *obj, FlexCanDrv_MsgObjType *msgObj)
{
    uint32_t *msgBufPtr;

    /* get current message buffer start address */
    msgBufPtr = (uint32_t *)FlexCanDrv_GetMsgBufStartAddr(obj, msgObj->msgBufId);

    /* set transmit code for the message buffer and start transmitting */
    *msgBufPtr |= (((uint32_t)FLEXCANDRV_MSG_BUF_CODE_TX_REQUEST) << FLEXCANDRV_MSG_BUF_CODE_SHIFT);
}

void FlexCanDrv_SetTxMsg(FlexCanDrvType *obj, FlexCanDrv_MsgObjType *msgObj)
{
    uint32_t *msgBufPtr;
    uint32_t  dataLen;

    /* get current message buffer start address */
    msgBufPtr = (uint32_t *)FlexCanDrv_GetMsgBufStartAddr(obj, msgObj->msgBufId);
    /* add data field address offset (2 * 32 bit) */
    msgBufPtr += 2;

    /* get data length from message buffer DLC */
    dataLen = FlexCanDrv_GetMsgBufDataLen(obj, msgObj->msgBufId);

    /* load the data into message buffer */
    uint8_t i = 0;
    for(i = 0; i < dataLen; i++)
    {
        /* load the data in byte length */
        if((i % 4) == 0)
        {
            /* clean the old data */
            *msgBufPtr = 0;

            /* lowest byte in 32-bit */
            *msgBufPtr |= (((uint32_t)msgObj->data[i]) << 24);
        }
        else if((i % 4) == 1)
        {
            /* 2nd byte in 32-bit */
            *msgBufPtr |= (((uint32_t)msgObj->data[i]) << 16);
        }
        else if((i % 4) == 2)
        {
            /* 3rd byte in 32-bit */
            *msgBufPtr |= (((uint32_t)msgObj->data[i]) << 8);
        }
        else if((i % 4) == 3)
        {
            /* highest byte in 32-bit */
            *msgBufPtr |= ((uint32_t)msgObj->data[i]);

            /* increment the 32-bit data pointer  */
            msgBufPtr++;
        }
    }
}

void FlexCanDrv_GetRxMsg(FlexCanDrvType *obj, FlexCanDrv_MsgObjType *msgObj)
{
    uint32_t *msgBufPtr;
    uint8_t   dataLen;
    bool      isExtMsgId;

    /* get current message buffer start address */
    msgBufPtr = (uint32_t *)FlexCanDrv_GetMsgBufStartAddr(obj, msgObj->msgBufId);

    /* get the rx message DLC */
    msgObj->dlc = FlexCanDrv_DLC2DataLen((FlexCanDrv_DLCType)((*msgBufPtr & MESSAGE_BUFFER_CS_DLC_MASK) >> MESSAGE_BUFFER_CS_DLC_SHIFT));
    /* get extended message ID bit (CS.IDE) */
    isExtMsgId = (*msgBufPtr & MESSAGE_BUFFER_CS_IDE_MASK) >> MESSAGE_BUFFER_CS_IDE_SHIFT;

    /* set message buffer pointer to ID field */
    msgBufPtr++;

    /* get message ID */
    if(isExtMsgId == true)
    {
        msgObj->msgId = (*msgBufPtr & MESSAGE_BUFFER_ID_EXT_ID_MASK) >> MESSAGE_BUFFER_ID_EXT_ID_SHIFT;
    }
    else
    {
        msgObj->msgId = (*msgBufPtr & MESSAGE_BUFFER_ID_STD_ID_MASK) >> MESSAGE_BUFFER_ID_STD_ID_SHIFT;
    }

    /* set the message buffer pointer to data field */
    msgBufPtr++;

    /* get data length from message buffer DLC */
    dataLen = FlexCanDrv_GetMsgBufDataLen(obj, msgObj->msgBufId);

    /* load the data from message buffer to data array */
    uint8_t i = 0;
    for(i = 0; i < dataLen; i++)
    {
        if((i % 4) == 0)
        {
            /* load 1 byte data from highest byte in 32-bit */
            msgObj->data[i] = (*msgBufPtr >> 24);
        }
        else if((i % 4) == 1)
        {
            /* load 1 byte data from 2nd highest byte in 32-bit */
            msgObj->data[i] = (*msgBufPtr >> 16);
        }
        else if((i % 4) == 2)
        {
            /* load 1 byte data from 3nd highest byte in 32-bit */
            msgObj->data[i] = (*msgBufPtr >> 8);
        }
        /* when (i % 4) == 3 */
        else
        {
            /* load 1 byte data from lowest byte in 32-bit */
            msgObj->data[i] = (*msgBufPtr >> 0);

            /* increment message buffer 32-bit data pointer */
            msgBufPtr++;
        }
    }

    /* unlock mailbox globally */
    FlexCanReg_GetFreeRunningTimer(obj->flexCanReg);
}

void FlexCanDrv_GetRxFifoMsg(FlexCanDrvType *obj, FlexCanDrv_MsgObjType *msgObj)
{
    bool                  isExtMsgId;
    FlexCanDrv_MsgObjType tempMsgObj;

    /* get Rx FIFO out port start address (message buffer 0) */
    uint32_t *msgBufPtr = (uint32_t *)FlexCanDrv_GetMsgBufStartAddr(obj, 0);

    /* get the rx message DLC */
    msgObj->dlc = FlexCanDrv_DLC2DataLen((FlexCanDrv_DLCType)((*msgBufPtr & MESSAGE_BUFFER_CS_DLC_MASK) >> MESSAGE_BUFFER_CS_DLC_SHIFT));
    /* get extended message ID bit (CS.IDE) */
    isExtMsgId = (*msgBufPtr & MESSAGE_BUFFER_CS_IDE_MASK) >> MESSAGE_BUFFER_CS_IDE_SHIFT;

    /* set message buffer pointer to ID field */
    msgBufPtr++;

    /* get message ID */
    if(isExtMsgId == true)
    {
        msgObj->msgId = (*msgBufPtr & MESSAGE_BUFFER_ID_EXT_ID_MASK) >> MESSAGE_BUFFER_ID_EXT_ID_SHIFT;
    }
    else
    {
        msgObj->msgId = (*msgBufPtr & MESSAGE_BUFFER_ID_STD_ID_MASK) >> MESSAGE_BUFFER_ID_STD_ID_SHIFT;
    }

    /* set message buffer pointer to DATA field */
    msgBufPtr++;

    /* load the data from message buffer to data array */
    uint8_t i = 0;
    for(i = 0; i < 8; i++)
    {
        if((i % 4) == 0)
        {
            /* load 1 byte data from highest byte in 32-bit */
            msgObj->data[i] = (*msgBufPtr >> 24);
        }
        else if((i % 4) == 1)
        {
            /* load 1 byte data from 2nd highest byte in 32-bit */
            msgObj->data[i] = (*msgBufPtr >> 16);
        }
        else if((i % 4) == 2)
        {
            /* load 1 byte data from 3nd highest byte in 32-bit */
            msgObj->data[i] = (*msgBufPtr >> 8);
        }
        /* when (i % 4) == 3 */
        else
        {
            /* load 1 byte data from lowest byte in 32-bit */
            msgObj->data[i] = (*msgBufPtr >> 0);

            /* increment message buffer 32-bit data pointer */
            msgBufPtr++;
        }
    }

    /* clear Rx FIFO available flag to update Rx FIFO */
    tempMsgObj.msgBufId = 5;
    FlexCanDrv_ClearMsgObjFlag(obj, &tempMsgObj);
}

void FlexCanDrv_ClearMsgObjFlag(FlexCanDrvType *obj, FlexCanDrv_MsgObjType *msgObj)
{
    uint32_t temp, temp1 = 0;

    temp1 = msgObj->msgBufId % 32;
    temp  = 0x00000001 << temp1;

    if(msgObj->msgBufId > 63)
    {
        FlexCanReg_SetMsgBufInterruptFlagReg(obj->flexCanReg, 2, temp);
    }
    else if(msgObj->msgBufId > 31)
    {
        FlexCanReg_SetMsgBufInterruptFlagReg(obj->flexCanReg, 1, temp);
    }
    else
    {
        FlexCanReg_SetMsgBufInterruptFlagReg(obj->flexCanReg, 0, temp);
    }
}

uint8_t FlexCanDrv_GetMsgObjFlag(FlexCanDrvType *obj, FlexCanDrv_MsgObjType *msgObj)
{
    uint32_t temp, temp1 = 0;
    temp1 = msgObj->msgBufId % 32;
    temp  = 0x00000001 << temp1;

    if(msgObj->msgBufId > 63)
    {
        temp &= FlexCanReg_GetMsgBufInterruptFlagReg(obj->flexCanReg, 2);
    }
    else if(msgObj->msgBufId > 31)
    {
        temp &= FlexCanReg_GetMsgBufInterruptFlagReg(obj->flexCanReg, 1);
    }
    else
    {
        temp &= FlexCanReg_GetMsgBufInterruptFlagReg(obj->flexCanReg, 0);
    }

    if(temp)
    {
        return (1);
    }
    else
    {
        return (0);
    }
}

void FlexCanDrv_ClearAllMsgObjFlags(FlexCanDrvType *obj)
{
    if(obj->initHwPar.canRamNum == 3)
    {
        FlexCanReg_SetMsgBufInterruptFlagReg(obj->flexCanReg, 2, 0xFFFFFFFF);
        FlexCanReg_SetMsgBufInterruptFlagReg(obj->flexCanReg, 1, 0xFFFFFFFF);
        FlexCanReg_SetMsgBufInterruptFlagReg(obj->flexCanReg, 0, 0xFFFFFFFF);
    }
    else if(obj->initHwPar.canRamNum == 2)
    {
        FlexCanReg_SetMsgBufInterruptFlagReg(obj->flexCanReg, 1, 0xFFFFFFFF);
        FlexCanReg_SetMsgBufInterruptFlagReg(obj->flexCanReg, 0, 0xFFFFFFFF);
    }
    else if(obj->initHwPar.canRamNum == 1)
    {
        FlexCanReg_SetMsgBufInterruptFlagReg(obj->flexCanReg, 0, 0xFFFFFFFF);
    }
}

void FlexCanDrv_EnableMsgObjInterrupt(FlexCanDrvType *obj, FlexCanDrv_MsgObjType *msgObj)
{
    uint32_t temp, temp1 = 0;
    temp1 = msgObj->msgBufId % 32;
    temp  = 0x00000001 << temp1;
    if(msgObj->msgBufId > 63)
    {
        FlexCanReg_SetMsgBufInterruptEnable(obj->flexCanReg, 2, temp);
    }
    else if(msgObj->msgBufId > 31)
    {
        FlexCanReg_SetMsgBufInterruptEnable(obj->flexCanReg, 1, temp);
    }
    else
    {
        FlexCanReg_SetMsgBufInterruptEnable(obj->flexCanReg, 0, temp);
    }
}

void FlexCanDrv_DisableMsgObjInterrupt(FlexCanDrvType *obj, FlexCanDrv_MsgObjType *msgObj)
{
    uint32_t temp, temp1 = 0;
    temp1 = msgObj->msgBufId % 32;
    temp  = ~(0x00000001 << temp1);
    if(msgObj->msgBufId > 63)
    {
        FlexCanReg_SetMsgBufInterruptDisable(obj->flexCanReg, 2, temp);
    }
    else if(msgObj->msgBufId > 31)
    {
        FlexCanReg_SetMsgBufInterruptDisable(obj->flexCanReg, 1, temp);
    }
    else
    {
        FlexCanReg_SetMsgBufInterruptDisable(obj->flexCanReg, 0, temp);
    }
}

FlexCanDrv_NodeStatType FlexCanDrv_GetNodeStatus(FlexCanDrvType *obj)
{
    FlexCanDrv_NodeStatType ret;

    if(FlexCanReg_GetTxState(obj->flexCanReg))
    {
        ret = FLEXCANDRV_NODESTAT_TRANSMITTING;
    }
    else if(FlexCanReg_GetRxState(obj->flexCanReg))
    {
        ret = FLEXCANDRV_NODESTAT_RECEIVING;
    }
    else if(FlexCanReg_GetIdleState(obj->flexCanReg))
    {
        ret = FLEXCANDRV_NODESTAT_IDLE;
    }
    else
    {
    }

    return ret;
}

void FlexCanDrv_GetErrorStates(FlexCanDrvType *obj, FlexCanDrv_ErrStsType *errSts)
{
    uint8_t tempSts;

    tempSts = FlexCanReg_GetFltConfinementState(obj->flexCanReg);
    if(tempSts & 0x2)
    {
        errSts->fltConfSts = FLEXCANDRV_FLTCONFSTS_BUSOFF;
    }
    else
    {
        errSts->fltConfSts = (FlexCanDrv_FltConfStsType)tempSts;
    }

    errSts->bit1Err  = FlexCanReg_GetBit1Error(obj->flexCanReg);
    errSts->bit0Err  = FlexCanReg_GetBit0Error(obj->flexCanReg);
    errSts->ackErr   = FlexCanReg_GetAckError(obj->flexCanReg);
    errSts->crcErr   = FlexCanReg_GetCrcError(obj->flexCanReg);
    errSts->formErr  = FlexCanReg_GetFormError(obj->flexCanReg);
    errSts->stuffErr = FlexCanReg_GetStuffError(obj->flexCanReg);
#if FEATURE_CAN_FD_INSTANCE_NUM == 1 && FEATURE_CAN_2_0_INSTANCE_NUM == 0
    errSts->bit1FastErr  = FlexCanReg_GetBit1FastError(obj->flexCanReg);
    errSts->bit0FastErr  = FlexCanReg_GetBit0FastError(obj->flexCanReg);
    errSts->crcFastErr   = FlexCanReg_GetCrcFastError(obj->flexCanReg);
    errSts->formFastErr  = FlexCanReg_GetFormFastError(obj->flexCanReg);
    errSts->stuffFastErr = FlexCanReg_GetStuffFastError(obj->flexCanReg);
#endif
}

uint8_t FlexCanDrv_AbortPendingTxMsg(FlexCanDrvType *obj, FlexCanDrv_MsgObjType *msgObj)
{
    uint32_t         *msgBufPtr;
    volatile uint32_t msgBufCode;
    uint8_t           ret;

    /* disable message buffer interrupt */
    FlexCanDrv_DisableMsgObjInterrupt(obj, msgObj);

    /* clear message buffer flag */
    FlexCanDrv_ClearMsgObjFlag(obj, msgObj);

    /* get current message buffer start address */
    msgBufPtr = (uint32_t *)FlexCanDrv_GetMsgBufStartAddr(obj, msgObj->msgBufId);
    /* clear message buffer CS.CODE field */
    *msgBufPtr &= (~FLEXCANDRV_MSG_BUF_CODE_MASK << FLEXCANDRV_MSG_BUF_CODE_SHIFT);
    /* set the message buffer code as TX ABORT */
    *msgBufPtr |= (((uint32_t)FLEXCANDRV_MSG_BUF_CODE_ABORT) << FLEXCANDRV_MSG_BUF_CODE_SHIFT);

    /* wait until message buffer flag assert */
    while(FlexCanDrv_GetMsgObjFlag(obj, msgObj) == 0)
    {
    }

    msgBufCode = (*msgBufPtr >> FLEXCANDRV_MSG_BUF_CODE_SHIFT) & FLEXCANDRV_MSG_BUF_CODE_MASK;

    /* check the tx pending message abort result */
    if(msgBufCode == FLEXCANDRV_MSG_BUF_CODE_ABORT)
    {
        ret = TX_PENDING_ABORT_SUCCESS;
    }
    else if(msgBufCode == FLEXCANDRV_MSG_BUF_CODE_TX_INACTIVE)
    {
        ret = TX_PENDING_ABORT_NO_TRANSFER;
    }
    else
    {
        ret = TX_PENDING_ABORT_FAILED;
    }

    /* clear message buffer flag */
    FlexCanDrv_ClearMsgObjFlag(obj, msgObj);

    return ret;
}

void FlexCanDrv_BitTimingCalc(FlexCanDrv_BitTimingType *bitTimingPara,
                              uint32_t                  moduleFreq,
                              uint32_t                  baudrate,
                              uint16_t                  samplePoint,
                              uint16_t                  sjw,
                              uint8_t                   isFd)
{
    int32_t maxPrescaler, maxTseg1, minTseg1, maxTseg2, maxNumTq, minNumTq;
    int32_t tempPrescaler, tempSjw, tempTseg1, tempNumTq;
    float   bestErr = 0;

    /* set default best value for all bit timing parameters */
    int32_t bestPrescaler = 1, bestSjw = 1, bestNumTq = 8, bestTseg1 = 3, bestTseg2 = 2;

    /* set the optimal value for baudrate error, number of time quatna */
    const int32_t optNumTq          = 20;
    const float   optBaudrateErr    = 0.1;
    const float   optSamplePntToler = 0.25;

    /* set the maximum & minimum value for all bit timing parameters */
    if(isFd == false)
    {
        maxPrescaler = FLEXCAN_CBT_EPRESDIV_MASK >> FLEXCAN_CBT_EPRESDIV_SHIFT;
        maxTseg1     = (FLEXCAN_CBT_EPROPSEG_MASK >> FLEXCAN_CBT_EPROPSEG_SHIFT) + (FLEXCAN_CBT_EPSEG1_MASK >> FLEXCAN_CBT_EPSEG1_SHIFT);
        minTseg1     = 3;
        maxTseg2     = FLEXCAN_CBT_EPSEG2_MASK >> FLEXCAN_CBT_EPSEG2_SHIFT;

        maxNumTq = maxTseg1 + maxTseg2 + 1;
        minNumTq = 8;
    }
    else
    {
        maxPrescaler = FLEXCAN_FDCBT_FPRESDIV_MASK >> FLEXCAN_FDCBT_FPRESDIV_SHIFT;
        maxTseg1     = (FLEXCAN_FDCBT_FPROPSEG_MASK >> FLEXCAN_FDCBT_FPROPSEG_SHIFT) + (FLEXCAN_FDCBT_FPSEG1_MASK >> FLEXCAN_FDCBT_FPSEG1_SHIFT);
        minTseg1     = 3;
        maxTseg2     = FLEXCAN_FDCBT_FPSEG2_MASK >> FLEXCAN_FDCBT_FPSEG2_SHIFT;

        maxNumTq = maxTseg1 + maxTseg2 + 1;
        minNumTq = 8;
    }

    /* calculate prescaler, number of Tq */
    bestErr = (float)baudrate;
    for(tempPrescaler = 1; tempPrescaler <= maxPrescaler; tempPrescaler++)
    {
        /* get the quanta frequency */
        float Fq = (float)moduleFreq / tempPrescaler;
        /* get the number of time quanta */
        tempNumTq = (int32_t)(Fq / baudrate);

        /* To avoid division by 0 and number of Tq less than minmum number of Tq */
        if((tempNumTq == 0) || (tempNumTq < minNumTq))
        {
            break;
        }

        /* calculate temp baudrate */
        float tempBaudrate = Fq / tempNumTq;
        /* calculate temp baudrate error */
        float tempErr = ((tempBaudrate - baudrate) > 0) ? (tempBaudrate - baudrate) : (baudrate - tempBaudrate);

        if((tempNumTq <= maxNumTq) && (tempErr <= bestErr))
        {
            /* set temporary prescaler, number of Tq, baudrate error as the best value */
            bestPrescaler = tempPrescaler;
            bestNumTq     = tempNumTq;
            bestErr       = tempErr;

            /* check if get the optimal condition */
            if((tempNumTq <= optNumTq) && tempErr < optBaudrateErr)
            {
                break;
            }
        }
    }

    /* calculate sample point (time segment 1) */
    bestErr = samplePoint * optSamplePntToler;

    /* saturate max time segment 1 value */
    if(bestNumTq < maxTseg1)
    {
        maxTseg1 = bestNumTq;
    }
    for(tempTseg1 = maxTseg1; tempTseg1 >= minTseg1; tempTseg1--)
    {
        /* get the temporary sample point */
        int32_t tempSamplePnt = ((tempTseg1 + 1) * 10000) / bestNumTq;
        /* get the temporary sample point error */
        int32_t tempErr = (tempSamplePnt - samplePoint) > 0 ? (tempSamplePnt - samplePoint) : (samplePoint - tempSamplePnt);

        /* break the for loop if the temporary sample point less
           than the user required sample point */
        if(tempSamplePnt < samplePoint)
        {
            break;
        }

        /* update the best T_seg1 and best error if met the tolerance */
        if(tempErr < bestErr)
        {
            bestTseg1 = tempTseg1;
            bestErr   = tempErr;
        }
    }

    /* calculate time segment 2 */
    bestTseg2 = bestNumTq - bestTseg1 - 1;

    /* calculate best SJW */
    bestErr = 10000;
    for(tempSjw = 1; tempSjw <= bestTseg2; tempSjw++)
    {
        /* get the temporary synchronize jump width percentage */
        int32_t tempSjwPercent = (tempSjw * 10000) / bestNumTq;
        int32_t tempErr        = ((tempSjwPercent - sjw) > 0) ? (tempSjwPercent - sjw) : (sjw - tempSjwPercent);

        /*  */
        if(tempErr < bestErr)
        {
            bestSjw = tempSjw;
            bestErr = tempErr;
        }
    }

    bitTimingPara->prescalerDiv    = bestPrescaler;
    bitTimingPara->propSeg         = (uint32_t)(bestTseg1 * 0.6);
    bitTimingPara->phaseSeg1       = bestTseg1 - bitTimingPara->propSeg;
    bitTimingPara->phaseSeg2       = bestTseg2;
    bitTimingPara->resyncJumpWidth = bestSjw;
}

void FlexCanDrv_SetRxFifoFlitElement(FlexCanDrvType *obj, uint8_t filtEleId, bool isExtId, uint32_t filtId)
{
    /* get the rx fifo filter format mode */
    FlexCanDrv_RxFifoFiltFormatType filtFormat = (FlexCanDrv_RxFifoFiltFormatType)FlexCanReg_GetRxFifoFiltMode(obj->flexCanReg);
    /* get the rx fifo filter table start address */
    uint32_t *rxFifoFiltTable = (uint32_t *)(((uint32_t)obj->flexCanReg) + RX_FIFO_FILT_TABLE_ADD_OFFSET);
    uint32_t temp = 0;
    uint8_t tempFiltEleId = 0;
    uint8_t tempOffset = 0;


    if(filtFormat == FLEXCANDRV_RXFIFO_FILTFORMAT_A)
    {
        /* clear the rx fifo filter element */
        rxFifoFiltTable[filtEleId] = 0;
        /* set the rx fifo filter element bit 1 to 29 as filter ID */
        rxFifoFiltTable[filtEleId] |= filtId << (isExtId ? 1 : 19);
        /* set the rx fifo filter element bit 30 as IDE */
        rxFifoFiltTable[filtEleId] |= ((uint32_t)isExtId) << 30;
    }
    else if (filtFormat == FLEXCANDRV_RXFIFO_FILTFORMAT_B)
    {
    	/* get the filter element ID and offset counter based on 16-bit width */
        tempFiltEleId = filtEleId / 2;
        tempOffset = (filtEleId % 2) * 16 ;

        /* get the existing value from the current two filter element */
        temp = rxFifoFiltTable[tempFiltEleId];
        /* clear the target filter element 16-bit */
        temp &= ~(0xFFFFUL << tempOffset);

        /* set the ID to the filter element */
        temp |= ((isExtId ? (filtId >> 15) : (filtId << 3)) << tempOffset);
        /* set the IDE bit to the filter element */
        temp |= (((uint32_t)isExtId) << 14 << tempOffset);

        /* set the 16-bit value to the target filter element ID */
        rxFifoFiltTable[tempFiltEleId] = temp;
    }
    else if (filtFormat == FLEXCANDRV_RXFIFO_FILTFORMAT_C)
    {
    	/* get the filter element ID and offset counter based on 8-bit width */
        tempFiltEleId = filtEleId / 4;
        tempOffset = (filtEleId % 4) * 8 ;

        /* get the existing value from the current 4 filter element */
        temp = rxFifoFiltTable[tempFiltEleId];
        /* clear the target filter element 8-bit */
        temp &= ~(0xFFUL << tempOffset);

        /* set the first rx filter Id to rx fifo filter element */
        temp |= ((isExtId ? (filtId >> 21) : (filtId >> 3)) << tempOffset);

        /* set the 8-bit value to the target filter element ID */
        rxFifoFiltTable[tempFiltEleId] = temp;
    }
    else
    {

    }
}
