Skip to content

Commit 385cb97

Browse files
bors[bot]James Munns
andauthored
Merge #225
225: Add CRC peripheral r=therealprof a=jamesmunns Co-authored-by: James Munns <james.munns@ferrous-systems.com>
2 parents 13455b4 + efa5ec8 commit 385cb97

File tree

3 files changed

+120
-0
lines changed

3 files changed

+120
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
2323
- Add missing `Write` implementation for `Serial` and implemented better error handling.
2424
- [breaking-change] ADC2 and ADC3 no longer allow access to VREF, VBAT, or the internal
2525
temperature measurement (ADC2 and ADC3 do not have an internal connection for these channels)
26+
- Improved Serial baudrate calculation to be correct for higher baudrates or lower PCLKs
2627

2728
### Added
2829

@@ -32,6 +33,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
3233
- Basic support for DAC
3334
- Add initial DMA support
3435
- Allow specification of ADC reference voltage in ADC configuraton structure
36+
- Added support for hardware-based CRC32 functionality
3537

3638
### Fixed
3739
- Stability fixes related to SD card write

src/crc32.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
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+
let mut new = Self { periph: crc };
23+
new.init();
24+
new
25+
}
26+
27+
/// Reset the internal CRC32 state to the default value (0xFFFF_FFFF)
28+
#[inline(always)]
29+
pub fn init(&mut self) {
30+
self.periph.cr.write(|w| w.reset().reset());
31+
}
32+
33+
/// Feed words into the CRC engine.
34+
///
35+
/// The resulting calculated CRC (including this and prior data
36+
/// since the last call to `init()` is returned.
37+
pub fn update(&mut self, data: &[u32]) -> u32 {
38+
// Feed each word into the engine
39+
for word in data {
40+
self.periph.dr.write(|w| unsafe { w.bits(*word) });
41+
}
42+
43+
// Retrieve the resulting CRC
44+
self.periph.dr.read().bits()
45+
}
46+
47+
/// Feed bytes into the CRC engine.
48+
///
49+
/// The resulting calculated CRC (including this and prior data
50+
/// since the last call to `init()` is returned.
51+
///
52+
/// NOTE: Each four-byte chunk will be copied into a scratch buffer. This
53+
/// is done to ensure alignment of the data (the CRC engine only processes
54+
/// full words at a time). If the number of bytes passed in are not a
55+
/// multiple of four, the MOST significant bytes of the remaining word will
56+
/// be zeroes.
57+
///
58+
/// This should be taken into consideration if attempting to feed bytes
59+
/// across multiple parts (that spurious zeroes will be inserted)! To
60+
/// avoid this, only feed multiples of 4 bytes in before the "final"
61+
/// part of the message.
62+
///
63+
/// Example: Given the following 7 bytes:
64+
///
65+
/// `[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]`
66+
///
67+
/// The following two words will be fed into the CRC engine:
68+
///
69+
/// 1. `0x4433_2211`
70+
/// 2. `0x0077_6655`
71+
pub fn update_bytes(&mut self, data: &[u8]) -> u32 {
72+
let chunks = data.chunks_exact(4);
73+
let remainder = chunks.remainder();
74+
75+
// For each full chunk of four bytes...
76+
chunks.for_each(|chunk| unsafe {
77+
// Create an uninitialized scratch buffer. We make it uninitialized
78+
// to avoid re-zeroing this data inside of the loop.
79+
let mut scratch: MaybeUninit<[u8; 4]> = MaybeUninit::uninit();
80+
81+
// Copy the (potentially unaligned) bytes from the input chunk to
82+
// our scratch bytes. We cast the `scratch` buffer from a `*mut [u8; 4]`
83+
// to a `*mut u8`.
84+
let src: *const u8 = chunk.as_ptr();
85+
let dst: *mut u8 = scratch.as_mut_ptr().cast::<u8>();
86+
copy_nonoverlapping(src, dst, 4);
87+
88+
// Mark the scratch bytes as initialized, and then convert it to a
89+
// native-endian u32. Feed this into the CRC peripheral
90+
self.periph
91+
.dr
92+
.write(|w| w.bits(u32::from_ne_bytes(scratch.assume_init())));
93+
});
94+
95+
// If we had a non-multiple of four bytes...
96+
if !remainder.is_empty() {
97+
// Create a zero-filled scratch buffer, and copy the data in
98+
let mut scratch = [0u8; 4];
99+
100+
// NOTE: We are on a little-endian processor. This means that copying
101+
// the 0..len range fills the LEAST significant bytes, leaving the
102+
// MOST significant bytes as zeroes
103+
scratch[..remainder.len()].copy_from_slice(remainder);
104+
self.periph
105+
.dr
106+
.write(|w| unsafe { w.bits(u32::from_ne_bytes(scratch)) });
107+
}
108+
109+
self.periph.dr.read().bits()
110+
}
111+
112+
/// Consume the HAL peripheral, returning the PAC peripheral
113+
pub fn free(self) -> CRC {
114+
self.periph
115+
}
116+
}

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)