Skip to content

Commit 6f5dc9b

Browse files
authored
Merge pull request #8 from hungatamazon/main
Implement crc-16-ccitt-false for cyphal header checksum
2 parents 2e30464 + 26cc36a commit 6f5dc9b

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

libudpard/udpard.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,45 @@ UDPARD_PRIVATE TransferCRC crcValue(const TransferCRC crc)
182182
return (uint32_t) (crc ^ CRC_XOR);
183183
}
184184

185+
/// --------------------------------------------- CYPHAL HEADER CRC ---------------------------------------------
186+
187+
typedef uint16_t CyphalHeaderCRC;
188+
189+
// Based on CRC-16-CCITT-FALSE Function
190+
#define CYPHAL_HEADER_CRC_INITIAL 0xFFFFU
191+
#define CYPHAL_HEADER_CRC_SIZE_BYTES 2U
192+
193+
UDPARD_PRIVATE CyphalHeaderCRC cyphalHeaderCrcAddByte(const CyphalHeaderCRC crc, const uint8_t byte)
194+
{
195+
// Based on CRC-16-CCITT-FALSE Function
196+
static const CyphalHeaderCRC Top = 0x8000U;
197+
static const CyphalHeaderCRC Poly = 0x1021U;
198+
CyphalHeaderCRC out = crc ^ (uint16_t) ((uint16_t) (byte) << BITS_PER_BYTE);
199+
// Do not fold this into a loop because a size-optimizing compiler won't unroll it degrading the performance.
200+
out = (uint16_t) ((uint16_t) (out << 1U) ^ (((out & Top) != 0U) ? Poly : 0U));
201+
out = (uint16_t) ((uint16_t) (out << 1U) ^ (((out & Top) != 0U) ? Poly : 0U));
202+
out = (uint16_t) ((uint16_t) (out << 1U) ^ (((out & Top) != 0U) ? Poly : 0U));
203+
out = (uint16_t) ((uint16_t) (out << 1U) ^ (((out & Top) != 0U) ? Poly : 0U));
204+
out = (uint16_t) ((uint16_t) (out << 1U) ^ (((out & Top) != 0U) ? Poly : 0U));
205+
out = (uint16_t) ((uint16_t) (out << 1U) ^ (((out & Top) != 0U) ? Poly : 0U));
206+
out = (uint16_t) ((uint16_t) (out << 1U) ^ (((out & Top) != 0U) ? Poly : 0U));
207+
out = (uint16_t) ((uint16_t) (out << 1U) ^ (((out & Top) != 0U) ? Poly : 0U));
208+
return out;
209+
}
210+
211+
UDPARD_PRIVATE CyphalHeaderCRC cyphalHeaderCrcAdd(const CyphalHeaderCRC crc, const size_t size, const void* const header)
212+
{
213+
UDPARD_ASSERT(header != NULL);
214+
CyphalHeaderCRC out = crc;
215+
const uint8_t* p = (const uint8_t*) header;
216+
for (size_t i = 0; i < size; i++)
217+
{
218+
out = cyphalHeaderCrcAddByte(out, *p);
219+
++p;
220+
}
221+
return out;
222+
}
223+
185224
// --------------------------------------------- TRANSMISSION ---------------------------------------------
186225

187226
/// This is a subclass of UdpardTxQueueItem. A pointer to this type can be cast to UdpardTxQueueItem safely.
@@ -324,11 +363,13 @@ UDPARD_PRIVATE void txMakeFrameHeader(UdpardFrameHeader* const header,
324363
{
325364
UDPARD_ASSERT(frame_index <= UDPARD_MAX_FRAME_INDEX);
326365
uint32_t end_of_transfer_mask = (uint32_t) (end_of_transfer ? 1 : 0) << (uint32_t) UDPARD_END_OF_TRANSFER_OFFSET;
366+
size_t cyphal_header_size_without_crc = sizeof(UdpardFrameHeader) - CYPHAL_HEADER_CRC_SIZE_BYTES;
327367
header->transfer_id = transfer_id;
328368
header->priority = (uint8_t) priority;
329369
header->frame_index_eot = end_of_transfer_mask | frame_index;
330370
header->source_node_id = src_node_id;
331371
header->destination_node_id = dst_node_id;
372+
header->cyphal_header_checksum = cyphalHeaderCrcAdd(CYPHAL_HEADER_CRC_INITIAL, cyphal_header_size_without_crc, header);
332373
if (transfer_kind == UdpardTransferKindMessage)
333374
{
334375
header->data_specifier = (uint16_t) UPDARD_DATA_SPECIFIER_MESSAGE & port_id; // SNM (0) + Subject ID

tests/exposed.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ extern "C" {
7979

8080
auto crcAdd(const std::uint32_t crc, const std::size_t size, const void* const bytes) -> std::uint32_t;
8181

82+
auto cyphalHeaderCrcAdd(const std::uint16_t crc, const std::size_t size, const void* const bytes) -> std::uint16_t;
83+
8284
auto crcValue(const std::uint32_t crc) -> std::uint32_t;
8385

8486
auto txMakeMessageSessionSpecifier(const UdpardPortID subject_id,

tests/test_private_crc.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,33 @@ TEST_CASE("TransferCRC")
2121
crc = crcAdd(crc, 6, "456789");
2222
REQUIRE(0xE3069283U == crcValue(crc));
2323
}
24+
25+
TEST_CASE("CyphalHeaderCRC")
26+
{
27+
using exposed::cyphalHeaderCrcAdd;
28+
29+
// Standard Cyphal header size = 24. The last 2 bytes are for the CRC.
30+
31+
// Standard use case. Header size = 24; CRC is calculated from the first 22 bytes.
32+
// The last two bytes (CRC) are ignored in the calculation.
33+
std::uint16_t crc = 0xFFFFU;
34+
const uint8_t* header = reinterpret_cast<const uint8_t*>("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x20\x21\x22\x23\x24");
35+
crc = cyphalHeaderCrcAdd(crc, 22, header);
36+
REQUIRE(0xB731 == crc);
37+
38+
// Verify CRC when the size field is equal to the size of the header.
39+
crc = 0xFFFFU;
40+
header = reinterpret_cast<const uint8_t*>("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x20\x21\x22");
41+
crc = cyphalHeaderCrcAdd(crc, 22, header);
42+
REQUIRE(0xB731 == crc);
43+
crc = 0xFFFFU;
44+
header = reinterpret_cast<const uint8_t*>("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x20\x21\x22\x23\x24");
45+
crc = cyphalHeaderCrcAdd(crc, 24, header);
46+
REQUIRE(0x96BB == crc);
47+
48+
// Verify CRC when the size field is less than the size of the header. Extra data is ignored.
49+
crc = 0xFFFFU;
50+
header = reinterpret_cast<const uint8_t*>("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x20\x21\x22\x23\x24");
51+
crc = cyphalHeaderCrcAdd(crc, 10, header);
52+
REQUIRE(0x9F09 == crc);
53+
}

0 commit comments

Comments
 (0)