Mistake on this page?
Report an issue in GitHub or email us
MbedCRC.h
1 /* mbed Microcontroller Library
2  * Copyright (c) 2018 ARM Limited
3  * SPDX-License-Identifier: Apache-2.0
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #ifndef MBED_CRC_API_H
18 #define MBED_CRC_API_H
19 
20 #include "drivers/TableCRC.h"
21 #include "hal/crc_api.h"
22 #include "platform/mbed_assert.h"
23 #include "platform/SingletonPtr.h"
24 #include "platform/PlatformMutex.h"
25 
26 /* This is an invalid warning from the compiler for the below section of code
27 if ((width < 8) && (NULL == _crc_table)) {
28  p_crc = (uint32_t)(p_crc << (8 - width));
29 }
30 Compiler warns of the shift operation with width as it is width=(std::uint8_t),
31 but we check for ( width < 8) before performing shift, so it should not be an issue.
32 */
33 #if defined ( __CC_ARM )
34 #pragma diag_suppress 62 // Shift count is negative
35 #elif defined ( __GNUC__ )
36 #pragma GCC diagnostic push
37 #pragma GCC diagnostic ignored "-Wshift-count-negative"
38 #elif defined (__ICCARM__)
39 #pragma diag_suppress=Pe062 // Shift count is negative
40 #endif
41 
42 namespace mbed {
43 /** \addtogroup drivers */
44 /** @{*/
45 
46 extern SingletonPtr<PlatformMutex> mbed_crc_mutex;
47 
48 /** CRC object provides CRC generation through hardware or software
49  *
50  * CRC sums can be generated using three different methods: hardware, software ROM tables
51  * and bitwise computation. The mode used is selected automatically based on required
52  * polynomial and hardware capabilities. Any polynomial in standard form (`x^3 + x + 1`)
53  * can be used for computation, but custom ones can affect the performance.
54  *
55  * First choice is the hardware mode. The supported polynomials are hardware specific, and
56  * you need to consult your MCU manual to discover them. Next, ROM polynomial tables
57  * are tried (you can find list of supported polynomials here ::crc_polynomial). If the selected
58  * configuration is supported, it will accelerate the software computations. If ROM tables
59  * are not available for the selected polynomial, then CRC is computed at run time bit by bit
60  * for all data input.
61  * @note Synchronization level: Thread safe
62  *
63  * @tparam polynomial CRC polynomial value in hex
64  * @tparam width CRC polynomial width
65  *
66  * Example: Compute CRC data
67  * @code
68  *
69  * #include "mbed.h"
70  *
71  * int main() {
72  * MbedCRC<POLY_32BIT_ANSI, 32> ct;
73  *
74  * char test[] = "123456789";
75  * uint32_t crc = 0;
76  *
77  * printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width());
78  *
79  * ct.compute((void *)test, strlen((const char*)test), &crc);
80  *
81  * printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
82  * return 0;
83  * }
84  * @endcode
85  * Example: Compute CRC with data available in parts
86  * @code
87  *
88  * #include "mbed.h"
89  * int main() {
90  * MbedCRC<POLY_32BIT_ANSI, 32> ct;
91  *
92  * char test[] = "123456789";
93  * uint32_t crc = 0;
94  *
95  * printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width());
96  * ct.compute_partial_start(&crc);
97  * ct.compute_partial((void *)&test, 4, &crc);
98  * ct.compute_partial((void *)&test[4], 5, &crc);
99  * ct.compute_partial_stop(&crc);
100  * printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
101  * return 0;
102  * }
103  * @endcode
104  * @ingroup drivers
105  */
106 template <uint32_t polynomial = POLY_32BIT_ANSI, uint8_t width = 32>
107 class MbedCRC {
108 
109 public:
110  enum CrcMode {
111 #if DEVICE_CRC
112  HARDWARE = 0,
113 #endif
114  TABLE = 1,
115  BITWISE
116  };
117 
118  typedef uint64_t crc_data_size_t;
119 
120  /** Lifetime of CRC object
121  *
122  * @param initial_xor Inital value/seed to Xor
123  * @param final_xor Final Xor value
124  * @param reflect_data
125  * @param reflect_remainder
126  * @note Default constructor without any arguments is valid only for supported CRC polynomials. :: crc_polynomial_t
127  * MbedCRC <POLY_7BIT_SD, 7> ct; --- Valid POLY_7BIT_SD
128  * MbedCRC <0x1021, 16> ct; --- Valid POLY_16BIT_CCITT
129  * MbedCRC <POLY_16BIT_CCITT, 32> ct; --- Invalid, compilation error
130  * MbedCRC <POLY_16BIT_CCITT, 32> ct (i,f,rd,rr) Constructor can be used for not supported polynomials
131  * MbedCRC<POLY_16BIT_CCITT, 16> sd(0, 0, false, false); Constructor can also be used for supported
132  * polynomials with different intial/final/reflect values
133  *
134  */
135  MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) :
136  _initial_value(initial_xor), _final_xor(final_xor), _reflect_data(reflect_data),
137  _reflect_remainder(reflect_remainder)
138  {
139  mbed_crc_ctor();
140  }
141  MbedCRC();
142  virtual ~MbedCRC()
143  {
144  // Do nothing
145  }
146 
147  /** Compute CRC for the data input
148  * Compute CRC performs the initialization, computation and collection of
149  * final CRC.
150  *
151  * @param buffer Data bytes
152  * @param size Size of data
153  * @param crc CRC is the output value
154  * @return 0 on success, negative error code on failure
155  */
156  int32_t compute(const void *buffer, crc_data_size_t size, uint32_t *crc)
157  {
158  MBED_ASSERT(crc != NULL);
159  int32_t status = 0;
160 
161  status = compute_partial_start(crc);
162  if (0 != status) {
163  unlock();
164  return status;
165  }
166 
167  status = compute_partial(buffer, size, crc);
168  if (0 != status) {
169  unlock();
170  return status;
171  }
172 
173  status = compute_partial_stop(crc);
174  if (0 != status) {
175  *crc = 0;
176  }
177 
178  return status;
179 
180  }
181 
182  /** Compute partial CRC for the data input.
183  *
184  * CRC data if not available fully, CRC can be computed in parts with available data.
185  *
186  * In case of hardware, intermediate values and states are saved by hardware. Mutex
187  * locking is used to serialize access to hardware CRC.
188  *
189  * In case of software CRC, previous CRC output should be passed as argument to the
190  * current compute_partial call. Please note the intermediate CRC value is maintained by
191  * application and not the driver.
192  *
193  * @pre: Call `compute_partial_start` to start the partial CRC calculation.
194  * @post: Call `compute_partial_stop` to get the final CRC value.
195  *
196  * @param buffer Data bytes
197  * @param size Size of data
198  * @param crc CRC value is intermediate CRC value filled by API.
199  * @return 0 on success or a negative error code on failure
200  * @note: CRC as output in compute_partial is not final CRC value, call `compute_partial_stop`
201  * to get final correct CRC value.
202  */
203  int32_t compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc)
204  {
205  int32_t status = 0;
206 
207  switch (_mode) {
208 #if DEVICE_CRC
209  case HARDWARE:
210  hal_crc_compute_partial(static_cast<const uint8_t *>(buffer), size);
211  *crc = 0;
212  break;
213 #endif
214  case TABLE:
215  status = table_compute_partial(buffer, size, crc);
216  break;
217  case BITWISE:
218  status = bitwise_compute_partial(buffer, size, crc);
219  break;
220  default:
221  status = -1;
222  break;
223  }
224 
225  return status;
226  }
227 
228  /** Compute partial start, indicate start of partial computation.
229  *
230  * This API should be called before performing any partial computation
231  * with compute_partial API.
232  *
233  * @param crc Initial CRC value set by the API
234  * @return 0 on success or a negative in case of failure
235  * @note: CRC is an out parameter and must be reused with compute_partial
236  * and `compute_partial_stop` without any modifications in application.
237  */
238  int32_t compute_partial_start(uint32_t *crc)
239  {
240  MBED_ASSERT(crc != NULL);
241 
242 #if DEVICE_CRC
243  if (_mode == HARDWARE) {
244  lock();
245  crc_mbed_config_t config;
246  config.polynomial = polynomial;
247  config.width = width;
248  config.initial_xor = _initial_value;
249  config.final_xor = _final_xor;
250  config.reflect_in = _reflect_data;
251  config.reflect_out = _reflect_remainder;
252 
254  }
255 #endif
256 
257  *crc = _initial_value;
258  return 0;
259  }
260 
261  /** Get the final CRC value of partial computation.
262  *
263  * CRC value available in partial computation is not correct CRC, as some
264  * algorithms require remainder to be reflected and final value to be XORed
265  * This API is used to perform final computation to get correct CRC value.
266  *
267  * @param crc CRC result
268  * @return 0 on success or a negative in case of failure.
269  */
270  int32_t compute_partial_stop(uint32_t *crc)
271  {
272  MBED_ASSERT(crc != NULL);
273 
274 #if DEVICE_CRC
275  if (_mode == HARDWARE) {
276  *crc = hal_crc_get_result();
277  unlock();
278  return 0;
279  }
280 #endif
281  uint32_t p_crc = *crc;
282  if ((width < 8) && (NULL == _crc_table)) {
283  p_crc = (uint32_t)(p_crc << (8 - width));
284  }
285  // Optimized algorithm for 32BitANSI does not need additional reflect_remainder
286  if ((TABLE == _mode) && (POLY_32BIT_REV_ANSI == polynomial)) {
287  *crc = (p_crc ^ _final_xor) & get_crc_mask();
288  } else {
289  *crc = (reflect_remainder(p_crc) ^ _final_xor) & get_crc_mask();
290  }
291  unlock();
292  return 0;
293  }
294 
295  /** Get the current CRC polynomial.
296  *
297  * @return Polynomial value
298  */
299  uint32_t get_polynomial(void) const
300  {
301  return polynomial;
302  }
303 
304  /** Get the current CRC width
305  *
306  * @return CRC width
307  */
308  uint8_t get_width(void) const
309  {
310  return width;
311  }
312 
313 #if !defined(DOXYGEN_ONLY)
314 private:
315  uint32_t _initial_value;
316  uint32_t _final_xor;
317  bool _reflect_data;
318  bool _reflect_remainder;
319  uint32_t *_crc_table;
320  CrcMode _mode;
321 
322  /** Acquire exclusive access to CRC hardware/software.
323  */
324  void lock()
325  {
326 #if DEVICE_CRC
327  if (_mode == HARDWARE) {
328  mbed_crc_mutex->lock();
329  }
330 #endif
331  }
332 
333  /** Release exclusive access to CRC hardware/software.
334  */
335  virtual void unlock()
336  {
337 #if DEVICE_CRC
338  if (_mode == HARDWARE) {
339  mbed_crc_mutex->unlock();
340  }
341 #endif
342  }
343 
344  /** Get the current CRC data size.
345  *
346  * @return CRC data size in bytes
347  */
348  uint8_t get_data_size(void) const
349  {
350  return (width <= 8 ? 1 : (width <= 16 ? 2 : 4));
351  }
352 
353  /** Get the top bit of current CRC.
354  *
355  * @return Top bit is set high for respective data width of current CRC
356  * Top bit for CRC width less then 8 bits will be set as 8th bit.
357  */
358  uint32_t get_top_bit(void) const
359  {
360  return (width < 8 ? (1u << 7) : (uint32_t)(1ul << (width - 1)));
361  }
362 
363  /** Get the CRC data mask.
364  *
365  * @return CRC data mask is generated based on current CRC width
366  */
367  uint32_t get_crc_mask(void) const
368  {
369  return (width < 8 ? ((1u << 8) - 1) : (uint32_t)((uint64_t)(1ull << width) - 1));
370  }
371 
372  /** Final value of CRC is reflected.
373  *
374  * @param data final crc value, which should be reflected
375  * @return Reflected CRC value
376  */
377  uint32_t reflect_remainder(uint32_t data) const
378  {
379  if (_reflect_remainder) {
380  uint32_t reflection = 0x0;
381  uint8_t const nBits = (width < 8 ? 8 : width);
382 
383  for (uint8_t bit = 0; bit < nBits; ++bit) {
384  if (data & 0x01) {
385  reflection |= (1 << ((nBits - 1) - bit));
386  }
387  data = (data >> 1);
388  }
389  return (reflection);
390  } else {
391  return data;
392  }
393  }
394 
395  /** Data bytes are reflected.
396  *
397  * @param data value to be reflected
398  * @return Reflected data value
399  */
400  uint32_t reflect_bytes(uint32_t data) const
401  {
402  if (_reflect_data) {
403  uint32_t reflection = 0x0;
404 
405  for (uint8_t bit = 0; bit < 8; ++bit) {
406  if (data & 0x01) {
407  reflection |= (1 << (7 - bit));
408  }
409  data = (data >> 1);
410  }
411  return (reflection);
412  } else {
413  return data;
414  }
415  }
416 
417  /** Bitwise CRC computation.
418  *
419  * @param buffer data buffer
420  * @param size size of the data
421  * @param crc CRC value is filled in, but the value is not the final
422  * @return 0 on success or a negative error code on failure
423  */
424  int32_t bitwise_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const
425  {
426  MBED_ASSERT(crc != NULL);
427 
428  const uint8_t *data = static_cast<const uint8_t *>(buffer);
429  uint32_t p_crc = *crc;
430 
431  if (width < 8) {
432  uint8_t data_byte;
433  for (crc_data_size_t byte = 0; byte < size; byte++) {
434  data_byte = reflect_bytes(data[byte]);
435  for (uint8_t bit = 8; bit > 0; --bit) {
436  p_crc <<= 1;
437  if ((data_byte ^ p_crc) & get_top_bit()) {
438  p_crc ^= polynomial;
439  }
440  data_byte <<= 1;
441  }
442  }
443  } else {
444  for (crc_data_size_t byte = 0; byte < size; byte++) {
445  p_crc ^= (reflect_bytes(data[byte]) << (width - 8));
446 
447  // Perform modulo-2 division, a bit at a time
448  for (uint8_t bit = 8; bit > 0; --bit) {
449  if (p_crc & get_top_bit()) {
450  p_crc = (p_crc << 1) ^ polynomial;
451  } else {
452  p_crc = (p_crc << 1);
453  }
454  }
455  }
456  }
457  *crc = p_crc & get_crc_mask();
458  return 0;
459  }
460 
461  /** CRC computation using ROM tables.
462  *
463  * @param buffer data buffer
464  * @param size size of the data
465  * @param crc CRC value is filled in, but the value is not the final
466  * @return 0 on success or a negative error code on failure
467  */
468  int32_t table_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const
469  {
470  MBED_ASSERT(crc != NULL);
471 
472  const uint8_t *data = static_cast<const uint8_t *>(buffer);
473  uint32_t p_crc = *crc;
474  uint8_t data_byte = 0;
475 
476  if (width <= 8) {
477  uint8_t *crc_table = (uint8_t *)_crc_table;
478  for (crc_data_size_t byte = 0; byte < size; byte++) {
479  data_byte = reflect_bytes(data[byte]) ^ p_crc;
480  p_crc = crc_table[data_byte];
481  }
482  } else if (width <= 16) {
483  uint16_t *crc_table = (uint16_t *)_crc_table;
484  for (crc_data_size_t byte = 0; byte < size; byte++) {
485  data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8));
486  p_crc = crc_table[data_byte] ^ (p_crc << 8);
487  }
488  } else {
489  uint32_t *crc_table = (uint32_t *)_crc_table;
490  if (POLY_32BIT_REV_ANSI == polynomial) {
491  for (crc_data_size_t i = 0; i < size; i++) {
492  p_crc = (p_crc >> 4) ^ crc_table[(p_crc ^ (data[i] >> 0)) & 0xf];
493  p_crc = (p_crc >> 4) ^ crc_table[(p_crc ^ (data[i] >> 4)) & 0xf];
494  }
495  } else {
496  for (crc_data_size_t byte = 0; byte < size; byte++) {
497  data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8));
498  p_crc = crc_table[data_byte] ^ (p_crc << 8);
499  }
500  }
501  }
502  *crc = p_crc & get_crc_mask();
503  return 0;
504  }
505 
506  /** Constructor init called from all specialized cases of constructor.
507  * Note: All constructor common code should be in this function.
508  */
509  void mbed_crc_ctor(void)
510  {
511  MBED_STATIC_ASSERT(width <= 32, "Max 32-bit CRC supported");
512 
513 #if DEVICE_CRC
514  if (POLY_32BIT_REV_ANSI == polynomial) {
515  _crc_table = (uint32_t *)Table_CRC_32bit_Rev_ANSI;
516  _mode = TABLE;
517  return;
518  }
519  crc_mbed_config_t config;
520  config.polynomial = polynomial;
521  config.width = width;
522  config.initial_xor = _initial_value;
523  config.final_xor = _final_xor;
524  config.reflect_in = _reflect_data;
525  config.reflect_out = _reflect_remainder;
526 
527  if (hal_crc_is_supported(&config)) {
528  _mode = HARDWARE;
529  return;
530  }
531 #endif
532 
533  switch (polynomial) {
534  case POLY_32BIT_ANSI:
535  _crc_table = (uint32_t *)Table_CRC_32bit_ANSI;
536  break;
537  case POLY_32BIT_REV_ANSI:
538  _crc_table = (uint32_t *)Table_CRC_32bit_Rev_ANSI;
539  break;
540  case POLY_8BIT_CCITT:
541  _crc_table = (uint32_t *)Table_CRC_8bit_CCITT;
542  break;
543  case POLY_7BIT_SD:
544  _crc_table = (uint32_t *)Table_CRC_7Bit_SD;
545  break;
546  case POLY_16BIT_CCITT:
547  _crc_table = (uint32_t *)Table_CRC_16bit_CCITT;
548  break;
549  case POLY_16BIT_IBM:
550  _crc_table = (uint32_t *)Table_CRC_16bit_IBM;
551  break;
552  default:
553  _crc_table = NULL;
554  break;
555  }
556  _mode = (_crc_table != NULL) ? TABLE : BITWISE;
557  }
558 #endif
559 };
560 
561 #if defined ( __CC_ARM )
562 #elif defined ( __GNUC__ )
563 #pragma GCC diagnostic pop
564 #elif defined (__ICCARM__)
565 #endif
566 
567 /** @}*/
568 } // namespace mbed
569 
570 #endif
int32_t compute(const void *buffer, crc_data_size_t size, uint32_t *crc)
Compute CRC for the data input Compute CRC performs the initialization, computation and collection of...
Definition: MbedCRC.h:156
uint32_t get_polynomial(void) const
Get the current CRC polynomial.
Definition: MbedCRC.h:299
x16+x15+x2+1
Definition: crc_api.h:35
x7+x3+1
Definition: crc_api.h:33
uint32_t final_xor
Final xor value for the computation.
Definition: crc_api.h:48
bool hal_crc_is_supported(const crc_mbed_config_t *config)
Determine if the current platform supports hardware CRC for given polynomial.
uint8_t get_width(void) const
Get the current CRC width.
Definition: MbedCRC.h:308
void hal_crc_compute_partial_start(const crc_mbed_config_t *config)
Initialize the hardware CRC module with the given polynomial.
#define MBED_ASSERT(expr)
MBED_ASSERT Declare runtime assertions: results in runtime error if condition is false.
Definition: mbed_assert.h:65
int32_t compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc)
Compute partial CRC for the data input.
Definition: MbedCRC.h:203
uint32_t width
CRC Bit Width.
Definition: crc_api.h:44
MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder)
Lifetime of CRC object.
Definition: MbedCRC.h:135
void unlock()
Unlock a PlatformMutex that the same thread has previously locked.
Definition: PlatformMutex.h:81
x16+x12+x5+1
Definition: crc_api.h:34
void lock()
Wait until a PlatformMutex becomes available.
Definition: PlatformMutex.h:71
int32_t compute_partial_start(uint32_t *crc)
Compute partial start, indicate start of partial computation.
Definition: MbedCRC.h:238
CRC object provides CRC generation through hardware or software.
Definition: MbedCRC.h:107
x8+x2+x+1
Definition: crc_api.h:32
void hal_crc_compute_partial(const uint8_t *data, const size_t size)
Writes data to the current CRC module.
bool reflect_out
Reflect bits in final result before returning.
Definition: crc_api.h:52
uint32_t polynomial
CRC Polynomial.
Definition: crc_api.h:42
x31+x30+x29+x27+x26+x24+x23+x21+x20+x19+x15+x9+x8+x5
Definition: crc_api.h:37
x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
Definition: crc_api.h:36
bool reflect_in
Reflect bits on input.
Definition: crc_api.h:50
#define MBED_STATIC_ASSERT(expr, msg)
MBED_STATIC_ASSERT Declare compile-time assertions, results in compile-time error if condition is fal...
Definition: mbed_assert.h:110
int32_t compute_partial_stop(uint32_t *crc)
Get the final CRC value of partial computation.
Definition: MbedCRC.h:270
uint32_t initial_xor
Initial seed value for the computation.
Definition: crc_api.h:46
Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.