/*
 * crc_core.c
 *
 *  Created on: 27/04/2021
 *      Author: alexrayne <alexraynepe196@gmail.com>
 * ------------------------------------------------------------------------------------
 *  RA CRC-core support
 */

#include "crc_core.h"
#include "mcu_crc.h"
#include "bsp_api.h"

//------------------------------------------------------------------------------
#if !defined(ASSERT_BSP)

#if !defined(RELEASE)
#include <assert.h>
#define ASSERT(...) assert(__VA_ARGS__)
#else
#define ASSERT(...)
#endif

#else
#define ASSERT(...) ASSERT_BSP(__VA_ARGS__)
#endif



static
void crc_core_append8(const void* data, unsigned datalen){
    const uint8_t* ps = (const uint8_t*)data;

#if 1
    // copy head bytes until aligned to Word32
    while (((uintptr_t)ps & 3) != 0){
        R_CRC->CRCDIR_BY = *ps++;
        --datalen;
        if (datalen == 0)
            return;
    }

    // this shuld be faster then simple bytes loop
    const uint32_t* pd = (const uint32_t*)ps;
    for (; datalen >=4 ; datalen -= 4 ) {
        uint32_t d = *pd++;
        R_CRC->CRCDIR_BY = (uint8_t)d;
        d = d >> 8;
        R_CRC->CRCDIR_BY = (uint8_t)d;
        d = d >> 8;
        R_CRC->CRCDIR_BY = (uint8_t)d;
        d = d >> 8;
        R_CRC->CRCDIR_BY = (uint8_t)d;
    }

    ps = (const uint8_t*)pd;
#endif

    while ( datalen > 0){
        R_CRC->CRCDIR_BY = *ps++;
        --datalen;
    }
}

unsigned short crc_core_append16(const void* data, unsigned datalen){
    crc_core_append8(data, datalen);
    return R_CRC->CRCDOR_HA;
}

unsigned long crc_core_append32(const void* data, unsigned datalen){

    ASSERT( (((uintptr_t)data) & 3) == 0 );
    ASSERT( (datalen & 3) == 0 );

    const uint32_t* pd = (const uint32_t*)data;
    for (; datalen >=4 ; datalen -= 4 ) {
        uint32_t d = *pd++;
        R_CRC->CRCDIR = d;
    }

    return R_CRC->CRCDOR;
}




/**
 * \brief      Calculate the CRC16 over a data area
 * \param data Pointer to the data
 * \param datalen The length of the data
 * \param acc  The accumulated CRC that is to be updated (or zero).
 * \return     The CRC16 checksum.
 *
 *             This function calculates the CRC16 checksum of a data area.
 *
 *             \note The algorithm used in this implementation is
 *             tailored for a running checksum and does not perform as
 *             well as a table-driven algorithm when checksumming an
 *             entire data block.
 */
// Poly  : 0x1021    x^16 + x^12 + x^5 + 1
unsigned short crc_core_16ccitt_lsb(const void* data, int datalen, unsigned short acc)
{
    R_CRC->CRCCR0       = CRC_LSB | CRCPOLY_CRC16CCITT;
    R_CRC->CRCDOR_HA    = acc;
    return crc_core_append16(data, datalen);
}

unsigned short crc_core_16ccitt_msb(const void* data, int datalen, unsigned short acc)
{
    R_CRC->CRCCR0       = CRC_MSB | CRCPOLY_CRC16CCITT;
    R_CRC->CRCDOR_HA    = acc;
    return crc_core_append16(data, datalen);
}

// Poly  : 0x8005    x^16 + x^15 + x^2 + 1
unsigned short crc_core_16ansi_lsb(const void* data, int datalen, unsigned short acc){
    R_CRC->CRCCR0       = CRC_LSB | CRCPOLY_CRC16;
    R_CRC->CRCDOR_HA    = acc;
    return crc_core_append16(data, datalen);
}

unsigned short crc_core_16ansi_msb(const void* data, int datalen, unsigned short acc){
    R_CRC->CRCCR0       = CRC_MSB | CRCPOLY_CRC16;
    R_CRC->CRCDOR_HA    = acc;
    return crc_core_append16(data, datalen);
}


unsigned char crc_core8(const void* data, unsigned datalen, unsigned char acc){
    R_CRC->CRCCR0       = CRC_MSB | CRCPOLY_CRC8;
    R_CRC->CRCDOR_BY    = acc;
    crc_core_append8(data, datalen);
    return R_CRC->CRCDOR_BY;
}

unsigned long crc_core32_msb(const void* data, unsigned datalen, unsigned long acc){
    R_CRC->CRCCR0       = CRC_MSB | CRCPOLY_CRC32;
    R_CRC->CRCDOR       = acc;
    return crc_core_append32(data, datalen);
}

unsigned long crc_core32_lsb(const void* data, unsigned datalen, unsigned long acc){
    R_CRC->CRCCR0       = CRC_LSB | CRCPOLY_CRC32;
    R_CRC->CRCDOR       = acc;
    return crc_core_append32(data, datalen);
}

unsigned long crc_core_32ccitt_lsb(const void* data, unsigned datalen, unsigned long acc){
    return ~crc_core32_lsb(data, datalen, ~acc);
}

unsigned long crc_core_32ccitt_msb(const void* data, unsigned datalen, unsigned long acc){
    return ~crc_core32_msb(data, datalen, ~acc);
}

