From 107bf0ae630f86fa96d627e91f2035f7e49d8cb5 Mon Sep 17 00:00:00 2001 From: Martine Lenders Date: Sun, 13 Nov 2016 03:30:04 +0100 Subject: [PATCH] ucrc16: provide lightweight CRC16 implementation --- sys/checksum/doc.txt | 21 ++++++++ sys/checksum/ucrc16.c | 53 +++++++++++++++++++ sys/include/checksum/crc16_ccitt.h | 5 ++ sys/include/checksum/ucrc16.h | 82 ++++++++++++++++++++++++++++++ 4 files changed, 161 insertions(+) create mode 100644 sys/checksum/ucrc16.c create mode 100644 sys/include/checksum/ucrc16.h diff --git a/sys/checksum/doc.txt b/sys/checksum/doc.txt index c3fc700e56..9c96b4e9a9 100644 --- a/sys/checksum/doc.txt +++ b/sys/checksum/doc.txt @@ -1,4 +1,5 @@ /* + * Copyright 2016 Freie Universität Berlin * Copyright 2016 Ludwig Knüpfer * * This file is subject to the terms and conditions of the GNU Lesser @@ -10,4 +11,24 @@ * @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 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. */ diff --git a/sys/checksum/ucrc16.c b/sys/checksum/ucrc16.c new file mode 100644 index 0000000000..8f29cac8b9 --- /dev/null +++ b/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 + */ + +#include +#include + +#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; +} + +/** @} */ diff --git a/sys/include/checksum/crc16_ccitt.h b/sys/include/checksum/crc16_ccitt.h index 1eaded39aa..a6d198f465 100644 --- a/sys/include/checksum/crc16_ccitt.h +++ b/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 diff --git a/sys/include/checksum/ucrc16.h b/sys/include/checksum/ucrc16.h new file mode 100644 index 0000000000..393347b6a5 --- /dev/null +++ b/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_crc_ccitt this is a generalized + * implementation. One can easily exchange generator polynomials and starting + * seeds. + * + * @{ + * + * @file + * @brief ucrc16 definitions + * + * @author Martine Lenders + */ +#ifndef UCRC16_H_ +#define UCRC16_H_ + +#include +#include + +#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_ */ +/** @} */