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}