Pedro Correia / mbed-dev

Fork of mbed-dev by mbed official

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