Browse Source

Merge pull request #6112 from miri64/checksum/feat/ucrc16

ucrc16: provide lightweight CRC16 implementation
pr/spi.typo
Ludwig Knüpfer 6 years ago committed by GitHub
parent
commit
ff2d23b038
  1. 22
      sys/checksum/doc.txt
  2. 53
      sys/checksum/ucrc16.c
  3. 5
      sys/include/checksum/crc16_ccitt.h
  4. 82
      sys/include/checksum/ucrc16.h
  5. 6
      tests/unittests/Makefile
  6. 142
      tests/unittests/tests-checksum/tests-checksum-ucrc16.c
  7. 1
      tests/unittests/tests-checksum/tests-checksum.c
  8. 7
      tests/unittests/tests-checksum/tests-checksum.h

22
sys/checksum/doc.txt

@ -1,4 +1,5 @@
/*
* Copyright 2016 Freie Universität Berlin
* Copyright 2016 Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
@ -10,4 +11,25 @@
* @defgroup sys_checksum Checksum
* @ingroup sys
* @brief Checksum function libraries
*
* This module provides a number of checksum functions. Most notably is the
* @ref sys_checksum_crc16_ccitt and the @ref sys_checksum_ucrc16 modules which
* provide support for the CRC16 checksum.
*
* @ref sys_checksum_crc16_ccitt only provides an implementation of the CCITT
* flavor of CRC16 (polynomial @$ x^{16} + x^{12} + x^{5} + 1 @$) for big-endian
* numbers with starting seed `0x1d0f` (though others can be provided), while
* @ref sys_checksum_ucrc16 is more generalized, since it takes the
* hexadecimal representation of the polynomial as a parameter and provides
* functions and standardized polynomials for both big- and little-endian
* numbers.
*
* The caveat of @ref sys_checksum_ucrc16 is that it is significantly slower
* (approximately factor 8) than @ref sys_checksum_crc16_ccitt since the latter
* is able to calculate the checksum byte-wise, while the first calculates
* needs to calculate it bit-wise. @ref sys_checksum_crc16_ccitt achieves this
* advantage by using a look-up table that provides the checksum for every
* possible byte-value. It thus trades of memory against speed. If your
* platform is rather small equipped in memory you should prefer the
* @ref sys_checksum_ucrc16 version.
*/

53
sys/checksum/ucrc16.c

@ -0,0 +1,53 @@
/*
* Copyright (C) 2016 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
*
* @file
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#include <assert.h>
#include <inttypes.h>
#include "checksum/ucrc16.h"
#define UINT8_BIT_SIZE (8U)
#define UINT16_BIT_SIZE (16U)
#define LEFTMOST_BIT_SET(value) ((value) & 0x8000U)
#define RIGHTMOST_BIT_SET(value) ((value) & 0x0001U)
uint16_t ucrc16_calc_be(const uint8_t *buf, size_t len, uint16_t poly,
uint16_t seed)
{
assert(buf != NULL);
for (unsigned c = 0; c < len; c++, buf++) {
uint32_t tmp = seed ^ (*buf << (UINT16_BIT_SIZE - UINT8_BIT_SIZE));
for (unsigned i = 0; i < UINT8_BIT_SIZE; i++) {
tmp = LEFTMOST_BIT_SET(tmp) ? ((tmp << 1) ^ poly) : (tmp << 1);
}
seed = tmp & UINT16_MAX;
}
return seed;
}
uint16_t ucrc16_calc_le(const uint8_t *buf, size_t len, uint16_t poly,
uint16_t seed)
{
assert(buf != NULL);
for (unsigned c = 0; c < len; c++, buf++) {
seed ^= (*buf);
for (unsigned i = 0; i < UINT8_BIT_SIZE; i++) {
seed = RIGHTMOST_BIT_SET(seed) ? ((seed >> 1) ^ poly) : (seed >> 1);
}
}
return seed;
}
/** @} */

5
sys/include/checksum/crc16_ccitt.h

