Skip to content

Commit

Permalink
Merge pull request stm32-rs#169 from allexoll/crc
Browse files Browse the repository at this point in the history
adding support for crc engine, shamelessly taken from stm32l4xx-hal.
  • Loading branch information
hannobraun authored May 4, 2021
2 parents e0f99a3 + a7515fc commit 1324d09
Show file tree
Hide file tree
Showing 5 changed files with 257 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,10 @@ required-features = ["rt"]
name = "button_irq_rtic"
required-features = ["rt"]

[[example]]
name = "crc"
required-features = ["stm32l0x2"]

[[example]]
name = "flash"
required-features = ["rt","stm32l082"]
Expand Down
41 changes: 41 additions & 0 deletions examples/crc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#![deny(unsafe_code)]
#![no_std]
#![no_main]

extern crate panic_halt;

use cortex_m_rt::entry;
use stm32l0xx_hal::{crc::*, pac, prelude::*, rcc::Config};

#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
let mut rcc = dp.RCC.freeze(Config::hsi16());

let crc = dp.CRC.constrain(&mut rcc);

// Lets use the Ethernet CRC. The polynomial is there by default
// but we need reflected in (by byte) and reflected out.
let mut crc = crc
.input_bit_reversal(BitReversal::ByByte)
.output_bit_reversal(true)
.freeze();

let data = [
0x21, 0x10, 0x00, 0x00, 0x63, 0x30, 0x42, 0x20, 0xa5, 0x50, 0x84, 0x40, 0xe7, 0x70, 0xc6,
0x60, 0x4a, 0xa1, 0x29, 0x91, 0x8c, 0xc1, 0x6b, 0xb1, 0xce, 0xe1, 0xad, 0xd1, 0x31, 0x12,
0xef, 0xf1, 0x52, 0x22, 0x73, 0x32, 0xa1, 0xb2, 0xc3,
];

crc.feed(&data);

// CRC32 has final XOR value of 0xFFFFFFFF
let result = crc.result() ^ 0xffff_ffff;

// check against https://crccalc.com/
// with 2110000063304220a5508440e770c6604aa129918cc16bb1cee1add13112eff152227332a1b2c3
// as hex input
assert!(result == 0x5C45_81C0);

loop {}
}
210 changes: 210 additions & 0 deletions src/crc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
//! CRC calculation unit
//!
//! This unit is modeled after the [corresponding unit in the stm32l4xx-hal](https://github.com/stm32-rs/stm32l4xx-hal/blob/master/src/crc.rs).
//!
//! Usage example:
//! ```
//! let crc = dp.CRC.constrain(&mut rcc.ahb1);
//!
//! // Lets use the CRC-16-CCITT polynomial
//! let mut crc = crc.polynomial(crc::Polynomial::L16(0x1021)).freeze();
//!
//! let data = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
//! crc.feed(&data);
//!
//! let result = crc.result();
//! assert!(result == 0x78cb);
//! ```
#![deny(missing_docs)]

use crate::pac::CRC;
use crate::rcc::Rcc;
use core::hash::Hasher;

/// Extension trait to constrain the CRC peripheral.
pub trait CrcExt {
/// Constrains the CRC peripheral to play nicely with the other abstractions
fn constrain(self, rcc: &mut Rcc) -> Config;
}

impl CrcExt for CRC {
fn constrain(self, rcc: &mut Rcc) -> Config {
// Enable power to CRC unit
rcc.rb.ahbenr.modify(|_, w| w.crcen().set_bit());

// Default values
Config {
initial_value: 0xffff_ffff,
polynomial: Polynomial::L32(0x04c1_1db7),
input_bit_reversal: None,
output_bit_reversal: false,
}
}
}

/// Polynomial settings.
pub enum Polynomial {
/// 7-bit polynomial, only the lowest 7 bits are valid
L7(u8),
/// 8-bit polynomial
L8(u8),
/// 16-bit polynomial
L16(u16),
/// 32-bit polynomial
L32(u32),
}

/// Bit reversal settings.
pub enum BitReversal {
/// Reverse bits by byte
ByByte,
/// Reverse bits by half-word
ByHalfWord,
/// Reverse bits by word
ByWord,
}

/// CRC configuration structure, uses builder pattern.
pub struct Config {
initial_value: u32,
polynomial: Polynomial,
input_bit_reversal: Option<BitReversal>,
output_bit_reversal: bool,
}

