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