ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
crc.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file crc.h
6 * @brief Parameterized CRC engine - one source of truth for every cyclic redundancy check.
7 *
8 * One implementation for every CRC in the tree: C37.118, DF1, DNP3, EnOcean, INTERBUS, Modbus,
9 * Modbus Plus, NEMA TS2, raw L2, SDI-12, SHT3x, Thread and Zigbee all checksum through it. Three
10 * checks stay outside it: the WAL is table-driven for bulk log throughput, RTCM3's CRC-24Q has no
11 * preset here, and DShot's "CRC" is a 4-bit XOR fold rather than a CRC at all.
12 *
13 * It is the standard Rocksoft / Williams model, so any published CRC is expressible as six numbers
14 * and needs no new code:
15 *
16 * - @ref protocore_crc_params::width register width in bits (8..32)
17 * - @ref protocore_crc_params::poly generator polynomial, normal form, implicit top bit dropped
18 * - @ref protocore_crc_params::init initial register value
19 * - @ref protocore_crc_params::refin reflect each input octet
20 * - @ref protocore_crc_params::refout reflect the final register
21 * - @ref protocore_crc_params::xorout final XOR
22 *
23 * Every preset below carries its catalogue **check value** - the CRC of the nine ASCII octets
24 * `"123456789"` - and `test_crc` asserts each one: a wrong polynomial or a flipped reflect flag
25 * cannot reproduce a published check value by accident.
26 *
27 * Bitwise, not table-driven: a 256-entry table per polynomial would cost more flash than the frames
28 * are worth on this class of device, and every caller here checksums tens to hundreds of octets, not
29 * megabytes. Pure, so it is host-testable and identical on device and host.
30 *
31 * @author Douglas Quigg (dstroy0)
32 * @date 2026
33 */
34
35#ifndef PROTOCORE_CRC_H
36#define PROTOCORE_CRC_H
37
38#include "protocore_config.h" // the entry point: protocore_types.h for the widths
39
40/** @brief One CRC's full definition (Rocksoft model). See the file comment. */
41typedef struct
42{
43 uint8_t width; ///< register width in bits, 8..32.
44 uint32_t poly; ///< generator polynomial, normal form (implicit top bit dropped).
45 uint32_t init; ///< initial register value.
46 proto_bool refin; ///< reflect each input octet before feeding it in.
47 proto_bool refout; ///< reflect the final register before the XOR.
48 uint32_t xorout; ///< XORed into the final register.
50
51/** @brief What one CRC step runs over: the definition, the running register, and the octets. */
52typedef struct
53{
54 const protocore_crc_params *params; ///< the CRC's full definition
55 uint32_t crc; ///< the running register a fold or a finish carries in
56 const uint8_t *data; ///< the octets a fold takes
57 size_t len; ///< how many
58} CrcArgs;
59
60/**
61 * @brief The Rocksoft CRC model.
62 *
63 * A caller sets the members a call takes, invokes it through ::Crc, and reads the register off the
64 * same handle. Nothing is held between calls: the running value is the caller's, carried in
65 * @ref CrcArgs::crc and reported in @ref CrcNs::value.
66 *
67 * @var CrcNs::args the definition, the running register, and the octets
68 * @var CrcNs::value the register a step produced
69 * @var CrcNs::begin the initial register value
70 * @var CrcNs::update fold args.len octets at args.data into args.crc
71 * @var CrcNs::final apply the output reflection and the final XOR to args.crc
72 * @var CrcNs::compute one-shot: begin, update and final over args.data
73 *
74 * The three steps are split so a caller can checksum a frame that is not contiguous in memory (a
75 * header struct then a payload buffer) without copying it together first. Input reflection is
76 * applied per octet by update; output reflection belongs to final, so an intermediate register is
77 * not a meaningful CRC on its own.
78 *
79 * No storage member: the register is the caller's and the presets below are constants.
80 */
81typedef struct
82{
84 uint32_t value;
85} CrcVars;
86
87/** @brief The operands and the outcome. */
88extern CrcVars CrcV;
89
90/** @brief The entries. */
91typedef struct
92{
93 void (*const begin)(uint8_t *work);
94 void (*const update)(uint8_t *work);
95 void (*const final)(uint8_t *work);
96 void (*const compute)(uint8_t *work);
97} CrcNs;
98
99// What the table binds, defined once in the .c and taking one parameter each: everything
100// else an entry needs is an operand in CrcV or a region of the borrow at a fixed offset.
101void protocore_crc_begin(uint8_t *work);
102void protocore_crc_update(uint8_t *work);
103void protocore_crc_final(uint8_t *work);
104void protocore_crc_compute(uint8_t *work);
105
106// `static const`, initialised HERE rather than `extern` against a definition in the .c: a
107// const object whose initializer every translation unit can see is a COMPILE-TIME FACT, so
108// `Crc.begin(work)` resolves to a named function and becomes a DIRECT call. An extern table
109// leaves the call indirect and the symbol live at every level, -O2 -flto included.
110static const CrcNs Crc __attribute__((unused)) = {
112 .update = protocore_crc_update,
113 .final = protocore_crc_final,
114 .compute = protocore_crc_compute,
115};
116
117// --- Catalogue presets ------------------------------------------------------------------------
118// Each carries its published check value: the CRC of the ASCII octets "123456789". test_crc asserts
119// every one of them, so an incorrect parameter here fails the suite rather than corrupting a codec.
120// Defined once in crc.c rather than per translation unit.
121
122/** @brief CRC-8/SMBUS (a.k.a. CRC-8). check = 0xF4. */
124/** @brief CRC-8/MAXIM-DOW (1-Wire / Dallas). check = 0xA1. */
126/** @brief CRC-8/NRSC-5 - the sensor CRC. check = 0xF7. Used by services/sht3x. */
128
129/** @brief CRC-16/ARC (a.k.a. CRC-16, IBM). check = 0xBB3D. */
131/** @brief CRC-16/MODBUS. check = 0x4B37. */
133/** @brief CRC-16/IBM-3740 (often called CCITT-FALSE). check = 0x29B1. */
135/** @brief CRC-16/XMODEM. check = 0x31C3. */
137/** @brief CRC-16/KERMIT (a.k.a. CRC-16/CCITT, reflected). check = 0x2189. */
139/** @brief CRC-16/X-25 (HDLC FCS). check = 0x906E. Used by services/radio/thread, mbplus, nema_ts2. */
141/** @brief CRC-16/DNP (DNP3 link-layer block check). check = 0xEA82. Used by services/dnp3. */
143
144/** @brief CRC-24/OPENPGP. check = 0x21CF02. */
146
147/** @brief CRC-32/ISO-HDLC (zlib / PKZIP / Ethernet). check = 0xCBF43926. */
149/** @brief CRC-32/BZIP2 (unreflected CRC-32). check = 0xFC891918. */
151
152#endif // PROTOCORE_CRC_H
const protocore_crc_params PROTOCORE_CRC8_SMBUS
CRC-8/SMBUS (a.k.a. CRC-8). check = 0xF4.
const protocore_crc_params PROTOCORE_CRC32_ISO_HDLC
CRC-32/ISO-HDLC (zlib / PKZIP / Ethernet). check = 0xCBF43926.
CrcVars CrcV
The operands and the outcome.
const protocore_crc_params PROTOCORE_CRC8_NRSC5
CRC-8/NRSC-5 - the sensor CRC. check = 0xF7. Used by services/sht3x.
const protocore_crc_params PROTOCORE_CRC32_BZIP2
CRC-32/BZIP2 (unreflected CRC-32). check = 0xFC891918.
void protocore_crc_final(uint8_t *work)
void protocore_crc_compute(uint8_t *work)
const protocore_crc_params PROTOCORE_CRC16_IBM_3740
CRC-16/IBM-3740 (often called CCITT-FALSE). check = 0x29B1.
const protocore_crc_params PROTOCORE_CRC16_XMODEM
CRC-16/XMODEM. check = 0x31C3.
const protocore_crc_params PROTOCORE_CRC16_X25
CRC-16/X-25 (HDLC FCS). check = 0x906E. Used by services/radio/thread, mbplus, nema_ts2.
const protocore_crc_params PROTOCORE_CRC8_MAXIM_DOW
CRC-8/MAXIM-DOW (1-Wire / Dallas). check = 0xA1.
const protocore_crc_params PROTOCORE_CRC16_DNP
CRC-16/DNP (DNP3 link-layer block check). check = 0xEA82. Used by services/dnp3.
const protocore_crc_params PROTOCORE_CRC24_OPENPGP
CRC-24/OPENPGP. check = 0x21CF02.
const protocore_crc_params PROTOCORE_CRC16_ARC
CRC-16/ARC (a.k.a. CRC-16, IBM). check = 0xBB3D.
void protocore_crc_update(uint8_t *work)
void protocore_crc_begin(uint8_t *work)
const protocore_crc_params PROTOCORE_CRC16_KERMIT
CRC-16/KERMIT (a.k.a. CRC-16/CCITT, reflected). check = 0x2189.
const protocore_crc_params PROTOCORE_CRC16_MODBUS
CRC-16/MODBUS. check = 0x4B37.
What one CRC step runs over: the definition, the running register, and the octets.
Definition crc.h:53
const uint8_t * data
the octets a fold takes
Definition crc.h:56
uint32_t crc
the running register a fold or a finish carries in
Definition crc.h:55
size_t len
how many
Definition crc.h:57
const protocore_crc_params * params
the CRC's full definition
Definition crc.h:54
The entries.
Definition crc.h:92
void(*const begin)(uint8_t *work)
Definition crc.h:93
Definition crc.h:82
CrcArgs args
Definition crc.h:83
uint32_t value
Definition crc.h:84
One CRC's full definition (Rocksoft model). See the file comment.
Definition crc.h:42
uint32_t init
initial register value.
Definition crc.h:45
uint8_t width
register width in bits, 8..32.
Definition crc.h:43
proto_bool refout
reflect the final register before the XOR.
Definition crc.h:47
uint32_t xorout
XORed into the final register.
Definition crc.h:48
proto_bool refin
reflect each input octet before feeding it in.
Definition crc.h:46
uint32_t poly
generator polynomial, normal form (implicit top bit dropped).
Definition crc.h:44
_Bool proto_bool
The truth value.
Definition types.h:64