/******************************************************************************
 * @file     crc.c
 * @brief    CRC firmware functions.
 * @version  V4.0.0
 * @date     30-July-2026
 *
 * @note
 * Copyright (C) 2022 Spintrol Electronic Technology (Shanghai) Co., Ltd.. All rights reserved.
 *
 * @attention
 * THIS SOFTWARE JUST PROVIDES CUSTOMERS WITH CODING INFORMATION REGARDING
 * THEIR PRODUCTS, WHICH AIMS AT SAVING TIME FOR THEM. SPINTROL SHALL NOT BE
 * LIABLE FOR THE USE OF THE SOFTWARE. SPINTROL DOES NOT GUARANTEE THE
 * CORRECTNESS OF THIS SOFTWARE AND RESERVES THE RIGHT TO MODIFY THE SOFTWARE
 * WITHOUT NOTIFICATION.
 *
 ******************************************************************************/

#include "crc.h"


#if defined(__CC_ARM)
    #pragma push
    #pragma O1
#endif




/******************************************************************************
 * @brief      Calculate CRC value of the specified data
 *
 * @param[in]  CRCx      :  Select the CRC module
 * @param[in]  pu8DataStr:  Pointer to the input data stream
 * @param[in]  u32DataLen:  Data length in byte
 *
 * @return     CRC result
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
uint32_t CRC_Calculate(CRC_REGS *CRCx, const uint8_t *pu8DataStr, uint32_t u32DataLen)
{
    uint32_t cnt;
    uint32_t divisor, tail;

    /* Set stream data length */
    CRC_SetStreamLen(CRCx, u32DataLen);

    /* Padding first and copy data in */
    divisor = u32DataLen / 4;
    tail    = u32DataLen % 4;

    switch (tail)
    {
        case 1:
            CRCx->CRCSTRIN.all = (((uint32_t)pu8DataStr[0]) << 24);
            break;

        case 2:
            CRCx->CRCSTRIN.all = ((((uint32_t)pu8DataStr[0]) << 16) |
                                  (((uint32_t)pu8DataStr[1]) << 24));
            break;

        case 3:
            CRCx->CRCSTRIN.all = ((((uint32_t)pu8DataStr[0]) << 8)  |
                                  (((uint32_t)pu8DataStr[1]) << 16) |
                                  (((uint32_t)pu8DataStr[2]) << 24));
            break;

        default:
            break;
    }

    /* Compose last data from byte to word and copy data in  */
    for (cnt = 0; cnt < divisor; cnt++)
    {
        CRCx->CRCSTRIN.all = (((uint32_t)pu8DataStr[4 * cnt + tail    ])        |
                              (((uint32_t)pu8DataStr[4 * cnt + tail + 1]) << 8)  |
                              (((uint32_t)pu8DataStr[4 * cnt + tail + 2]) << 16) |
                              (((uint32_t)pu8DataStr[4 * cnt + tail + 3]) << 24));
    }

    /* Wait for the result */
    while (CRC_IsCalculateDone(CRCx) == 0);

    /* Clear interrupt flag */
    CRC_ClearInt(CRCx);

    /* Return the result */
    return CRC_GetResult(CRCx);
}




