all
Chapter 16 of 20

Software CRC Module — Tiva C

Eslam El Hefny Apr 16, 2025 7 min read
80% done

Software CRC Module — Tiva C

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

Next Chapter

DMA Controller

Share: