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
//! CRC32 Calculation Unit
//!
//! This is a hardware accelerated CRC32 calculation unit.
//!
//! It is hardcoded to use the CRC-32 polynomial 0x04C1_1DB7.
//!
//! It operates word-at-a-time, and takes 4 AHB/HCLK cycles per word
//! to calculate. This operation stalls the AHB bus for that time.
use crate::pac::{CRC, RCC};
use crate::rcc::{Enable, Reset};
use core::mem::MaybeUninit;
use core::ptr::copy_nonoverlapping;
/// A handle to a HAL CRC32 peripheral
pub struct Crc32 {
periph: CRC,
}
impl Crc32 {
/// Create a new Crc32 HAL peripheral
pub fn new(crc: CRC) -> Self {
unsafe {
// NOTE(unsafe) this reference will only be used for atomic writes with no side effects.
let rcc = &(*RCC::ptr());
// enable CRC clock.
CRC::enable(rcc);
CRC::reset(rcc);
}
let mut new = Self { periph: crc };
new.init();
new
}
/// Reset the internal CRC32 state to the default value (0xFFFF_FFFF)
#[inline(always)]
pub fn init(&mut self) {
self.periph.cr.write(|w| w.reset().reset());
}
/// Feed words into the CRC engine.
///
/// The resulting calculated CRC (including this and prior data
/// since the last call to `init()` is returned.
pub fn update(&mut self, data: &[u32]) -> u32 {
// Feed each word into the engine
for word in data {
self.periph.dr.write(|w| unsafe { w.bits(*word) });
}
// Retrieve the resulting CRC
self.periph.dr.read().bits()
}
/// Feed bytes into the CRC engine.
///
/// The resulting calculated CRC (including this and prior data
/// since the last call to `init()` is returned.
///
/// NOTE: Each four-byte chunk will be copied into a scratch buffer. This
/// is done to ensure alignment of the data (the CRC engine only processes
/// full words at a time). If the number of bytes passed in are not a
/// multiple of four, the MOST significant bytes of the remaining word will
/// be zeroes.
///
/// This should be taken into consideration if attempting to feed bytes
/// across multiple parts (that spurious zeroes will be inserted)! To
/// avoid this, only feed multiples of 4 bytes in before the "final"
/// part of the message.
///
/// Example: Given the following 7 bytes:
///
/// `[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]`
///
/// The following two words will be fed into the CRC engine:
///
/// 1. `0x4433_2211`
/// 2. `0x0077_6655`
pub fn update_bytes(&mut self, data: &[u8]) -> u32 {
let chunks = data.chunks_exact(4);
let remainder = chunks.remainder();
// For each full chunk of four bytes...
chunks.for_each(|chunk| unsafe {
// Create an uninitialized scratch buffer. We make it uninitialized
// to avoid re-zeroing this data inside of the loop.
let mut scratch: MaybeUninit<[u8; 4]> = MaybeUninit::uninit();
// Copy the (potentially unaligned) bytes from the input chunk to
// our scratch bytes. We cast the `scratch` buffer from a `*mut [u8; 4]`
// to a `*mut u8`.
let src: *const u8 = chunk.as_ptr();
let dst: *mut u8 = scratch.as_mut_ptr().cast::<u8>();
copy_nonoverlapping(src, dst, 4);
// Mark the scratch bytes as initialized, and then convert it to a
// native-endian u32. Feed this into the CRC peripheral
self.periph
.dr
.write(|w| w.bits(u32::from_ne_bytes(scratch.assume_init())));
});
// If we had a non-multiple of four bytes...
if !remainder.is_empty() {
// Create a zero-filled scratch buffer, and copy the data in
let mut scratch = [0u8; 4];
// NOTE: We are on a little-endian processor. This means that copying
// the 0..len range fills the LEAST significant bytes, leaving the
// MOST significant bytes as zeroes
scratch[..remainder.len()].copy_from_slice(remainder);
self.periph
.dr
.write(|w| unsafe { w.bits(u32::from_ne_bytes(scratch)) });
}
self.periph.dr.read().bits()
}
/// Consume the HAL peripheral, returning the PAC peripheral
pub fn release(self) -> CRC {
unsafe {
// NOTE(unsafe) this reference will only be used for atomic writes with no side effects.
let rcc = &(*RCC::ptr());
// Disable CRC clock
CRC::disable(rcc);
}
self.periph
}
}