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) Constructor 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. 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 * @return 0 on success or a negative in case of failure. 00262 */ 00263 int32_t compute_partial_stop(uint32_t *crc) 00264 { 00265 MBED_ASSERT(crc != NULL); 00266 00267 #ifdef DEVICE_CRC 00268 if (_mode == HARDWARE) { 00269 *crc = hal_crc_get_result(); 00270 unlock(); 00271 return 0; 00272 } 00273 #endif 00274 uint32_t p_crc = *crc; 00275 if ((width < 8) && (NULL == _crc_table)) { 00276 p_crc = (uint32_t)(p_crc << (8 - width)); 00277 } 00278 // Optimized algorithm for 32BitANSI does not need additional reflect_remainder 00279 if ((TABLE == _mode) && (POLY_32BIT_REV_ANSI == polynomial)) { 00280 *crc = (p_crc ^ _final_xor) & get_crc_mask(); 00281 } else { 00282 *crc = (reflect_remainder(p_crc) ^ _final_xor) & get_crc_mask(); 00283 } 00284 unlock(); 00285 return 0; 00286 } 00287 00288 /** Get the current CRC polynomial. 00289 * 00290 * @return Polynomial value 00291 */ 00292 uint32_t get_polynomial(void) const 00293 { 00294 return polynomial; 00295 } 00296 00297 /** Get the current CRC width 00298 * 00299 * @return CRC width 00300 */ 00301 uint8_t get_width(void) const 00302 { 00303 return width; 00304 } 00305 00306 private: 00307 uint32_t _initial_value; 00308 uint32_t _final_xor; 00309 bool _reflect_data; 00310 bool _reflect_remainder; 00311 uint32_t *_crc_table; 00312 CrcMode _mode; 00313 00314 /** Acquire exclusive access to CRC hardware/software. 00315 */ 00316 void lock() 00317 { 00318 #ifdef DEVICE_CRC 00319 if (_mode == HARDWARE) { 00320 mbed_crc_mutex->lock(); 00321 } 00322 #endif 00323 } 00324 00325 /** Release exclusive access to CRC hardware/software. 00326 */ 00327 virtual void unlock() 00328 { 00329 #ifdef DEVICE_CRC 00330 if (_mode == HARDWARE) { 00331 mbed_crc_mutex->unlock(); 00332 } 00333 #endif 00334 } 00335 00336 /** Get the current CRC data size. 00337 * 00338 * @return CRC data size in bytes 00339 */ 00340 uint8_t get_data_size(void) const 00341 { 00342 return (width <= 8 ? 1 : (width <= 16 ? 2 : 4)); 00343 } 00344 00345 /** Get the top bit of current CRC. 00346 * 00347 * @return Top bit is set high for respective data width of current CRC 00348 * Top bit for CRC width less then 8 bits will be set as 8th bit. 00349 */ 00350 uint32_t get_top_bit(void) const 00351 { 00352 return (width < 8 ? (1u << 7) : (uint32_t)(1ul << (width - 1))); 00353 } 00354 00355 /** Get the CRC data mask. 00356 * 00357 * @return CRC data mask is generated based on current CRC width 00358 */ 00359 uint32_t get_crc_mask(void) const 00360 { 00361 return (width < 8 ? ((1u << 8) - 1) : (uint32_t)((uint64_t)(1ull << width) - 1)); 00362 } 00363 00364 /** Final value of CRC is reflected. 00365 * 00366 * @param data final crc value, which should be reflected 00367 * @return Reflected CRC value 00368 */ 00369 uint32_t reflect_remainder(uint32_t data) const 00370 { 00371 if (_reflect_remainder) { 00372 uint32_t reflection = 0x0; 00373 uint8_t const nBits = (width < 8 ? 8 : width); 00374 00375 for (uint8_t bit = 0; bit < nBits; ++bit) { 00376 if (data & 0x01) { 00377 reflection |= (1 << ((nBits - 1) - bit)); 00378 } 00379 data = (data >> 1); 00380 } 00381 return (reflection); 00382 } else { 00383 return data; 00384 } 00385 } 00386 00387 /** Data bytes are reflected. 00388 * 00389 * @param data value to be reflected 00390 * @return Reflected data value 00391 */ 00392 uint32_t reflect_bytes(uint32_t data) const 00393 { 00394 if (_reflect_data) { 00395 uint32_t reflection = 0x0; 00396 00397 for (uint8_t bit = 0; bit < 8; ++bit) { 00398 if (data & 0x01) { 00399 reflection |= (1 << (7 - bit)); 00400 } 00401 data = (data >> 1); 00402 } 00403 return (reflection); 00404 } else { 00405 return data; 00406 } 00407 } 00408 00409 /** Bitwise CRC computation. 00410 * 00411 * @param buffer data buffer 00412 * @param size size of the data 00413 * @param crc CRC value is filled in, but the value is not the final 00414 * @return 0 on success or a negative error code on failure 00415 */ 00416 int32_t bitwise_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const 00417 { 00418 MBED_ASSERT(crc != NULL); 00419 00420 const uint8_t *data = static_cast<const uint8_t *>(buffer); 00421 uint32_t p_crc = *crc; 00422 00423 if (width < 8) { 00424 uint8_t data_byte; 00425 for (crc_data_size_t byte = 0; byte < size; byte++) { 00426 data_byte = reflect_bytes(data[byte]); 00427 for (uint8_t bit = 8; bit > 0; --bit) { 00428 p_crc <<= 1; 00429 if ((data_byte ^ p_crc) & get_top_bit()) { 00430 p_crc ^= polynomial; 00431 } 00432 data_byte <<= 1; 00433 } 00434 } 00435 } else { 00436 for (crc_data_size_t byte = 0; byte < size; byte++) { 00437 p_crc ^= (reflect_bytes(data[byte]) << (width - 8)); 00438 00439 // Perform modulo-2 division, a bit at a time 00440 for (uint8_t bit = 8; bit > 0; --bit) { 00441 if (p_crc & get_top_bit()) { 00442 p_crc = (p_crc << 1) ^ polynomial; 00443 } else { 00444 p_crc = (p_crc << 1); 00445 } 00446 } 00447 } 00448 } 00449 *crc = p_crc & get_crc_mask(); 00450 return 0; 00451 } 00452 00453 /** CRC computation using ROM tables. 00454 * 00455 * @param buffer data buffer 00456 * @param size size of the data 00457 * @param crc CRC value is filled in, but the value is not the final 00458 * @return 0 on success or a negative error code on failure 00459 */ 00460 int32_t table_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const 00461 { 00462 MBED_ASSERT(crc != NULL); 00463 00464 const uint8_t *data = static_cast<const uint8_t *>(buffer); 00465 uint32_t p_crc = *crc; 00466 uint8_t data_byte = 0; 00467 00468 if (width <= 8) { 00469 uint8_t *crc_table = (uint8_t *)_crc_table; 00470 for (crc_data_size_t byte = 0; byte < size; byte++) { 00471 data_byte = reflect_bytes(data[byte]) ^ p_crc; 00472 p_crc = crc_table[data_byte]; 00473 } 00474 } else if (width <= 16) { 00475 uint16_t *crc_table = (uint16_t *)_crc_table; 00476 for (crc_data_size_t byte = 0; byte < size; byte++) { 00477 data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8)); 00478 p_crc = crc_table[data_byte] ^ (p_crc << 8); 00479 } 00480 } else { 00481 uint32_t *crc_table = (uint32_t *)_crc_table; 00482 if (POLY_32BIT_REV_ANSI == polynomial) { 00483 for (crc_data_size_t i = 0; i < size; i++) { 00484 p_crc = (p_crc >> 4) ^ crc_table[(p_crc ^ (data[i] >> 0)) & 0xf]; 00485 p_crc = (p_crc >> 4) ^ crc_table[(p_crc ^ (data[i] >> 4)) & 0xf]; 00486 } 00487 } 00488 else { 00489 for (crc_data_size_t byte = 0; byte < size; byte++) { 00490 data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8)); 00491 p_crc = crc_table[data_byte] ^ (p_crc << 8); 00492 } 00493 } 00494 } 00495 *crc = p_crc & get_crc_mask(); 00496 return 0; 00497 } 00498 00499 /** Constructor init called from all specialized cases of constructor. 00500 * Note: All construtor common code should be in this function. 00501 */ 00502 void mbed_crc_ctor(void) 00503 { 00504 MBED_STATIC_ASSERT(width <= 32, "Max 32-bit CRC supported"); 00505 00506 #ifdef DEVICE_CRC 00507 if (POLY_32BIT_REV_ANSI == polynomial) { 00508 _crc_table = (uint32_t *)Table_CRC_32bit_Rev_ANSI; 00509 _mode = TABLE; 00510 return; 00511 } 00512 crc_mbed_config_t config; 00513 config.polynomial = polynomial; 00514 config.width = width; 00515 config.initial_xor = _initial_value; 00516 config.final_xor = _final_xor; 00517 config.reflect_in = _reflect_data; 00518 config.reflect_out = _reflect_remainder; 00519 00520 if (hal_crc_is_supported(&config)) { 00521 _mode = HARDWARE; 00522 return; 00523 } 00524 #endif 00525 00526 switch (polynomial) { 00527 case POLY_32BIT_ANSI: 00528 _crc_table = (uint32_t *)Table_CRC_32bit_ANSI; 00529 break; 00530 case POLY_32BIT_REV_ANSI: 00531 _crc_table = (uint32_t *)Table_CRC_32bit_Rev_ANSI; 00532 break; 00533 case POLY_8BIT_CCITT: 00534 _crc_table = (uint32_t *)Table_CRC_8bit_CCITT; 00535 break; 00536 case POLY_7BIT_SD: 00537 _crc_table = (uint32_t *)Table_CRC_7Bit_SD; 00538 break; 00539 case POLY_16BIT_CCITT: 00540 _crc_table = (uint32_t *)Table_CRC_16bit_CCITT; 00541 break; 00542 case POLY_16BIT_IBM: 00543 _crc_table = (uint32_t *)Table_CRC_16bit_IBM; 00544 break; 00545 default: 00546 _crc_table = NULL; 00547 break; 00548 } 00549 _mode = (_crc_table != NULL) ? TABLE : BITWISE; 00550 } 00551 }; 00552 00553 #if defined ( __CC_ARM ) 00554 #elif defined ( __GNUC__ ) 00555 #pragma GCC diagnostic pop 00556 #elif defined (__ICCARM__) 00557 #endif 00558 00559 /** @}*/ 00560 } // namespace mbed 00561 00562 #endif
Generated on Wed Jul 13 2022 01:21:56 by
1.7.2