Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CRC module from gd32vf103-hal crate #36

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
298 changes: 298 additions & 0 deletions src/crc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
// Copyright 2019 Luo Jia
// MIT License

//! CRC calculation unit
//!
//! The hardware cyclic redundancy check (CRC) unit on GD32VF103 has 32-bit data
//! input and 32-bit data output. Calculation period is 4 AHB clock cycles
//! for 32-bit input data size, from data entered to the calculation result
//! available.
//!
//! This unit uses fixed polynomial 0x4C11DB7, which is a common polynomial
//! used in Ethernet.
//!
//! Ref: Section 8, the User Manual; Firmware/Source/gd32vf103_crc.c
//!
//! # Usage
//!
//! ## CRC calculation
//!
//! To use this module, create a [`Crc`] wrapper using [`Crc::crc`] function. This
//! function requires peripheral CRC and ownership of RCU reset and clock unit for AHB; it turns
//! on the CRC clock and creates an owned `Crc` wrapper.
//! After the wrapper is created, you need to create a [`Digest`] struct.
//! The function [`crc.new_digest()`] will clear the underlying CRC buffer and return
//! an owned Digest struct to get ready for calculation.
//! With the Digest struct, you may keep writing `u32` values with function
//! [`digest.write_u32(value)`]. To read the digest value out, use [`digest.finish()`].
//! Further values can still be written after the digest value is read out.
//! After all the processes, you may stop writing to digests and get the `Crc` wrapper
//! back with function [`digest.free()`].
//! To release and turn off the clock to get CRC peripheral back, use [`crc.release()`].
//!
//! [`Crc`]: struct.Crc.html
//! [`Crc::crc`]: struct.Crc.html#method.crc
//! [`crc.new_digest()`]: struct.Crc.html#method.new_digest
//! [`Digest`]: struct.Digest.html
//! [`digest.write_u32(value)`]: struct.Digest.html#method.write_u32
//! [`digest.finish()`]: struct.Digest.html#method.finish
//! [`digest.free()`]: struct.Digest.html#method.free
//! [`crc.release()`]: struct.Crc.html#method.release
//!
//! ## Free data register `fdata`
//!
//! The `fdata` register provides you with additional 8-bit global storage. You may read
//! from or write to this register using function [`fdata_read`] and [`fdata_write`].
//!
//! [`fdata_read`]: fn.fdata_read.html
//! [`fdata_write`]: fn.fdata_write.html
//!
//! # Example
//!
//! This example is tested on GD32VF103C-START board. It calculated the CRC value of
//! `0xABCD1234`. The desired result is `0xF7018A40`; if the underlying hardware had
//! calculated correctly, PA7 is set high to turn on the LED with anode connected to
//! the MCU.
//!
//! ```
//! #![no_std]
//! #![no_main]
//!
//! use gd32vf103_hal as hal;
//! use hal::{crc::Crc, pac, prelude::*};
//! use panic_halt as _;
//!
//! #[riscv_rt::entry]
//! fn main() -> ! {
//! let dp = pac::Peripherals::take().unwrap();
//! let mut rcu = dp.RCU.constrain();
//! let mut gpioa = dp.GPIOA.split(&mut rcu);
//! let mut pa7 = gpioa.pa7.into_push_pull_output();
//!
//! let src: u32 = 0xABCD1234;
//! let crc = Crc::crc(dp.CRC, &mut rcu);
//! let mut digest = crc.new_digest();
//! digest.write_u32(src);
//!
//! if digest.finish() == 0xF7018A40 {
//! pa7.set_high().unwrap();
//! }
//!
//! loop {}
//! }
//! ```

use crate::pac::CRC;
use crate::rcu::Rcu;

/// Read the value of the free data register `fdata`.
#[inline]
pub fn fdata_read() -> u8 {
// note(unsafe): separate ownership, volatile read
unsafe { &*CRC::ptr() }.fdata.read().fdata().bits()
}

/// Write data to the free data register `fdata`.
#[inline]
pub fn fdata_write(byte: u8) {
// note(unsafe): separate ownership, volatile write
// for high 24 bits we may keep reset value
unsafe { &*CRC::ptr() }
.fdata
.modify(|_, w| unsafe { w.fdata().bits(byte) });
}

/// CRC module abstraction
///
/// Owns `CRC_DATA` and `CRC_CTL`.
pub struct Crc {
crc: CRC,
}

