SatCat5
crc16_checksum.cc
1 // Copyright 2024-2025 The Aerospace Corporation.
3 // This file is a part of SatCat5, licensed under CERN-OHL-W v2 or later.
5 
7 
12 
13 // Set default table size.
14 #ifndef SATCAT5_CRC_TABLE_BITS
15 #define SATCAT5_CRC_TABLE_BITS 8
16 #endif
17 
18 // Tables for nybble-by-nybble CRC updates (32 bytes each)
19 // (See "sim/python/crc_table.py" for table-generator logic.)
20 #if SATCAT5_CRC_TABLE_BITS == 4
21 static const u16 TABLE_KERMIT[16] = {
22  0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387,
23  0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F,
24 };
25 
26 static const u16 TABLE_XMODEM[16] = {
27  0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
28  0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
29 };
30 
31 static inline void kermit_update(u16& crc, u8 next) {
32  u16 next1 = next >> 0; // First nybble
33  u16 next2 = next >> 4; // Second nybble
34  u8 index1 = (crc ^ next1) & 0x0F; // Table index
35  crc = (crc >> 4) ^ TABLE_KERMIT[index1]; // XOR with table
36  u8 index2 = (crc ^ next2) & 0x0F; // Table index
37  crc = (crc >> 4) ^ TABLE_KERMIT[index2]; // XOR with table
38 }
39 
40 static inline void xmodem_update(u16& crc, u8 next) {
41  u16 next1 = next >> 4; // First nybble
42  u16 next2 = next >> 0; // Second nybble
43  u8 index1 = ((crc >> 12) ^ next1) & 0x0Ful; // Table index
44  crc = (crc << 4) ^ TABLE_XMODEM[index1]; // XOR with table
45  u8 index2 = ((crc >> 12) ^ next2) & 0x0Ful; // Table index
46  crc = (crc << 4) ^ TABLE_XMODEM[index2]; // XOR with table
47 }
48 
49 inline u16 kermit_format(u16 crc) {
50  return __builtin_bswap16(crc);
51 }
52 
53 inline u16 xmodem_format(u16 crc) {
54  return crc;
55 }
56 #endif // SATCAT5_CRC_TABLE_BITS == 4
57 
58 // Table for byte-by-byte CRC updates (512 bytes each)
59 // (See "sim/python/crc_table.py" for table-generator logic.)
60 #if SATCAT5_CRC_TABLE_BITS == 8
61 static const u16 TABLE_KERMIT[256] = {
62  0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
63  0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
64  0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
65  0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
66  0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
67  0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
68  0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
69  0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
70  0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
71  0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
72  0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
73  0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
74  0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
75  0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
76  0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
77  0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
78  0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
79  0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
80  0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
81  0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
82  0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
83  0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
84  0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
85  0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
86  0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
87  0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
88  0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
89  0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
90  0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
91  0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
92  0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
93  0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78,
94 };
95 
96 static const u16 TABLE_XMODEM[256] = {
97  0x0000, 0x2110, 0x4220, 0x6330, 0x8440, 0xA550, 0xC660, 0xE770,
98  0x0881, 0x2991, 0x4AA1, 0x6BB1, 0x8CC1, 0xADD1, 0xCEE1, 0xEFF1,
99  0x3112, 0x1002, 0x7332, 0x5222, 0xB552, 0x9442, 0xF772, 0xD662,
100  0x3993, 0x1883, 0x7BB3, 0x5AA3, 0xBDD3, 0x9CC3, 0xFFF3, 0xDEE3,
101  0x6224, 0x4334, 0x2004, 0x0114, 0xE664, 0xC774, 0xA444, 0x8554,
102  0x6AA5, 0x4BB5, 0x2885, 0x0995, 0xEEE5, 0xCFF5, 0xACC5, 0x8DD5,
103  0x5336, 0x7226, 0x1116, 0x3006, 0xD776, 0xF666, 0x9556, 0xB446,
104  0x5BB7, 0x7AA7, 0x1997, 0x3887, 0xDFF7, 0xFEE7, 0x9DD7, 0xBCC7,
105  0xC448, 0xE558, 0x8668, 0xA778, 0x4008, 0x6118, 0x0228, 0x2338,
106  0xCCC9, 0xEDD9, 0x8EE9, 0xAFF9, 0x4889, 0x6999, 0x0AA9, 0x2BB9,
107  0xF55A, 0xD44A, 0xB77A, 0x966A, 0x711A, 0x500A, 0x333A, 0x122A,
108  0xFDDB, 0xDCCB, 0xBFFB, 0x9EEB, 0x799B, 0x588B, 0x3BBB, 0x1AAB,
109  0xA66C, 0x877C, 0xE44C, 0xC55C, 0x222C, 0x033C, 0x600C, 0x411C,
110  0xAEED, 0x8FFD, 0xECCD, 0xCDDD, 0x2AAD, 0x0BBD, 0x688D, 0x499D,
111  0x977E, 0xB66E, 0xD55E, 0xF44E, 0x133E, 0x322E, 0x511E, 0x700E,
112  0x9FFF, 0xBEEF, 0xDDDF, 0xFCCF, 0x1BBF, 0x3AAF, 0x599F, 0x788F,
113  0x8891, 0xA981, 0xCAB1, 0xEBA1, 0x0CD1, 0x2DC1, 0x4EF1, 0x6FE1,
114  0x8010, 0xA100, 0xC230, 0xE320, 0x0450, 0x2540, 0x4670, 0x6760,
115  0xB983, 0x9893, 0xFBA3, 0xDAB3, 0x3DC3, 0x1CD3, 0x7FE3, 0x5EF3,
116  0xB102, 0x9012, 0xF322, 0xD232, 0x3542, 0x1452, 0x7762, 0x5672,
117  0xEAB5, 0xCBA5, 0xA895, 0x8985, 0x6EF5, 0x4FE5, 0x2CD5, 0x0DC5,
118  0xE234, 0xC324, 0xA014, 0x8104, 0x6674, 0x4764, 0x2454, 0x0544,
119  0xDBA7, 0xFAB7, 0x9987, 0xB897, 0x5FE7, 0x7EF7, 0x1DC7, 0x3CD7,
120  0xD326, 0xF236, 0x9106, 0xB016, 0x5766, 0x7676, 0x1546, 0x3456,
121  0x4CD9, 0x6DC9, 0x0EF9, 0x2FE9, 0xC899, 0xE989, 0x8AB9, 0xABA9,
122  0x4458, 0x6548, 0x0678, 0x2768, 0xC018, 0xE108, 0x8238, 0xA328,
123  0x7DCB, 0x5CDB, 0x3FEB, 0x1EFB, 0xF98B, 0xD89B, 0xBBAB, 0x9ABB,
124  0x754A, 0x545A, 0x376A, 0x167A, 0xF10A, 0xD01A, 0xB32A, 0x923A,
125  0x2EFD, 0x0FED, 0x6CDD, 0x4DCD, 0xAABD, 0x8BAD, 0xE89D, 0xC98D,
126  0x267C, 0x076C, 0x645C, 0x454C, 0xA23C, 0x832C, 0xE01C, 0xC10C,
127  0x1FEF, 0x3EFF, 0x5DCF, 0x7CDF, 0x9BAF, 0xBABF, 0xD98F, 0xF89F,
128  0x176E, 0x367E, 0x554E, 0x745E, 0x932E, 0xB23E, 0xD10E, 0xF01E,
129 };
130 
131 static inline void kermit_update(u16& crc, u8 next) {
132  u8 index = (crc ^ (u16)next) & 0xFFul; // Table index
133  crc = (crc >> 8) ^ TABLE_KERMIT[index]; // XOR with table
134 }
135 
136 static inline void xmodem_update(u16& crc, u8 next) {
137  u8 index = (crc ^ (u16)next) & 0xFFul; // Table index
138  crc = (crc >> 8) ^ TABLE_XMODEM[index]; // XOR with table
139 }
140 
141 inline u16 kermit_format(u16 crc) {
142  return __builtin_bswap16(crc);
143 }
144 
145 inline u16 xmodem_format(u16 crc) {
146  return __builtin_bswap16(crc);
147 }
148 
149 #endif // SATCAT5_CRC_TABLE_BITS == 8
150 
151 u16 satcat5::crc16::kermit(unsigned nbytes, const void* data) {
152  // Byte-by-byte CRC16 calculation.
153  const u8* data8 = (const u8*)data;
154  u16 crc = 0;
155  for (unsigned a = 0 ; a < nbytes ; ++a)
156  kermit_update(crc, data8[a]);
157  return kermit_format(crc);
158 }
159 
160 u16 satcat5::crc16::xmodem(unsigned nbytes, const void* data) {
161  // Byte-by-byte CRC16 calculation.
162  const u8* data8 = (const u8*)data;
163  u16 crc = 0;
164  for (unsigned a = 0 ; a < nbytes ; ++a)
165  xmodem_update(crc, data8[a]);
166  return xmodem_format(crc);
167 }
168 
169 KermitTx::KermitTx(satcat5::io::Writeable* dst, u16 init, u16 xorout)
170  : satcat5::io::ChecksumTx<u16,2>(dst, init)
171  , m_xorout(xorout)
172 {
173  // Nothing else to initialize
174 }
175 
176 bool KermitTx::write_finalize() {
177  u16 fcs = kermit_format(m_chk ^ m_xorout);
178  m_dst->write_u16(fcs); // Append FCS
179  return chk_finalize() && m_dst->write_finalize();
180 }
181 
182 void KermitTx::write_next(u8 data) {
183  ++m_frm_len; // Update parent state
184  kermit_update(m_chk, data); // Update internal state
185  m_dst->write_u8(data); // Forward new data
186 }
187 
188 KermitRx::KermitRx(satcat5::io::Writeable* dst, u16 init, u16 xorout)
189  : satcat5::io::ChecksumRx<u16,2>(dst, init)
190  , m_xorout(xorout)
191 {
192  // Nothing else to initialize
193 }
194 
196  return sreg_match(kermit_format(m_chk) ^ m_xorout);
197 }
198 
199 void KermitRx::write_next(u8 data) {
200  if (sreg_push(data)) kermit_update(m_chk, data);
201 }
202 
203 XmodemTx::XmodemTx(satcat5::io::Writeable* dst, u16 init, u16 xorout)
204  : satcat5::io::ChecksumTx<u16,2>(dst, init)
205  , m_xorout(xorout)
206 {
207  // Nothing else to initialize
208 }
209 
211  u16 fcs = xmodem_format(m_chk ^ m_xorout);
212  m_dst->write_u16(fcs); // Append formatted FCS
213  return chk_finalize() && m_dst->write_finalize();
214 }
215 
216 void XmodemTx::write_next(u8 data) {
217  ++m_frm_len; // Update parent state
218  xmodem_update(m_chk, data); // Update internal state
219  m_dst->write_u8(data); // Forward new data
220 }
221 
222 XmodemRx::XmodemRx(satcat5::io::Writeable* dst, u16 init, u16 xorout)
223  : satcat5::io::ChecksumRx<u16,2>(dst, init)
224  , m_xorout(xorout)
225 {
226  // Nothing else to initialize
227 }
228 
230  return sreg_match(xmodem_format(m_chk) ^ m_xorout);
231 }
232 
233 void XmodemRx::write_next(u8 data) {
234  if (sreg_push(data)) xmodem_update(m_chk, data);
235 }
Check and remove FCS from each incoming frame ("KERMIT" variant).
void write_next(u8 data) override
Write the next byte to the underlying buffer or device.
bool write_finalize() override
Mark end of frame and release temporary working data.
Append FCS to each outgoing frame ("KERMIT" variant).
Check and remove FCS from each incoming frame ("XMODEM" variant).
void write_next(u8 data) override
Write the next byte to the underlying buffer or device.
bool write_finalize() override
Mark end of frame and release temporary working data.
Append FCS to each outgoing frame ("XMODEM" variant).
void write_next(u8 data) override
Write the next byte to the underlying buffer or device.
bool write_finalize() override
Mark end of frame and release temporary working data.
bool sreg_match(u16 fcs)
Child class MUST call sreg_match(...) during write_finalize().
Definition: io_checksum.h:164
bool sreg_push(u8 &data)
Child class MUST call sreg_push(...) during write_next().
Definition: io_checksum.h:186
bool chk_finalize()
Reset internal state and return true if the frame is valid.
Definition: io_checksum.cc:24
satcat5::io::Writeable *const m_dst
Output object.
Definition: io_checksum.h:68
unsigned m_frm_len
Length of current frame.
Definition: io_counter.h:72
Abstract API for writing byte-streams and packets.
Definition: io_writeable.h:24
void write_u8(u8 data)
One of many functions for writing integer/floating point values, see details.
Definition: io_writeable.cc:20
virtual bool write_finalize()
Mark end of frame and release temporary working data.
Inline CRC16 Checksum insertion and verification.
u16 xmodem(unsigned nbytes, const void *data)
Directly calculate CRC16 on a block of data.
u16 kermit(unsigned nbytes, const void *data)
Directly calculate CRC16 on a block of data.