atsamd_hal/peripherals/
dsu.rs

1//! # Device Service Unit
2//!
3//! This module allows users to interact with a DSU peripheral.
4//!
5//! - Run a CRC32 checksum over memory
6#![warn(missing_docs)]
7
8use crate::pac::{self, Pac};
9
10/// Device Service Unit
11pub struct Dsu {
12    /// PAC peripheral
13    dsu: pac::Dsu,
14}
15
16/// Errors from hardware
17#[derive(Debug, Clone, Copy, Eq, PartialEq)]
18#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19pub enum PeripheralError {
20    /// Usually misaligned address of length
21    BusError,
22}
23
24/// Error from within the DSU
25#[derive(Debug, Clone, Copy, Eq, PartialEq)]
26#[cfg_attr(feature = "defmt", derive(defmt::Format))]
27pub enum Error {
28    /// Address or length was not word aligned
29    AlignmentError,
30    /// The PAC would not unlock the DSU for us
31    PacUnlockFailed,
32    /// CRC32 operation failed
33    CrcFailed,
34    /// Hardware-generated errors
35    Peripheral(PeripheralError),
36}
37
38/// NVM result type
39pub type Result<T> = core::result::Result<T, Error>;
40
41impl Dsu {
42    /// Unlock the DSU and instantiate peripheral
43    #[inline]
44    pub fn new(dsu: pac::Dsu, pac: &Pac) -> Result<Self> {
45        // Attempt to unlock DSU
46        pac.wrctrl()
47            .write(|w| unsafe { w.perid().bits(33).key().clr() });
48
49        // Check if DSU was unlocked
50        if pac.statusb().read().dsu_().bit_is_set() {
51            Err(Error::PacUnlockFailed)
52        } else {
53            Ok(Self { dsu })
54        }
55    }
56
57    /// Clear bus error bit
58    fn clear_bus_error(&mut self) {
59        self.dsu.statusa().write(|w| w.berr().set_bit());
60    }
61
62    /// Read bus error bit
63    fn bus_error(&mut self) -> bool {
64        self.dsu.statusa().read().berr().bit()
65    }
66
67    /// Check if operation is done
68    fn is_done(&self) -> bool {
69        self.dsu.statusa().read().done().bit_is_set()
70    }
71
72    /// Check if an operation has failed
73    fn has_failed(&self) -> bool {
74        self.dsu.statusa().read().fail().bit_is_set()
75    }
76
77    /// Set target address given as number of words offset
78    fn set_address(&mut self, address: u32) -> Result<()> {
79        self.dsu.addr().write(|w| unsafe { w.addr().bits(address) });
80        Ok(())
81    }
82
83    /// Set length given as number of words
84    fn set_length(&mut self, length: u32) -> Result<()> {
85        self.dsu
86            .length()
87            .write(|w| unsafe { w.length().bits(length) });
88        Ok(())
89    }
90
91    /// Seed CRC32
92    fn seed(&mut self, data: u32) {
93        self.dsu.data().write(|w| unsafe { w.data().bits(data) });
94    }
95
96    /// Calculate CRC32 of a memory region
97    ///
98    /// - `address` is an address within a flash; must be word-aligned
99    /// - `length` is a length of memory region that is being processed. Must be
100    ///   word-aligned
101    #[inline]
102    pub fn crc32(&mut self, address: u32, length: u32) -> Result<u32> {
103        // The algorithm employed is the industry standard CRC32 algorithm using the
104        // generator polynomial 0xEDB88320
105        // (reversed representation of 0x04C11DB7).
106        //
107        // https://crccalc.com/, Hex input same as memory contents, Calc CRC-32
108        // but output is reversed
109
110        if address % 4 != 0 {
111            return Err(Error::AlignmentError);
112        }
113
114        if length % 4 != 0 {
115            return Err(Error::AlignmentError);
116        }
117
118        let num_words = length / 4;
119
120        // Calculate target flash address
121        let flash_address = address / 4;
122
123        // Set the ADDR of where to start calculation, as number of words
124        self.set_address(flash_address)?;
125
126        // Amount of words to check
127        self.set_length(num_words)?;
128
129        // Set CRC32 seed
130        self.seed(0xffff_ffff);
131
132        // Clear the status flags indicating termination of the operation
133        self.dsu
134            .statusa()
135            .write(|w| w.done().set_bit().fail().set_bit());
136
137        // Initialize CRC calculation
138        self.dsu.ctrl().write(|w| w.crc().set_bit());
139
140        // Wait until done or failed
141        while !self.is_done() && !self.has_failed() {}
142        if self.has_failed() {
143            return Err(Error::CrcFailed);
144        }
145
146        // CRC startup generated a bus error
147        // Generally misaligned length or address
148        if self.bus_error() {
149            // Return the reported bus error and clear it
150            self.clear_bus_error();
151            Err(Error::Peripheral(PeripheralError::BusError))
152        } else {
153            // Return the calculated CRC32 (complement of data register)
154            Ok(!self.dsu.data().read().data().bits())
155        }
156    }
157}