/********************************************************************************
 * @brief      When Init Value is not 0, 
 *             make input data stream and feed data in CRC,
 *             must first feed u32FrameHead to generate special Init Value
 *
 * @param[in]  CRCx         :  Select the CRC module
 * @param[in]  pu8DataStr   :  input data stream
 * @param[in]  u32DataLen   :  data length in byte
 * @param[in]  u32FrameHead :  Feed u32FrameHead first to generate special Init Value
 *                             For CRC8,  low 3 bytes of u32FrameHead is always 0x00, 
 *                             like 0xXX000000
 *                             For CRC16, low 2 bytes of u32FrameHead is always 0x00,
 *                             like 0xXXXX0000
 *                             CRC32, none byte of u32FrameHead is always 0x00,
 *                             like 0xXXXXXXXX
 * @param[in]  u32Result    :  CRC result
 *
 * @return     SUCCESS or ERROR
 *
 *******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
ErrorStatus CRC_CalculateWithInitValueIsNotZero(CRC_REGS *CRCx, const uint8_t *pu8DataStr, 
    uint32_t u32DataLen, uint32_t u32FrameHead, uint32_t *u32Result)
{
    volatile uint32_t u32TimeOut;
    uint32_t cnt;
    uint32_t divisor, tail;

    /* Set stream data length */
    CRC_SetStreamLen(CRCx, (u32DataLen + 4));

    /* Padding first and copy data in */
    divisor = u32DataLen / 4;
    tail    = u32DataLen % 4;

    switch (tail)
    {
        case 1:
            CRC_FeedData(CRCx, u32FrameHead << 24);
            CRC_FeedData(CRCx, ((u32FrameHead >> 8)
                               | (((uint32_t)pu8DataStr[0]) << 24)));
            break;

        case 2:
            CRC_FeedData(CRCx, u32FrameHead << 16);
            CRC_FeedData(CRCx, ((u32FrameHead >> 16)
                               | (((uint32_t)pu8DataStr[0]) << 16) 
                               | (((uint32_t)pu8DataStr[1]) << 24)));
            break;

        case 3:
            CRC_FeedData(CRCx, u32FrameHead << 8);
            CRC_FeedData(CRCx, ((u32FrameHead >> 24)
                               | (((uint32_t)pu8DataStr[0]) << 8)  
                               | (((uint32_t)pu8DataStr[1]) << 16) 
                               | (((uint32_t)pu8DataStr[2]) << 24)));
            break;

        default:
            CRC_FeedData(CRCx, u32FrameHead);
            break;
    }

    /* Compose last data from byte to word and copy data in  */
    for (cnt = 0; cnt < divisor; cnt++)
    {
        CRCx->CRCSTRIN.all = (((uint32_t)pu8DataStr[4 * cnt + tail    ])        |
                              (((uint32_t)pu8DataStr[4 * cnt + tail + 1]) << 8)  |
                              (((uint32_t)pu8DataStr[4 * cnt + tail + 2]) << 16) |
                              (((uint32_t)pu8DataStr[4 * cnt + tail + 3]) << 24));
    }

    /* Wait for the result */
    for (u32TimeOut = 0xFFFFFFFFUL; u32TimeOut > 0; u32TimeOut--)
    {
        if (CRC_IsCalculateDone(CRCx))
        {
            break;
        }
    }
    
    /* Clear interrupt flag */
    CRC_ClearInt(CRCx);
    
    /* Save the result */
    *u32Result = CRC_GetResult(CRCx);

    if (u32TimeOut == 0)
    {
        return ERROR;
    }
    
    return SUCCESS;
}




/******************************************************************************
 * @brief      Feed data stream to CRC input register
 *
 * @param[in]  CRCx      :  Select the CRC module
 * @param[in]  pu8DataStr:  Pointer to the input data stream
 * @param[in]  u32DataLen:  Data length in byte
 *
 * @return     none
 *
 ******************************************************************************/
#if defined(__ICCARM__)
#pragma optimize=low
#endif
void CRC_FeedStream(CRC_REGS *CRCx, const uint8_t *pu8DataStr, uint32_t u32DataLen)
{
    uint32_t cnt;
    uint32_t divisor, tail;

    /* Padding first and copy data in */
    divisor = u32DataLen / 4;
    tail    = u32DataLen % 4;

    switch (tail)
    {
        case 1:
            CRCx->CRCSTRIN.all = (((uint32_t)pu8DataStr[0]) << 24);
            break;

        case 2:
            CRCx->CRCSTRIN.all = ((((uint32_t)pu8DataStr[0]) << 16) |
                                  (((uint32_t)pu8DataStr[1]) << 24));
            break;

        case 3:
            CRCx->CRCSTRIN.all = ((((uint32_t)pu8DataStr[0]) << 8)  |
                                  (((uint32_t)pu8DataStr[1]) << 16) |
                                  (((uint32_t)pu8DataStr[2]) << 24));
            break;

        default:
            break;
    }

    /* Compose last data from byte to word and copy data in  */
    for (cnt = 0; cnt < divisor; cnt++)
    {
        CRCx->CRCSTRIN.all = (((uint32_t)pu8DataStr[4 * cnt + tail    ])        |
                              (((uint32_t)pu8DataStr[4 * cnt + tail + 1]) << 8)  |
                              (((uint32_t)pu8DataStr[4 * cnt + tail + 2]) << 16) |
                              (((uint32_t)pu8DataStr[4 * cnt + tail + 3]) << 24));
    }
}


#if defined(__CC_ARM)
    #pragma pop
#endif


/******************* Copyright (C) 2022 Spintrol Electronic Technology (Shanghai) Co., Ltd. ***** END OF FILE ****/
