Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b848c3b

Browse files
committedFeb 18, 2025
Imported crc calculation from MbedOS
1 parent 1ff70a7 commit b848c3b

File tree

1 file changed

+892
-0
lines changed

1 file changed

+892
-0
lines changed
 

‎libraries/KVStore/src/MbedCRC.h

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

0 commit comments

Comments
 (0)
Please sign in to comment.