@ -17,6 +17,11 @@
* crc16_ccitt_update() with the desired start value
* instead of crc16_ccitt_calc().
*
* There is a more generalized version in @ref sys_checksum_ucrc16,
* that does not utilize a look-up table as this implementation
* does (and is thus also for more memory efficient). Its caveat
* however is that it is slower by about factor 8 than this version.
*
* @{
* @file
* @author Ludwig Knüpfer <ludwig.knuepfer@fu-berlin.de>

82
sys/include/checksum/ucrc16.h

@ -0,0 +1,82 @@
/*
* Copyright (C) 2016 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup sys_checksum_ucrc16 CRC16 (lightweight)
* @ingroup sys_checksum
* @brief Lightweight CRC16 checksum algorithms
*
* This CRC16 implementation does not use pre-calculated lookup tables and is
* thus very lightweight (memory-wise), but as a caveat slower (about factor 8)
* than the version with pre-calculated lookup tables.
*
* Additionally compared to @ref sys_checksum_crc16_ccitt this is a generalized
* implementation. One can easily exchange generator polynomials and starting
* seeds.
*
* @{
*
* @file
* @brief ucrc16 definitions
*
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#ifndef UCRC16_H_
#define UCRC16_H_
#include <stdint.h>
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @{
* @brief Various generator polynomials
*/
#define UCRC16_CCITT_POLY_BE (0x1021) /**< CRC16-CCITT polynomial (big-endian) */
#define UCRC16_CCITT_POLY_LE (0x8408) /**< CRC16-CCITT polynomial (little-endian) */
/** @} */
/**
* @brief Calculate CRC16 (big-endian version)
*
* @param[in] buf Start of memory are to checksum
* @param[in] len Number of bytes in @p buf to calculate checksum for
* @param[in] poly The generator polynomial for the checksum
* @param[in] seed The seed (starting value) for the checksum
*
* @return Checksum of the specified memory area based on @p seed and @p poly
*
* @note The return value is not the complement of the sum but the sum itself
*/
uint16_t ucrc16_calc_be(const uint8_t *buf, size_t len, uint16_t poly,
uint16_t seed);
/**
* @brief Calculate CRC16 (little-endian version)
*
* @param[in] buf Start of memory are to checksum
* @param[in] len Number of bytes in @p buf to calculate checksum for
* @param[in] poly The generator polynomial for the checksum
* @param[in] seed The seed (starting value) for the checksum
*
* @return Checksum of the specified memory area based on @p seed and @p poly
*
* @note The return value is not the complement of the sum but the sum itself
*/
uint16_t ucrc16_calc_le(const uint8_t *buf, size_t len, uint16_t poly,
uint16_t seed);
#ifdef __cplusplus
}
#endif
#endif /* UCRC16_H_ */
/** @} */

6
tests/unittests/Makefile

@ -1,7 +1,8 @@
APPLICATION = unittests
include ../Makefile.tests_common
BOARD_INSUFFICIENT_MEMORY := airfy-beacon cc2650stk chronos msb-430 msb-430h pca10000 \
BOARD_INSUFFICIENT_MEMORY := airfy-beacon cc2650stk chronos ek-lm4f120xl \
msb-430 msb-430h pca10000 \
pca10005 spark-core stm32f0discovery stm32f3discovery \
telosb wsn430-v1_3b wsn430-v1_4 z1 nucleo-f103 \
nucleo-f334 yunjia-nrf51822 samr21-xpro \
@ -9,7 +10,8 @@ BOARD_INSUFFICIENT_MEMORY := airfy-beacon cc2650stk chronos msb-430 msb-430h pca
weio waspmote-pro nucleo-f072 arduino-uno \
arduino-duemilanove sodaq-autonomo arduino-zero \
nucleo-f030 nucleo-f070 nucleo-f091 pba-d-01-kw2x \
saml21-xpro microbit calliope-mini limifrog-v1
saml21-xpro microbit calliope-mini limifrog-v1 \
slwstk6220a
USEMODULE += embunit

142
tests/unittests/tests-checksum/tests-checksum-ucrc16.c

