stm32_hal2/crc.rs
1//! Cyclic Redundancy Check (CRC) support
2
3// Based on `stm32h7xx-hal`
4
5use core::{convert::TryInto, fmt};
6
7use cfg_if::cfg_if;
8
9use crate::{
10 error::{Error, Result},
11 pac::{CRC, RCC},
12};
13
14// todo: Redo this in the style of the rest of our modules.
15
16pub trait CrcExt {
17 /// Enable the CRC unit.
18 fn crc(self, rcc: &mut RCC) -> Crc;
19}
20
21impl CrcExt for CRC {
22 fn crc(self, rcc: &mut RCC) -> Crc {
23 cfg_if! {
24 if #[cfg(feature = "f3")] {
25 rcc.ahbenr().modify(|_, w| w.crcen().bit(true));
26 // F3 doesn't appear to have a crcrst field in `ahbrstr`, per RM.
27 } else if #[cfg(any(feature = "g0", feature = "c0"))] {
28 rcc.ahbenr().modify(|_, w| w.crcen().bit(true));
29 rcc.ahbrstr().modify(|_, w| w.crcrst().bit(true));
30 rcc.ahbrstr().modify(|_, w| w.crcrst().clear_bit());
31 } else if #[cfg(any(feature = "l4", feature = "wb", feature = "l5", feature = "g4"))] {
32 rcc.ahb1enr().modify(|_, w| w.crcen().bit(true));
33 rcc.ahb1rstr().modify(|_, w| w.crcrst().bit(true));
34 rcc.ahb1rstr().modify(|_, w| w.crcrst().clear_bit());
35 } else { // H7
36 rcc.ahb4enr().modify(|_, w| w.crcen().bit(true));
37 rcc.ahb4rstr().modify(|_, w| w.crcrst().bit(true));
38 rcc.ahb4rstr().modify(|_, w| w.crcrst().clear_bit());
39 }
40 }
41
42 Crc {
43 regs: self,
44 output_xor: 0,
45 }
46 }
47}
48
49/// The hardware CRC unit.
50pub struct Crc {
51 pub regs: CRC,
52 output_xor: u32,
53}
54
55impl Crc {
56 /// Set the unit's configuration, discarding previous state.
57 pub fn set_config(&mut self, config: &Config) {
58 self.output_xor = config.output_xor & config.poly.xor_mask();
59
60 // manual says unit must be reset (or DR read) before change of polynomial
61 // (technically only in case of ongoing calculation, but DR is buffered)
62 self.regs.cr().modify(|_, w| unsafe {
63 w.polysize()
64 .bits(config.poly.polysize())
65 .rev_in()
66 .bits(config.get_reverse_input())
67 .rev_out()
68 .bit(config.reverse_output)
69 .reset()
70 .bit(true)
71 });
72 cfg_if! {
73 if #[cfg(any(feature = "h7"))] {
74 self.regs.pol().write(|w| unsafe { w.pol().bits(config.poly.pol())});
75 } else {
76 self.regs.pol().write(|w| unsafe { w.bits(config.poly.pol()) });
77 }
78 }
79
80 // writing to INIT sets DR to its value
81 // #[cfg(not(any(feature = "h7", feature = "l4")))]
82 // self.regs
83 // .init()
84 // .write(|w| unsafe { w.crc_init().bits(config.initial) });
85 // #[cfg(any(feature = "h7", feature = "l4"))]
86 self.regs
87 .init()
88 .write(|w| unsafe { w.init().bits(config.initial) });
89 }
90
91 /// Write data to the CRC unit. Note that CRC calculation works
92 /// faster if more data is given at once.
93 pub fn update(&mut self, data: &[u8]) {
94 // write 4 bytes at once, then 2, then 1, as appropriate
95 // in the case of a single large slice this improves speed by >3x
96 let mut words = data.chunks_exact(4);
97 for word in words.by_ref() {
98 let word = u32::from_be_bytes(word.try_into().unwrap());
99 self.regs.dr().write(unsafe { |w| w.dr().bits(word) });
100 }
101
102 // there will be at most 3 bytes remaining, so 1 half-word and 1 byte
103 let mut half_word = words.remainder().chunks_exact(2);
104 if let Some(half_word) = half_word.next() {
105 let _half_word = u16::from_be_bytes(half_word.try_into().unwrap());
106 self.regs
107 .dr16()
108 .write(unsafe { |w| w.dr16().bits(_half_word) });
109 }
110
111 if let Some(byte) = half_word.remainder().first() {
112 self.regs.dr8().write(unsafe { |w| w.dr8().bits(*byte) });
113 }
114 }
115
116 /// Write data to the CRC unit, return CRC so far. This function should
117 /// only be used if you need its result, as retrieving the CRC takes time.
118 #[must_use = "retrieving the CRC takes time, use update() if not needed"]
119 pub fn update_and_read(&mut self, data: &[u8]) -> u32 {
120 self.update(data);
121 self.read_crc()
122 }
123
124 /// Read the CRC and reset DR to initial value in preparation for a new CRC.
125 /// This does not reset the configuration options.
126 pub fn finish(&mut self) -> u32 {
127 let result = self.read_crc();
128 self.regs.cr().modify(|_, w| w.reset().bit(true));
129 result
130 }
131
132 /// Read the CRC without resetting the unit.
133 #[inline]
134 pub fn read_crc(&self) -> u32 {
135 self.read_crc_no_xor() ^ self.output_xor
136 }
137
138 /// Read the state of the CRC calculation. When used as the initial value
139 /// of an otherwise identical CRC config, this allows resuming calculation
140 /// from the current state.
141 ///
142 /// This is equivalent to [`read_crc()`](Self::read_crc) in the case of an
143 /// algorithm that does not apply an output XOR or reverse the output bits.
144 pub fn read_state(&self) -> u32 {
145 let state = self.read_crc_no_xor();
146 if self.regs.cr().read().rev_out().bit_is_set() {
147 state.reverse_bits()
148 } else {
149 state
150 }
151 }
152
153 /// Read the CRC without applying output XOR.
154 #[inline(always)]
155 fn read_crc_no_xor(&self) -> u32 {
156 #[cfg(not(any(feature = "h7", feature = "l4")))]
157 return self.regs.dr().read().dr().bits();
158 #[cfg(any(feature = "h7", feature = "l4"))]
159 return self.regs.dr().read().dr().bits();
160 }
161
162 cfg_if! {
163 if #[cfg(any(feature = "f3x4", feature = "g0", feature = "g4", feature = "h7", feature = "wb",
164 feature = "l5", feature = "c0"))] {
165 /// Write the independent data register. The IDR can be used as
166 /// temporary storage. It is not cleared on CRC hash reset.
167 ///
168 /// The IDR is not involved with CRC calculation.
169 pub fn set_idr(&mut self, value: u32) {
170 self.regs.idr().write(|w| unsafe { w.idr().bits(value) });
171 }
172 } else {
173 /// Write the independent data register. The IDR can be used as
174 /// temporary storage. It is not cleared on CRC hash reset.
175 ///
176 /// The IDR is not involved with CRC calculation.
177 pub fn set_idr(&mut self, value: u8) {
178 self.regs.idr().write(|w| unsafe { w.idr().bits(value) });
179 }
180 }
181 }
182
183 cfg_if! {
184 if #[cfg(any(feature = "f3x4", feature = "g0", feature = "g4", feature = "h7", feature = "wb",
185 feature = "l5", feature = "c0"))] {
186 /// Get the current value of the independent data register.
187 ///
188 /// The IDR is not involved with CRC calculation.
189 pub fn get_idr(&self) -> u32 {
190 self.regs.idr().read().idr().bits()
191 }
192 } else {
193 /// Get the current value of the independent data register.
194 ///
195 /// The IDR is not involved with CRC calculation.
196 pub fn get_idr(&self) -> u8 {
197 self.regs.idr().read().idr().bits()
198 }
199 }
200 }
201}
202
203#[macro_use]
204mod macros {
205 /// Generate an error if number passed is even
206 macro_rules! ensure_is_odd {
207 ( $x:expr ) => {
208 if $x % 2 == 0 {
209 Err(PolynomialError::EvenPoly)
210 } else {
211 Ok($x)
212 }
213 };
214 }
215}
216
217/// A CRC polynomial.
218///
219/// The STM32H7 CRC unit only supports odd polynomials, and the constructors
220/// will check to ensure the polynomial is odd unless the `_unchecked` variants
221/// are used.
222///
223/// Even polynomials are essentially never used in CRCs, so you most likely
224/// don't need to worry about this if you aren't creating your own algorithm.
225///
226/// A polynomial being even means that the least significant bit is `0`
227/// in the polynomial's normal representation.
228#[derive(Copy, Clone, Debug, PartialEq)]
229pub struct Polynomial(Poly);
230
231impl Polynomial {
232 /// Create a 7-bit polynomial. Returns an error if the polynomial passed
233 /// has the MSB set or is even.
234 pub fn bits7(poly: u8) -> Result<Self> {
235 if poly <= 0x7F {
236 Ok(Self(Poly::B7(ensure_is_odd!(poly)?)))
237 } else {
238 Err(Error::PolynomialError(PolynomialError::TooLarge))
239 }
240 }
241
242 /// Create an 8-bit polynomial. Returns an error if the polynomial passed is even.
243 pub fn bits8(poly: u8) -> Result<Self> {
244 Ok(Self(Poly::B8(ensure_is_odd!(poly)?)))
245 }
246
247 /// Create a 16-bit polynomial. Returns an error if the polynomial passed is even.
248 pub fn bits16(poly: u16) -> Result<Self> {
249 Ok(Self(Poly::B16(ensure_is_odd!(poly)?)))
250 }
251
252 /// Create a 32-bit polynomial. Returns an error if the polynomial passed is even.
253 pub fn bits32(poly: u32) -> Result<Self> {
254 Ok(Self(Poly::B32(ensure_is_odd!(poly)?)))
255 }
256
257 /// Create a 7-bit polynomial. If the polynomial passed is even the
258 /// CRC unit will give incorrect results.
259 pub const fn bits7_unchecked(poly: u8) -> Self {
260 Self(Poly::B7(poly))
261 }
262
263 /// Create an 8-bit polynomial. If the polynomial passed is even the
264 /// CRC unit will give incorrect results.
265 pub const fn bits8_unchecked(poly: u8) -> Self {
266 Self(Poly::B8(poly))
267 }
268
269 /// Create a 16-bit polynomial. If the polynomial passed is even the
270 /// CRC unit will give incorrect results.
271 pub const fn bits16_unchecked(poly: u16) -> Self {
272 Self(Poly::B16(poly))
273 }
274
275 /// Create a 32-bit polynomial. If the polynomial passed is even the
276 /// CRC unit will give incorrect results.
277 pub const fn bits32_unchecked(poly: u32) -> Self {
278 Self(Poly::B32(poly))
279 }
280
281 /// Return POLYSIZE register value.
282 /// todo: Use our normal register enum format.
283 fn polysize(self) -> u8 {
284 match self.0 {
285 Poly::B7(_) => 0b11,
286 Poly::B8(_) => 0b10,
287 Poly::B16(_) => 0b01,
288 Poly::B32(_) => 0b00,
289 }
290 }
291
292 /// Return POL register value.
293 fn pol(self) -> u32 {
294 match self.0 {
295 Poly::B7(pol) | Poly::B8(pol) => pol as u32,
296 Poly::B16(pol) => pol as u32,
297 Poly::B32(pol) => pol,
298 }
299 }
300
301 /// Return mask for output XOR according to size.
302 fn xor_mask(self) -> u32 {
303 match self.0 {
304 Poly::B7(_) => 0x7F,
305 Poly::B8(_) => 0xFF,
306 Poly::B16(_) => 0xFFFF,
307 Poly::B32(_) => 0xFFFF_FFFF,
308 }
309 }
310}
311
312impl Default for Polynomial {
313 /// Returns the 32-bit polynomial `0x04C1_1DB7`.
314 fn default() -> Self {
315 Self(Poly::B32(0x04C1_1DB7))
316 }
317}
318
319/// Errors generated when trying to create invalid polynomials.
320#[cfg_attr(feature = "defmt", derive(defmt::Format))]
321#[derive(Copy, Clone, Debug, PartialEq, Eq)]
322pub enum PolynomialError {
323 /// Tried to create an even polynomial.
324 /// The hardware CRC unit only supports odd polynomials.
325 EvenPoly,
326 /// Tried to create a 7-bit polynomial with an 8-bit number
327 /// (greater than `0x7F`).
328 TooLarge,
329}
330
331impl fmt::Display for PolynomialError {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 match *self {
334 Self::EvenPoly => f.write_str("tried to create an even polynomial"),
335 Self::TooLarge => {
336 f.write_str("tried to create a 7-bit polynomial with an 8-bit number")
337 }
338 }
339 }
340}
341
342/// Internal representation of a polynomial.
343#[derive(Copy, Clone, Debug, PartialEq)]
344enum Poly {
345 /// 7-bit polynomial
346 B7(u8),
347 /// 8-bit polynomial
348 B8(u8),
349 /// 16-bit polynomial
350 B16(u16),
351 /// 32-bit polynomial
352 B32(u32),
353}
354
355/// How to reverse the input bits.
356///
357/// ST refers to this as both 'reversal' and 'inversion'. If a CRC calls for
358/// 'reflection' it most likely wants [`BitReversal::Byte`] and output reversal
359/// enabled.
360#[derive(Copy, Clone, Debug, PartialEq)]
361#[repr(u8)]
362pub enum BitReversal {
363 /// Each input byte has its bits reversed. `0x1A2B3C4D` becomes `0x58D43CB2`.
364 Byte = 0b01,
365 /// Bits reversed by half-word. `0x1A2B3C4D` becomes `0xD458B23C`.
366 HalfWord = 0b10,
367 /// Bits reversed by word. `0x1A2B3C4D` becomes `0xB23CD458`.
368 Word = 0b11,
369}
370
371/// CRC unit configuration.
372#[derive(Clone, Debug, PartialEq)]
373pub struct Config {
374 poly: Polynomial,
375 initial: u32,
376 reverse_input: Option<BitReversal>,
377 reverse_output: bool,
378 output_xor: u32,
379}
380
381impl Config {
382 /// Creates the Config struct with the default configuration of the STM32H7 CRC unit:
383 ///
384 /// * `0x04C1_1DB7` polynomial
385 /// * `0xFFFF_FFFF` initial value
386 /// * Bits not reflected
387 /// * `0` output XOR
388 ///
389 /// This configuration is the MPEG-2 CRC.
390 pub const fn new() -> Self {
391 Self {
392 poly: Polynomial(Poly::B32(0x04C1_1DB7)),
393 initial: 0xFFFF_FFFF,
394 reverse_input: None,
395 reverse_output: false,
396 output_xor: 0,
397 }
398 }
399
400 /// Set the polynomial.
401 pub const fn polynomial(mut self, poly: Polynomial) -> Self {
402 self.poly = poly;
403 self
404 }
405
406 /// Set the initial value of the CRC. The CRC unit will only use the needed
407 /// bits to match the polynomial; the default initial value `0xFFFF_FFFF`
408 /// will actually write `0x7F` in the case of a 7-bit CRC, for example.
409 pub const fn initial_value(mut self, initial: u32) -> Self {
410 self.initial = initial;
411 self
412 }
413
414 /// Set how to reverse the bits of the input. `None` means no reversal.
415 pub const fn reverse_input(mut self, reverse: Option<BitReversal>) -> Self {
416 self.reverse_input = reverse;
417 self
418 }
419
420 /// Get the register value of input reversal setting.
421 fn get_reverse_input(&self) -> u8 {
422 self.reverse_input.map(|rev| rev as u8).unwrap_or(0b00)
423 }
424
425 /// Set whether to reverse the bits of the output.
426 pub const fn reverse_output(mut self, reverse: bool) -> Self {
427 self.reverse_output = reverse;
428 self
429 }
430
431 /// Set whether to reflect the CRC. When enabled, reflection is
432 /// [`BitReversal::Byte`] and output reversal enabled. This is simply
433 /// a convenience function as many CRC algorithms call for this.
434 pub fn reflect(self, reflect: bool) -> Self {
435 // Rust 1.46 and higher: This function can be const.
436 if reflect {
437 self.reverse_input(Some(BitReversal::Byte))
438 .reverse_output(true)
439 } else {
440 self.reverse_input(None).reverse_output(false)
441 }
442 }
443
444 /// Set the value to XOR the output with. Automatically masked to the proper size.
445 pub const fn output_xor(mut self, output_xor: u32) -> Self {
446 self.output_xor = output_xor;
447 self
448 }
449}
450
451impl Default for Config {
452 /// Calls [`Config::new`].
453 fn default() -> Self {
454 Self::new()
455 }
456}