Periferica CRC su STM32: Checksum Hardware per Integrità Dati, Bootloader e Comunicazione
Ogni STM32 integra un'unità hardware per il calcolo del CRC, eppure molti sviluppatori continuano a implementarlo in software — sprecando cicli CPU e flash su qualcosa che il microcontrollore esegue in due cicli di bus. La periferica CRC gestisce la divisione polinomiale, l'inversione dei byte e l'XOR finale in hardware, ed è perfetta per la verifica dei bootloader, la validazione di protocolli di comunicazione e il controllo d'integrità della memoria flash. Ecco esattamente come configurarla e usarla a livello di registri su STM32F4, STM32G4 e STM32U5.
Ho perso il conto di quanti bootloader in produzione ho visto che calcolano un CRC32 sull'intera immagine firmware usando un loop software con lookup table. La flash è lì. La CPU è già accesa. Ma la periferica CRC è seduta sul bus, configurata al reset con il polinomio Ethernet CRC32 (0x04C11DB7), e pronta a produrre un risultato in meno cicli di una singola lookup table. Il problema è quasi sempre l'ignoranza dell'esistenza della periferica, non un vincolo tecnico.
Questo articolo copre la periferica CRC partendo dalla mappa dei registri: configurazione del polinomio, dimensione dei dati e inversione, il modello di calcolo one-shot, CRC incrementale (multi-blocco), verifica d'integrità del bootloader e CRC per comunicazioni CAN e SPI. Uso STM32F401 come riferimento, ma la mappa dei registri è identica su F4, G4, L4, L5, U5, H5 e H7. L'unica differenza è il valore iniziale del CRC (alcune famiglie resettano a 0xFFFFFFFF, altre a 0x00000000) — impostarlo sempre esplicitamente.
Mappa dei registri CRC (RM0368, RM0399)
La periferica CRC su STM32 ha esattamente quattro registri — né più, né meno:
| Offset | Registro | Scopo |
|---|---|---|
0x00 | CRC_DR | Ingresso dati / Uscita CRC |
0x04 | CRC_IDR | Registro dati indipendente (non parte del calcolo CRC) |
0x08 | CRC_CR | Registro di controllo: reset + dimensione polinomio |
0x10 (rev. V2) | CRC_INIT | Valore iniziale CRC (programmabile su serie recenti) |
Il registro dati CRC_DR è il cavallo di battaglia. Ogni scrittura alimenta 8, 16 o 32 bit nel calcolo. Ogni lettura restituisce il resto CRC corrente. Scrivere 1 in CRC_CR resetta la periferica allo stato iniziale (default: 0xFFFFFFFF con il polinomio Ethernet).
CRC one-shot: verifica di una pagina flash
Questo è il caso d'uso più comune: generare un CRC su un blocco di memoria e confrontarlo con un riferimento memorizzato. Ecco la sequenza esatta a registri per STM32F4:
/* Reset della periferica CRC — imposta il resto a 0xFFFFFFFF */
CRC->CR = CRC_CR_RESET;
/* Alimenta un buffer flash di 256 word (1 KB) parola per parola */
uint32_t *src = (uint32_t *)0x08040000;
for (int i = 0; i < 256; i++) {
CRC->DR = src[i];
}
/* Legge il CRC finale */
uint32_t crc_calcolato = CRC->DR;
/* Confronta con il valore memorizzato */
if (crc_calcolato != crc_atteso) {
/* Pagina corrotta o modificata — attiva recovery */
bootloader_jump_to_recovery();
}
La periferica assorbe una parola per ciclo di bus — due cicli CPU su un accesso flash a zero wait-state. Una pagina da 1 KB richiede circa 512 cicli. Lo stesso CRC in software con lookup table richiede circa 10–15 cicli per byte, ovvero 10.000–15.000 cicli per 1 KB. L'hardware lo fa nel 5–10% del tempo.
CRC streaming (multi-blocco)
Il modello one-shot resetta il CRC prima di iniziare. Ma si può anche calcolare un CRC incrementale: calcolare sul blocco A, catturare il resto intermedio, poi continuare con il blocco B. È utile quando:
- I dati arrivano in pacchetti via UART o SPI e serve un unico CRC sull'intero flusso.
- Il bootloader verifica un'immagine firmware multi-settore settore per settore.
- Si calcola un CRC su un file da scheda SD in blocchi da 512 byte.
/* Primo blocco */
CRC->CR = CRC_CR_RESET;
for (int i = 0; i < 128; i++) CRC->DR = blocco1[i];
uint32_t medio = CRC->DR; /* snapshot — NON resettare */
/* Secondo blocco — continua dal resto precedente */
for (int i = 0; i < 128; i++) CRC->DR = blocco2[i];
uint32_t finale = CRC->DR; /* CRC su entrambi i blocchi */
Il vincolo chiave: non scrivere CRC_CR_RESET tra un blocco e l'altro. La periferica CRC mantiene il resto corrente nel suo registro a scorrimento interno. Una lettura di CRC_DR restituisce una copia senza disturbare lo stato — la periferica ricarica trasparentemente il resto dopo che la lettura è completata.
Polinomio, valore iniziale e XOR finale
Sulle famiglie STM32 più recenti (G4, L4+, L5, U5, H5, H7), la periferica CRC ha una versione 2 (rev. V2) con polinomio programmabile, valore iniziale e configurazione dell'XOR finale attraverso i registri CRC_INIT e CRC_POL. Su F4 e serie precedenti, questi sono fissi: polinomio = 0x04C11DB7, iniziale = 0xFFFFFFFF, XOR finale = 0x00000000.
Se si usa il built-in __CRC32__ di GCC (__builtin_arm_crc32w) o il CRC hardware del Cortex-M4/M7, il polinomio corrisponde al default STM32. Ma il valore iniziale e l'XOR finale differiscono tra implementazioni. La periferica CRC degli STM32 implementa quello che comunemente viene chiamato CRC-32/MPEG2: iniziale = 0xFFFFFFFF, nessun XOR finale. Il CRC-32 Ethernet standard (PKZIP, CRC-32/ISO-HDLC) usa iniziale = 0xFFFFFFFF e XOR finale = 0xFFFFFFFF.
Per calcolare il CRC-32 standard su un STM32F4, fare XOR del risultato con 0xFFFFFFFF:
CRC->CR = CRC_CR_RESET;
CRC->DR = dati;
uint32_t std_crc32 = CRC->DR ^ 0xFFFFFFFF;
Su G4/U5 con il CRC configurabile, impostare CRC_INIT = 0xFFFFFFFF e configurare l'XOR finale in CRC_CFG (se disponibile sul modello specifico) per corrispondere esattamente.
Esempio pratico: verifica integrità firmware su STM32F401
Un bootloader di produzione deve verificare l'immagine applicativa prima di saltarci. La periferica CRC permette di farlo in microsecondi invece che millisecondi. Ecco un pattern completo:
#define APP_START 0x08010000
#define APP_SIZE (128 * 1024) /* 128 KB */
#define CRC_OFFSET (APP_SIZE - 4) /* CRC memorizzato negli ultimi 4 byte */
uint32_t calcola_crc_firmware(void) {
uint32_t *p = (uint32_t *)APP_START;
uint32_t words = (CRC_OFFSET) / 4;
CRC->CR = CRC_CR_RESET;
for (uint32_t i = 0; i < words; i++) {
CRC->DR = p[i];
}
return CRC->DR;
}
uint32_t leggi_crc_memorizzato(void) {
return *(uint32_t *)(APP_START + CRC_OFFSET);
}
int verifica_firmware(void) {
uint32_t memorizzato = leggi_crc_memorizzato();
uint32_t calcolato = calcola_crc_firmware();
if ((calcolato ^ 0xFFFFFFFF) == memorizzato) {
return 1; /* firmware valido */
}
return 0;
}
Metto la word CRC negli ultimi 4 byte dell'immagine firmware e la sostituisco durante il build con uno script post-link. Il bootloader calcola il CRC su tutto tranne l'ultima word e fa XOR con 0xFFFFFFFF per corrispondere al CRC-32 standard che la pipeline di build genera.
Esempio pratico: validazione CRC messaggi CAN
La periferica CRC non è limitata alla verifica della flash. Quando si implementa un livello di trasporto personalizzato su CAN o SPI che usa CRC-32 per l'integrità dei messaggi, l'unità hardware è più veloce di qualsiasi implementazione software e non aggiunge codice:
/* Alla ricezione di un messaggio CAN: calcola CRC sul payload */
void can_rx_valida(CAN_Msg *msg) {
CRC->CR = CRC_CR_RESET;
uint32_t *payload = (uint32_t *)msg->data;
for (int i = 0; i < msg->len / 4; i++) {
CRC->DR = payload[i];
}
/* Gestisci byte rimanenti se len % 4 != 0 */
uint32_t crc_raw = CRC->DR;
if ((crc_raw ^ 0xFFFFFFFF) != msg->crc_field) {
error_handler("CRC CAN non corrispondente");
}
}
Per l'offloading del CRC hardware durante traffico CAN attivo, assicurarsi che la periferica CRC sia clockata (RCC abilitato), ed evitare di resettarla a metà flusso se più messaggi consecutivi condividono un singolo contesto CRC.
Checklist pratica
- Abilitare il clock della periferica:
RCC->AHB1ENR |= RCC_AHB1ENR_CRCENsu F4/G4;RCC->AHB1ENR |= RCC_AHB1ENR_CRCENsu U5. - Resettare sempre prima di un nuovo calcolo:
CRC->CR = CRC_CR_RESET. Non dare per scontato che il valore di reset sia corretto dopo un reset software della sola periferica. - Abbinare la dimensione dei dati: Scrivere valori a 8 bit in
CRC_DRsu alcune famiglie imposta solo gli 8 bit bassi del registro d'ingresso. Preferire scritture a 32 bit e allineare i dati a word quando possibile. - XOR del risultato se serve CRC-32 standard: La periferica CRC STM32 calcola CRC-32/MPEG2, non CRC-32/ISO-HDLC.
risultato ^= 0xFFFFFFFFlo converte. - Non leggere CRC_DR in mezzo a un loop: Ogni lettura è uno snapshot, non un reset. Leggere dentro il loop non rompe il calcolo, ma costa due cicli bus per nulla.
- Attenzione all'ordine dei byte nei messaggi multi-byte: Se il tool host calcola CRC su uno stream little-endian ma lo STM32 lo alimenta word per word, i CRC non corrisponderanno. Invertire i byte in ingresso o allineare l'host all'ordine dati dell'hardware.
Come lo affronterei su un progetto cliente
Il primo giorno di qualsiasi progetto embedded che necessita di verifica d'integrità — e sono quasi tutti quelli con un bootloader — aggiungo un modulo crc_hw.c con tre funzioni: crc_reset(), crc_feed_block(indirizzo, words) e crc_result(). L'implementazione è un sottile wrapper attorno ai quattro registri CRC. Lo aggiungo al bootloader e all'applicazione. Se il cliente passa successivamente a un Cortex-M0 (che non ha la periferica CRC), sostituisco il backend con un CRC software con la stessa API. Il codice applicativo non cambia mai — solo il livello di astrazione HAL-like. Questa separazione mi ha salvato mesi di rielaborazione firmware su due progetti dove l'MCU è stato cambiato a metà sviluppo.
La periferica CRC è anche il modo più veloce per validare un'immagine firmware attraverso una sonda di debug lenta. Invece di leggere l'intera flash via SWD (che richiede secondi), lascio che il target calcoli il CRC in hardware e rimandi indietro il singolo risultato a 32 bit. Una lettura di registro via SWD invece di 128 KB di letture bulk.
Fonti
- RM0368 — Manuale di Riferimento STM32F401, Capitolo 12: Unità di calcolo CRC.
- RM0399 — Manuale di Riferimento Serie STM32G4, Capitolo 11: Unità di calcolo CRC.
- RM0456 — Manuale di Riferimento Serie STM32U5, Capitolo 13: Unità di calcolo CRC (rev. V2).
- AN4187 — Using the CRC peripheral in STM32 MCUs
- AN3961 — CRC for STM32F4xx

Comments
Have comments? Send me an email.