Knight KE / Mbed OS Game_Master
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MbedCRC.h Source File

MbedCRC.h

00001 /* mbed Microcontroller Library
00002  * Copyright (c) 2018 ARM Limited
00003  *
00004  * Licensed under the Apache License, Version 2.0 (the "License");
00005  * you may not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  *     http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an "AS IS" BASIS,
00012  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 #ifndef MBED_CRC_API_H
00017 #define MBED_CRC_API_H
00018 
00019 #include "drivers/TableCRC.h"
00020 #include "hal/crc_api.h"
00021 #include "platform/mbed_assert.h"
00022 
00023 /* This is invalid warning from the compiler for below section of code
00024 if ((width < 8) && (NULL == _crc_table)) {
00025     p_crc = (uint32_t)(p_crc << (8 - width));
00026 }
00027 Compiler warns of the shift operation with width as it is width=(std::uint8_t),
00028 but we check for ( width < 8) before performing shift, so it should not be an issue.
00029 */
00030 #if defined ( __CC_ARM )
00031 #pragma diag_suppress 62  // Shift count is negative
00032 #elif defined ( __GNUC__ )
00033 #pragma GCC diagnostic push
00034 #pragma GCC diagnostic ignored "-Wshift-count-negative"
00035 #elif defined (__ICCARM__)
00036 #pragma diag_suppress=Pe062  // Shift count is negative
00037 #endif
00038 
00039 namespace mbed {
00040 /** \addtogroup drivers */
00041 /** @{*/
00042 
00043 /** CRC object provides CRC generation through hardware/software
00044  *
00045  *  ROM polynomial tables for supported polynomials (:: crc_polynomial_t) will be used for
00046  *  software CRC computation, if ROM tables are not available then CRC is computed runtime
00047  *  bit by bit for all data input.
00048  *
00049  *  @tparam  polynomial CRC polynomial value in hex
00050  *  @tparam  width CRC polynomial width
00051  *
00052  * Example: Compute CRC data
00053  * @code
00054  *
00055  *  #include "mbed.h"
00056  *
00057  *  int main() {
00058  *      MbedCRC<POLY_32BIT_ANSI, 32> ct;
00059  *
00060  *      char  test[] = "123456789";
00061  *      uint32_t crc = 0;
00062  *
00063  *      printf("\nPolynomial = 0x%lx  Width = %d \n", ct.get_polynomial(), ct.get_width());
00064  *
00065  *      ct.compute((void *)test, strlen((const char*)test), &crc);
00066  *
00067  *      printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
00068  *      return 0;
00069  *  }
00070  * @endcode
00071  * Example: Compute CRC with data available in parts
00072  * @code
00073  *
00074  *  #include "mbed.h"
00075  *  int main() {
00076  *      MbedCRC<POLY_32BIT_ANSI, 32> ct;
00077  *
00078  *      char  test[] = "123456789";
00079  *      uint32_t crc = 0;
00080  *
00081  *      printf("\nPolynomial = 0x%lx  Width = %d \n", ct.get_polynomial(), ct.get_width());
00082  *
00083  *      ct.compute_partial_start(&crc);
00084  *      ct.compute_partial((void *)&test, 4, &crc);
00085  *      ct.compute_partial((void *)&test[4], 5, &crc);
00086  *      ct.compute_partial_stop(&crc);
00087  *
00088  *      printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
00089  *      return 0;
00090  *  }
00091  * @endcode
00092  * @ingroup drivers
00093  */
00094 
00095 template <uint32_t polynomial=POLY_32BIT_ANSI, uint8_t width=32>
00096 class MbedCRC
00097 {
00098 public:
00099     enum CrcMode { HARDWARE = 0, TABLE, BITWISE };
00100 
00101 public:
00102     typedef uint64_t crc_data_size_t;
00103 
00104     /** Lifetime of CRC object
00105      *
00106      *  @param  initial_xor  Inital value/seed to Xor
00107      *  @param  final_xor  Final Xor value
00108      *  @param  reflect_data
00109      *  @param  reflect_remainder
00110 *  @note   Default constructor without any arguments is valid only for supported CRC polynomials. :: crc_polynomial_t
00111      *          MbedCRC <POLY_7BIT_SD, 7> ct; --- Valid POLY_7BIT_SD
00112      *          MbedCRC <0x1021, 16> ct; --- Valid POLY_16BIT_CCITT
00113      *          MbedCRC <POLY_16BIT_CCITT, 32> ct; --- Invalid, compilation error
00114      *          MbedCRC <POLY_16BIT_CCITT, 32> ct (i,f,rd,rr) Consturctor can be used for not supported polynomials
00115      *          MbedCRC<POLY_16BIT_CCITT, 16> sd(0, 0, false, false); Constructor can also be used for supported
00116      *             polynomials with different intial/final/reflect values
00117      *
00118      */
00119     MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) :
00120                     _initial_value(initial_xor), _final_xor(final_xor), _reflect_data(reflect_data),
00121                     _reflect_remainder(reflect_remainder), _crc_table(NULL)
00122     {
00123         mbed_crc_ctor();
00124     }
00125     MbedCRC();
00126     virtual ~MbedCRC()
00127     {
00128         // Do nothing
00129     }
00130 
00131     /** Compute CRC for the data input
00132      *
00133      *  @param  buffer  Data bytes
00134      *  @param  size  Size of data
00135      *  @param  crc  CRC is the output value
00136      *  @return  0 on success, negative error code on failure
00137      */
00138     int32_t compute(void *buffer, crc_data_size_t size, uint32_t *crc)
00139     {
00140         MBED_ASSERT(crc != NULL);
00141         int32_t status;
00142         if (0 != (status = compute_partial_start(crc))) {
00143             *crc = 0;
00144             return status;
00145         }
00146         if (0 != (status = compute_partial(buffer, size, crc))) {
00147             *crc = 0;
00148             return status;
00149         }
00150         if (0 != (status = compute_partial_stop(crc))) {
00151             *crc = 0;
00152             return status;
00153         }
00154         return 0;
00155     }
00156 
00157     /** Compute partial CRC for the data input.
00158      *
00159      *  CRC data if not available fully, CRC can be computed in parts with available data.
00160      *  Previous CRC output should be passed as argument to the current compute_partial call.
00161      *  @pre: Call \ref compute_partial_start to start the partial CRC calculation.
00162      *  @post: Call \ref compute_partial_stop to get the final CRC value.
00163      *
00164      *  @param  buffer  Data bytes
00165      *  @param  size  Size of data
00166      *  @param  crc  CRC value is intermediate CRC value filled by API.
00167      *  @return  0  on success or a negative error code on failure
00168      *  @note: CRC as output in compute_partial is not final CRC value, call @ref compute_partial_stop
00169      *         to get final correct CRC value.
00170      */
00171     int32_t compute_partial(void *buffer, crc_data_size_t size, uint32_t *crc)
00172     {
00173         switch (_mode)
00174         {
00175             case HARDWARE:
00176 #ifdef DEVICE_CRC
00177                 hal_crc_compute_partial((uint8_t *)buffer, size);
00178 #endif // DEVICE_CRC
00179                 *crc = 0;
00180                 return 0;
00181             case TABLE:
00182                 return table_compute_partial(buffer, size, crc);
00183             case BITWISE:
00184                 return bitwise_compute_partial(buffer, size, crc);
00185         }
00186 
00187         return -1;
00188     }
00189 
00190     /** Compute partial start, indicate start of partial computation
00191      *
00192      *  This API should be called before performing any partial computation
00193      *  with compute_partial API.
00194      *
00195      *  @param  crc  Initial CRC value set by the API
00196      *  @return  0  on success or a negative in case of failure
00197      *  @note: CRC is an out parameter and must be reused with compute_partial
00198      *         and compute_partial_stop without any modifications in application.
00199      */
00200     int32_t compute_partial_start(uint32_t *crc)
00201     {
00202         MBED_ASSERT(crc != NULL);
00203 
00204 #ifdef DEVICE_CRC
00205         if (_mode == HARDWARE) {
00206             crc_mbed_config_t config;
00207             config.polynomial  = polynomial;
00208             config.width       = width;
00209             config.initial_xor = _initial_value;
00210             config.final_xor   = _final_xor;
00211             config.reflect_in  = _reflect_data;
00212             config.reflect_out = _reflect_remainder;
00213 
00214             hal_crc_compute_partial_start(&config);
00215         }
00216 #endif // DEVICE_CRC
00217 
00218         *crc = _initial_value;
00219         return 0;
00220     }
00221 
00222     /** Get the final CRC value of partial computation.
00223      *
00224      *  CRC value available in partial computation is not correct CRC, as some
00225      *  algorithms require remainder to be reflected and final value to be XORed
00226      *  This API is used to perform final computation to get correct CRC value.
00227      *
00228      *  @param crc  CRC result
00229      */
00230     int32_t compute_partial_stop(uint32_t *crc)
00231     {
00232         MBED_ASSERT(crc != NULL);
00233 
00234         if (_mode == HARDWARE) {
00235 #ifdef DEVICE_CRC
00236             *crc = hal_crc_get_result();
00237             return 0;
00238 #else
00239             return -1;
00240 #endif
00241         }
00242 
00243         uint32_t p_crc = *crc;
00244         if ((width < 8) && (NULL == _crc_table)) {
00245             p_crc = (uint32_t)(p_crc << (8 - width));
00246         }
00247         *crc = (reflect_remainder(p_crc) ^ _final_xor) & get_crc_mask();
00248         return 0;
00249     }
00250 
00251     /** Get the current CRC polynomial
00252      *
00253      * @return  Polynomial value
00254      */
00255     uint32_t get_polynomial(void) const
00256     {
00257         return polynomial;
00258     }
00259 
00260     /** Get the current CRC width
00261      *
00262      * @return  CRC width
00263      */
00264     uint8_t get_width(void) const
00265     {
00266         return width;
00267     }
00268 
00269 private:
00270     uint32_t _initial_value;
00271     uint32_t _final_xor;
00272     bool _reflect_data;
00273     bool _reflect_remainder;
00274     uint32_t *_crc_table;
00275     CrcMode _mode;
00276 
00277     /** Get the current CRC data size
00278      *
00279      * @return  CRC data size in bytes
00280      */
00281     uint8_t get_data_size(void) const
00282     {
00283         return (width <= 8 ? 1 : (width <= 16 ? 2 : 4));
00284     }
00285 
00286     /** Get the top bit of current CRC
00287      *
00288      * @return  Top bit is set high for respective data width of current CRC
00289      *          Top bit for CRC width less then 8 bits will be set as 8th bit.
00290      */
00291     uint32_t get_top_bit(void) const
00292     {
00293         return (width < 8 ? (1u << 7) : (uint32_t)(1ul << (width - 1)));
00294     }
00295 
00296     /** Get the CRC data mask
00297      *
00298      * @return  CRC data mask is generated based on current CRC width
00299      */
00300     uint32_t get_crc_mask(void) const
00301     {
00302         return (width < 8 ? ((1u << 8) - 1) : (uint32_t)((uint64_t)(1ull << width) - 1));
00303     }
00304 
00305     /** Final value of CRC is reflected
00306      *
00307      * @param  data final crc value, which should be reflected
00308      * @return  Reflected CRC value
00309      */
00310     uint32_t reflect_remainder(uint32_t data) const
00311     {
00312         if (_reflect_remainder) {
00313             uint32_t reflection = 0x0;
00314             uint8_t const nBits = (width < 8 ? 8 : width);
00315 
00316             for (uint8_t bit = 0; bit < nBits; ++bit) {
00317                 if (data & 0x01) {
00318                     reflection |= (1 << ((nBits - 1) - bit));
00319                 }
00320                 data = (data >> 1);
00321             }
00322             return (reflection);
00323         } else {
00324             return data;
00325         }
00326     }
00327 
00328     /** Data bytes are reflected
00329      *
00330      * @param  data value to be reflected
00331      * @return  Reflected data value
00332      */
00333     uint32_t reflect_bytes(uint32_t data) const
00334     {
00335         if(_reflect_data) {
00336             uint32_t reflection = 0x0;
00337 
00338             for (uint8_t bit = 0; bit < 8; ++bit) {
00339                 if (data & 0x01) {
00340                     reflection |= (1 << (7 - bit));
00341                 }
00342                 data = (data >> 1);
00343             }
00344             return (reflection);
00345         } else {
00346             return data;
00347         }
00348     }
00349 
00350     /** Bitwise CRC computation
00351      *
00352      * @param  buffer  data buffer
00353      * @param  size  size of the data
00354      * @param  crc  CRC value is filled in, but the value is not the final
00355      * @return  0  on success or a negative error code on failure
00356      */
00357     int32_t bitwise_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const
00358     {
00359         MBED_ASSERT(crc != NULL);
00360         MBED_ASSERT(buffer != NULL);
00361 
00362         const uint8_t *data = static_cast<const uint8_t *>(buffer);
00363         uint32_t p_crc = *crc;
00364 
00365         if (width < 8) {
00366             uint8_t data_byte;
00367             for (crc_data_size_t byte = 0; byte < size; byte++) {
00368                 data_byte = reflect_bytes(data[byte]);
00369                 for (uint8_t bit = 8; bit > 0; --bit) {
00370                     p_crc <<= 1;
00371                     if (( data_byte ^ p_crc) & get_top_bit()) {
00372                         p_crc ^= polynomial;
00373                     }
00374                     data_byte <<= 1;
00375                 }
00376             }
00377         } else {
00378             for (crc_data_size_t byte = 0; byte < size; byte++) {
00379                 p_crc ^= (reflect_bytes(data[byte]) << (width - 8));
00380 
00381                 // Perform modulo-2 division, a bit at a time
00382                 for (uint8_t bit = 8; bit > 0; --bit) {
00383                     if (p_crc & get_top_bit()) {
00384                         p_crc = (p_crc << 1) ^ polynomial;
00385                     } else {
00386                         p_crc = (p_crc << 1);
00387                     }
00388                 }
00389             }
00390         }
00391         *crc = p_crc & get_crc_mask();
00392         return 0;
00393     }
00394 
00395      /** CRC computation using ROM tables
00396      *
00397      * @param  buffer  data buffer
00398      * @param  size  size of the data
00399      * @param  crc  CRC value is filled in, but the value is not the final
00400      * @return  0  on success or a negative error code on failure
00401      */
00402     int32_t table_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const
00403     {
00404         MBED_ASSERT(crc != NULL);
00405         MBED_ASSERT(buffer != NULL);
00406 
00407         const uint8_t *data = static_cast<const uint8_t *>(buffer);
00408         uint32_t p_crc = *crc;
00409         uint8_t data_byte = 0;
00410 
00411         if (width <= 8) {
00412             uint8_t *crc_table = (uint8_t *)_crc_table;
00413             for (crc_data_size_t byte = 0; byte < size; byte++) {
00414                 data_byte = reflect_bytes(data[byte]) ^ p_crc;
00415                 p_crc = crc_table[data_byte];
00416             }
00417         } else if (width <= 16) {
00418             uint16_t *crc_table = (uint16_t *)_crc_table;
00419             for (crc_data_size_t byte = 0; byte < size; byte++) {
00420                 data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8));
00421                 p_crc = crc_table[data_byte] ^ (p_crc << 8);
00422             }
00423         } else {
00424             uint32_t *crc_table = (uint32_t *)_crc_table;
00425             for (crc_data_size_t byte = 0; byte < size; byte++) {
00426                 data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8));
00427                 p_crc = crc_table[data_byte] ^ (p_crc << 8);
00428             }
00429         }
00430         *crc = p_crc & get_crc_mask();
00431         return 0;
00432     }
00433 
00434     /** Constructor init called from all specialized cases of constructor
00435      *  Note: All construtor common code should be in this function.
00436      */
00437     void mbed_crc_ctor(void)
00438     {
00439         MBED_STATIC_ASSERT(width <= 32, "Max 32-bit CRC supported");
00440 
00441         _mode = (_crc_table != NULL) ? TABLE : BITWISE;
00442 
00443 #ifdef DEVICE_CRC
00444         crc_mbed_config_t config;
00445         config.polynomial  = polynomial;
00446         config.width       = width;
00447         config.initial_xor = _initial_value;
00448         config.final_xor   = _final_xor;
00449         config.reflect_in  = _reflect_data;
00450         config.reflect_out = _reflect_remainder;
00451 
00452         if (hal_crc_is_supported(&config)) {
00453             _mode = HARDWARE;
00454         }
00455 #endif
00456     }
00457 };
00458 
00459 #if   defined ( __CC_ARM )
00460 #elif defined ( __GNUC__ )
00461 #pragma GCC diagnostic pop
00462 #elif defined (__ICCARM__)
00463 #endif
00464 
00465 /** @}*/
00466 } // namespace mbed
00467 
00468 #endif