impl Config {
/// Sets the initial value of the CRC.
pub fn initial_value(mut self, init: u32) -> Self {
self.initial_value = init;

self
}

/// Sets the polynomial of the CRC.
pub fn polynomial(mut self, polynomial: Polynomial) -> Self {
self.polynomial = polynomial;

self
}

/// Enables bit reversal of the inputs.
pub fn input_bit_reversal(mut self, rev: BitReversal) -> Self {
self.input_bit_reversal = Some(rev);

self
}

/// Enables bit reversal of the outputs.
pub fn output_bit_reversal(mut self, rev: bool) -> Self {
self.output_bit_reversal = rev;

self
}

/// Freezes the peripheral, making the configuration take effect.
pub fn freeze(self) -> Crc {
let crc = unsafe { &(*CRC::ptr()) };

let (poly, poly_bits, init) = match self.polynomial {
Polynomial::L7(val) => ((val & 0x7f) as u32, 0b11, self.initial_value & 0x7f),
Polynomial::L8(val) => (val as u32, 0b10, self.initial_value & 0xff),
Polynomial::L16(val) => (val as u32, 0b01, self.initial_value & 0xffff),
Polynomial::L32(val) => (val, 0b00, self.initial_value),
};

let in_rev_bits = match self.input_bit_reversal {
None => 0b00,
Some(BitReversal::ByByte) => 0b01,
Some(BitReversal::ByHalfWord) => 0b10,
Some(BitReversal::ByWord) => 0b11,
};

crc.init.write(|w| unsafe { w.bits(init) });
crc.pol.write(|w| unsafe { w.bits(poly) });
crc.cr.write(|w| {
w.rev_in()
.bits(in_rev_bits)
.polysize()
.bits(poly_bits)
.reset()
.set_bit();

if self.output_bit_reversal {
w.rev_out().set_bit()
} else {
w.rev_out().clear_bit()
}
});

Crc {}
}
}

/// Constrained CRC peripheral.
pub struct Crc {}

impl Crc {
/// This will reset the CRC to its initial condition.
#[inline]
pub fn reset(&mut self) {
let crc = unsafe { &(*CRC::ptr()) };

crc.cr.modify(|_, w| w.reset().set_bit());
}

/// This will reset the CRC to its initial condition, however with a specific initial value.
/// This is very useful if many task are sharing the CRC peripheral, as one can read out the
/// intermediate result, store it until the next time a task runs, and initialize with the
/// intermediate result to continue where the task left off.
#[inline]
pub fn reset_with_inital_value(&mut self, initial_value: u32) {
let crc = unsafe { &(*CRC::ptr()) };

crc.init.write(|w| unsafe { w.bits(initial_value) });
crc.cr.modify(|_, w| w.reset().set_bit());
}

/// Feed the CRC with data
#[inline]
pub fn feed(&mut self, data: &[u8]) {
let crc = unsafe { &(*CRC::ptr()) };
for &byte in data {
crc.dr8().write(|w| unsafe { w.bits(byte) });
}
}

/// Get the result of the CRC, depending on the polynomial chosen only a certain amount of the
/// bits are the result. This will reset the CRC peripheral after use.
#[inline]
pub fn result(&mut self) -> u32 {
let ret = self.peek_result();

self.reset();

ret
}

/// Get a peed at the result of the CRC, depending on the polynomial chosen only a certain
/// amount of the bits are the result.
#[inline]
pub fn peek_result(&self) -> u32 {
let crc = unsafe { &(*CRC::ptr()) };

crc.dr().read().bits()
}
}

impl Hasher for Crc {
#[inline]
fn finish(&self) -> u64 {
// `peek_result` as `core::hash::Hasher` required that the `finish` method does not reset
// the hasher.
self.peek_result() as u64
}

#[inline]
fn write(&mut self, data: &[u8]) {
self.feed(data);
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub use stm32l0::stm32l0x3 as pac;
pub mod adc;
pub mod aes;
pub mod calibration;
pub mod crc;
pub mod delay;
pub mod dma;
pub mod encoder;
Expand Down
1 change: 1 addition & 0 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub use embedded_hal::{

pub use crate::{
adc::AdcExt as _,
crc::CrcExt as _stm32l0_hal_CrcExt,
delay::DelayExt as _,
encoder::{EncoderExt, PinCh1, PinCh2, Pins},
gpio::GpioExt as _,
Expand Down

0 comments on commit 1324d09

Please sign in to comment.