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/internal/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-public-api */
44 /** @{*/
45 /**
46  * \defgroup drivers_MbedCRC MbedCRC class
47  * @{
48  */
49 
50 extern SingletonPtr<PlatformMutex> mbed_crc_mutex;
51 
52 /** CRC object provides CRC generation through hardware or software
53  *
54  * CRC sums can be generated using three different methods: hardware, software ROM tables
55  * and bitwise computation. The mode used is selected automatically based on required
56  * polynomial and hardware capabilities. Any polynomial in standard form (`x^3 + x + 1`)
57  * can be used for computation, but custom ones can affect the performance.
58  *
59  * First choice is the hardware mode. The supported polynomials are hardware specific, and
60  * you need to consult your MCU manual to discover them. Next, ROM polynomial tables
61  * are tried (you can find list of supported polynomials here ::crc_polynomial). If the selected
62  * configuration is supported, it will accelerate the software computations. If ROM tables
63  * are not available for the selected polynomial, then CRC is computed at run time bit by bit
64  * for all data input.
65  * @note Synchronization level: Thread safe
66  *
67  * @tparam polynomial CRC polynomial value in hex
68  * @tparam width CRC polynomial width
69  *
70  * Example: Compute CRC data
71  * @code
72  *
73  * #include "mbed.h"
74  *
75  * int main() {
76  * MbedCRC<POLY_32BIT_ANSI, 32> ct;
77  *
78  * char test[] = "123456789";
79  * uint32_t crc = 0;
80  *
81  * printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width());
82  *
83  * ct.compute((void *)test, strlen((const char*)test), &crc);
84  *
85  * printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
86  * return 0;
87  * }
88  * @endcode
89  * Example: Compute CRC with data available in parts
90  * @code
91  *
92  * #include "mbed.h"
93  * int main() {
94  * MbedCRC<POLY_32BIT_ANSI, 32> ct;
95  *
96  * char test[] = "123456789";
97  * uint32_t crc = 0;
98  *
99  * printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width());
100  * ct.compute_partial_start(&crc);
101  * ct.compute_partial((void *)&test, 4, &crc);
102  * ct.compute_partial((void *)&test[4], 5, &crc);
103  * ct.compute_partial_stop(&crc);
104  * printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
105  * return 0;
106  * }
107  * @endcode
108  */
109 template <uint32_t polynomial = POLY_32BIT_ANSI, uint8_t width = 32>
110 class MbedCRC {
111 
112 public:
113  enum CrcMode {
114 #if DEVICE_CRC
115  HARDWARE = 0,
116 #endif
117  TABLE = 1,
118  BITWISE
119  };
120 
121  typedef uint64_t crc_data_size_t;
122 
123  /** Lifetime of CRC object
124  *
125  * @param initial_xor Inital value/seed to Xor
126  * @param final_xor Final Xor value
127  * @param reflect_data
128  * @param reflect_remainder
129  * @note Default constructor without any arguments is valid only for supported CRC polynomials. :: crc_polynomial_t
130  * MbedCRC <POLY_7BIT_SD, 7> ct; --- Valid POLY_7BIT_SD
131  * MbedCRC <0x1021, 16> ct; --- Valid POLY_16BIT_CCITT
132  * MbedCRC <POLY_16BIT_CCITT, 32> ct; --- Invalid, compilation error
133  * MbedCRC <POLY_16BIT_CCITT, 32> ct (i,f,rd,rr) Constructor can be used for not supported polynomials
134  * MbedCRC<POLY_16BIT_CCITT, 16> sd(0, 0, false, false); Constructor can also be used for supported
135  * polynomials with different intial/final/reflect values
136  *
137  */
138  MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) :
139  _initial_value(initial_xor), _final_xor(final_xor), _reflect_data(reflect_data),
140  _reflect_remainder(reflect_remainder)
141  {
142  mbed_crc_ctor();
143  }
144  MbedCRC();
145  virtual ~MbedCRC()
146  {
147  // Do nothing
148  }
149 
150  /** Compute CRC for the data input
151  * Compute CRC performs the initialization, computation and collection of
152  * final CRC.
153  *
154  * @param buffer Data bytes
155  * @param size Size of data
156  * @param crc CRC is the output value
157  * @return 0 on success, negative error code on failure
158  */
159  int32_t compute(const void *buffer, crc_data_size_t size, uint32_t *crc)
160  {
161  MBED_ASSERT(crc != NULL);
162  int32_t status = 0;
163 
164  status = compute_partial_start(crc);
165  if (0 != status) {
166  unlock();
167  return status;
168  }
169 
170  status = compute_partial(buffer, size, crc);
171  if (0 != status) {
172  unlock();
173  return status;
174  }
175 
176  status = compute_partial_stop(crc);
177  if (0 != status) {
178  *crc = 0;
179  }
180 
181  return status;
182 
183  }
184 
185  /** Compute partial CRC for the data input.
186  *
187  * CRC data if not available fully, CRC can be computed in parts with available data.
188  *
189  * In case of hardware, intermediate values and states are saved by hardware. Mutex
190  * locking is used to serialize access to hardware CRC.
191  *
192  * In case of software CRC, previous CRC output should be passed as argument to the
193  * current compute_partial call. Please note the intermediate CRC value is maintained by
194  * application and not the driver.
195  *
196  * @pre: Call `compute_partial_start` to start the partial CRC calculation.
197  * @post: Call `compute_partial_stop` to get the final CRC value.
198  *
199  * @param buffer Data bytes
200  * @param size Size of data
201  * @param crc CRC value is intermediate CRC value filled by API.
202  * @return 0 on success or a negative error code on failure
203  * @note: CRC as output in compute_partial is not final CRC value, call `compute_partial_stop`
204  * to get final correct CRC value.
205  */
206  int32_t compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc)
207  {
208  int32_t status = 0;
209 
210  switch (_mode) {
211 #if DEVICE_CRC
212  case HARDWARE:
213  hal_crc_compute_partial(static_cast<const uint8_t *>(buffer), size);
214  *crc = 0;
215  break;
216 #endif
217  case TABLE:
218  status = table_compute_partial(buffer, size, crc);
219  break;
220  case BITWISE:
221  status = bitwise_compute_partial(buffer, size, crc);
222  break;
223  default:
224  status = -1;
225  break;
226  }
227 
228  return status;
229  }
230 
231  /** Compute partial start, indicate start of partial computation.
232  *
233  * This API should be called before performing any partial computation
234  * with compute_partial API.
235  *
236  * @param crc Initial CRC value set by the API
237  * @return 0 on success or a negative in case of failure
238  * @note: CRC is an out parameter and must be reused with compute_partial
239  * and `compute_partial_stop` without any modifications in application.
240  */
241  int32_t compute_partial_start(uint32_t *crc)
242  {
243  MBED_ASSERT(crc != NULL);
244 
245 #if DEVICE_CRC
246  if (_mode == HARDWARE) {
247  lock();
248  crc_mbed_config_t config;
249  config.polynomial = polynomial;
250  config.width = width;
251  config.initial_xor = _initial_value;
252  config.final_xor = _final_xor;
253  config.reflect_in = _reflect_data;
254  config.reflect_out = _reflect_remainder;
255 
257  }
258 #endif
259 
260  *crc = _initial_value;
261  return 0;
262  }
263 
264  /** Get the final CRC value of partial computation.
265  *
266  * CRC value available in partial computation is not correct CRC, as some
267  * algorithms require remainder to be reflected and final value to be XORed
268  * This API is used to perform final computation to get correct CRC value.
269  *
270  * @param crc CRC result
271  * @return 0 on success or a negative in case of failure.
272  */
273  int32_t compute_partial_stop(uint32_t *crc)
274  {
275  MBED_ASSERT(crc != NULL);
276 
277 #if DEVICE_CRC
278  if (_mode == HARDWARE) {
279  *crc = hal_crc_get_result();
280  unlock();
281  return 0;
282  }
283 #endif
284  uint32_t p_crc = *crc;
285  if ((width < 8) && (NULL == _crc_table)) {
286  p_crc = (uint32_t)(p_crc << (8 - width));
287  }
288  // Optimized algorithm for 32BitANSI does not need additional reflect_remainder
289  if ((TABLE == _mode) && (POLY_32BIT_REV_ANSI == polynomial)) {
290  *crc = (p_crc ^ _final_xor) & get_crc_mask();
291  } else {
292  *crc = (reflect_remainder(p_crc) ^ _final_xor) & get_crc_mask();
293  }
294  unlock();
295  return 0;
296  }
297 
298  /** Get the current CRC polynomial.
299  *
300  * @return Polynomial value
301  */
302  uint32_t get_polynomial(void) const
303  {
304  return polynomial;
305  }
306 
307  /** Get the current CRC width
308  *
309  * @return CRC width
310  */
311  uint8_t get_width(void) const
312  {
313  return width;
314  }
315 
316 #if !defined(DOXYGEN_ONLY)
317 private:
318  uint32_t _initial_value;
319  uint32_t _final_xor;
320  bool _reflect_data;
321  bool _reflect_remainder;
322  uint32_t *_crc_table;
323  CrcMode _mode;
324 
325  /** Acquire exclusive access to CRC hardware/software.
326  */
327  void lock()
328  {
329 #if DEVICE_CRC
330  if (_mode == HARDWARE) {
331  mbed_crc_mutex->lock();
332  }
333 #endif
334  }
335 
336  /** Release exclusive access to CRC hardware/software.
337  */
338  virtual void unlock()
339  {
340 #if DEVICE_CRC
341  if (_mode == HARDWARE) {
342  mbed_crc_mutex->unlock();
343  }
344 #endif
345  }
346 
347  /** Get the current CRC data size.
348  *
349  * @return CRC data size in bytes
350  */
351  uint8_t get_data_size(void) const
352  {
353  return (width <= 8 ? 1 : (width <= 16 ? 2 : 4));
354  }
355 
356  /** Get the top bit of current CRC.
357  *
358  * @return Top bit is set high for respective data width of current CRC
359  * Top bit for CRC width less then 8 bits will be set as 8th bit.
360  */
361  uint32_t get_top_bit(void) const
362  {
363  return (width < 8 ? (1u << 7) : (uint32_t)(1ul << (width - 1)));
364  }
365 
366  /** Get the CRC data mask.
367  *
368  * @return CRC data mask is generated based on current CRC width
369  */
370  uint32_t get_crc_mask(void) const
371  {
372  return (width < 8 ? ((1u << 8) - 1) : (uint32_t)((uint64_t)(1ull << width) - 1));
373  }
374 
375  /** Final value of CRC is reflected.
376  *
377  * @param data final crc value, which should be reflected
378  * @return Reflected CRC value
379  */
380  uint32_t reflect_remainder(uint32_t data) const
381  {
382  if (_reflect_remainder) {
383  uint32_t reflection = 0x0;
384  uint8_t const nBits = (width < 8 ? 8 : width);
385 
386  for (uint8_t bit = 0; bit < nBits; ++bit) {
387  if (data & 0x01) {
388  reflection |= (1 << ((nBits - 1) - bit));
389  }
390  data = (data >> 1);
391  }
392  return (reflection);
393  } else {
394  return data;
395  }
396  }
397 
398  /** Data bytes are reflected.
399  *
400  * @param data value to be reflected
401  * @return Reflected data value
402  */
403  uint32_t reflect_bytes(uint32_t data) const
404  {
405  if (_reflect_data) {
406  uint32_t reflection = 0x0;
407 
408  for (uint8_t bit = 0; bit < 8; ++bit) {
409  if (data & 0x01) {
410  reflection |= (1 << (7 - bit));
411  }
412  data = (data >> 1);
413  }
414  return (reflection);
415  } else {
416  return data;
417  }
418  }
419 
420  /** Bitwise CRC computation.
421  *
422  * @param buffer data buffer
423  * @param size size of the data
424  * @param crc CRC value is filled in, but the value is not the final
425  * @return 0 on success or a negative error code on failure
426  */
427  int32_t bitwise_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const
428  {
429  MBED_ASSERT(crc != NULL);
430 
431  const uint8_t *data = static_cast<const uint8_t *>(buffer);
432  uint32_t p_crc = *crc;
433 
434  if (width < 8) {
435  uint8_t data_byte;
436  for (crc_data_size_t byte = 0; byte < size; byte++) {
437  data_byte = reflect_bytes(data[byte]);
438  for (uint8_t bit = 8; bit > 0; --bit) {
439  p_crc <<= 1;
440  if ((data_byte ^ p_crc) & get_top_bit()) {
441  p_crc ^= polynomial;
442  }
443  data_byte <<= 1;
444  }
445  }
446  } else {
447  for (crc_data_size_t byte = 0; byte < size; byte++) {
448  p_crc ^= (reflect_bytes(data[byte]) << (width - 8));
449 
450  // Perform modulo-2 division, a bit at a time
451  for (uint8_t bit = 8; bit > 0; --bit) {
452  if (p_crc & get_top_bit()) {
453  p_crc = (p_crc << 1) ^ polynomial;
454  } else {
455  p_crc = (p_crc << 1);
456  }
457  }
458  }
459  }
460  *crc = p_crc & get_crc_mask();
461  return 0;
462  }
463 
464  /** CRC computation using ROM tables.
465  *
466  * @param buffer data buffer
467  * @param size size of the data
468  * @param crc CRC value is filled in, but the value is not the final
469  * @return 0 on success or a negative error code on failure
470  */
471  int32_t table_compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc) const
472  {
473  MBED_ASSERT(crc != NULL);
474 
475  const uint8_t *data = static_cast<const uint8_t *>(buffer);
476  uint32_t p_crc = *crc;
477  uint8_t data_byte = 0;
478 
479  if (width <= 8) {
480  uint8_t *crc_table = (uint8_t *)_crc_table;
481  for (crc_data_size_t byte = 0; byte < size; byte++) {
482  data_byte = reflect_bytes(data[byte]) ^ p_crc;
483  p_crc = crc_table[data_byte];
484  }
485  } else if (width <= 16) {
486  uint16_t *crc_table = (uint16_t *)_crc_table;
487  for (crc_data_size_t byte = 0; byte < size; byte++) {
488  data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8));
489  p_crc = crc_table[data_byte] ^ (p_crc << 8);
490  }
491  } else {
492  uint32_t *crc_table = (uint32_t *)_crc_table;
493  if (POLY_32BIT_REV_ANSI == polynomial) {
494  for (crc_data_size_t i = 0; i < size; i++) {
495  p_crc = (p_crc >> 4) ^ crc_table[(p_crc ^ (data[i] >> 0)) & 0xf];
496  p_crc = (p_crc >> 4) ^ crc_table[(p_crc ^ (data[i] >> 4)) & 0xf];
497  }
498  } else {
499  for (crc_data_size_t byte = 0; byte < size; byte++) {
500  data_byte = reflect_bytes(data[byte]) ^ (p_crc >> (width - 8));
501  p_crc = crc_table[data_byte] ^ (p_crc << 8);
502  }
503  }
504  }
505  *crc = p_crc & get_crc_mask();
506  return 0;
507  }
508 
509  /** Constructor init called from all specialized cases of constructor.
510  * Note: All constructor common code should be in this function.
511  */
512  void mbed_crc_ctor(void)
513  {
514  MBED_STATIC_ASSERT(width <= 32, "Max 32-bit CRC supported");
515 
516 #if DEVICE_CRC
517  if (POLY_32BIT_REV_ANSI != polynomial) {
518  crc_mbed_config_t config;
519  config.polynomial = polynomial;
520  config.width = width;
521  config.initial_xor = _initial_value;
522  config.final_xor = _final_xor;
523  config.reflect_in = _reflect_data;
524  config.reflect_out = _reflect_remainder;
525 
526  if (hal_crc_is_supported(&config)) {
527  _mode = HARDWARE;
528  return;
529  }
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 /** @}*/
569 
570 } // namespace mbed
571 
572 #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:159
uint32_t get_polynomial(void) const
Get the current CRC polynomial.
Definition: MbedCRC.h:302
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:311
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:206
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:138
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:241
CRC object provides CRC generation through hardware or software.
Definition: MbedCRC.h:110
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:273
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.