Skip to main content

ra_hal/
crc.rs

1//! Cyclic Redundancy Check Calculator (`CRC`).
2//!
3//! # Notes
4//! * The driver will turn the module off (`MSTPC1=1`) when it is dropped.
5
6use embassy_hal_internal::Peri;
7
8use crate::{module_stop::ModuleStop, pac::crc::vals::Gps, peripherals::CRC};
9
10/// Polynomial to use for CRC calculation.
11#[derive(Default, Copy, Clone, PartialEq)]
12pub enum Polynomial {
13    /// This will panic
14    None,
15
16    /// CRC-8, polynomial = `0x07`
17    Crc8,
18
19    /// CRC-16, polynomial = `0x8005`
20    #[default]
21    Crc16,
22
23    /// CRC-16/CCIT, polynomial = `0x1021`
24    CrcCcit,
25
26    /// CRC-32, polynomial = `0x04C11DB7`
27    Crc32,
28
29    /// CRC-32C, polynomial = `0x1EDC6F41`
30    Crc32C,
31}
32
33/// CRC Configuration
34#[derive(Default)]
35pub struct Config {
36    #[allow(missing_docs)]
37    pub polynomial: Polynomial,
38    /// Calculate CRC by shifting right if true.
39    /// CRC Calculation Switching in Renesas-speak, ยง32.2.1.
40    pub reverse: bool,
41    /// Initial output value.
42    pub seed: u32,
43    /// Apply a bitwise NOT operation to the result if true.
44    pub reflect_output: bool,
45}
46
47/// `CRC` driver.
48pub struct Crc<'d> {
49    _peri: Peri<'d, CRC>,
50    config: Config,
51}
52
53impl<'d> Crc<'d> {
54    /// Consumes the `CRC` peripheral and returns a driver initialized with the provided values.
55    ///
56    /// # Arguments
57    /// * `peri` The [`CRC`] peripheral.
58    /// * `config` Algorithm configuration.
59    ///
60    /// # Returns
61    ///
62    /// A `CRC` driver.
63    pub fn new(peri: Peri<'d, CRC>, config: Config) -> Self {
64        CRC::start_module();
65
66        let mut instance = Self {
67            _peri: peri,
68            config,
69        };
70
71        instance.reset();
72        instance
73    }
74
75    /// Applies a new algorithm configuration and resets the state.
76    ///
77    /// # Arguments
78    /// * `config` Algorithm configuration to apply.
79    pub fn set_config(&mut self, config: Config) {
80        self.config = config;
81        self.reset();
82    }
83
84    /// Applies the current configuration and initializes the output buffer with the selected seed.
85    pub fn reset(&mut self) {
86        let crc = crate::pac::CRC;
87
88        let gps = match self.config.polynomial {
89            Polynomial::None => Gps::None,
90            Polynomial::Crc8 => Gps::Crc8,
91            Polynomial::Crc16 => Gps::Crc16,
92            Polynomial::CrcCcit => Gps::CrcCcit,
93            Polynomial::Crc32 => Gps::Crc32,
94            Polynomial::Crc32C => Gps::Crc32C,
95        };
96
97        let lms = !self.config.reverse;
98
99        crc.crccr0().write(|w| {
100            w.set_gps(gps);
101            w.set_lms(lms);
102            w.set_dorclr(true);
103        });
104
105        if self.config.seed != 0 {
106            match self.config.polynomial {
107                Polynomial::None => unimplemented!(),
108                Polynomial::Crc8 => {
109                    crc.crcdor_by().write_value((self.config.seed & 0xFF) as u8);
110                }
111                Polynomial::Crc16 | Polynomial::CrcCcit => {
112                    crc.crcdor_ha()
113                        .write_value((self.config.seed & 0xFFFF) as u16);
114                }
115                Polynomial::Crc32 | Polynomial::Crc32C => {
116                    crc.crcdor().write_value(self.config.seed);
117                }
118            }
119        }
120    }
121
122    /// Computes a CRC value for given input.
123    ///
124    /// # Arguments
125    /// * `bytes` Input data
126    ///
127    /// # Notes
128    /// * CRC-32 / CRC-32C require 32-bit input values.
129    ///   If a 32-bit polynomial is selected and the provided input is not a multiple of `4` bytes the function will panic.
130    /// * TODO: implement a function that takes a `&[u32]` slice and writes to the 32-bit data register.
131    pub fn feed_bytes(&mut self, bytes: &[u8]) {
132        let crc = crate::pac::CRC;
133
134        match self.config.polynomial {
135            Polynomial::Crc8 | Polynomial::Crc16 | Polynomial::CrcCcit => {
136                for byte in bytes.iter() {
137                    crc.crcdir_by().write_value(*byte);
138                }
139            }
140            Polynomial::Crc32 | Polynomial::Crc32C => {
141                if !bytes.len().is_multiple_of(4) {
142                    unimplemented!("CRC-32 input len must be a multiple of 4");
143                }
144
145                match self.config.reverse {
146                    false => {
147                        for chunk in bytes.chunks_exact(4) {
148                            let word = u32::from_be_bytes(chunk.try_into().unwrap());
149                            crc.crcdir().write_value(word);
150                        }
151                    }
152                    true => {
153                        for chunk in bytes.chunks_exact(4) {
154                            let word = u32::from_ne_bytes(chunk.try_into().unwrap());
155                            crc.crcdir().write_value(word);
156                        }
157                    }
158                }
159            }
160            Polynomial::None => unimplemented!(),
161        };
162    }
163
164    /// Returns the CRC calculated from the data inputted.
165    pub fn read(&mut self) -> u32 {
166        let crc = crate::pac::CRC;
167
168        let output = match self.config.polynomial {
169            Polynomial::None => unimplemented!(),
170            Polynomial::Crc8 | Polynomial::Crc16 | Polynomial::CrcCcit => {
171                if self.config.polynomial == Polynomial::Crc8 {
172                    crc.crcdor_by().read() as _
173                } else {
174                    crc.crcdor_ha().read() as _
175                }
176            }
177            Polynomial::Crc32 | Polynomial::Crc32C => crc.crcdor().read(),
178        };
179
180        if self.config.reflect_output {
181            let mask = match self.config.polynomial {
182                Polynomial::Crc8 => u8::MAX as _,
183                Polynomial::Crc16 | Polynomial::CrcCcit => u16::MAX as _,
184                Polynomial::Crc32 | Polynomial::Crc32C => u32::MAX,
185                _ => unimplemented!(),
186            };
187            (!output) & mask
188        } else {
189            output
190        }
191    }
192}
193
194impl<'d> Drop for Crc<'d> {
195    /// Turns off `CRC` and sets `MSTPC1=true`.
196    fn drop(&mut self) {
197        CRC::stop_module();
198    }
199}