stm32g0xx_hal/
crc.rs

1//! CRC calculation unit
2//!
3//! This unit is modeled after the [corresponding unit in the stm32l4xx-hal](https://github.com/stm32-rs/stm32l4xx-hal/blob/master/src/crc.rs).
4//!
5//! Usage example:
6//! ```
7//! let crc = dp.RCC.constrain();
8//!
9//! // Lets use the CRC-16-CCITT polynomial
10//! let mut crc = crc.polynomial(crc::Polynomial::L16(0x1021)).freeze();
11//!
12//! let data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
13//! crc.feed(&data);
14//!
15//! let result = crc.result();
16//! assert!(result == 0x78cb);
17//! ```
18
19#![deny(missing_docs)]
20
21use crate::rcc::{Enable, Rcc, Reset};
22use crate::stm32::CRC;
23use core::hash::Hasher;
24use core::ptr;
25
26/// Extension trait to constrain the CRC peripheral.
27pub trait CrcExt {
28    /// Constrains the CRC peripheral to play nicely with the other abstractions
29    fn constrain(self, rcc: &mut Rcc) -> Config;
30}
31
32impl CrcExt for CRC {
33    fn constrain(self, rcc: &mut Rcc) -> Config {
34        CRC::enable(rcc);
35        CRC::reset(rcc);
36
37        // Default values
38        Config {
39            initial_value: 0xffff_ffff,
40            polynomial: Polynomial::L32(0x04c1_1db7),
41            input_bit_reversal: None,
42            output_bit_reversal: false,
43        }
44    }
45}
46
47/// Polynomial settings.
48pub enum Polynomial {
49    /// 7-bit polynomial, only the lowest 7 bits are valid
50    L7(u8),
51    /// 8-bit polynomial
52    L8(u8),
53    /// 16-bit polynomial
54    L16(u16),
55    /// 32-bit polynomial
56    L32(u32),
57}
58
59/// Bit reversal settings.
60pub enum BitReversal {
61    /// Reverse bits by byte
62    ByByte,
63    /// Reverse bits by half-word
64    ByHalfWord,
65    /// Reverse bits by word
66    ByWord,
67}
68
69/// CRC configuration structure, uses builder pattern.
70pub struct Config {
71    initial_value: u32,
72    polynomial: Polynomial,
73    input_bit_reversal: Option<BitReversal>,
74    output_bit_reversal: bool,
75}
76
77impl Config {
78    /// Sets the initial value of the CRC.
79    pub fn initial_value(mut self, init: u32) -> Self {
80        self.initial_value = init;
81
82        self
83    }
84
85    /// Sets the polynomial of the CRC.
86    pub fn polynomial(mut self, polynomial: Polynomial) -> Self {
87        self.polynomial = polynomial;
88
89        self
90    }
91
92    /// Enables bit reversal of the inputs.
93    pub fn input_bit_reversal(mut self, rev: Option<BitReversal>) -> Self {
94        self.input_bit_reversal = rev;
95
96        self
97    }
98
99    /// Enables bit reversal of the outputs.
100    pub fn output_bit_reversal(mut self, rev: bool) -> Self {
101        self.output_bit_reversal = rev;
102
103        self
104    }
105
106    /// Freezes the peripheral, making the configuration take effect.
107    pub fn freeze(self) -> Crc {
108        let crc = unsafe { &(*CRC::ptr()) };
109
110        let (poly, poly_bits, init) = match self.polynomial {
111            Polynomial::L7(val) => ((val & 0x7f) as u32, 0b11, self.initial_value & 0x7f),
112            Polynomial::L8(val) => (val as u32, 0b10, self.initial_value & 0xff),
113            Polynomial::L16(val) => (val as u32, 0b01, self.initial_value & 0xffff),
114            Polynomial::L32(val) => (val, 0b00, self.initial_value),
115        };
116
117        let in_rev_bits = match self.input_bit_reversal {
118            None => 0b00,
119            Some(BitReversal::ByByte) => 0b01,
120            Some(BitReversal::ByHalfWord) => 0b10,
121            Some(BitReversal::ByWord) => 0b11,
122        };
123
124        crc.init.write(|w| unsafe { w.crc_init().bits(init) });
125        crc.pol.write(|w| unsafe { w.bits(poly) });
126        crc.cr.write(|w| {
127            unsafe {
128                w.rev_in()
129                    .bits(in_rev_bits)
130                    .polysize()
131                    .bits(poly_bits)
132                    .reset()
133                    .set_bit();
134            }
135
136            if self.output_bit_reversal {
137                w.rev_out().set_bit()
138            } else {
139                w.rev_out().clear_bit()
140            }
141        });
142
143        Crc {}
144    }
145}
146
147/// Constrained CRC peripheral.
148pub struct Crc {}
149
150impl Crc {
151    /// This will reset the CRC to its initial condition.
152    #[inline]
153    pub fn reset(&mut self) {
154        let crc = unsafe { &(*CRC::ptr()) };
155
156        crc.cr.modify(|_, w| w.reset().set_bit());
157    }
158
159    /// This will reset the CRC to its initial condition, however with a specific initial value.
160    /// This is very useful if many task are sharing the CRC peripheral, as one can read out the
161    /// intermediate result, store it until the next time a task runs, and initialize with the
162    /// intermediate result to continue where the task left off.
163    #[inline]
164    pub fn reset_with_inital_value(&mut self, initial_value: u32) {
165        let crc = unsafe { &(*CRC::ptr()) };
166
167        crc.init
168            .write(|w| unsafe { w.crc_init().bits(initial_value) });
169        crc.cr.modify(|_, w| w.reset().set_bit());
170    }
171
172    /// Feed the CRC with data
173    #[inline]
174    pub fn feed(&mut self, data: &[u8]) {
175        let crc = unsafe { &(*CRC::ptr()) };
176        for byte in data {
177            unsafe {
178                // Workaround with svd2rust, it does not generate the byte interface to the DR
179                // register
180                ptr::write_volatile(&crc.dr as *const _ as *mut u8, *byte);
181            }
182        }
183    }
184
185    /// Get the result of the CRC, depending on the polynomial chosen only a certain amount of the
186    /// bits are the result. This will reset the CRC peripheral after use.
187    #[inline]
188    pub fn result(&mut self) -> u32 {
189        let ret = self.peek_result();
190
191        self.reset();
192
193        ret
194    }
195
196    /// Get a peed at the result of the CRC, depending on the polynomial chosen only a certain
197    /// amount of the bits are the result.
198    #[inline]
199    pub fn peek_result(&self) -> u32 {
200        let crc = unsafe { &(*CRC::ptr()) };
201
202        crc.dr.read().bits()
203    }
204}
205
206impl Hasher for Crc {
207    #[inline]
208    fn finish(&self) -> u64 {
209        // `peek_result` as `core::hash::Hasher` required that the `finish` method does not reset
210        // the hasher.
211        self.peek_result() as u64
212    }
213
214    #[inline]
215    fn write(&mut self, data: &[u8]) {
216        self.feed(data);
217    }
218}