Skip to main content

gd32e2_hal/
crc.rs

1//! Hardware CRC calculation unit.
2//!
3//! ```ignore
4//! let mut crc = Crc::new_32bit(&mut rcu, dp.crc, 0x04C1_1DB7, CrcConfig::default());
5//! crc.reset_with(0xFFFF_FFFF);
6//! for &word in data {
7//!     crc.write_32bit(word);
8//! }
9//! let result = crc.read_32bit();
10//! ```
11
12use core::marker::PhantomData;
13
14use crate::pac;
15use crate::rcu::{Enable, Rcu};
16
17/// 32-bit polynomial size, selects [`Crc::new_32bit`].
18pub struct B32;
19/// 16-bit polynomial size, selects [`Crc::new_16bit`].
20pub struct B16;
21/// 8-bit polynomial size, selects [`Crc::new_8bit`].
22pub struct B8;
23/// 7-bit polynomial size, selects [`Crc::new_7bit`].
24pub struct B7;
25
26/// Input data bit-reversal granularity (`REV_I`).
27#[derive(Clone, Copy, PartialEq, Eq)]
28#[cfg_attr(feature = "defmt", derive(defmt::Format))]
29pub enum ReverseInput {
30    /// No reversal.
31    Disabled = 0b00,
32    /// Reverse the bit order within each byte.
33    Byte = 0b01,
34    /// Reverse the bit order within each half-word.
35    HalfWord = 0b10,
36    /// Reverse the bit order within each word.
37    Word = 0b11,
38}
39
40/// Output data bit reversal (`REV_O`).
41#[derive(Clone, Copy, PartialEq, Eq)]
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43pub enum ReverseOutput {
44    /// The result is read out as computed.
45    Disabled = 0,
46    /// The result is bit-reversed on read.
47    Enabled = 1,
48}
49
50/// Settings shared by every polynomial size.
51#[derive(Clone, Copy, PartialEq, Eq)]
52#[cfg_attr(feature = "defmt", derive(defmt::Format))]
53#[must_use]
54pub struct CrcConfig {
55    reverse_input: ReverseInput,
56    reverse_output: ReverseOutput,
57}
58
59impl CrcConfig {
60    /// Creates a configuration with the given input/output reversal.
61    pub const fn new(reverse_input: ReverseInput, reverse_output: ReverseOutput) -> Self {
62        Self {
63            reverse_input,
64            reverse_output,
65        }
66    }
67}
68
69impl Default for CrcConfig {
70    fn default() -> Self {
71        Self {
72            reverse_input: ReverseInput::Disabled,
73            reverse_output: ReverseOutput::Disabled,
74        }
75    }
76}
77
78/// Writes the fields shared by every polynomial size.
79fn configure(crc: &pac::Crc, ps: u8, poly: u32, config: CrcConfig) {
80    let rev_o = matches!(config.reverse_output, ReverseOutput::Enabled);
81    crc.ctl().modify(|_, w| {
82        // SAFETY: `ps` is one of the four `PS` codes, passed as a literal by
83        // the constructors.
84        unsafe { w.ps().bits(ps) };
85        w.rev_o()
86            .bit(rev_o)
87            .rev_i()
88            .bits(config.reverse_input as u8)
89    });
90    // SAFETY: every `u32` is a polynomial; narrower ones arrive zero-extended.
91    crc.poly().write(|w| unsafe { w.bits(poly) });
92}
93
94/// Loads `idata` into the result register through a pulse of `RST`.
95fn new_seed(crc: &pac::Crc, idata: u32) {
96    crc.idata().write(|w| w.idata().bits(idata));
97    crc.ctl().modify(|_, w| w.rst().reset());
98}
99
100/// Hardware CRC calculation unit.
101///
102/// `PS` ([`B32`]/[`B16`]/[`B8`]/[`B7`]) fixes the polynomial width for the
103/// lifetime of the value, selected by which constructor built it.
104///
105/// Construction does not seed the result: call
106/// [`reset_with`](Crc::<B32>::reset_with) before the first write.
107pub struct Crc<PS> {
108    crc: pac::Crc,
109    _poly_size: PhantomData<PS>,
110}
111
112impl Crc<B32> {
113    /// Enables the peripheral's clock and configures a 32-bit polynomial.
114    pub fn new_32bit(rcu: &mut Rcu, crc: pac::Crc, poly: u32, config: CrcConfig) -> Self {
115        <pac::Crc as Enable>::enable(rcu);
116        configure(&crc, 0b00, poly, config);
117        Self {
118            crc,
119            _poly_size: PhantomData,
120        }
121    }
122
123    /// Feeds one 32-bit word into the running CRC, combining it with the
124    /// current result rather than replacing it.
125    pub fn write_32bit(&mut self, data: u32) {
126        let data_reg = self.crc.data().as_ptr();
127        // SAFETY: `data_reg` points at `DATA`, a live register aligned to four
128        // bytes, so a write of any width up to a word is valid.
129        unsafe { data_reg.write_volatile(data) };
130    }
131    /// Reads the current accumulated CRC result.
132    pub fn read_32bit(&self) -> u32 {
133        self.read()
134    }
135
136    /// Sets `IDATA` to `seed` and pulses `RST`, so the result reads back as
137    /// `seed` until the next [`write_32bit`](Self::write_32bit).
138    pub fn reset_with(&mut self, seed: u32) {
139        new_seed(&self.crc, seed);
140    }
141}
142
143impl Crc<B16> {
144    /// Enables the peripheral's clock and configures a 16-bit polynomial.
145    pub fn new_16bit(rcu: &mut Rcu, crc: pac::Crc, poly: u16, config: CrcConfig) -> Self {
146        <pac::Crc as Enable>::enable(rcu);
147        configure(&crc, 0b01, poly as u32, config);
148        Self {
149            crc,
150            _poly_size: PhantomData,
151        }
152    }
153
154    /// Feeds one 16-bit word into the running CRC, combining it with the
155    /// current result rather than replacing it.
156    pub fn write_16bit(&mut self, data: u16) {
157        let data_reg = self.crc.data().as_ptr().cast::<u16>();
158        // SAFETY: `data_reg` points at `DATA`, a live register aligned to four
159        // bytes, so a write of any width up to a word is valid.
160        unsafe { data_reg.write_volatile(data) };
161    }
162    /// Reads the current accumulated CRC result.
163    pub fn read_16bit(&self) -> u16 {
164        self.read() as u16
165    }
166
167    /// Sets `IDATA` to `seed` and pulses `RST`, so the result reads back as
168    /// `seed` until the next [`write_16bit`](Self::write_16bit).
169    pub fn reset_with(&mut self, seed: u16) {
170        new_seed(&self.crc, seed as u32);
171    }
172}
173
174impl Crc<B8> {
175    /// Enables the peripheral's clock and configures an 8-bit polynomial.
176    pub fn new_8bit(rcu: &mut Rcu, crc: pac::Crc, poly: u8, config: CrcConfig) -> Self {
177        <pac::Crc as Enable>::enable(rcu);
178        configure(&crc, 0b10, poly as u32, config);
179        Self {
180            crc,
181            _poly_size: PhantomData,
182        }
183    }
184
185    /// Feeds one byte into the running CRC, combining it with the current
186    /// result rather than replacing it.
187    pub fn write_8bit(&mut self, data: u8) {
188        let data_reg = self.crc.data().as_ptr().cast::<u8>();
189        // SAFETY: `data_reg` points at `DATA`, a live register aligned to four
190        // bytes, so a write of any width up to a word is valid.
191        unsafe { data_reg.write_volatile(data) };
192    }
193    /// Reads the current accumulated CRC result.
194    pub fn read_8bit(&self) -> u8 {
195        self.read() as u8
196    }
197
198    /// Sets `IDATA` to `seed` and pulses `RST`, so the result reads back as
199    /// `seed` until the next [`write_8bit`](Self::write_8bit).
200    pub fn reset_with(&mut self, seed: u8) {
201        new_seed(&self.crc, seed as u32);
202    }
203}
204
205impl Crc<B7> {
206    /// Enables the peripheral's clock and configures a 7-bit polynomial.
207    pub fn new_7bit(rcu: &mut Rcu, crc: pac::Crc, poly: u8, config: CrcConfig) -> Self {
208        <pac::Crc as Enable>::enable(rcu);
209        configure(&crc, 0b11, poly as u32, config);
210        Self {
211            crc,
212            _poly_size: PhantomData,
213        }
214    }
215
216    /// Feeds the low 7 bits of `data` into the running CRC, combining them
217    /// with the current result rather than replacing it. The top bit is
218    /// ignored by hardware.
219    pub fn write_7bit(&mut self, data: u8) {
220        let data_reg = self.crc.data().as_ptr().cast::<u8>();
221        // SAFETY: `data_reg` points at `DATA`, a live register aligned to four
222        // bytes, so a write of any width up to a word is valid.
223        unsafe { data_reg.write_volatile(data) };
224    }
225    /// Reads the current accumulated CRC result, in the low 7 bits.
226    pub fn read_7bit(&self) -> u8 {
227        self.read() as u8
228    }
229
230    /// Sets `IDATA` to `seed` and pulses `RST`, so the result reads back as
231    /// `seed` until the next [`write_7bit`](Self::write_7bit).
232    pub fn reset_with(&mut self, seed: u8) {
233        new_seed(&self.crc, seed as u32);
234    }
235}
236
237impl<PS> Crc<PS> {
238    /// Reads the whole `DATA` register, not cut down to the polynomial width.
239    pub fn read(&self) -> u32 {
240        self.crc.data().read().data().bits()
241    }
242
243    /// Returns the peripheral with its clock stopped.
244    pub fn release(self, rcu: &mut Rcu) -> pac::Crc {
245        <pac::Crc as Enable>::disable(rcu);
246        self.crc
247    }
248
249    /// Writes an arbitrary byte to the scratch `FDATA` register. Unrelated to
250    /// the CRC calculation — hardware never reads or modifies it on its own.
251    pub fn set_fdata(&mut self, fdata: u8) {
252        self.crc.fdata().write(|w| w.fdata().bits(fdata));
253    }
254    /// Reads back the byte last written with [`set_fdata`](Self::set_fdata).
255    pub fn fdata(&self) -> u8 {
256        self.crc.fdata().read().fdata().bits()
257    }
258}