atsamd-hal 0.15.1

HAL and Peripheral access API for ATSAMD11, ATSAMD21, ATSAMD51, ATSAME51, ATSAME53 and ATSAME54 microcontrollers
Documentation
//! # 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())
        }
    }
}