impl Crc {
/// Take ownership of CRC and enable CRC clock.
///
/// To create struct `Crc`, it's need to pass the peripheral `CRC` and a mutable
/// reference of `RCU` reset and clock unit. Get the ownership of `CRC` from the device
/// peripheral struct `pac::Peripherals` (may already exists in your code) as `dp`
/// then use `dp.CRC`. To get AHB you need to constrain `RCU` module using
/// `dp.RCU.constrain()`, then use `&mut rcu` to get its mutable reference.
///
/// # Examples
///
/// Basic usage:
///
/// ```no_run
/// // Prepare CRC peripheral for calculation
/// let crc = Crc::crc(dp.CRC, &mut rcu);
/// ```
#[inline]
pub fn crc(crc: CRC, rcu: &mut Rcu) -> Self {
rcu.regs.ahben.modify(|_, w| w.crcen().set_bit());
Crc { crc }
}

/// Create new Digest struct for CRC calculation.
///
/// The underlying CRC buffer is cleaned to prepare for incoming values. Write
/// operations could be later performed using functions in `Digest`. You may
/// refer to [`digest.write_u32(value)`] for how to write the value for CRC
/// calculation.
///
/// [`digest.write_u32(value)`]: struct.Digest.html#method.write_u32
///
/// # Examples
///
/// Basic usage:
///
/// ```no_run
/// // Prepare CRC peripheral for calculation
/// let crc = Crc::crc(dp.CRC, &mut rcu);
/// // Create a Digest instance to write values for calculation
/// let mut digest = crc.new_digest();
/// ```
#[inline]
pub fn new_digest(self) -> Digest {
self.crc.ctl.modify(|_, w| w.rst().set_bit());
// after initialization finished, hardware would set `rst` bit to `false`.
while self.crc.ctl.read().rst() == true {}
Digest { crc: self.crc }
}

/// Disable the CRC clock and release the peripheral.
///
/// The clock is switched off using AHB; you must provide a mutable referrence
/// of RCU to release the CRC peripheral. After release, the peripheral is freed
/// for further use.
///
/// # Examples
///
/// Basic usage:
///
/// ```no_run
/// // Prepare CRC peripheral for calculation
/// let crc = Crc::crc(dp.CRC, &mut rcu);
/// // ... actual calculations with `crc`
/// // Release the wrapper and get CRC peripheral back
/// let crc = crc.release(&mut rcu);
/// ```
#[inline]
pub fn release(self, rcu: &mut Rcu) -> CRC {
rcu.regs.ahben.modify(|_, w| w.crcen().clear_bit());
self.crc
}
}

/// Digest struct for CRC calculation.
///
/// This struct is created using [`Crc::new_digest`] function. Use [`write_u32`]
/// function to write data for calculation; use [`finish`] to read the result.
/// After calculation, use [`free`] to get the Crc wrapper back.
///
/// [`Crc::new_digest`]: ./struct.Crc.html#method.new_digest
/// [`write_u32`]: #method.write_u32
/// [`finish`]: #method.finish
/// [`free`]: #method.free
///
/// # Examples
///
/// Calculate CRC result of single value:
///
/// ```no_run
/// // Write a single value
/// digest.write_u32(0x12345678);
/// // Read its CRC calculation result
/// let ans = digest.finish();
/// ```
///
/// Calculate CRC reuslt of an array of values:
///
/// ```no_run
/// // Write all values of an array
/// for value in array {
/// digest.write_u32(value);
/// }
/// // Read the CRC calculation result of this array
/// let ans = digest.finish();
/// ```
pub struct Digest {
crc: CRC,
}

impl Digest {
/// Writes a single u32 into this hasher.
///
/// Multiple u32 values may be written one by one using this function.
/// After all values written for calculation, you may read the CRC result
/// using function [`finish`].
///
/// [`finish`]: #method.finish
///
/// # Examples
///
/// Write a single value:
///
/// ```no_run
/// // Write a single value
/// digest.write_u32(0x12345678);
/// ```
///
/// Write an array of values:
///
/// ```no_run
/// // Write all values of an array
/// for value in array {
/// digest.write_u32(value);
/// }
/// ```
#[inline]
pub fn write_u32(&mut self, i: u32) {
self.crc.data.write(|w| unsafe { w.data().bits(i) });
}

/// Returns the hash value for the values written so far.
///
/// # Examples
///
/// Get CRC reuslt of a single value:
///
/// ```no_run
/// // Write a single value
/// digest.write_u32(0x12345678);
/// // Read its CRC calculation result
/// let ans = digest.finish();
/// ```
///
/// Get CRC reuslt of an array of values:
///
/// ```no_run
/// // Write all values of an array
/// for value in array {
/// digest.write_u32(value);
/// }
/// // Read the CRC calculation result of this array
/// let ans = digest.finish();
/// ```
#[inline]
pub fn finish(&self) -> u32 {
self.crc.data.read().data().bits()
}

/// Frees the Digest struct to return the underlying Crc peripheral.
///
/// # Examples
///
/// Basic usage:
///
/// ```no_run
/// // Calculate CRC of a single value
/// digest.write_u32(0x12345678);
/// let ans = digest.finish();
/// // Free the Digest to get the CRC wrapper back
/// let crc = digest.free();
/// ```
#[inline]
pub fn free(self) -> Crc {
Crc { crc: self.crc }
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use embedded_hal as hal;

pub mod afio;
pub mod backup_domain;
pub mod crc;
pub mod delay;
pub mod eclic;
pub mod exmc;
Expand Down