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
//! # 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::{DSU, PAC};
/// Device Service Unit
pub struct Dsu {
/// PAC peripheral
dsu: DSU,
}
/// Errors from hardware
#[derive(Debug)]
pub enum PeripheralError {
/// Usually misaligned address of length
BusError,
}
/// Error from within the DSU
#[derive(Debug)]
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: 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())
}
}
}