123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- /* mbed Microcontroller Library
- * Copyright (c) 2018 ARM Limited
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #ifndef MBED_CRC_API_H
- #define MBED_CRC_API_H
- #include "drivers/TableCRC.h"
- #include "hal/crc_api.h"
- #include "platform/mbed_assert.h"
- /* This is invalid warning from the compiler for below section of code
- if ((width < 8) && (NULL == _crc_table)) {
- p_crc = (uint32_t)(p_crc << (8 - width));
- }
- Compiler warns of the shift operation with width as it is width=(std::uint8_t),
- but we check for ( width < 8) before performing shift, so it should not be an issue.
- */
- #if defined ( __CC_ARM )
- #pragma diag_suppress 62 // Shift count is negative
- #elif defined ( __GNUC__ )
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wshift-count-negative"
- #elif defined (__ICCARM__)
- #pragma diag_suppress=Pe062 // Shift count is negative
- #endif
- namespace mbed {
- /** \addtogroup drivers */
- /** @{*/
- /** CRC object provides CRC generation through hardware/software
- *
- * ROM polynomial tables for supported polynomials (:: crc_polynomial_t) will be used for
- * software CRC computation, if ROM tables are not available then CRC is computed runtime
- * bit by bit for all data input.
- *
- * @tparam polynomial CRC polynomial value in hex
- * @tparam width CRC polynomial width
- *
- * Example: Compute CRC data
- * @code
- *
- * #include "mbed.h"
- *
- * int main() {
- * MbedCRC<POLY_32BIT_ANSI, 32> ct;
- *
- * char test[] = "123456789";
- * uint32_t crc = 0;
- *
- * printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width());
- *
- * ct.compute((void *)test, strlen((const char*)test), &crc);
- *
- * printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
- * return 0;
- * }
- * @endcode
- * Example: Compute CRC with data available in parts
- * @code
- *
- * #include "mbed.h"
- * int main() {
- * MbedCRC<POLY_32BIT_ANSI, 32> ct;
- *
- * char test[] = "123456789";
- * uint32_t crc = 0;
- *
- * printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width());
- *
- * ct.compute_partial_start(&crc);
- * ct.compute_partial((void *)&test, 4, &crc);
- * ct.compute_partial((void *)&test[4], 5, &crc);
- * ct.compute_partial_stop(&crc);
- *
- * printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
- * return 0;
- * }
- * @endcode
- * @ingroup drivers
- */
- template <uint32_t polynomial = POLY_32BIT_ANSI, uint8_t width = 32>
- class MbedCRC {
- public:
- enum CrcMode { HARDWARE = 0, TABLE, BITWISE };
- public:
- typedef uint64_t crc_data_size_t;
- /** Lifetime of CRC object
- *
- * @param initial_xor Inital value/seed to Xor
- * @param final_xor Final Xor value
- * @param reflect_data
- * @param reflect_remainder
- * @note Default constructor without any arguments is valid only for supported CRC polynomials. :: crc_polynomial_t
- * MbedCRC <POLY_7BIT_SD, 7> ct; --- Valid POLY_7BIT_SD
- * MbedCRC <0x1021, 16> ct; --- Valid POLY_16BIT_CCITT
- * MbedCRC <POLY_16BIT_CCITT, 32> ct; --- Invalid, compilation error
- * MbedCRC <POLY_16BIT_CCITT, 32> ct (i,f,rd,rr) Consturctor can be used for not supported polynomials
- * MbedCRC<POLY_16BIT_CCITT, 16> sd(0, 0, false, false); Constructor can also be used for supported
- * polynomials with different intial/final/reflect values
- *
- */
- MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) :
- _initial_value(initial_xor), _final_xor(final_xor), _reflect_data(reflect_data),
- _reflect_remainder(reflect_remainder), _crc_table(NULL)
- {
- mbed_crc_ctor();
- }
- MbedCRC();
- virtual ~MbedCRC()
- {
- // Do nothing
- }
- /** Compute CRC for the data input
- *
- * @param buffer Data bytes
- * @param size Size of data
- * @param crc CRC is the output value
- * @return 0 on success, negative error code on failure
- */
- int32_t compute(void *buffer, crc_data_size_t size, uint32_t *crc)
- {
- MBED_ASSERT(crc != NULL);
- int32_t status;
- if (0 != (status = compute_partial_start(crc))) {
- *crc = 0;
- return status;
- }
- if (0 != (status = compute_partial(buffer, size, crc))) {
- *crc = 0;
- return status;
- }
- if (0 != (status = compute_partial_stop(crc))) {
- *crc = 0;
- return status;
- }
- return 0;
- }
- /** Compute partial CRC for the data input.
- *
- * CRC data if not available fully, CRC can be computed in parts with available data.
- * Previous CRC output should be passed as argument to the current compute_partial call.
- * @pre: Call \ref compute_partial_start to start the partial CRC calculation.
- * @post: Call \ref compute_partial_stop to get the final CRC value.
- *
- * @param buffer Data bytes
- * @param size Size of data
- * @param crc CRC value is intermediate CRC value filled by API.
- * @return 0 on success or a negative error code on failure
- * @note: CRC as output in compute_partial is not final CRC value, call @ref compute_partial_stop
- * to get final correct CRC value.
- */
- int32_t compute_partial(void *buffer, crc_data_size_t size, uint32_t *crc)
- {
- switch (_mode) {
- case HARDWARE:
- #ifdef DEVICE_CRC
- hal_crc_compute_partial((uint8_t *)buffer, size);
- #endif // DEVICE_CRC
- *crc = 0;
- return 0;
- case TABLE:
- return table_compute_partial(buffer, size, crc);
- case BITWISE:
- return bitwise_compute_partial(buffer, size, crc);
- }
- return -1;
- }
- /** Compute partial start, indicate start of partial computation
- *
- * This API should be called before performing any partial computation
- * with compute_partial API.
- *
- * @param crc Initial CRC value set by the API
- * @return 0 on success or a negative in case of failure
- * @note: CRC is an out parameter and must be reused with compute_partial
- * and compute_partial_stop without any modifications in application.
- */
- int32_t compute_partial_start(uint32_t *crc)
- {
- MBED_ASSERT(crc != NULL);
- #ifdef DEVICE_CRC
- if (_mode == HARDWARE) {
- crc_mbed_config_t config;
- config.polynomial = polynomial;
- config.width = width;
- config.initial_xor = _initial_value;
- config.final_xor = _final_xor;
- config.reflect_in = _reflect_data;
- config.reflect_out = _reflect_remainder;
- hal_crc_compute_partial_start(&config);
- }
- #endif // DEVICE_CRC
- *crc = _initial_value;
- return 0;
- }
- /** Get the final CRC value of partial computation.
- *
- * CRC value available in partial computation is not correct CRC, as some
- * algorithms require remainder to be reflected and final value to be XORed
- * This API is used to perform final computation to get correct CRC value.
- *
- * @param crc CRC result
- */
- int32_t compute_partial_stop(uint32_t *crc)
- {
- MBED_ASSERT(crc != NULL);
- if (_mode == HARDWARE) {
- #ifdef DEVICE_CRC
- *crc = hal_crc_get_result();
- return 0;
- #else
- return -1;
- #endif
- }
- uint32_t p_crc = *crc;
- if ((width < 8) && (NULL == _crc_table)) {
- p_crc = (uint32_t)(p_crc << (8 - width));
- }
- *crc = (reflect_remainder(p_crc) ^ _final_xor) & get_crc_mask();
- return 0;
- }
- /** Get the current CRC polynomial
- *
- * @return Polynomial value
- */
- uint32_t get_polynomial(void) const
- {
- return polynomial;
- }
- /** Get the current CRC width
- *
- * @return CRC width
- */
- uint8_t get_width(void) const
- {
- return width;
- }
- private:
- uint32_t _initial_value;
- uint32_t _final_xor;
- bool _reflect_data;
- bool _reflect_remainder;
- uint32_t *_crc_table;
- CrcMode _mode;
- /** Get the current CRC data size
- *
- * @return CRC data size in bytes
- */
- uint8_t get_data_size(void) const
- {
- return (width <= 8 ? 1 : (width <= 16 ? 2 : 4));
- }
- /** Get the top bit of current CRC
- *
- * @return Top bit is set high for respective data width of current CRC
- * Top bit for CRC width less then 8 bits will be set as 8th bit.
- */
- uint32_t get_top_bit(void) const
- {
- return (width < 8 ? (1u << 7) : (uint32_t)(1ul << (width - 1)));
- }
- /** Get the CRC data mask
- *
- * @return CRC data mask is generated based on current CRC width
- */
- uint32_t get_crc_mask(void) const
- {
- return (width < 8 ? ((1u << 8) - 1) : (uint32_t)((uint64_t)(1ull << width) - 1));
- }
- /** Final value of CRC is reflected
- *
- * @param data final crc value, which should be reflected
- * @return Reflected CRC value
- */
- uint32_t reflect_remainder(uint32_t data) const
- {
- if (_reflect_remainder) {
- uint32_t reflection = 0x0;
- uint8_t const nBits = (width < 8 ? 8 : width);
- for (uint8_t bit = 0; bit < nBits; ++bit) {
- if (data & 0x01) {
- reflection |= (1 << ((nBits - 1) - bit));
- }
- data = (data >> 1);
- }
- return (reflection);
- } else {
- return data;
- }
- }
- /** Data bytes are reflected
- *
- * @param data value to be reflected
- * @return Reflected data value
- */
- uint32_t reflect_bytes(uint32_t data) const
- {
- if (_reflect_data) {
- uint32_t reflection = 0x0;
- for (uint8_t bit = 0; bit < 8; ++bit) {
- if (data & 0x01) {
- reflection |= (1 << (7 - bit));
- }
- data = (data >> 1);
- }
- return (reflection);
- } else {
- return data;
- }
- }
- /** Bitwise CRC computation
- *
- * @param buffer data buffer
- * @param size size of the data
- * @param crc CRC value is filled in, but the value is not the final
- * @return 0 on success or a negative error code on failure
- */
- int32_t bitwise_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const
- {
- MBED_ASSERT(crc != NULL);
- MBED_ASSERT(buffer != NULL);
- const uint8_t *data = static_cast<const uint8_t *>(buffer);
- uint32_t p_crc = *crc;
- if (width < 8) {
- uint8_t data_byte;
- for (crc_data_size_t byte = 0; byte < size; byte++) {
- data_byte = reflect_bytes(data[byte]);
- for (uint8_t bit = 8; bit > 0; --bit) {
- p_crc <<= 1;
- if ((data_byte ^ p_crc) & get_top_bit()) {
- p_crc ^= polynomial;
- }
- data_byte <<= 1;
- }
- }
- } else {
- for (crc_data_size_t byte = 0; byte < size; byte++) {
- p_crc ^= (reflect_bytes(data[byte]) << (width - 8));
- // Perform modulo-2 division, a bit at a time
- for (uint8_t bit = 8; bit > 0; --bit) {
- if (p_crc & get_top_bit()) {
- p_crc = (p_crc << 1) ^ polynomial;
- } else {
- p_crc = (p_crc << 1);
- }
- }
- }
- }
- *crc = p_crc & get_crc_mask();
- return 0;
- }
- /** CRC computation using ROM tables
- *
- * @param buffer data buffer
- * @param size size of the data
- * @param crc CRC value is filled in, but the value is not the final
- * @return 0 on success or a negative error code on failure
- */
- int32_t table_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const
- {
- MBED_ASSERT(crc != NULL);
- MBED_ASSERT(buffer != NULL);
- const uint8_t *data = static_cast<const uint8_t *>(buffer);
- uint32_t p_crc = *crc;
- uint8_t data_byte = 0;
- if (width <= 8) {
- uint8_t *crc_table = (uint8_t *)_crc_table;
- for (crc_data_size_t byte = 0; byte < size; byte++) {
- data_byte = reflect_bytes(data[byte]) ^ p_crc;
- p_crc = crc_table[data_byte];
- }
- } else if (width <= 16) {
- uint16_t *crc_table = (uint16_t *)_crc_table;
- for (crc_data_size_t byte = 0; byte < size; byte++) {
- data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8));
- p_crc = crc_table[data_byte] ^ (p_crc << 8);
- }
- } else {
- uint32_t *crc_table = (uint32_t *)_crc_table;
- for (crc_data_size_t byte = 0; byte < size; byte++) {
- data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8));
- p_crc = crc_table[data_byte] ^ (p_crc << 8);
- }
- }
- *crc = p_crc & get_crc_mask();
- return 0;
- }
- /** Constructor init called from all specialized cases of constructor
- * Note: All construtor common code should be in this function.
- */
- void mbed_crc_ctor(void)
- {
- MBED_STATIC_ASSERT(width <= 32, "Max 32-bit CRC supported");
- _mode = (_crc_table != NULL) ? TABLE : BITWISE;
- #ifdef DEVICE_CRC
- crc_mbed_config_t config;
- config.polynomial = polynomial;
- config.width = width;
- config.initial_xor = _initial_value;
- config.final_xor = _final_xor;
- config.reflect_in = _reflect_data;
- config.reflect_out = _reflect_remainder;
- if (hal_crc_is_supported(&config)) {
- _mode = HARDWARE;
- }
- #endif
- }
- };
- #if defined ( __CC_ARM )
- #elif defined ( __GNUC__ )
- #pragma GCC diagnostic pop
- #elif defined (__ICCARM__)
- #endif
- /** @}*/
- } // namespace mbed
- #endif
|