Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
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 #include "platform/SingletonPtr.h" 00023 #include "platform/PlatformMutex.h" 00024 00025 /* This is invalid warning from the compiler for below section of code 00026 if ((width < 8) && (NULL == _crc_table)) { 00027 p_crc = (uint32_t)(p_crc << (8 - width)); 00028 } 00029 Compiler warns of the shift operation with width as it is width=(std::uint8_t), 00030 but we check for ( width < 8) before performing shift, so it should not be an issue. 00031 */ 00032 #if defined ( __CC_ARM ) 00033 #pragma diag_suppress 62 // Shift count is negative 00034 #elif defined ( __GNUC__ ) 00035 #pragma GCC diagnostic push 00036 #pragma GCC diagnostic ignored "-Wshift-count-negative" 00037 #elif defined (__ICCARM__) 00038 #pragma diag_suppress=Pe062 // Shift count is negative 00039 #endif 00040 00041 namespace mbed { 00042 /** \addtogroup drivers */ 00043 /** @{*/ 00044 00045 /** CRC object provides CRC generation through hardware/software 00046 * 00047 * ROM polynomial tables for supported polynomials (:: crc_polynomial_t) will be used for 00048 * software CRC computation, if ROM tables are not available then CRC is computed runtime 00049 * bit by bit for all data input. 00050 * @note Synchronization level: Thread safe 00051 * 00052 * @tparam polynomial CRC polynomial value in hex 00053 * @tparam width CRC polynomial width 00054 * 00055 * Example: Compute CRC data 00056 * @code 00057 * 00058 * #include "mbed.h" 00059 * 00060 * int main() { 00061 * MbedCRC<POLY_32BIT_ANSI, 32> ct; 00062 * 00063 * char test[] = "123456789"; 00064 * uint32_t crc = 0; 00065 * 00066 * printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width()); 00067 * 00068 * ct.compute((void *)test, strlen((const char*)test), &crc); 00069 * 00070 * printf("The CRC of data \"123456789\" is : 0x%lx\n", crc); 00071 * return 0; 00072 * } 00073 * @endcode 00074 * Example: Compute CRC with data available in parts 00075 * @code 00076 * 00077 * #include "mbed.h" 00078 * int main() { 00079 * MbedCRC<POLY_32BIT_ANSI, 32> ct; 00080 * 00081 * char test[] = "123456789"; 00082 * uint32_t crc = 0; 00083 * 00084 * printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width()); 00085 * ct.compute_partial_start(&crc); 00086 * ct.compute_partial((void *)&test, 4, &crc); 00087 * ct.compute_partial((void *)&test[4], 5, &crc); 00088 * ct.compute_partial_stop(&crc); 00089 * printf("The CRC of data \"123456789\" is : 0x%lx\n", crc); 00090 * return 0; 00091 * } 00092 * @endcode 00093 * @ingroup drivers 00094 */ 00095 00096 extern SingletonPtr<PlatformMutex> mbed_crc_mutex; 00097 00098 template <uint32_t polynomial = POLY_32BIT_ANSI, uint8_t width = 32> 00099 class MbedCRC { 00100 00101 public: 00102 enum CrcMode 00103 { 00104 #ifdef DEVICE_CRC 00105 HARDWARE = 0, 00106 #endif 00107 TABLE = 1, 00108 BITWISE 00109 }; 00110 00111 typedef uint64_t crc_data_size_t; 00112 00113 /** Lifetime of CRC object 00114 * 00115 * @param initial_xor Inital value/seed to Xor 00116 * @param final_xor Final Xor value 00117 * @param reflect_data 00118 * @param reflect_remainder 00119 * @note Default constructor without any arguments is valid only for supported CRC polynomials. :: crc_polynomial_t 00120 * MbedCRC <POLY_7BIT_SD, 7> ct; --- Valid POLY_7BIT_SD 00121 * MbedCRC <0x1021, 16> ct; --- Valid POLY_16BIT_CCITT 00122 * MbedCRC <POLY_16BIT_CCITT, 32> ct; --- Invalid, compilation error 00123 * MbedCRC <POLY_16BIT_CCITT, 32> ct (i,f,rd,rr) Consturctor can be used for not supported polynomials 00124 * MbedCRC<POLY_16BIT_CCITT, 16> sd(0, 0, false, false); Constructor can also be used for supported 00125 * polynomials with different intial/final/reflect values 00126 * 00127 */ 00128 MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) : 00129 _initial_value(initial_xor), _final_xor(final_xor), _reflect_data(reflect_data), 00130 _reflect_remainder(reflect_remainder) 00131 { 00132 mbed_crc_ctor(); 00133 } 00134 MbedCRC(); 00135 virtual ~MbedCRC() 00136 { 00137 // Do nothing 00138 } 00139 00140 /** Compute CRC for the data input 00141 * Compute CRC performs the initialization, computation and collection of 00142 * final CRC. 00143 * 00144 * @param buffer Data bytes 00145 * @param size Size of data 00146 * @param crc CRC is the output value 00147 * @return 0 on success, negative error code on failure 00148 */ 00149 int32_t compute(void *buffer, crc_data_size_t size, uint32_t *crc) 00150 { 00151 MBED_ASSERT(crc != NULL); 00152 int32_t status = 0; 00153 00154 status = compute_partial_start(crc); 00155 if (0 != status) { 00156 unlock(); 00157 return status; 00158 } 00159 00160 status = compute_partial(buffer, size, crc); 00161 if (0 != status) { 00162 unlock(); 00163 return status; 00164 } 00165 00166 status = compute_partial_stop(crc); 00167 if (0 != status) { 00168 *crc = 0; 00169 } 00170 00171 return status; 00172 00173 } 00174 00175 /** Compute partial CRC for the data input. 00176 * 00177 * CRC data if not available fully, CRC can be computed in parts with available data. 00178 * 00179 * In case of hardware, intermediate values and states are saved by hardware and mutex 00180 * locking is used to serialize access to hardware CRC. 00181 * 00182 * In case of software CRC, previous CRC output should be passed as argument to the 00183 * current compute_partial call. Please note the intermediate CRC value is maintained by 00184 * application and not the driver. 00185 * 00186 * @pre: Call `compute_partial_start` to start the partial CRC calculation. 00187 * @post: Call `compute_partial_stop` to get the final CRC value. 00188 * 00189 * @param buffer Data bytes 00190 * @param size Size of data 00191 * @param crc CRC value is intermediate CRC value filled by API. 00192 * @return 0 on success or a negative error code on failure 00193 * @note: CRC as output in compute_partial is not final CRC value, call `compute_partial_stop` 00194 * to get final correct CRC value. 00195 */ 00196 int32_t compute_partial(void *buffer, crc_data_size_t size, uint32_t *crc) 00197 { 00198 int32_t status = 0; 00199 00200 switch (_mode) { 00201 #ifdef DEVICE_CRC 00202 case HARDWARE: 00203 hal_crc_compute_partial((uint8_t *)buffer, size); 00204 *crc = 0; 00205 break; 00206 #endif 00207 case TABLE: 00208 status = table_compute_partial(buffer, size, crc); 00209 break; 00210 case BITWISE: 00211 status = bitwise_compute_partial(buffer, size, crc); 00212 break; 00213 default: 00214 status = -1; 00215 break; 00216 } 00217 00218 return status; 00219 } 00220 00221 /** Compute partial start, indicate start of partial computation 00222 * 00223 * This API should be called before performing any partial computation 00224 * with compute_partial API. 00225 * 00226 * @param crc Initial CRC value set by the API 00227 * @return 0 on success or a negative in case of failure 00228 * @note: CRC is an out parameter and must be reused with compute_partial 00229 * and `compute_partial_stop` without any modifications in application. 00230 */ 00231 int32_t compute_partial_start(uint32_t *crc) 00232 { 00233 MBED_ASSERT(crc != NULL); 00234 00235 #ifdef DEVICE_CRC 00236 if (_mode == HARDWARE) { 00237 lock(); 00238 crc_mbed_config_t config; 00239 config.polynomial = polynomial; 00240 config.width = width; 00241 config.initial_xor = _initial_value; 00242 config.final_xor = _final_xor; 00243 config.reflect_in = _reflect_data; 00244 config.reflect_out = _reflect_remainder; 00245 00246 hal_crc_compute_partial_start(&config); 00247 } 00248 #endif 00249 00250 *crc = _initial_value; 00251 return 0; 00252 } 00253 00254 /** Get the final CRC value of partial computation. 00255 * 00256 * CRC value available in partial computation is not correct CRC, as some 00257 * algorithms require remainder to be reflected and final value to be XORed 00258 * This API is used to perform final computation to get correct CRC value. 00259 * 00260 * @param crc CRC result 00261 */ 00262 int32_t compute_partial_stop(uint32_t *crc) 00263 { 00264 MBED_ASSERT(crc != NULL); 00265 00266 #ifdef DEVICE_CRC 00267 if (_mode == HARDWARE) { 00268 *crc = hal_crc_get_result(); 00269 unlock(); 00270 return 0; 00271 } 00272 #endif 00273 uint32_t p_crc = *crc; 00274 if ((width < 8) && (NULL == _crc_table)) { 00275 p_crc = (uint32_t)(p_crc << (8 - width)); 00276 } 00277 // Optimized algorithm for 32BitANSI does not need additional reflect_remainder 00278 if ((TABLE == _mode) && (POLY_32BIT_REV_ANSI == polynomial)) { 00279 *crc = (p_crc ^ _final_xor) & get_crc_mask(); 00280 } else { 00281 *crc = (reflect_remainder(p_crc) ^ _final_xor) & get_crc_mask(); 00282 } 00283 unlock(); 00284 return 0; 00285 } 00286 00287 /** Get the current CRC polynomial 00288 * 00289 * @return Polynomial value 00290 */ 00291 uint32_t get_polynomial(void) const 00292 { 00293 return polynomial; 00294 } 00295 00296 /** Get the current CRC width 00297 * 00298 * @return CRC width 00299 */ 00300 uint8_t get_width(void) const 00301 { 00302 return width; 00303 } 00304 00305 private: 00306 uint32_t _initial_value; 00307 uint32_t _final_xor; 00308 bool _reflect_data; 00309 bool _reflect_remainder; 00310 uint32_t *_crc_table; 00311 CrcMode _mode; 00312 00313 /** Acquire exclusive access to CRC hardware/software 00314 */ 00315 void lock() 00316 { 00317 #ifdef DEVICE_CRC 00318 if (_mode == HARDWARE) { 00319 mbed_crc_mutex->lock(); 00320 } 00321 #endif 00322 } 00323 00324 /** Release exclusive access to CRC hardware/software 00325 */ 00326 virtual void unlock() 00327 { 00328 #ifdef DEVICE_CRC 00329 if (_mode == HARDWARE) { 00330 mbed_crc_mutex->unlock(); 00331 } 00332 #endif 00333 } 00334 00335 /** Get the current CRC data size 00336 * 00337 * @return CRC data size in bytes 00338 */ 00339 uint8_t get_data_size(void) const 00340 { 00341 return (width <= 8 ? 1 : (width <= 16 ? 2 : 4)); 00342 } 00343 00344 /** Get the top bit of current CRC 00345 * 00346 * @return Top bit is set high for respective data width of current CRC 00347 * Top bit for CRC width less then 8 bits will be set as 8th bit. 00348 */ 00349 uint32_t get_top_bit(void) const 00350 { 00351 return (width < 8 ? (1u << 7) : (uint32_t)(1ul << (width - 1))); 00352 } 00353 00354 /** Get the CRC data mask 00355 * 00356 * @return CRC data mask is generated based on current CRC width 00357 */ 00358 uint32_t get_crc_mask(void) const 00359 { 00360 return (width < 8 ? ((1u << 8) - 1) : (uint32_t)((uint64_t)(1ull << width) - 1)); 00361 } 00362 00363 /** Final value of CRC is reflected 00364 * 00365 * @param data final crc value, which should be reflected 00366 * @return Reflected CRC value 00367 */ 00368 uint32_t reflect_remainder(uint32_t data) const 00369 { 00370 if (_reflect_remainder) { 00371 uint32_t reflection = 0x0; 00372 uint8_t const nBits = (width < 8 ? 8 : width); 00373 00374 for (uint8_t bit = 0; bit < nBits; ++bit) { 00375 if (data & 0x01) { 00376 reflection |= (1 << ((nBits - 1) - bit)); 00377 } 00378 data = (data >> 1); 00379 } 00380 return (reflection); 00381 } else { 00382 return data; 00383 } 00384 } 00385 00386 /** Data bytes are reflected 00387 * 00388 * @param data value to be reflected 00389 * @return Reflected data value 00390 */ 00391 uint32_t reflect_bytes(uint32_t data) const 00392 { 00393 if (_reflect_data) { 00394 uint32_t reflection = 0x0; 00395 00396 for (uint8_t bit = 0; bit < 8; ++bit) { 00397 if (data & 0x01) { 00398 reflection |= (1 << (7 - bit)); 00399 } 00400 data = (data >> 1); 00401 } 00402 return (reflection); 00403 } else { 00404 return data; 00405 } 00406 } 00407 00408 /** Bitwise CRC computation 00409 * 00410 * @param buffer data buffer 00411 * @param size size of the data 00412 * @param crc CRC value is filled in, but the value is not the final 00413 * @return 0 on success or a negative error code on failure 00414 */ 00415 int32_t bitwise_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const 00416 { 00417 MBED_ASSERT(crc != NULL); 00418 00419 const uint8_t *data = static_cast<const uint8_t *>(buffer); 00420 uint32_t p_crc = *crc; 00421 00422 if (width < 8) { 00423 uint8_t data_byte; 00424 for (crc_data_size_t byte = 0; byte < size; byte++) { 00425 data_byte = reflect_bytes(data[byte]); 00426 for (uint8_t bit = 8; bit > 0; --bit) { 00427 p_crc <<= 1; 00428 if ((data_byte ^ p_crc) & get_top_bit()) { 00429 p_crc ^= polynomial; 00430 } 00431 data_byte <<= 1; 00432 } 00433 } 00434 } else { 00435 for (crc_data_size_t byte = 0; byte < size; byte++) { 00436 p_crc ^= (reflect_bytes(data[byte]) << (width - 8)); 00437 00438 // Perform modulo-2 division, a bit at a time 00439 for (uint8_t bit = 8; bit > 0; --bit) { 00440 if (p_crc & get_top_bit()) { 00441 p_crc = (p_crc << 1) ^ polynomial; 00442 } else { 00443 p_crc = (p_crc << 1); 00444 } 00445 } 00446 } 00447 } 00448 *crc = p_crc & get_crc_mask(); 00449 return 0; 00450 } 00451 00452 /** CRC computation using ROM tables 00453 * 00454 * @param buffer data buffer 00455 * @param size size of the data 00456 * @param crc CRC value is filled in, but the value is not the final 00457 * @return 0 on success or a negative error code on failure 00458 */ 00459 int32_t table_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const 00460 { 00461 MBED_ASSERT(crc != NULL); 00462 00463 const uint8_t *data = static_cast<const uint8_t *>(buffer); 00464 uint32_t p_crc = *crc; 00465 uint8_t data_byte = 0; 00466 00467 if (width <= 8) { 00468 uint8_t *crc_table = (uint8_t *)_crc_table; 00469 for (crc_data_size_t byte = 0; byte < size; byte++) { 00470 data_byte = reflect_bytes(data[byte]) ^ p_crc; 00471 p_crc = crc_table[data_byte]; 00472 } 00473 } else if (width <= 16) { 00474 uint16_t *crc_table = (uint16_t *)_crc_table; 00475 for (crc_data_size_t byte = 0; byte < size; byte++) { 00476 data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8)); 00477 p_crc = crc_table[data_byte] ^ (p_crc << 8); 00478 } 00479 } else { 00480 uint32_t *crc_table = (uint32_t *)_crc_table; 00481 if (POLY_32BIT_REV_ANSI == polynomial) { 00482 for (crc_data_size_t i = 0; i < size; i++) { 00483 p_crc = (p_crc >> 4) ^ crc_table[(p_crc ^ (data[i] >> 0)) & 0xf]; 00484 p_crc = (p_crc >> 4) ^ crc_table[(p_crc ^ (data[i] >> 4)) & 0xf]; 00485 } 00486 } 00487 else { 00488 for (crc_data_size_t byte = 0; byte < size; byte++) { 00489 data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8)); 00490 p_crc = crc_table[data_byte] ^ (p_crc << 8); 00491 } 00492 } 00493 } 00494 *crc = p_crc & get_crc_mask(); 00495 return 0; 00496 } 00497 00498 /** Constructor init called from all specialized cases of constructor 00499 * Note: All construtor common code should be in this function. 00500 */ 00501 void mbed_crc_ctor(void) 00502 { 00503 MBED_STATIC_ASSERT(width <= 32, "Max 32-bit CRC supported"); 00504 00505 #ifdef DEVICE_CRC 00506 if (POLY_32BIT_REV_ANSI == polynomial) { 00507 _crc_table = (uint32_t *)Table_CRC_32bit_Rev_ANSI; 00508 _mode = TABLE; 00509 return; 00510 } 00511 crc_mbed_config_t config; 00512 config.polynomial = polynomial; 00513 config.width = width; 00514 config.initial_xor = _initial_value; 00515 config.final_xor = _final_xor; 00516 config.reflect_in = _reflect_data; 00517 config.reflect_out = _reflect_remainder; 00518 00519 if (hal_crc_is_supported(&config)) { 00520 _mode = HARDWARE; 00521 return; 00522 } 00523 #endif 00524 00525 switch (polynomial) { 00526 case POLY_32BIT_ANSI: 00527 _crc_table = (uint32_t *)Table_CRC_32bit_ANSI; 00528 break; 00529 case POLY_32BIT_REV_ANSI: 00530 _crc_table = (uint32_t *)Table_CRC_32bit_Rev_ANSI; 00531 break; 00532 case POLY_8BIT_CCITT: 00533 _crc_table = (uint32_t *)Table_CRC_8bit_CCITT; 00534 break; 00535 case POLY_7BIT_SD: 00536 _crc_table = (uint32_t *)Table_CRC_7Bit_SD; 00537 break; 00538 case POLY_16BIT_CCITT: 00539 _crc_table = (uint32_t *)Table_CRC_16bit_CCITT; 00540 break; 00541 case POLY_16BIT_IBM: 00542 _crc_table = (uint32_t *)Table_CRC_16bit_IBM; 00543 break; 00544 default: 00545 _crc_table = NULL; 00546 break; 00547 } 00548 _mode = (_crc_table != NULL) ? TABLE : BITWISE; 00549 } 00550 }; 00551 00552 #if defined ( __CC_ARM ) 00553 #elif defined ( __GNUC__ ) 00554 #pragma GCC diagnostic pop 00555 #elif defined (__ICCARM__) 00556 #endif 00557 00558 /** @}*/ 00559 } // namespace mbed 00560 00561 #endif
Generated on Tue Aug 9 2022 00:37:15 by
1.7.2