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#[derive(Copy, Clone, Debug, PartialEq, Eq, defmt::Format)]
321pub enum PolynomialError {
322    /// Tried to create an even polynomial.
323    /// The hardware CRC unit only supports odd polynomials.
324    EvenPoly,
325    /// Tried to create a 7-bit polynomial with an 8-bit number
326    /// (greater than `0x7F`).
327    TooLarge,
328}
329
330impl fmt::Display for PolynomialError {
331    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332        match *self {
333            Self::EvenPoly => f.write_str("tried to create an even polynomial"),
334            Self::TooLarge => {
335                f.write_str("tried to create a 7-bit polynomial with an 8-bit number")
336            }
337        }
338    }
339}
340
341/// Internal representation of a polynomial.
342#[derive(Copy, Clone, Debug, PartialEq)]
343enum Poly {
344    /// 7-bit polynomial
345    B7(u8),
346    /// 8-bit polynomial
347    B8(u8),
348    /// 16-bit polynomial
349    B16(u16),
350    /// 32-bit polynomial
351    B32(u32),
352}
353
354/// How to reverse the input bits.
355///
356/// ST refers to this as both 'reversal' and 'inversion'. If a CRC calls for
357/// 'reflection' it most likely wants [`BitReversal::Byte`] and output reversal
358/// enabled.
359#[derive(Copy, Clone, Debug, PartialEq)]
360#[repr(u8)]
361pub enum BitReversal {
362    /// Each input byte has its bits reversed. `0x1A2B3C4D` becomes `0x58D43CB2`.
363    Byte = 0b01,
364    /// Bits reversed by half-word. `0x1A2B3C4D` becomes `0xD458B23C`.
365    HalfWord = 0b10,
366    /// Bits reversed by word. `0x1A2B3C4D` becomes `0xB23CD458`.
367    Word = 0b11,
368}
369
370/// CRC unit configuration.
371#[derive(Clone, Debug, PartialEq)]
372pub struct Config {
373    poly: Polynomial,
374    initial: u32,
375    reverse_input: Option<BitReversal>,
376    reverse_output: bool,
377    output_xor: u32,
378}
379
380impl Config {
381    /// Creates the Config struct with the default configuration of the STM32H7 CRC unit:
382    ///
383    /// * `0x04C1_1DB7` polynomial
384    /// * `0xFFFF_FFFF` initial value
385    /// * Bits not reflected
386    /// * `0` output XOR
387    ///
388    /// This configuration is the MPEG-2 CRC.
389    pub const fn new() -> Self {
390        Self {
391            poly: Polynomial(Poly::B32(0x04C1_1DB7)),
392            initial: 0xFFFF_FFFF,
393            reverse_input: None,
394            reverse_output: false,
395            output_xor: 0,
396        }
397    }
398
399    /// Set the polynomial.
400    pub const fn polynomial(mut self, poly: Polynomial) -> Self {
401        self.poly = poly;
402        self
403    }
404
405    /// Set the initial value of the CRC. The CRC unit will only use the needed
406    /// bits to match the polynomial; the default initial value `0xFFFF_FFFF`
407    /// will actually write `0x7F` in the case of a 7-bit CRC, for example.
408    pub const fn initial_value(mut self, initial: u32) -> Self {
409        self.initial = initial;
410        self
411    }
412
413    /// Set how to reverse the bits of the input. `None` means no reversal.
414    pub const fn reverse_input(mut self, reverse: Option<BitReversal>) -> Self {
415        self.reverse_input = reverse;
416        self
417    }
418
419    /// Get the register value of input reversal setting.
420    fn get_reverse_input(&self) -> u8 {
421        self.reverse_input.map(|rev| rev as u8).unwrap_or(0b00)
422    }
423
424    /// Set whether to reverse the bits of the output.
425    pub const fn reverse_output(mut self, reverse: bool) -> Self {
426        self.reverse_output = reverse;
427        self
428    }
429
430    /// Set whether to reflect the CRC. When enabled, reflection is
431    /// [`BitReversal::Byte`] and output reversal enabled. This is simply
432    /// a convenience function as many CRC algorithms call for this.
433    pub fn reflect(self, reflect: bool) -> Self {
434        // Rust 1.46 and higher: This function can be const.
435        if reflect {
436            self.reverse_input(Some(BitReversal::Byte))
437                .reverse_output(true)
438        } else {
439            self.reverse_input(None).reverse_output(false)
440        }
441    }
442
443    /// Set the value to XOR the output with. Automatically masked to the proper size.
444    pub const fn output_xor(mut self, output_xor: u32) -> Self {
445        self.output_xor = output_xor;
446        self
447    }
448}
449
450impl Default for Config {
451    /// Calls [`Config::new`].
452    fn default() -> Self {
453        Self::new()
454    }
455}