@ -0,0 +1,142 @@
/*
* Copyright (C) 2016 Freie Universität Berlin
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @{
*
* @file
* @author Martine Lenders <m.lenders@fu-berlin.de>
*/
#include <stdint.h>
#include "embUnit/embUnit.h"
#include "checksum/ucrc16.h"
#include "tests-checksum.h"
#define CRC16_CCITT_SEED (0x1d0f)
/* equivalent to crc16-ccitt version */
static int calc_and_compare_crc_be_with_update(const uint8_t *buf,
size_t len, size_t split, uint16_t expected)
{
uint16_t result = ucrc16_calc_be(buf, split, UCRC16_CCITT_POLY_BE,
CRC16_CCITT_SEED);
result = ucrc16_calc_be(buf + split, len - split, UCRC16_CCITT_POLY_BE,
result);
return result == expected;
}
static int calc_and_compare_crc_be(const uint8_t *buf, size_t len,
uint16_t expected)
{
uint16_t result = ucrc16_calc_be(buf, len, UCRC16_CCITT_POLY_BE,
CRC16_CCITT_SEED);
return result == expected;
}
static void test_checksum_ucrc16_be_sequence_empty(void)
{
uint8_t buf[] = "";
uint16_t expect = CRC16_CCITT_SEED;
TEST_ASSERT(calc_and_compare_crc_be(buf, sizeof(buf) - 1, expect));
TEST_ASSERT(calc_and_compare_crc_be_with_update(buf, sizeof(buf) - 1,
(sizeof(buf) - 1) / 2, expect));
}
static void test_checksum_ucrc16_be_sequence_1a(void)
{
uint8_t buf[] = "A";
uint16_t expect = 0x9479;
TEST_ASSERT(calc_and_compare_crc_be(buf, sizeof(buf) - 1, expect));
TEST_ASSERT(calc_and_compare_crc_be_with_update(buf, sizeof(buf) - 1,
(sizeof(buf) - 1) / 2, expect));
}
static void test_checksum_ucrc16_be_sequence_256a(void)
{
uint8_t buf[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
uint16_t expect = 0xE938;
TEST_ASSERT(calc_and_compare_crc_be(buf, sizeof(buf) - 1, expect));
TEST_ASSERT(calc_and_compare_crc_be_with_update(buf, sizeof(buf) - 1,
(sizeof(buf) - 1) / 2, expect));
}
static void test_checksum_ucrc16_be_sequence_1to9(void)
{
uint8_t buf[] = "123456789";
uint16_t expect = 0xE5CC;
TEST_ASSERT(calc_and_compare_crc_be(buf, sizeof(buf) - 1, expect));
TEST_ASSERT(calc_and_compare_crc_be_with_update(buf, sizeof(buf)
- 1, (sizeof(buf) - 1) / 2, expect));
}
static void test_checksum_ucrc16_be_sequence_4bytes(void)
{
uint8_t buf[] = { 0x12, 0x34, 0x56, 0x78 };
uint16_t expect = 0xBA3C;
TEST_ASSERT(calc_and_compare_crc_be(buf, sizeof(buf), expect));
TEST_ASSERT(calc_and_compare_crc_be_with_update(buf, sizeof(buf),
sizeof(buf) / 2, expect));
}
static void test_checksum_ucrc16_le_ieee802164_frame(void)
{
/* test data taken from first packet in
* https://wiki.wireshark.org/SampleCaptures?action=AttachFile&do=get&target=6LoWPAN.pcap.gz */
uint8_t buf[] = { 0x41, 0xcc, 0xa4, 0xff, 0xff, 0x8a, 0x18, 0x00,
0xff, 0xff, 0xda, 0x1c, 0x00, 0x88, 0x18, 0x00,
0xff, 0xff, 0xda, 0x1c, 0x00, 0x41, 0x60, 0x00,
0x00, 0x00, 0x00, 0x19, 0x11, 0x40, 0xfe, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
0xda, 0xff, 0xff, 0x00, 0x18, 0x88, 0xfe, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c,
0xda, 0xff, 0xff, 0x00, 0x18, 0x8a, 0x04, 0x01,
0xf0, 0xb1, 0x00, 0x19, 0xea, 0x8a, 0x48, 0x65,
0x6c, 0x6c, 0x6f, 0x20, 0x30, 0x30, 0x33, 0x20,
0x30, 0x78, 0x43, 0x35, 0x39, 0x41, 0x0a };
uint16_t expect = 0x31f9;
uint16_t result = ucrc16_calc_le(buf, sizeof(buf), UCRC16_CCITT_POLY_LE, 0);
TEST_ASSERT_EQUAL_INT(expect, result);
}
Test *tests_checksum_ucrc16_tests(void)
{
EMB_UNIT_TESTFIXTURES(fixtures) {
/* Reference values according to
* http://srecord.sourceforge.net/crc16-ccitt.html */
new_TestFixture(test_checksum_ucrc16_be_sequence_empty),
new_TestFixture(test_checksum_ucrc16_be_sequence_1a),
new_TestFixture(test_checksum_ucrc16_be_sequence_256a),
new_TestFixture(test_checksum_ucrc16_be_sequence_1to9),
new_TestFixture(test_checksum_ucrc16_be_sequence_4bytes),
new_TestFixture(test_checksum_ucrc16_le_ieee802164_frame),
};
EMB_UNIT_TESTCALLER(checksum_ucrc16_tests, NULL, NULL, fixtures);
return (Test *)&checksum_ucrc16_tests;
}

1
tests/unittests/tests-checksum/tests-checksum.c

@ -13,4 +13,5 @@ void tests_checksum(void)
TESTS_RUN(tests_checksum_crc16_ccitt_tests());
TESTS_RUN(tests_checksum_fletcher16_tests());
TESTS_RUN(tests_checksum_fletcher32_tests());
TESTS_RUN(tests_checksum_ucrc16_tests());
}

7
tests/unittests/tests-checksum/tests-checksum.h

@ -50,6 +50,13 @@ Test *tests_checksum_fletcher16_tests(void);
*/
Test *tests_checksum_fletcher32_tests(void);
/**
* @brief Generates tests for checksum/ucrc16.h
*
* @return embUnit tests if successful, NULL if not.
*/
Test *tests_checksum_ucrc16_tests(void);
#ifdef __cplusplus
}
#endif

Loading…
Cancel
Save