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}