Software CRC Module — Tiva C
- Eslam El Hefny
- Tutorials, Tiva c
- April 16, 2025
Overview
Cyclic Redundancy Check (CRC) is a mathematical algorithm that generates a fixed-size “fingerprint” from any block of data. The TM4C123GH6PM includes a hardware CRC accelerator (part of the AES/CRC module) supporting CRC32 (IEEE 802.3), CRC16-CCITT, and CRC8 in hardware. This chapter covers both the hardware accelerator and pure software implementations for use cases where the hardware module is unavailable.
Beginner Level — What & Why
What is CRC?
CRC is a type of error-detection code. You compute a number (the CRC value) from your data before storing or transmitting it. Later, you compute the CRC again on the received data and compare. If the values differ, the data was corrupted.
Real-World Analogy
CRC is like the check digit on a credit card number. The last digit is calculated from all the others using a mathematical formula. If any digit changes during transmission, the check digit no longer matches and you know something went wrong.
What Problem Does It Solve?
- Detecting flash corruption in firmware update processes
- Verifying EEPROM configuration blocks after power loss
- Packet integrity in communication protocols (Ethernet, CAN, UART framing)
- Safety-critical systems requiring data integrity checks
Key Terms
| Term | Meaning |
|---|---|
| Polynomial | The divisor used in CRC division (defines the algorithm) |
| Seed | Initial value of the CRC register |
| CRC32 | 32-bit CRC, polynomial 0x04C11DB7 (IEEE 802.3 / Ethernet) |
| CRC16-CCITT | 16-bit CRC, polynomial 0x1021 |
| CRC8 | 8-bit CRC, polynomial 0x07 |
| Residue | Expected CRC when re-computing over data+CRC bytes |
Intermediate Level — How It Works
Hardware CRC Accelerator on TM4C123
The TM4C123 includes an AES/CRC module. The CRC block processes data bytes through a hardware shift register:
| Register | Address | Description |
|---|---|---|
| CRCSEED | 0x44004010 |
Seed/initial value |
| CRCDIN | 0x44004014 |
Data input (write 32-bit words) |
| CRCRSLTPP | 0x44004018 |
Result (post-processed/final) |
CRCC register (configuration):
| Bits | Field | Description |
|---|---|---|
| 3:0 | TYPE | CRC type: 0=CRC8, 1=P16, 4=CRC32, 8=TCP |
| 4 | ENDIAN | 0=little-endian, 1=big-endian |
| 5 | BR | Bit-reverse input |
| 8 | OBR | Output bit-reverse |
| 9 | RESINV | Invert result |
| 15:12 | SIZE | Input word size |
| 31:16 | INIT | Initial value when AUTO mode |
TivaWare CRC API
#include "driverlib/crc.h"
/* Enable CRC module clock */
SysCtlPeripheralEnable(SYSCTL_PERIPH_CCM0);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_CCM0));
/* Configure for CRC32 (IEEE 802.3) */
CRCConfigSet(CCM0_BASE,
CRC_CFG_INIT_SEED | // use CRCSEED register
CRC_CFG_TYPE_P32 | // CRC32 polynomial
CRC_CFG_SIZE_8BIT); // process 8-bit at a time
/* Set initial seed */
CRCSeedSet(CCM0_BASE, 0xFFFFFFFF); // standard CRC32 initial value
/* Process data */
uint8_t data[] = {0x31, 0x32, 0x33, 0x34}; // "1234"
uint32_t crc = CRCDataProcess(CCM0_BASE, (uint32_t *)data, 4, false);
/* false = data is byte array, not word array */
/* Final XOR: CRC32 requires XOR with 0xFFFFFFFF */
crc ^= 0xFFFFFFFF;
/* crc now holds the standard CRC32 value */
Software CRC32 Implementation
For systems without the hardware module or when computing CRC on arrays in Flash before enabling the module:
/* CRC32 lookup table (IEEE 802.3 polynomial 0x04C11DB7) */
static const uint32_t crc32_table[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
/* ... full 256-entry table omitted for brevity;
generate with: poly=0xEDB88320 (bit-reversed) */
};
/* Compute CRC32 over a byte array */
uint32_t CRC32_Compute(const uint8_t *data, uint32_t len)
{
uint32_t crc = 0xFFFFFFFF;
while (len--)
{
uint8_t idx = (uint8_t)(crc ^ *data++);
crc = crc32_table[idx] ^ (crc >> 8);
}
return crc ^ 0xFFFFFFFF; // final XOR
}
Software CRC16-CCITT
uint16_t CRC16_CCITT(const uint8_t *data, uint32_t len)
{
uint16_t crc = 0xFFFF; // standard initial value
while (len--)
{
crc ^= ((uint16_t)*data++ << 8);
for (int i = 0; i < 8; i++)
{
if (crc & 0x8000)
crc = (crc << 1) ^ 0x1021;
else
crc <<= 1;
}
}
return crc;
}
Software CRC8
/* CRC8 with polynomial 0x07 (CCITT CRC8) */
uint8_t CRC8_Compute(const uint8_t *data, uint32_t len)
{
uint8_t crc = 0x00;
while (len--)
{
crc ^= *data++;
for (int i = 0; i < 8; i++)
{
if (crc & 0x80)
crc = (crc << 1) ^ 0x07;
else
crc <<= 1;
}
}
return crc;
}
Advanced Level — Deep Dive
Bare-Metal Hardware CRC32
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_ccm.h"
/* CRC32 using hardware accelerator, byte-by-byte input */
uint32_t HW_CRC32(const uint8_t *pData, uint32_t ui32Len)
{
uint32_t i;
/* Enable CCM0 clock */
HWREG(SYSCTL_RCGCCCM) |= (1U << 0);
while (!(HWREG(SYSCTL_PRCCM) & (1U << 0)));
/* Configure: CRC32, byte input, seed mode, no bit-reverse */
HWREG(CCM0_BASE + CCM_O_CRCCTRL) =
CCM_CRCCTRL_TYPE_P32 | // CRC32 polynomial
CCM_CRCCTRL_SIZE_8BIT | // 8-bit (byte) input
CCM_CRCCTRL_INIT_SEED; // use CRCSEED
/* Set standard CRC32 seed */
HWREG(CCM0_BASE + CCM_O_CRCSEED) = 0xFFFFFFFF;
/* Feed bytes */
for (i = 0; i < ui32Len; i++)
HWREG(CCM0_BASE + CCM_O_CRCDIN) = pData[i];
/* Read result and apply final XOR */
return HWREG(CCM0_BASE + CCM_O_CRCRSLTPP) ^ 0xFFFFFFFF;
}
Firmware Integrity Check Pattern
A common embedded pattern stores a CRC32 at the end of the firmware image and verifies it at startup:
/* Linker script provides these symbols */
extern uint32_t _flash_start;
extern uint32_t _flash_end;
extern uint32_t _stored_crc; /* Last 4 bytes of image */
bool Firmware_VerifyIntegrity(void)
{
uint32_t len = (uint32_t)&_flash_end - (uint32_t)&_flash_start - 4;
uint32_t computed = CRC32_Compute((const uint8_t *)&_flash_start, len);
uint32_t stored = _stored_crc;
return (computed == stored);
}
/* In main: */
if (!Firmware_VerifyIntegrity())
{
/* Enter safe mode or signal error */
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, GPIO_PIN_1); // Red LED
while (1);
}
Using CRCDataWrite and CRCResultRead for Streaming
/* Stream data word by word (faster than byte-by-byte) */
CRCConfigSet(CCM0_BASE, CRC_CFG_INIT_SEED | CRC_CFG_TYPE_P32 |
CRC_CFG_SIZE_32BIT);
CRCSeedSet(CCM0_BASE, 0xFFFFFFFF);
uint32_t words[] = {0x12345678, 0xABCDEF01};
CRCDataWrite(CCM0_BASE, words[0]);
CRCDataWrite(CCM0_BASE, words[1]);
uint32_t result = CRCResultRead(CCM0_BASE, false) ^ 0xFFFFFFFF;
Performance Comparison
| Method | Speed at 80 MHz | Notes |
|---|---|---|
| Software (table lookup) | ~3 MB/s | 1 byte per ~27 cycles |
| Software (bit-by-bit) | ~0.5 MB/s | 1 byte per ~160 cycles |
| Hardware CRC accelerator | ~40 MB/s | 1 byte per ~2 cycles |
Gotchas
- CRC32 bit ordering matters — normal vs reflected polynomial changes the result. Standard Ethernet CRC32 uses the reflected polynomial 0xEDB88320.
- Seed and final XOR must match the algorithm specification. CRC32 uses seed=0xFFFFFFFF and XOR=0xFFFFFFFF; CRC16-CCITT uses seed=0xFFFF and no final XOR.
- Word alignment — the hardware CRC in 32-bit mode requires word-aligned input. Byte mode is safer for arbitrary data.
- The CCM0 module (AES/CRC) is not enabled by default —
SysCtlPeripheralEnable(SYSCTL_PERIPH_CCM0)is required.
Step-by-Step Example
/*
* crc_verify.c
* Computes CRC32 over a test buffer in three ways:
* 1. Software table lookup
* 2. Software bit-by-bit
* 3. Hardware accelerator
* Prints results via UART0 to confirm all three match
* Board : TM4C123GXL EK LaunchPad
* SDK : TivaWare_C_Series-2.2.x
*/
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/uart.h"
#include "driverlib/pin_map.h"
#include "driverlib/crc.h"
#include "utils/uartstdio.h"
/* Test data: ASCII "123456789" — standard CRC32 should be 0xCBF43926 */
static const uint8_t testData[] = {
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39
};
extern uint32_t CRC32_Compute(const uint8_t *data, uint32_t len);
int main(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL |
SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
/* UART0 for output */
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_UART0));
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_GPIOA));
GPIOPinConfigure(GPIO_PA0_U0RX); GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
UARTStdioConfig(0, 115200, SysCtlClockGet());
/* Enable hardware CRC */
SysCtlPeripheralEnable(SYSCTL_PERIPH_CCM0);
while (!SysCtlPeripheralReady(SYSCTL_PERIPH_CCM0));
UARTprintf("=== CRC32 Verification Demo ===\r\n");
UARTprintf("Data: \"123456789\"\r\n");
UARTprintf("Expected CRC32: 0xCBF43926\r\n\r\n");
/* Method 1: software table lookup */
uint32_t sw_crc = CRC32_Compute(testData, sizeof(testData));
UARTprintf("Software CRC32 : 0x%08X %s\r\n",
sw_crc, sw_crc == 0xCBF43926 ? "PASS" : "FAIL");
/* Method 2: hardware accelerator */
CRCConfigSet(CCM0_BASE,
CRC_CFG_INIT_SEED | CRC_CFG_TYPE_P32 | CRC_CFG_SIZE_8BIT);
CRCSeedSet(CCM0_BASE, 0xFFFFFFFF);
uint32_t hw_crc = CRCDataProcess(CCM0_BASE,
(uint32_t *)testData,
sizeof(testData), false);
hw_crc ^= 0xFFFFFFFF;
UARTprintf("Hardware CRC32 : 0x%08X %s\r\n",
hw_crc, hw_crc == 0xCBF43926 ? "PASS" : "FAIL");
while (1);
}
Summary
| Key Point | Details |
|---|---|
| CRC32 polynomial | 0xEDB88320 (bit-reversed) / 0x04C11DB7 (normal) |
| CRC32 seed | 0xFFFFFFFF |
| CRC32 final XOR | 0xFFFFFFFF |
| CRC16-CCITT polynomial | 0x1021 |
| CRC8 polynomial | 0x07 |
| Hardware module | CCM0 (AES/CRC), requires SYSCTL_PERIPH_CCM0 |
| Hardware speed | ~40 MB/s |
| Test vector | “123456789” → CRC32 = 0xCBF43926 |
| Use case | Firmware integrity, config verification, protocol framing |