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