/*!*****************************************************************************
  \file     ucrc32.c                                                            
                                                                                
  \remarks  API for calculation of CRC-32 with uCRC-Module of ARM M3            
                                                                                
  \author   Stefan Büchner, M&P GmbH 2013                                       
  \date     April 2013                                                          
********************************************************************************/

#include "basetype.h"
#include "inc/hw_ucrc.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "ucrc32.h"

/*******************************************************************************
 *  local definitions                                                           
 *******************************************************************************/

//! Remap address for CRC calculation
#define UCRC_REMAP_ADDRESS(x)    ((unsigned long) x | 0x04000000)

//! CRC type defined in UCRC Config Register: CRC32 (Polynomial = 0x04C11DB7)
#define UCRC_CONFIG_CRC32         0x00000003L
//! Initial value
#define UCRC32_INITIAL_VALUE      0xFFFFFFFFL
//! Initial value
#define UCRC32_FINAL_XOR_VALUE    0xFFFFFFFFL
//! Reflect data bytes
#define UCRC32_REFLECT_DATA_BYTES         TRUE
//! Reflect CRC result before Final XOR
#define UCRC32_REFLECT_BEFORE_FINAL_XOR   TRUE

/*******************************************************************************
 *  private function prototypes - called intern only                            
 *******************************************************************************/
static inline UINT32 crc_reflect(UINT32 data, UINT8 data_len);

/*******************************************************************************
 *  public functions                                                            
 *******************************************************************************/

/*******************************************************************************
* \fn void UCRC32_Reset(void)                                                   
********************************************************************************/
/*!\param     -                                                                 
*                                                                               
*  \remarks   Initialize the uCRC-Module for calculation of CRC-32, set the     
*             initial value                                                     
*                                                                               
*  \retval    -                                                                 
********************************************************************************/
void UCRC32_Reset(void)
{
  // Config the uCRC Module to CRC-32 polynom.
  HWREG(UCRC_BASE + UCRC_O_CONFIG) = UCRC_CONFIG_CRC32;
  // Set the result register to initiall value.
  HWREG(UCRC_BASE + UCRC_O_RES) = UCRC32_INITIAL_VALUE;
}

/*******************************************************************************
* \fn void UCRC32_Update(UINT8* pData, UINT32 numBytes)                         
********************************************************************************/
/*!\param     pData - pointer to data which has to be enumerated                
*  \param     numBytwes - Number of bytes to be enumerated
*                                                                               
*  \remarks   Update the crc value in the UCRC result register with new data    
*                                                                               
*  \retval    -                                                                 
********************************************************************************/
void UCRC32_Update(UINT8* pData, UINT32 numBytes)
{
  UINT32 ulTemp;
  UINT8 byteBuf;
  UINT8* pByteBuf;

  // uCRC only calculates on reads from a re-mapped space of memory,
  // so we need to remap the address before we start calculation.
  pByteBuf = (unsigned char *)UCRC_REMAP_ADDRESS(&byteBuf);

  // Read the buffer while calculating CRC
  while(numBytes--)
  {
  #ifdef UCRC32_REFLECT_DATA_BYTES
    // reflected data byte
    byteBuf = crc_reflect(*pData++, 8);
  #else
    // plain data byte
    byteBuf = *pData++;
  #endif
    ulTemp = HWREGB(pByteBuf);
  }
  ulTemp = ulTemp; // prevent compiler warning
}


/*******************************************************************************
* \fn UINT32 UCRC32_Finalize(void)                                              
********************************************************************************/
/*!\param     -                                                                 
*                                                                               
*  \remarks   Calculate the final crc-32 value                                  
*                                                                               
*  \retval    CRC-32 of enumerated data used by the Update-function             
********************************************************************************/
UINT32 UCRC32_Finalize(void)
{
  UINT32 crc32;
  
#ifdef UCRC32_REFLECT_BEFORE_FINAL_XOR
  // reflected result from uCRC result register
  crc32 = crc_reflect(HWREG(UCRC_BASE + UCRC_O_RES), 32);
#else
  // plain result from uCRC result register
  crc32 = HWREG(UCRC_BASE + UCRC_O_RES);
#endif
  // XOR the result with 
  return (crc32 ^ UCRC32_FINAL_XOR_VALUE) & 0xffffffff;
}

/*******************************************************************************
 *  private functions                                                           
 *******************************************************************************/

/*******************************************************************************
* \fn static inline UINT32 crc_reflect(UINT32 data, UINT8 data_len)             
********************************************************************************/
/*!\param     data      - The data word to be reflected.                        
*  \param     data_len  - The width of \a data expressed in number of bits.     
*                                                                               
*  \remarks   Reflect all bits of a data word of data_len bytes.                
*                                                                               
*  \retval    the reflected data                                                
********************************************************************************/
static inline UINT32 crc_reflect(UINT32 data, UINT8 data_len)
{
  UINT8 i;
  UINT32 ret;

  ret = data & 0x01;
  for (i = 1; i < data_len; i++)
  {
    data >>= 1;
    ret = (ret << 1) | (data & 0x01);
  }
  return ret;
}
