8.2.4.1.4 CRC Algorithms
Hexmate has several algorithms that implement the robust cyclic redundancy checks (CRC). There is a choice of two algorithms that correspond to the selector values 5 and -5 in the algorithm suboption and that implement a CRC calculation and reflected CRC calculation, respectively. The reflected algorithm works on the least significant bit of the data first.
The polynomial to be used and the initial value can be specified in the option. Hexmate will automatically store the CRC result in the HEX file at the address specified in the checksum option.
Some devices implement a CRC module in hardware that can be used to
calculate a CRC at runtime. These modules can stream data read from program memory using
a Scanner module. To ensure that the order of the bytes processed by Hexmate and the
CRC/Scanner module are identical, you must specify a reverse word width of 2, which will
read each 2-byte word in the HEX file in order, but process the bytes within those words
in reverse order. When using
the MPLAB XC8 compiler, use the suboption revword=2
When
running Hexmate explicitly, use the r2
suboption to
-ck
.
The function shown below can be customized to work with any result width
(result_t
). It calculates a CRC hash value using the polynomial
specified by the POLYNOMIAL
macro.
#include <stdint.h>
typedef uint16_t result_t; // size of CRC result
#define POLYNOMIAL 0x1021
#define WIDTH (8 * sizeof(result_t))
#define MSb ((result_t)1 << (WIDTH - 1))
result_t
crc(const unsigned char * data, unsigned n, result_t remainder) {
unsigned pos;
unsigned char bitp;
for (pos = 0; pos != n; pos++) {
remainder ^= ((result_t)data[pos] << (WIDTH - 8));
for (bitp = 8; bitp > 0; bitp--) {
if (remainder & MSb) {
remainder = (remainder << 1) ^ POLYNOMIAL;
} else {
remainder <<= 1;
}
}
}
return remainder;
}
The result_t
type definition should be adjusted to
suit the result width. When using
MPLAB XC8 and for a size of 1, use a char
type; for a size of 4,
use a long
type, etc.
Here is how this function might be used when, for example, a 2-byte-wide CRC hash value is to be calculated values over the address range 0x0 to 0xFF, starting with an initial value of 0xFFFF. The result is to be stored at 0x100 and 0x101 in little endian format.
=
in the
Checksum field in the Additional
options Option category in the XC8
Linker
category.-mchecksum=0-FF@100,offset=0xFFFF,algorithm=5,width=-2,polynomial=0x1021
-ck=0-ff@100+ffffg5w-2p1021
Adapt the following MPLAB XC8 code snippet for PIC devices, which calls
crc()
and compares the runtime hash result with that stored by Hexmate at compile time.
extern const unsigned char ck_range[0x100] __at(0x0);
extern const result_t hexmate __at(0x100);
result_t result;
result = crc(ck_range, sizeof(ck_range), 0xFFFF);
if(result != hexmate){
// something’s not right, take appropriate action
ck_failure();
}
// data verifies okay, continue with the program
The reflected CRC result can be calculated by reflecting the input data
and final result, or by reflecting the polynomial. The functions shown below can be
customized to work with any result width (result_t
). The
crc_reflected_IO()
function calculates a reflected CRC hash value by
reflecting the data stream bit positions. Alternatively, the
crc_reflected_poly()
function does not adjust the data stream but
reflects instead the polynomial, which in both functions is specified by the
POLYNOMIAL
macro. Both functions use the reflect()
function to perform bit reflection.
#include <stdint.h>
typedef uint16_t result_t; // size of CRC result
typedef unsigned char read_t;
typedef unsigned int reflectWidth;
// This is the polynomial used by the CRC-16 algorithm we are using.
#define POLYNOMIAL 0x1021
#define WIDTH (8 * sizeof(result_t))
#define MSb ((result_t)1 << (WIDTH - 1))
#define LSb (1)
#define REFLECT_DATA(X) ((read_t) reflect((X), 8))
#define REFLECT_REMAINDER(X) (reflect((X), WIDTH))
reflectWidth
reflect(reflectWidth data, unsigned char nBits)
{
reflectWidth reflection = 0;
reflectWidth reflectMask = (reflectWidth)1 << nBits - 1;
unsigned char bitp;
for (bitp = 0; bitp != nBits; bitp++) {
if (data & 0x01) {
reflection |= reflectMask;
}
data >>= 1;
reflectMask >>= 1;
}
return reflection;
}
result_t
crc_reflected_IO(const unsigned char * data, unsigned n, result_t remainder) {
unsigned pos;
unsigned char reflected;
unsigned char bitp;
for (pos = 0; pos != n; pos++) {
reflected = REFLECT_DATA(data[pos]);
remainder ^= ((result_t)reflected << (WIDTH - 8));
for (bitp = 8; bitp > 0; bitp--) {
if (remainder & MSb) {
remainder = (remainder << 1) ^ POLYNOMIAL;
} else {
remainder <<= 1;
}
}
}
remainder = REFLECT_REMAINDER(remainder);
return remainder;
}
result_t
crc_reflected_poly(const unsigned char * data, unsigned n, result_t remainder) {
unsigned pos;
unsigned char bitp;
result_t rpoly;
rpoly = reflect(POLYNOMIAL, WIDTH);
for (pos = 0; pos != n; pos++) {
remainder ^= data[pos];
for (bitp = 8; bitp > 0; bitp--) {
if (remainder & LSb) {
remainder = (remainder >> 1) ^ rpoly;
} else {
remainder >>= 1;
}
}
}
return remainder;
}
Here is how this function might be used when, for example, a 2-byte-wide reflected CRC result is to be calculated over the address range 0x0 to 0xFF, starting with an initial value of 0xFFFF. The result is to be stored at 0x100 and 0x101 in little endian format.
=
in the
Checksum field in the Additional
options Option category in the XC8
Linker category. Note the algorithm selected is negative 5 in this
case.-mchecksum=0-FF@100,offset=0xFFFF,algorithm=-5,width=-2,polynomial=0x1021
-ck=0-ff@100+ffffg-5w-2p1021
In your project, call either the crc_reflected_IO()
or
crc_reflected_poly()
functions, as shown previously.