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}