diff --git a/src/crc.rs b/src/crc.rs new file mode 100644 index 0000000..c7eaa54 --- /dev/null +++ b/src/crc.rs @@ -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 } + } +} diff --git a/src/lib.rs b/src/lib.rs index ea049b1..0bd162d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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;