Mbed OS Reference
Loading...
Searching...
No Matches
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 "cmsis.h"
21#include "hal/crc_api.h"
22#ifdef DEVICE_CRC
23#include "device.h"
24#endif
25#include "platform/mbed_assert.h"
26
27#ifdef __cplusplus
28
29#include "platform/SingletonPtr.h"
30#include "rtos/Mutex.h"
31
32#include <mstd_type_traits>
33
34namespace mbed {
35/** \addtogroup drivers-public-api */
36/** @{*/
37/**
38 * \defgroup drivers_MbedCRC MbedCRC class
39 * @{
40 */
41
42extern SingletonPtr<rtos::Mutex> mbed_crc_mutex;
43
44/** CRC mode selection
45 */
46enum class CrcMode {
47 HARDWARE, /// Use hardware (if available), else table-based computation
48 TABLE, /// Use table-based computation (if table available), else bitwise
49 BITWISE /// Always use bitwise manual computation
50};
51
52/** @}*/
53/** @}*/
54
55#ifndef DOXYGEN_ONLY
56namespace impl {
57template<uint32_t polynomial, uint8_t width, CrcMode mode>
58class MbedCRC;
59
60constexpr bool have_crc_table(uint32_t polynomial, uint8_t width)
61{
62#if MBED_CRC_TABLE_SIZE > 0
63 return (polynomial == POLY_32BIT_ANSI && width == 32) ||
64 (polynomial == POLY_16BIT_IBM && width == 16) ||
65 (polynomial == POLY_16BIT_CCITT && width == 16) ||
66 (polynomial == POLY_8BIT_CCITT && width == 8) ||
67 (polynomial == POLY_7BIT_SD && width == 7);
68#else
69 return false;
70#endif
71}
72
73constexpr CrcMode choose_crc_mode(uint32_t polynomial, uint8_t width, CrcMode mode_limit)
74{
75 return
76#if DEVICE_CRC
77 mode_limit == CrcMode::HARDWARE && HAL_CRC_IS_SUPPORTED(polynomial, width) ? CrcMode::HARDWARE :
78#endif
79 mode_limit <= CrcMode::TABLE && have_crc_table(polynomial, width) ? CrcMode::TABLE :
80 CrcMode::BITWISE;
81}
82#endif // DOXYGEN_ONLY
83
84} // namespace impl
85
86/**
87 * \ingroup drivers_MbedCRC
88 * @{
89 */
90/** CRC object provides CRC generation through hardware or software
91 *
92 * CRC sums can be generated using three different methods: hardware, software ROM tables
93 * and bitwise computation. The mode used is normally selected automatically based on required
94 * polynomial and hardware capabilities. Any polynomial in standard form (`x^3 + x + 1`)
95 * can be used for computation, but custom ones can affect the performance.
96 *
97 * First choice is the hardware mode. The supported polynomials are hardware specific, and
98 * you need to consult your MCU manual to discover them. Next, ROM polynomial tables
99 * are tried (you can find list of supported polynomials here ::crc_polynomial). If the selected
100 * configuration is supported, it will accelerate the software computations. If ROM tables
101 * are not available for the selected polynomial, then CRC is computed at run time bit by bit
102 * for all data input.
103 *
104 * If desired, the mode can be manually limited for a given instance by specifying the mode_limit
105 * template parameter. This might be appropriate to ensure a table is not pulled in for a
106 * non-speed-critical CRC, or to avoid the hardware set-up overhead if you know you will be
107 * calling `compute` with very small data sizes.
108 *
109 * @note Synchronization level: Thread safe
110 *
111 * @tparam polynomial CRC polynomial value in hex
112 * @tparam width CRC polynomial width
113 * @tparam mode_limit Maximum amount of acceleration to use
114 *
115 * Example: Compute CRC data
116 * @code
117 *
118 * #include "mbed.h"
119 *
120 * int main() {
121 * MbedCRC<POLY_32BIT_ANSI, 32> ct;
122 *
123 * char test[] = "123456789";
124 * uint32_t crc = 0;
125 *
126 * printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width());
127 *
128 * ct.compute((void *)test, strlen((const char*)test), &crc);
129 *
130 * printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
131 * return 0;
132 * }
133 * @endcode
134 * Example: Compute CRC with data available in parts
135 * @code
136 *
137 * #include "mbed.h"
138 * int main() {
139 * MbedCRC<POLY_32BIT_ANSI, 32> ct;
140 *
141 * char test[] = "123456789";
142 * uint32_t crc = 0;
143 *
144 * printf("\nPolynomial = 0x%lx Width = %d \n", ct.get_polynomial(), ct.get_width());
145 * ct.compute_partial_start(&crc);
146 * ct.compute_partial((void *)&test, 4, &crc);
147 * ct.compute_partial((void *)&test[4], 5, &crc);
148 * ct.compute_partial_stop(&crc);
149 * printf("The CRC of data \"123456789\" is : 0x%lx\n", crc);
150 * return 0;
151 * }
152 * @endcode
153 */
154template <uint32_t polynomial = POLY_32BIT_ANSI, uint8_t width = 32, CrcMode mode_limit = CrcMode::HARDWARE>
155class MbedCRC {
156 impl::MbedCRC<polynomial, width, impl::choose_crc_mode(polynomial, width, mode_limit)> crc_impl;
157
158public:
159 /* Backwards compatibility */
160 enum CrcMode {
161#if DEVICE_CRC
162 HARDWARE = int(::mbed::CrcMode::HARDWARE),
163#endif
164 TABLE = int(::mbed::CrcMode::TABLE),
165 BITWISE = int(::mbed::CrcMode::BITWISE)
166 };
167
168 typedef size_t crc_data_size_t;
169
170 /** Lifetime of CRC object
171 *
172 * @param initial_xor Initial value/seed to Xor
173 * @param final_xor Final Xor value
174 * @param reflect_data
175 * @param reflect_remainder
176 * @note Default constructor without any arguments is valid only for supported CRC polynomials. :: crc_polynomial_t
177 * MbedCRC <POLY_7BIT_SD, 7> ct; --- Valid POLY_7BIT_SD
178 * MbedCRC <0x1021, 16> ct; --- Valid POLY_16BIT_CCITT
179 * MbedCRC <POLY_16BIT_CCITT, 32> ct; --- Invalid, compilation error
180 * MbedCRC <POLY_16BIT_CCITT, 32> ct (i,f,rd,rr) Constructor can be used for not supported polynomials
181 * MbedCRC<POLY_16BIT_CCITT, 16> sd(0, 0, false, false); Constructor can also be used for supported
182 * polynomials with different initial/final/reflect values
183 *
184 */
185 constexpr
186 MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) :
187 crc_impl(initial_xor, final_xor, reflect_data, reflect_remainder)
188 {
189 }
190
191 /* Default values for different types of polynomials
192 */
193 // *INDENT-OFF*
194 template<uint32_t poly = polynomial, std::enable_if_t<poly == POLY_32BIT_ANSI && width == 32, int> = 0>
195 constexpr MbedCRC() : MbedCRC(0xFFFFFFFF, 0xFFFFFFFF, true, true)
196 {
197 }
198
199 template<uint32_t poly = polynomial, std::enable_if_t<poly == POLY_16BIT_IBM && width == 16, int> = 0>
200 constexpr MbedCRC() : MbedCRC(0, 0, true, true)
201 {
202 }
203
204 template<uint32_t poly = polynomial, std::enable_if_t<poly == POLY_16BIT_CCITT && width == 16, int> = 0>
205 constexpr MbedCRC() : MbedCRC(0xFFFF, 0, false, false)
206 {
207 }
208
209 template<uint32_t poly = polynomial, std::enable_if_t<poly == POLY_7BIT_SD && width == 7, int> = 0>
210 constexpr MbedCRC() : MbedCRC(0, 0, false, false)
211 {
212 }
213
214 template<uint32_t poly = polynomial, std::enable_if_t<poly == POLY_8BIT_CCITT && width == 8, int> = 0>
215 constexpr MbedCRC() : MbedCRC(0, 0, false, false)
216 {
217 }
218 // *INDENT-ON*
219
220 /** Compute CRC for the data input
221 * Compute CRC performs the initialization, computation and collection of
222 * final CRC.
223 *
224 * @param buffer Data bytes
225 * @param size Size of data
226 * @param crc CRC is the output value
227 * @return 0 on success, negative error code on failure
228 */
229 int32_t compute(const void *buffer, crc_data_size_t size, uint32_t *crc)
230 {
231 return crc_impl.compute(buffer, size, crc);
232 }
233
234 /** Compute partial CRC for the data input.
235 *
236 * CRC data if not available fully, CRC can be computed in parts with available data.
237 *
238 * In case of hardware, intermediate values and states are saved by hardware. Mutex
239 * locking is used to serialize access to hardware CRC.
240 *
241 * In case of software CRC, previous CRC output should be passed as argument to the
242 * current compute_partial call. Please note the intermediate CRC value is maintained by
243 * application and not the driver.
244 *
245 * @pre: Call `compute_partial_start` to start the partial CRC calculation.
246 * @post: Call `compute_partial_stop` to get the final CRC value.
247 *
248 * @param buffer Data bytes
249 * @param size Size of data
250 * @param crc CRC value is intermediate CRC value filled by API.
251 * @return 0 on success or a negative error code on failure
252 * @note: CRC as output in compute_partial is not final CRC value, call `compute_partial_stop`
253 * to get final correct CRC value.
254 */
255 int32_t compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc)
256 {
257 return crc_impl.compute_partial(buffer, size, crc);
258 }
259
260 /** Compute partial start, indicate start of partial computation.
261 *
262 * This API should be called before performing any partial computation
263 * with compute_partial API.
264 *
265 * @param crc Initial CRC value set by the API
266 * @return 0 on success or a negative in case of failure
267 * @note: CRC is an out parameter and must be reused with compute_partial
268 * and `compute_partial_stop` without any modifications in application.
269 */
270 int32_t compute_partial_start(uint32_t *crc)
271 {
272 return crc_impl.compute_partial_start(crc);
273 }
274
275 /** Get the final CRC value of partial computation.
276 *
277 * CRC value available in partial computation is not correct CRC, as some
278 * algorithms require remainder to be reflected and final value to be XORed
279 * This API is used to perform final computation to get correct CRC value.
280 *
281 * @param crc CRC result
282 * @return 0 on success or a negative in case of failure.
283 */
284 int32_t compute_partial_stop(uint32_t *crc)
285 {
286 return crc_impl.compute_partial_stop(crc);
287 }
288
289 /** Get the current CRC polynomial.
290 *
291 * @return Polynomial value
292 */
293 static constexpr uint32_t get_polynomial()
294 {
295 return polynomial;
296 }
297
298 /** Get the current CRC width
299 *
300 * @return CRC width
301 */
302 static constexpr uint8_t get_width()
303 {
304 return width;
305 }
306};
307
308/// @}
309
310#if !defined(DOXYGEN_ONLY)
311/* Internal implementation - basically same as public, but actual mode locked in */
312namespace impl {
313
314template <uint32_t polynomial, uint8_t width, CrcMode mode>
315class MbedCRC {
316public:
317 typedef size_t crc_data_size_t;
318
319 constexpr
320 MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder) :
321 _initial_value(adjust_initial_value(initial_xor, reflect_data)),
322 _final_xor(final_xor),
323 _reflect_data(reflect_data),
324 _reflect_remainder(reflect_remainder)
325 {
326 static_assert(width <= 32, "Max 32-bit CRC supported");
327 }
328
329 /** Compute CRC for the data input
330 * Compute CRC performs the initialization, computation and collection of
331 * final CRC.
332 *
333 * @param buffer Data bytes
334 * @param size Size of data
335 * @param crc CRC is the output value
336 * @return 0 on success, negative error code on failure
337 */
338 int32_t compute(const void *buffer, crc_data_size_t size, uint32_t *crc)
339 {
340 int32_t status;
341
342 status = compute_partial_start(crc);
343 if (0 != status) {
344 return status;
345 }
346
347 status = compute_partial(buffer, size, crc);
348 if (0 != status) {
349 return status;
350 }
351
352 status = compute_partial_stop(crc);
353 return status;
354 }
355
356 /** Compute partial CRC for the data input.
357 *
358 * CRC data if not available fully, CRC can be computed in parts with available data.
359 *
360 * In case of hardware, intermediate values and states are saved by hardware. Mutex
361 * locking is used to serialize access to hardware CRC.
362 *
363 * In case of software CRC, previous CRC output should be passed as argument to the
364 * current compute_partial call. Please note the intermediate CRC value is maintained by
365 * application and not the driver.
366 *
367 * @pre: Call `compute_partial_start` to start the partial CRC calculation.
368 * @post: Call `compute_partial_stop` to get the final CRC value.
369 *
370 * @param buffer Data bytes
371 * @param size Size of data
372 * @param crc CRC value is intermediate CRC value filled by API.
373 * @return 0 on success or a negative error code on failure
374 * @note: CRC as output in compute_partial is not final CRC value, call `compute_partial_stop`
375 * to get final correct CRC value.
376 */
377 int32_t compute_partial(const void *buffer, crc_data_size_t size, uint32_t *crc)
378 {
379 const uint8_t *data = static_cast<const uint8_t *>(buffer);
380 return do_compute_partial(data, size, crc);
381 }
382
383 /** Compute partial start, indicate start of partial computation.
384 *
385 * This API should be called before performing any partial computation
386 * with compute_partial API.
387 *
388 * @param crc Initial CRC value set by the API
389 * @return 0 on success or a negative in case of failure
390 * @note: CRC is an out parameter and must be reused with compute_partial
391 * and `compute_partial_stop` without any modifications in application.
392 */
393 int32_t compute_partial_start(uint32_t *crc)
394 {
395#if DEVICE_CRC
396 if (mode == CrcMode::HARDWARE) {
397 lock();
398 crc_mbed_config_t config;
399 config.polynomial = polynomial;
400 config.width = width;
401 config.initial_xor = _initial_value;
402 config.final_xor = _final_xor;
403 config.reflect_in = _reflect_data;
404 config.reflect_out = _reflect_remainder;
405
407 }
408#endif
409
410 *crc = _initial_value;
411 return 0;
412 }
413
414 /** Get the final CRC value of partial computation.
415 *
416 * CRC value available in partial computation is not correct CRC, as some
417 * algorithms require remainder to be reflected and final value to be XORed
418 * This API is used to perform final computation to get correct CRC value.
419 *
420 * @param crc CRC result
421 * @return 0 on success or a negative in case of failure.
422 */
423 int32_t compute_partial_stop(uint32_t *crc)
424 {
425#if DEVICE_CRC
426 if (mode == CrcMode::HARDWARE) {
427 *crc = hal_crc_get_result();
428 unlock();
429 return 0;
430 }
431#endif
432 uint_fast32_t p_crc = *crc;
433 if (mode == CrcMode::BITWISE) {
434 if (_reflect_data) {
435 /* CRC has MSB in bottom bit of register */
436 if (!_reflect_remainder) {
437 p_crc = reflect_crc(p_crc);
438 }
439 } else {
440 /* CRC has MSB in top bit of register */
441 p_crc = _reflect_remainder ? reflect(p_crc) : shift_right(p_crc);
442 }
443 } else { // TABLE
444 /* CRC has MSB in bottom bit of register */
445 if (!_reflect_remainder) {
446 p_crc = reflect_crc(p_crc);
447 }
448 }
449
450 p_crc ^= _final_xor;
451 p_crc &= get_crc_mask();
452 *crc = p_crc;
453
454 return 0;
455 }
456
457private:
458 /** Guaranteed constexpr reflection (all toolchains)
459 *
460 * @note This should never be run-time evaluated - very inefficient
461 * @param Register value to be reflected (full 32-bit value)
462 * @return Reflected value (full 32-bit value)
463 */
464 static constexpr uint32_t reflect_constant(uint32_t data)
465 {
466 /* Doing this hard way to keep it C++11 constexpr and hence ARM C 5 compatible */
467 return ((data & 0x00000001) << 31) |
468 ((data & 0x00000002) << 29) |
469 ((data & 0x00000004) << 27) |
470 ((data & 0x00000008) << 25) |
471 ((data & 0x00000010) << 23) |
472 ((data & 0x00000020) << 21) |
473 ((data & 0x00000040) << 19) |
474 ((data & 0x00000080) << 17) |
475 ((data & 0x00000100) << 15) |
476 ((data & 0x00000200) << 13) |
477 ((data & 0x00000400) << 11) |
478 ((data & 0x00000800) << 9) |
479 ((data & 0x00001000) << 7) |
480 ((data & 0x00002000) << 5) |
481 ((data & 0x00004000) << 3) |
482 ((data & 0x00008000) << 1) |
483 ((data & 0x00010000) >> 1) |
484 ((data & 0x00020000) >> 3) |
485 ((data & 0x00040000) >> 5) |
486 ((data & 0x00080000) >> 7) |
487 ((data & 0x00100000) >> 9) |
488 ((data & 0x00200000) >> 11) |
489 ((data & 0x00400000) >> 13) |
490 ((data & 0x00800000) >> 15) |
491 ((data & 0x01000000) >> 17) |
492 ((data & 0x02000000) >> 19) |
493 ((data & 0x04000000) >> 21) |
494 ((data & 0x08000000) >> 23) |
495 ((data & 0x10000000) >> 25) |
496 ((data & 0x20000000) >> 27) |
497 ((data & 0x40000000) >> 29) |
498 ((data & 0x80000000) >> 31);
499 }
500
501 /** General reflection
502 *
503 * @note This is used when we may need to perform run-time computation, so
504 * we need the possibility to produce the optimal run-time RBIT instruction. But
505 * if the compiler doesn't treat RBIT as a built-in, it's useful to have a C fallback
506 * for the constant case, avoiding runtime RBIT(0) computations. This is an
507 * optimization only available for some toolchains; others will always use runtime
508 * RBIT. If we require a constant expression, use reflect_constant instead.
509 *
510 * @param Register value to be reflected (full 32-bit value)
511 * @return Reflected value (full 32-bit value)
512 */
513#ifdef MSTD_HAS_IS_CONSTANT_EVALUATED
514 static constexpr uint32_t reflect(uint32_t data)
515 {
516 return mstd::is_constant_evaluated() ? reflect_constant(data) : __RBIT(data);
517 }
518#else
519 static uint32_t reflect(uint32_t data)
520 {
521 return __RBIT(data);
522 }
523#endif
524
525 /** Data bytes may need to be reflected.
526 *
527 * @param data value to be reflected (bottom 8 bits)
528 * @return Reflected value (bottom 8 bits)
529 */
530 static MSTD_CONSTEXPR_IF_HAS_IS_CONSTANT_EVALUATED
531 uint_fast32_t reflect_byte(uint_fast32_t data)
532 {
533 return reflect(data) >> 24;
534 }
535
536 /** Get the current CRC polynomial, reflected at bottom of register.
537 *
538 * @return Reflected polynomial value (so x^width term would be at bit -1)
539 */
540 static constexpr uint32_t get_reflected_polynomial()
541 {
542 return shift_right(reflect_constant(polynomial));
543 }
544
545 /** Get the current CRC polynomial, at top of register.
546 *
547 * @return Shifted polynomial value (so x^width term would be at bit 32)
548 */
549 static constexpr uint32_t get_top_polynomial()
550 {
551 return shift_left(polynomial);
552 }
553
554 const uint32_t _initial_value;
555 const uint32_t _final_xor;
556 const bool _reflect_data;
557 const bool _reflect_remainder;
558
559 // *INDENT-OFF*
560 using crc_table_t = std::conditional_t<width <= 8, uint8_t,
561 std::conditional_t<width <= 16, uint16_t,
562 uint32_t
563 >>;
564 // *INDENT-ON*
565
566#if MBED_CRC_TABLE_SIZE > 0
567 /* Tables only actually defined for mode == TABLE, and certain polynomials - see below */
568 static const crc_table_t _crc_table[MBED_CRC_TABLE_SIZE];
569#endif
570
571 static constexpr uint32_t adjust_initial_value(uint32_t initial_xor, bool reflect_data)
572 {
573 if (mode == CrcMode::BITWISE) {
574 /* For bitwise calculation, CRC register is reflected if data is, to match input.
575 * (MSB at bottom of register). If not reflected, it is at the top of the register
576 * (MSB at top of register).
577 */
578 return reflect_data ? reflect_crc(initial_xor) : shift_left(initial_xor);
579 } else if (mode == CrcMode::TABLE) {
580 /* For table calculation, CRC value is reflected, to match tables.
581 * (MSB at bottom of register). */
582 return reflect_crc(initial_xor);
583 } else { // CrcMode::HARDWARE
584 return initial_xor;
585 }
586 }
587
588 /** Acquire exclusive access to CRC hardware/software.
589 */
590 static void lock()
591 {
592#if DEVICE_CRC
593 if (mode == CrcMode::HARDWARE) {
594 mbed_crc_mutex->lock();
595 }
596#endif
597 }
598
599 /** Release exclusive access to CRC hardware/software.
600 */
601 static void unlock()
602 {
603#if DEVICE_CRC
604 if (mode == CrcMode::HARDWARE) {
605 mbed_crc_mutex->unlock();
606 }
607#endif
608 }
609
610 /** Get the CRC data mask.
611 *
612 * @return CRC data mask is generated based on current CRC width
613 */
614 static constexpr uint32_t get_crc_mask()
615 {
616 return (uint32_t)((uint32_t)2U << (width - 1)) - 1U;
617 }
618
619 /** CRC values may need to be reflected.
620 *
621 * @param CRC value to be reflected (width bits at bottom of 32-bit word)
622 * @return Reflected value (still at bottom of 32-bit word)
623 */
624 static MSTD_CONSTEXPR_IF_HAS_IS_CONSTANT_EVALUATED
625 uint32_t reflect_crc(uint32_t data)
626 {
627 return reflect(data) >> (32 - width);
628 }
629
630 /** Register values may need to be shifted left.
631 *
632 * @param Register value to be shifted up (in bottom width bits)
633 * @return Shifted value (in top width bits)
634 */
635 static constexpr uint32_t shift_left(uint32_t data)
636 {
637 return data << (32 - width);
638 }
639
640 /** Register values may need to be shifted right.
641 *
642 * @param Register value to be shifted right (in top width bits)
643 * @return Shifted value (in bottom width bits)
644 */
645 static constexpr uint32_t shift_right(uint32_t data)
646 {
647 return data >> (32 - width);
648 }
649
650 /* Check to see if we can do assembler optimizations */
651#if (defined __GNUC__ || defined __clang__) && \
652 (defined __arm__ || defined __ARM_ARCH)
653#if (__ARM_ARCH_7M__ == 1U) || \
654 (__ARM_ARCH_7EM__ == 1U) || \
655 (__ARM_ARCH_8M_MAIN__ == 1U) || \
656 (__ARM_ARCH_8_1M_MAIN__ == 1U) || \
657 (__ARM_ARCH_7A__ == 1U)
658 /* ARM that has Thumb-2 - same unified assembly is good for either ARM or Thumb state (LSRS; IT CS; EORCS reg/imm) */
659#define MBED_CRC_ARM_THUMB2 1
660#define MBED_CRC_THUMB1 0
661#elif (__ARM_ARCH_6M__ == 1U) || \
662 (__ARM_ARCH_8M_BASE__ == 1U)
663 /* Thumb-1-only ARM-M device - use Thumb-1 compatible assembly with branch (LSRS; BCC; EORS reg) */
664#define MBED_CRC_ARM_THUMB2 0
665#define MBED_CRC_THUMB1 1
666#else // __ARM_ARCH_xxx
667#error "Unknown ARM architecture for CRC optimization"
668#endif // __ARM_ARCH_xxx
669#else // __arm__ || defined __ICC_ARM__ || defined __ARM_ARCH
670 /* Seem to be compiling for non-ARM, or an unsupported toolchain, so stick with C implementations */
671#define MBED_CRC_ARM_THUMB2 0
672#define MBED_CRC_THUMB1 0
673#endif
674
675 // *INDENT-OFF*
676 /** Process 1 bit of non-reflected CRC
677 *
678 * Shift the p_crc register left 1 bit - if a one is shifted
679 * out, exclusive-or with the polynomial mask.
680 *
681 * Assembler optimizations can be applied here, to make
682 * use of the CPU's carry output from shifts.
683 *
684 * @param p_crc input register value
685 * @return updated register value
686 */
687 static uint_fast32_t do_1_bit_normal(uint_fast32_t p_crc)
688 {
689#if MBED_CRC_ARM_THUMB2
690 __asm(".syntax unified\n\t"
691 "LSLS" "\t%[p_crc], %[p_crc], #1\n\t"
692 "IT" "\tCS\n\t"
693 "EORCS" "\t%[p_crc], %[poly]"
694 : [p_crc] "+&r" (p_crc)
695 : [poly] "rI" (get_top_polynomial())
696 : "cc");
697#elif MBED_CRC_THUMB1
698 __asm(".syntax unified\n\t"
699 "LSLS" "\t%[p_crc], %[p_crc], #1\n\t"
700 "BCC" "\t%=f\n\t"
701 "EORS" "\t%[p_crc], %[poly]\n"
702 "%=:"
703 : [p_crc] "+&l" (p_crc)
704 : [poly] "l" (get_top_polynomial())
705 : "cc");
706#else
707 if (p_crc & 0x80000000) {
708 p_crc = (p_crc << 1) ^ get_top_polynomial();
709 } else {
710 p_crc = (p_crc << 1);
711 }
712#endif
713 return p_crc;
714 }
715
716 /** Process 1 bit of reflected CRC
717 *
718 * Shift the p_crc register right 1 bit - if a one is shifted
719 * out, exclusive-or with the polynomial mask.
720 *
721 * Assembler optimizations can be applied here, to make
722 * use of the CPU's carry output from shifts.
723 *
724 * @param p_crc input register value
725 * @return updated register value
726 */
727 static uint_fast32_t do_1_bit_reflected(uint_fast32_t p_crc)
728 {
729#if MBED_CRC_ARM_THUMB2
730 __asm(".syntax unified\n\t"
731 "LSRS" "\t%[p_crc], %[p_crc], #1\n\t"
732 "IT" "\tCS\n\t"
733 "EORCS" "\t%[p_crc], %[poly]"
734 : [p_crc] "+&r" (p_crc)
735 : [poly] "rI" (get_reflected_polynomial())
736 : "cc");
737#elif MBED_CRC_THUMB1
738 __asm(".syntax unified\n\t"
739 "LSRS" "\t%[p_crc], %[p_crc], #1\n\t"
740 "BCC" "\t%=f\n\t"
741 "EORS" "\t%[p_crc], %[poly]\n"
742 "%=:"
743 : [p_crc] "+&l" (p_crc)
744 : [poly] "l" (get_reflected_polynomial())
745 : "cc");
746#else
747 if (p_crc & 1) {
748 p_crc = (p_crc >> 1) ^ get_reflected_polynomial();
749 } else {
750 p_crc = (p_crc >> 1);
751 }
752#endif
753 return p_crc;
754 }
755 // *INDENT-ON*
756
757 /** Bitwise CRC computation.
758 *
759 * @param buffer data buffer
760 * @param size size of the data
761 * @param crc CRC value is filled in, but the value is not the final
762 * @return 0 on success or a negative error code on failure
763 */
764 template<CrcMode mode_ = mode>
765 std::enable_if_t<mode_ == CrcMode::BITWISE, int32_t>
766 do_compute_partial(const uint8_t *data, crc_data_size_t size, uint32_t *crc) const
767 {
768 uint_fast32_t p_crc = *crc;
769
770 if (_reflect_data) {
771 /* Everything is reflected to match data - MSB of polynomial at bottom of 32-bit register */
772 for (crc_data_size_t byte = 0; byte < size; byte++) {
773 p_crc ^= data[byte];
774
775 // Perform modulo-2 division, a bit at a time
776 for (unsigned int bit = 8; bit > 0; --bit) {
777 p_crc = do_1_bit_reflected(p_crc);
778 }
779 }
780 } else {
781 /* Polynomial is shifted to put MSB of polynomial at top of 32-bit register */
782 for (crc_data_size_t byte = 0; byte < size; byte++) {
783 p_crc ^= (uint_fast32_t) data[byte] << 24;
784
785 // Perform modulo-2 division, a bit at a time
786 for (unsigned int bit = 8; bit > 0; --bit) {
787 p_crc = do_1_bit_normal(p_crc);
788 }
789 }
790 }
791
792 *crc = p_crc;
793
794 return 0;
795 }
796
797#if MBED_CRC_TABLE_SIZE > 0
798 /** CRC computation using ROM tables.
799 *
800 * @param buffer data buffer
801 * @param size size of the data
802 * @param crc CRC value is filled in, but the value is not the final
803 * @return 0 on success or a negative error code on failure
804 */
805 template<CrcMode mode_ = mode>
806 std::enable_if_t<mode_ == CrcMode::TABLE, int32_t>
807 do_compute_partial(const uint8_t *data, crc_data_size_t size, uint32_t *crc) const
808 {
809 uint_fast32_t p_crc = *crc;
810 // GCC has been observed to not hoist the load of _reflect_data out of the loop
811 // Note the inversion because table and CRC are reflected - data must be
812 bool reflect = !_reflect_data;
813
814 for (crc_data_size_t byte = 0; byte < size; byte++) {
815 uint_fast32_t data_byte = data[byte];
816 if (reflect) {
817 data_byte = reflect_byte(data_byte);
818 }
819#if MBED_CRC_TABLE_SIZE == 16
820 p_crc = _crc_table[(data_byte ^ p_crc) & 0xF] ^ (p_crc >> 4);
821 data_byte >>= 4;
822 p_crc = _crc_table[(data_byte ^ p_crc) & 0xF] ^ (p_crc >> 4);
823#else
824 p_crc = _crc_table[(data_byte ^ p_crc) & 0xFF] ^ (p_crc >> 8);
825#endif
826 }
827 *crc = p_crc;
828 return 0;
829 }
830#endif
831
832#ifdef DEVICE_CRC
833 /** Hardware CRC computation.
834 *
835 * @param buffer data buffer
836 * @param size size of the data
837 * @return 0 on success or a negative error code on failure
838 */
839 template<CrcMode mode_ = mode>
840 std::enable_if_t<mode_ == CrcMode::HARDWARE, int32_t>
841 do_compute_partial(const uint8_t *data, crc_data_size_t size, uint32_t *) const
842 {
843 hal_crc_compute_partial(data, size);
844 return 0;
845 }
846#endif
847
848};
849
850#if MBED_CRC_TABLE_SIZE > 0
851/* Declarations of the tables we provide. (Not strictly needed, but compilers
852 * can warn if they see us using the template without a generic definition, so
853 * let it know we have provided these specialisations.)
854 */
855template<>
856const uint8_t MbedCRC<POLY_7BIT_SD, 7, CrcMode::TABLE>::_crc_table[MBED_CRC_TABLE_SIZE];
857
858template<>
859const uint8_t MbedCRC<POLY_8BIT_CCITT, 8, CrcMode::TABLE>::_crc_table[MBED_CRC_TABLE_SIZE];
860
861template<>
862const uint16_t MbedCRC<POLY_16BIT_CCITT, 16, CrcMode::TABLE>::_crc_table[MBED_CRC_TABLE_SIZE];
863
864template<>
865const uint16_t MbedCRC<POLY_16BIT_IBM, 16, CrcMode::TABLE>::_crc_table[MBED_CRC_TABLE_SIZE];
866
867template<>
868const uint32_t MbedCRC<POLY_32BIT_ANSI, 32, CrcMode::TABLE>::_crc_table[MBED_CRC_TABLE_SIZE];
869
870#endif // MBED_CRC_TABLE_SIZE > 0
871
872} // namespace impl
873
874#endif // !defined(DOXYGEN_ONLY)
875
876
877
878} // namespace mbed
879
880#endif // __cplusplus
881
882/* Internal helper for mbed_error.c crash recovery */
883#ifdef __cplusplus
884extern "C"
885#endif
886uint32_t mbed_tiny_compute_crc32(const void *data, int datalen);
887
888#endif
CRC object provides CRC generation through hardware or software.
Definition: MbedCRC.h:155
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:229
int32_t compute_partial_stop(uint32_t *crc)
Get the final CRC value of partial computation.
Definition: MbedCRC.h:284
constexpr MbedCRC(uint32_t initial_xor, uint32_t final_xor, bool reflect_data, bool reflect_remainder)
Lifetime of CRC object.
Definition: MbedCRC.h:186
static constexpr uint8_t get_width()
Get the current CRC width.
Definition: MbedCRC.h:302
int32_t compute_partial_start(uint32_t *crc)
Compute partial start, indicate start of partial computation.
Definition: MbedCRC.h:270
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:255
static constexpr uint32_t get_polynomial()
Get the current CRC polynomial.
Definition: MbedCRC.h:293
void unlock()
Unlock the mutex that has previously been locked by the same thread.
void lock()
Wait until a Mutex becomes available.
CrcMode
CRC mode selection.
Definition: MbedCRC.h:46
@ BITWISE
Use table-based computation (if table available), else bitwise.
@ TABLE
Use hardware (if available), else table-based computation.
void hal_crc_compute_partial(const uint8_t *data, const size_t size)
Writes data to the current CRC module.
void hal_crc_compute_partial_start(const crc_mbed_config_t *config)
Initialize the hardware CRC module with the given polynomial.
@ POLY_8BIT_CCITT
x8+x2+x+1
Definition: crc_api.h:32
@ POLY_7BIT_SD
x7+x3+1
Definition: crc_api.h:31
@ POLY_16BIT_IBM
x16+x15+x2+1
Definition: crc_api.h:34
@ POLY_16BIT_CCITT
x16+x12+x5+1
Definition: crc_api.h:33
@ POLY_32BIT_ANSI
x32+x26+x23+x22+x16+x12+x11+x10+x8+x7+x5+x4+x2+x+1
Definition: crc_api.h:35
Utility class for creating and using a singleton.
Definition: SingletonPtr.h:88
uint32_t initial_xor
Initial seed value for the computation.
Definition: crc_api.h:44
uint32_t width
CRC Bit Width.
Definition: crc_api.h:42
bool reflect_in
Reflect bits on input.
Definition: crc_api.h:48
uint32_t polynomial
CRC Polynomial.
Definition: crc_api.h:40
bool reflect_out
Reflect bits in final result before returning.
Definition: crc_api.h:50
uint32_t final_xor
Final xor value for the computation.
Definition: crc_api.h:46