Skip to content

Commit af75383

Browse files
author
James Munns
committed
Add CRC peripheral
1 parent e809257 commit af75383

File tree

2 files changed

+146
-0
lines changed

2 files changed

+146
-0
lines changed

src/crc32.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//! CRC32 Calculation Unit
2+
//!
3+
//! This is a hardware accelerated CRC32 calculation unit.
4+
//!
5+
//! It is hardcoded to use the CRC-32 polynomial 0x04C1_1DB7.
6+
//!
7+
//! It operates word-at-a-time, and takes 4 AHB/HCLK cycles per word
8+
//! to calculate. This operation stalls the AHB bus for that time.
9+
10+
use crate::stm32::CRC;
11+
use core::mem::MaybeUninit;
12+
use core::ptr::copy_nonoverlapping;
13+
14+
/// A handle to a HAL CRC32 peripheral
15+
pub struct Crc32 {
16+
periph: CRC,
17+
}
18+
19+
impl Crc32 {
20+
/// Create a new Crc32 HAL peripheral
21+
pub fn new(crc: CRC) -> Self {
22+
crc.cr.write(|w| w.reset().reset());
23+
Self { periph: crc }
24+
}
25+
26+
/// Feed words into the CRC engine without pre-clearing the CRC state.
27+
///
28+
/// The resulting calculated CRC (including this and prior data)
29+
/// is returned.
30+
///
31+
/// This is useful when using the engine to process multi-part data.
32+
pub fn feed_without_clear(&mut self, data: &[u32]) -> u32 {
33+
// Feed each word into the engine
34+
for word in data {
35+
self.periph.dr.write(|w| unsafe { w.bits(*word) });
36+
}
37+
38+
// Retrieve the resulting CRC
39+
self.periph.dr.read().bits()
40+
}
41+
42+
/// Feed words into the CRC engine, first clearing the CRC state.
43+
///
44+
/// The resulting calculated CRC (of ONLY the given data) is returned.
45+
pub fn feed(&mut self, data: &[u32]) -> u32 {
46+
self.periph.cr.write(|w| w.reset().reset());
47+
self.feed_without_clear(data)
48+
}
49+
50+
/// Feed bytes into the CRC engine without pre-clearing the CRC state.
51+
///
52+
/// The resulting calculated CRC (including this and prior data)
53+
/// is returned.
54+
///
55+
/// NOTE: Each four-byte chunk will be copied into a scratch buffer. This
56+
/// is done to ensure alignment of the data (the CRC engine only processes
57+
/// full words at a time). If the number of bytes passed in are not a
58+
/// multiple of four, the MOST significant bytes of the remaining word will
59+
/// be zeroes.
60+
///
61+
/// This should be taken into consideration if attempting to feed bytes
62+
/// across multiple parts (that spurious zeroes will be inserted)! To
63+
/// avoid this, only feed multiples of 4 bytes in before the "final"
64+
/// part of the message.
65+
///
66+
/// Example: Given the following 7 bytes:
67+
///
68+
/// `[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]`
69+
///
70+
/// The following two words will be fed into the CRC engine:
71+
///
72+
/// 1. `0x4433_2211`
73+
/// 2. `0x0077_6655`
74+
///
75+
/// This is useful when using the engine to process multi-part data.
76+
pub fn feed_bytes_without_clear(&mut self, data: &[u8]) -> u32 {
77+
let chunks = data.chunks_exact(4);
78+
let remainder = chunks.remainder();
79+
80+
// For each full chunk of four bytes...
81+
chunks.for_each(|chunk| unsafe {
82+
// Create an uninitialized scratch buffer. We make it uninitialized
83+
// to avoid re-zeroing this data inside of the loop.
84+
let mut scratch: MaybeUninit<[u8; 4]> = MaybeUninit::uninit();
85+
86+
// Copy the (potentially unaligned) bytes from the input chunk to
87+
// our scratch bytes. We cast the `scratch` buffer from a `*mut [u8; 4]`
88+
// to a `*mut u8`.
89+
let src: *const u8 = chunk.as_ptr();
90+
let dst: *mut u8 = scratch.as_mut_ptr().cast::<u8>();
91+
copy_nonoverlapping(src, dst, 4);
92+
93+
// Mark the scratch bytes as initialized, and then convert it to a
94+
// native-endian u32. Feed this into the CRC peripheral
95+
self.periph
96+
.dr
97+
.write(|w| w.bits(u32::from_ne_bytes(scratch.assume_init())));
98+
});
99+
100+
// If we had a non-multiple of four bytes...
101+
if !remainder.is_empty() {
102+
// Create a zero-filled scratch buffer, and copy the data in
103+
let mut scratch = [0u8; 4];
104+
105+
// NOTE: We are on a little-endian processor. This means that copying
106+
// the 0..len range fills the LEAST significant bytes, leaving the
107+
// MOST significant bytes as zeroes
108+
scratch[..remainder.len()].copy_from_slice(remainder);
109+
self.periph
110+
.dr
111+
.write(|w| unsafe { w.bits(u32::from_ne_bytes(scratch)) });
112+
}
113+
114+
self.periph.dr.read().bits()
115+
}
116+
117+
/// Feed words into the CRC engine, first clearing the CRC state.
118+
///
119+
/// NOTE: Each four-byte chunk will be copied into a scratch buffer. This
120+
/// is done to ensure alignment of the data (the CRC engine only processes
121+
/// full words at a time). If the number of bytes passed in are not a
122+
/// multiple of four, the MOST significant bytes of the remaining word will
123+
/// be zeroes.
124+
///
125+
/// Example: Given the following 7 bytes:
126+
///
127+
/// `[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]`
128+
///
129+
/// The following two words will be fed into the CRC engine:
130+
///
131+
/// 1. `0x4433_2211`
132+
/// 2. `0x0077_6655`
133+
///
134+
/// The resulting calculated CRC (of ONLY the given data) is returned.
135+
pub fn feed_bytes(&mut self, data: &[u8]) -> u32 {
136+
self.periph.cr.write(|w| w.reset().reset());
137+
self.feed_bytes_without_clear(data)
138+
}
139+
140+
/// Consume the HAL peripheral, returning the PAC peripheral
141+
pub fn free(self) -> CRC {
142+
self.periph
143+
}
144+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ pub use crate::stm32::interrupt;
8787
pub mod adc;
8888
#[cfg(feature = "device-selected")]
8989
pub mod bb;
90+
#[cfg(feature = "device-selected")]
91+
pub mod crc32;
9092
#[cfg(all(
9193
feature = "device-selected",
9294
not(any(feature = "stm32f411", feature = "stm32f412", feature = "stm32f401",))

0 commit comments

Comments
 (0)