From ae285b84ed551aa5c81443fcacb73e788a6d36c2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 3 Dec 2025 14:07:57 -0800 Subject: CRC Fixes #74 Signed-off-by: Felipe Balbi --- embassy-mcxa/src/clocks/mod.rs | 2 + embassy-mcxa/src/crc.rs | 756 +++++++++++++++++++++++++++++++++++++++++ embassy-mcxa/src/lib.rs | 1 + examples/mcxa/src/bin/crc.rs | 39 +++ 4 files changed, 798 insertions(+) create mode 100644 embassy-mcxa/src/crc.rs create mode 100644 examples/mcxa/src/bin/crc.rs diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs index 9c9e6ef3d..1b23a9d9f 100644 --- a/embassy-mcxa/src/clocks/mod.rs +++ b/embassy-mcxa/src/clocks/mod.rs @@ -923,6 +923,8 @@ pub(crate) mod gate { impl_cc_gate!(GPIO3, mrcc_glb_cc2, mrcc_glb_rst2, gpio3, NoConfig); impl_cc_gate!(GPIO4, mrcc_glb_cc2, mrcc_glb_rst2, gpio4, NoConfig); + impl_cc_gate!(CRC0, mrcc_glb_cc0, mrcc_glb_rst0, crc0, NoConfig); + // These peripherals DO have meaningful configuration, and could fail if the system // clocks do not match their needs. #[cfg(not(feature = "time"))] diff --git a/embassy-mcxa/src/crc.rs b/embassy-mcxa/src/crc.rs new file mode 100644 index 000000000..83b5eca8d --- /dev/null +++ b/embassy-mcxa/src/crc.rs @@ -0,0 +1,756 @@ +//! Cyclic Redundandy Check (CRC) + +use core::marker::PhantomData; + +use embassy_hal_internal::Peri; +use mcxa_pac::crc0::ctrl::{Fxor, Tot, Totr}; + +use crate::clocks::enable_and_reset; +use crate::clocks::periph_helpers::NoConfig; +use crate::peripherals::CRC0; + +/// CRC driver. +pub struct Crc<'d, M> { + _peri: Peri<'d, CRC0>, + _phantom: PhantomData, +} + +impl<'d, M: Mode> Crc<'d, M> { + fn new_inner(_peri: Peri<'d, CRC0>) -> Self { + _ = unsafe { enable_and_reset::(&NoConfig) }; + + Crc { + _peri, + _phantom: PhantomData, + } + } + + // Configure the underlying peripheral. `f` is expected to set the + // operating mode to either 16- or 32-bits. + fn configure(config: Config, f: F) + where + F: FnOnce(), + { + f(); + + Self::regs().ctrl().modify(|_, w| { + w.fxor() + .variant(config.complement_out.into()) + .totr() + .variant(config.reflect_out.into()) + .tot() + .variant(config.reflect_in.into()) + .was() + .data() + }); + + Self::regs().gpoly32().write(|w| unsafe { w.bits(config.polynomial) }); + + Self::regs().ctrl().modify(|_, w| w.was().seed()); + Self::regs().data32().write(|w| unsafe { w.bits(config.seed) }); + Self::regs().ctrl().modify(|_, w| w.was().data()); + } + + fn regs() -> &'static crate::pac::crc0::RegisterBlock { + unsafe { &*crate::pac::Crc0::ptr() } + } + + /// Feeds a byte into the CRC peripheral. + fn feed_byte(&mut self, byte: u8) { + Self::regs().data8().write(|w| unsafe { w.bits(byte) }); + } + + /// Feeds a halfword into the CRC peripheral. + fn feed_halfword(&mut self, halfword: u16) { + Self::regs().data16().write(|w| unsafe { w.bits(halfword) }); + } + + /// Feeds a word into the CRC peripheral. + fn feed_word(&mut self, word: u32) { + Self::regs().data32().write(|w| unsafe { w.bits(word) }); + } +} + +impl<'d> Crc<'d, Crc16> { + /// Instantiates a new CRC peripheral driver in 16-bit mode + pub fn new_crc16(peri: Peri<'d, CRC0>, config: Config) -> Self { + let inst = Self::new_inner(peri); + + Self::configure(config, || { + Self::regs().ctrl().modify(|_, w| w.tcrc().b16()); + }); + + inst + } + + /// Instantiates a new CRC peripheral driver for the given `Algorithm16`. + pub fn new_algorithm16(peri: Peri<'d, CRC0>, algorithm: Algorithm16) -> Self { + Self::new_crc16(peri, algorithm.into_config()) + } + + /// Instantiates a new CRC peripheral for the `A` algorithm. + pub fn new_a(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::A) + } + + /// Instantiates a new CRC peripheral for the `AugCcitt` algorithm. + pub fn new_aug_ccitt(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::AugCcitt) + } + + /// Instantiates a new CRC peripheral for the `Arc` algorithm. + pub fn new_arc(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Arc) + } + + /// Instantiates a new CRC peripheral for the `Buypass` algorithm. + pub fn new_buypass(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Buypass) + } + + /// Instantiates a new CRC peripheral for the `CcittFalse` algorithm. + pub fn new_ccitt_false(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::CcittFalse) + } + + /// Instantiates a new CRC peripheral for the `CcittZero` algorithm. + pub fn new_ccitt_zero(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::CcittZero) + } + + /// Instantiates a new CRC peripheral for the `Cdma2000` algorithm. + pub fn new_cdma_2000(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Cdma2000) + } + + /// Instantiates a new CRC peripheral for the `Dds110` algorithm. + pub fn new_dds_110(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Dds110) + } + + /// Instantiates a new CRC peripheral for the `DectX` algorithm. + pub fn new_dect_x(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::DectX) + } + + /// Instantiates a new CRC peripheral for the `Dnp` algorithm. + pub fn new_dnp(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Dnp) + } + + /// Instantiates a new CRC peripheral for the `En13757` algorithm. + pub fn new_en13757(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::En13757) + } + + /// Instantiates a new CRC peripheral for the `Genibus` algorithm. + pub fn new_genibus(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Genibus) + } + + /// Instantiates a new CRC peripheral for the `Kermit` algorithm. + pub fn new_kermit(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Kermit) + } + + /// Instantiates a new CRC peripheral for the `Maxim` algorithm. + pub fn new_maxim(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Maxim) + } + + /// Instantiates a new CRC peripheral for the `Mcrf4xx` algorithm. + pub fn new_mcrf4xx(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Mcrf4xx) + } + + /// Instantiates a new CRC peripheral for the `Modbus` algorithm. + pub fn new_modbus(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Modbus) + } + + /// Instantiates a new CRC peripheral for the `Riello` algorithm. + pub fn new_riello(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Riello) + } + + /// Instantiates a new CRC peripheral for the `T10Dif` algorithm. + pub fn new_t10_dif(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::T10Dif) + } + + /// Instantiates a new CRC peripheral for the `Teledisk` algorithm. + pub fn new_teledisk(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Teledisk) + } + + /// Instantiates a new CRC peripheral for the `Tms37157` algorithm. + pub fn new_tms_37157(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Tms37157) + } + + /// Instantiates a new CRC peripheral for the `Usb` algorithm. + pub fn new_usb(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Usb) + } + + /// Instantiates a new CRC peripheral for the `X25` algorithm. + pub fn new_x25(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::X25) + } + + /// Instantiates a new CRC peripheral for the `Xmodem` algorithm. + pub fn new_xmodem(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm16(peri, Algorithm16::Xmodem) + } + + fn read_crc(&mut self) -> u16 { + // Reference manual states: + // + // "After writing all the data, you must wait for at least two + // clock cycles to read the data from CRC Data (DATA) + // register." + cortex_m::asm::delay(2); + + let ctrl = Self::regs().ctrl().read(); + + // if transposition is enabled, result sits in the upper 16 bits + if ctrl.totr().is_byts_trnps() || ctrl.totr().is_byts_bts_trnps() { + (Self::regs().data32().read().bits() >> 16) as u16 + } else { + Self::regs().data16().read().bits() + } + } + + /// Feeds a slice of bytes into the CRC peripheral. Returns the computed checksum. + pub fn feed_bytes(&mut self, bytes: &[u8]) -> u16 { + let (prefix, data, suffix) = unsafe { bytes.align_to::() }; + + for b in prefix { + self.feed_byte(*b); + } + + // use 32-bit writes as long as possible + for w in data { + self.feed_word(*w); + } + + for b in suffix { + self.feed_byte(*b); + } + + // read back result. + self.read_crc() + } + + /// Feeds a slice of halfwords into the CRC peripheral. Returns the computed checksum. + pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u16 { + for halfword in halfwords { + self.feed_halfword(*halfword); + } + + self.read_crc() + } +} + +impl<'d> Crc<'d, Crc32> { + /// Instantiates a new CRC peripheral driver in 32-bit mode + pub fn new_crc32(peri: Peri<'d, CRC0>, config: Config) -> Self { + let inst = Self::new_inner(peri); + + Self::configure(config, || { + Self::regs().ctrl().modify(|_, w| w.tcrc().b32()); + }); + + inst + } + + /// Instantiates a new CRC peripheral driver for the given `Algorithm32`. + pub fn new_algorithm32(peri: Peri<'d, CRC0>, algorithm: Algorithm32) -> Self { + Self::new_crc32(peri, algorithm.into_config()) + } + + /// Instantiates a new CRC peripheral for the `Bzip2` algorithm. + pub fn new_bzip2(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm32(peri, Algorithm32::Bzip2) + } + + /// Instantiates a new CRC peripheral for the `C` algorithm. + pub fn new_c(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm32(peri, Algorithm32::C) + } + + /// Instantiates a new CRC peripheral for the `D` algorithm. + pub fn new_d(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm32(peri, Algorithm32::D) + } + + /// Instantiates a new CRC peripheral for the `IsoHdlc` algorithm. + pub fn new_iso_hdlc(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm32(peri, Algorithm32::IsoHdlc) + } + + /// Instantiates a new CRC peripheral for the `JamCrc` algorithm. + pub fn new_jam_crc(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm32(peri, Algorithm32::JamCrc) + } + + /// Instantiates a new CRC peripheral for the `Mpeg2` algorithm. + pub fn new_mpeg2(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm32(peri, Algorithm32::Mpeg2) + } + + /// Instantiates a new CRC peripheral for the `Posix` algorithm. + pub fn new_posix(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm32(peri, Algorithm32::Posix) + } + + /// Instantiates a new CRC peripheral for the `Q` algorithm. + pub fn new_q(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm32(peri, Algorithm32::Q) + } + + /// Instantiates a new CRC peripheral for the `Xfer` algorithm. + pub fn new_xfer(peri: Peri<'d, CRC0>) -> Self { + Self::new_algorithm32(peri, Algorithm32::Xfer) + } + + fn read_crc(&mut self) -> u32 { + // Reference manual states: + // + // "After writing all the data, you must wait for at least two + // clock cycles to read the data from CRC Data (DATA) + // register." + cortex_m::asm::delay(2); + Self::regs().data32().read().bits() + } + + /// Feeds a slice of bytes into the CRC peripheral. Returns the computed checksum. + pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 { + let (prefix, data, suffix) = unsafe { bytes.align_to::() }; + + for b in prefix { + self.feed_byte(*b); + } + + // use 32-bit writes as long as possible + for w in data { + self.feed_word(*w); + } + + for b in suffix { + self.feed_byte(*b); + } + + // read back result. + self.read_crc() + } + + /// Feeds a slice of halfwords into the CRC peripheral. Returns the computed checksum. + pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 { + for halfword in halfwords { + self.feed_halfword(*halfword); + } + + self.read_crc() + } + + /// Feeds a slice of words into the CRC peripheral. Returns the computed checksum. + pub fn feed_words(&mut self, words: &[u32]) -> u32 { + for word in words { + self.feed_word(*word); + } + + self.read_crc() + } +} + +mod sealed { + pub trait SealedMode {} +} + +/// Mode of operation: 32 or 16-bit CRC. +#[allow(private_bounds)] +pub trait Mode: sealed::SealedMode {} + +/// 16-bit CRC. +pub struct Crc16; +impl sealed::SealedMode for Crc16 {} +impl Mode for Crc16 {} + +/// 32-bit CRC. +pub struct Crc32; +impl sealed::SealedMode for Crc32 {} +impl Mode for Crc32 {} + +/// CRC configuration. +#[derive(Copy, Clone, Debug)] +#[non_exhaustive] +pub struct Config { + /// The CRC polynomial to be used. + pub polynomial: u32, + + /// Reflect bit order of input? + pub reflect_in: Reflect, + + /// Reflect CRC bit order? + pub reflect_out: Reflect, + + /// 1's complement CRC? + pub complement_out: Complement, + + /// CRC Seed + pub seed: u32, +} + +impl Config { + /// Create a new CRC config. + #[must_use] + pub fn new( + polynomial: u32, + reflect_in: Reflect, + reflect_out: Reflect, + complement_out: Complement, + seed: u32, + ) -> Self { + Config { + polynomial, + reflect_in, + reflect_out, + complement_out, + seed, + } + } +} + +impl Default for Config { + fn default() -> Self { + Self { + polynomial: 0, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0xffff_ffff, + } + } +} + +/// Supported standard CRC16 algorithms. +#[derive(Copy, Clone, Debug)] +pub enum Algorithm16 { + A, + Arc, + AugCcitt, + Buypass, + CcittFalse, + CcittZero, + Cdma2000, + Dds110, + DectX, + Dnp, + En13757, + Genibus, + Kermit, + Maxim, + Mcrf4xx, + Modbus, + Riello, + T10Dif, + Teledisk, + Tms37157, + Usb, + X25, + Xmodem, +} + +impl Algorithm16 { + fn into_config(self) -> Config { + match self { + Algorithm16::A => Config { + polynomial: 0x1021, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::No, + seed: 0xc6c6, + }, + Algorithm16::Arc => Config { + polynomial: 0x8005, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::No, + seed: 0, + }, + Algorithm16::AugCcitt => Config { + polynomial: 0x1021, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0x1d0f, + }, + Algorithm16::Buypass => Config { + polynomial: 0x8005, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0, + }, + Algorithm16::CcittFalse => Config { + polynomial: 0x1021, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0xffff, + }, + Algorithm16::CcittZero => Config { + polynomial: 0x1021, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0, + }, + Algorithm16::Cdma2000 => Config { + polynomial: 0xc867, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0xffff, + }, + Algorithm16::Dds110 => Config { + polynomial: 0x8005, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0x800d, + }, + Algorithm16::DectX => Config { + polynomial: 0x0589, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0, + }, + Algorithm16::Dnp => Config { + polynomial: 0x3d65, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::Yes, + seed: 0, + }, + Algorithm16::En13757 => Config { + polynomial: 0x3d65, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::Yes, + seed: 0, + }, + Algorithm16::Genibus => Config { + polynomial: 0x1021, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::Yes, + seed: 0xffff, + }, + Algorithm16::Kermit => Config { + polynomial: 0x1021, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::No, + seed: 0, + }, + Algorithm16::Maxim => Config { + polynomial: 0x8005, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::Yes, + seed: 0, + }, + Algorithm16::Mcrf4xx => Config { + polynomial: 0x1021, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::No, + seed: 0xffff, + }, + Algorithm16::Modbus => Config { + polynomial: 0x8005, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::No, + seed: 0xffff, + }, + Algorithm16::Riello => Config { + polynomial: 0x1021, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::No, + seed: 0xb2aa, + }, + Algorithm16::T10Dif => Config { + polynomial: 0x8bb7, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0, + }, + Algorithm16::Teledisk => Config { + polynomial: 0xa097, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0, + }, + Algorithm16::Tms37157 => Config { + polynomial: 0x1021, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::No, + seed: 0x89ec, + }, + Algorithm16::Usb => Config { + polynomial: 0x8005, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::No, + seed: 0xffff, + }, + Algorithm16::X25 => Config { + polynomial: 0x1021, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::Yes, + seed: 0xffff, + }, + Algorithm16::Xmodem => Config { + polynomial: 0x1021, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0, + }, + } + } +} + +/// Supported standard CRC32 algorithms. +#[derive(Copy, Clone, Debug)] +pub enum Algorithm32 { + Bzip2, + C, + D, + IsoHdlc, + JamCrc, + Mpeg2, + Posix, + Q, + Xfer, +} + +impl Algorithm32 { + fn into_config(self) -> Config { + match self { + Algorithm32::Bzip2 => Config { + polynomial: 0x04c1_1db7, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::Yes, + seed: 0xffff_ffff, + }, + Algorithm32::C => Config { + polynomial: 0x1edc_6f41, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::Yes, + seed: 0xffff_ffff, + }, + Algorithm32::D => Config { + polynomial: 0xa833_982b, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::Yes, + seed: 0xffff_ffff, + }, + Algorithm32::IsoHdlc => Config { + polynomial: 0x04c1_1db7, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::Yes, + seed: 0xffff_ffff, + }, + Algorithm32::JamCrc => Config { + polynomial: 0x04c1_1db7, + reflect_in: Reflect::Yes, + reflect_out: Reflect::Yes, + complement_out: Complement::No, + seed: 0xffff_ffff, + }, + Algorithm32::Mpeg2 => Config { + polynomial: 0x04c1_1db7, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0xffff_ffff, + }, + Algorithm32::Posix => Config { + polynomial: 0x04c1_1db7, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::Yes, + seed: 0, + }, + Algorithm32::Q => Config { + polynomial: 0x8141_41ab, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0, + }, + Algorithm32::Xfer => Config { + polynomial: 0x0000_00af, + reflect_in: Reflect::No, + reflect_out: Reflect::No, + complement_out: Complement::No, + seed: 0, + }, + } + } +} + +/// Reflect bit order. +#[derive(Copy, Clone, Debug)] +pub enum Reflect { + No, + Yes, +} + +impl From for Tot { + fn from(value: Reflect) -> Tot { + match value { + Reflect::No => Tot::BytsTrnps, + Reflect::Yes => Tot::BytsBtsTrnps, + } + } +} + +impl From for Totr { + fn from(value: Reflect) -> Totr { + match value { + Reflect::No => Totr::Notrnps, + Reflect::Yes => Totr::BytsBtsTrnps, + } + } +} + +/// 1's complement output. +#[derive(Copy, Clone, Debug)] +pub enum Complement { + No, + Yes, +} + +impl From for Fxor { + fn from(value: Complement) -> Fxor { + match value { + Complement::No => Fxor::Noxor, + Complement::Yes => Fxor::Invert, + } + } +} diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs index c6d8adc8f..64eeb4012 100644 --- a/embassy-mcxa/src/lib.rs +++ b/embassy-mcxa/src/lib.rs @@ -12,6 +12,7 @@ pub mod pins; // pin mux helpers pub mod adc; pub mod clkout; pub mod config; +pub mod crc; pub mod i2c; pub mod interrupt; pub mod lpuart; diff --git a/examples/mcxa/src/bin/crc.rs b/examples/mcxa/src/bin/crc.rs new file mode 100644 index 000000000..417b4f865 --- /dev/null +++ b/examples/mcxa/src/bin/crc.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] + +use embassy_executor::Spawner; +use hal::config::Config; +use hal::crc::Crc; +use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let config = Config::default(); + let mut p = hal::init(config); + + defmt::info!("CRC example"); + + let buf = b"123456789"; + + let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); + let sum = crc.feed_bytes(buf); + assert_eq!(sum, 0x29b1); + + let mut crc = Crc::new_maxim(p.CRC0.reborrow()); + let sum = crc.feed_bytes(buf); + assert_eq!(sum, 0x44c2); + + let mut crc = Crc::new_kermit(p.CRC0.reborrow()); + let sum = crc.feed_bytes(buf); + assert_eq!(sum, 0x2189); + + let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); + let sum = crc.feed_bytes(buf); + assert_eq!(sum, 0xcbf4_3926); + + let mut crc = Crc::new_posix(p.CRC0.reborrow()); + let sum = crc.feed_bytes(buf); + assert_eq!(sum, 0x765e_7680); + + defmt::info!("CRC successful"); +} -- cgit From b943d71365ef05b23db7066727f8bb6158b1a624 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 5 Dec 2025 08:41:43 -0800 Subject: mcxa/crc: document the use of align_to --- embassy-mcxa/src/crc.rs | 20 ++++++++++++++++++-- examples/mcxa/src/bin/crc.rs | 10 +++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/embassy-mcxa/src/crc.rs b/embassy-mcxa/src/crc.rs index 83b5eca8d..f01a671c4 100644 --- a/embassy-mcxa/src/crc.rs +++ b/embassy-mcxa/src/crc.rs @@ -222,7 +222,15 @@ impl<'d> Crc<'d, Crc16> { } /// Feeds a slice of bytes into the CRC peripheral. Returns the computed checksum. - pub fn feed_bytes(&mut self, bytes: &[u8]) -> u16 { + /// + /// The input is split using [`align_to::`] into: + /// - `prefix`: unaligned leading bytes, + /// - `data`: aligned `u32` words, + /// - `suffix`: trailing bytes. + /// + /// This allows efficient 32‑bit writes where possible, falling back to byte writes + /// for the remainder. + pub fn feed(&mut self, bytes: &[u8]) -> u16 { let (prefix, data, suffix) = unsafe { bytes.align_to::() }; for b in prefix { @@ -325,7 +333,15 @@ impl<'d> Crc<'d, Crc32> { } /// Feeds a slice of bytes into the CRC peripheral. Returns the computed checksum. - pub fn feed_bytes(&mut self, bytes: &[u8]) -> u32 { + /// + /// The input is split using [`align_to::`] into: + /// - `prefix`: unaligned leading bytes, + /// - `data`: aligned `u32` words, + /// - `suffix`: trailing bytes. + /// + /// This allows efficient 32‑bit writes where possible, falling back to byte writes + /// for the remainder. + pub fn feed(&mut self, bytes: &[u8]) -> u32 { let (prefix, data, suffix) = unsafe { bytes.align_to::() }; for b in prefix { diff --git a/examples/mcxa/src/bin/crc.rs b/examples/mcxa/src/bin/crc.rs index 417b4f865..f963620cc 100644 --- a/examples/mcxa/src/bin/crc.rs +++ b/examples/mcxa/src/bin/crc.rs @@ -16,23 +16,23 @@ async fn main(_spawner: Spawner) { let buf = b"123456789"; let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); - let sum = crc.feed_bytes(buf); + let sum = crc.feed(buf); assert_eq!(sum, 0x29b1); let mut crc = Crc::new_maxim(p.CRC0.reborrow()); - let sum = crc.feed_bytes(buf); + let sum = crc.feed(buf); assert_eq!(sum, 0x44c2); let mut crc = Crc::new_kermit(p.CRC0.reborrow()); - let sum = crc.feed_bytes(buf); + let sum = crc.feed(buf); assert_eq!(sum, 0x2189); let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); - let sum = crc.feed_bytes(buf); + let sum = crc.feed(buf); assert_eq!(sum, 0xcbf4_3926); let mut crc = Crc::new_posix(p.CRC0.reborrow()); - let sum = crc.feed_bytes(buf); + let sum = crc.feed(buf); assert_eq!(sum, 0x765e_7680); defmt::info!("CRC successful"); -- cgit From 1f537bca418f5f09536e8922381ab7a633336292 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 5 Dec 2025 09:58:17 -0800 Subject: mcxa: crc: add generic methods to reduce duplication Signed-off-by: Felipe Balbi --- embassy-mcxa/src/crc.rs | 234 +++++++++++++++++++++++++++--------------------- 1 file changed, 134 insertions(+), 100 deletions(-) diff --git a/embassy-mcxa/src/crc.rs b/embassy-mcxa/src/crc.rs index f01a671c4..0db36273f 100644 --- a/embassy-mcxa/src/crc.rs +++ b/embassy-mcxa/src/crc.rs @@ -55,19 +55,65 @@ impl<'d, M: Mode> Crc<'d, M> { unsafe { &*crate::pac::Crc0::ptr() } } - /// Feeds a byte into the CRC peripheral. - fn feed_byte(&mut self, byte: u8) { - Self::regs().data8().write(|w| unsafe { w.bits(byte) }); + /// Read the computed CRC value + fn read_crc(&mut self) -> W { + // Reference manual states: + // + // "After writing all the data, you must wait for at least two + // clock cycles to read the data from CRC Data (DATA) + // register." + cortex_m::asm::delay(2); + + let ctrl = Self::regs().ctrl().read(); + + if W::is_16bit() { + // if transposition is enabled, result sits in the upper 16 bits + if ctrl.totr().is_byts_trnps() || ctrl.totr().is_byts_bts_trnps() { + W::from_u32(Self::regs().data32().read().bits() >> 16) + } else { + W::from_u16(Self::regs().data16().read().bits()) + } + } else { + W::from_u32(Self::regs().data32().read().bits()) + } } - /// Feeds a halfword into the CRC peripheral. - fn feed_halfword(&mut self, halfword: u16) { - Self::regs().data16().write(|w| unsafe { w.bits(halfword) }); + fn feed_word(&mut self, word: W) { + if W::is_32bit() { + Self::regs().data32().write(|w| unsafe { w.bits(word.to_u32()) }); + } else if W::is_16bit() { + Self::regs().data16().write(|w| unsafe { w.bits(word.to_u16()) }); + } else { + Self::regs().data8().write(|w| unsafe { w.bits(word.to_u8()) }); + } } - /// Feeds a word into the CRC peripheral. - fn feed_word(&mut self, word: u32) { - Self::regs().data32().write(|w| unsafe { w.bits(word) }); + /// Feeds a slice of `Word`s into the CRC peripheral. Returns the + /// computed checksum. + /// + /// The input is split using [`align_to::`] into: + /// - `prefix`: unaligned leading data, + /// - `data`: aligned `u32` words, + /// - `suffix`: trailing data. + /// + /// This allows efficient 32‑bit writes where possible, falling + /// back to `Word` writes for the remainder. + fn feed_inner(&mut self, data: &[W]) -> R { + let (prefix, aligned, suffix) = unsafe { data.align_to::() }; + + for w in prefix { + self.feed_word(*w); + } + + for w in aligned { + self.feed_word(*w); + } + + for w in suffix { + self.feed_word(*w); + } + + self.read_crc::() } } @@ -203,60 +249,17 @@ impl<'d> Crc<'d, Crc16> { Self::new_algorithm16(peri, Algorithm16::Xmodem) } - fn read_crc(&mut self) -> u16 { - // Reference manual states: - // - // "After writing all the data, you must wait for at least two - // clock cycles to read the data from CRC Data (DATA) - // register." - cortex_m::asm::delay(2); - - let ctrl = Self::regs().ctrl().read(); - - // if transposition is enabled, result sits in the upper 16 bits - if ctrl.totr().is_byts_trnps() || ctrl.totr().is_byts_bts_trnps() { - (Self::regs().data32().read().bits() >> 16) as u16 - } else { - Self::regs().data16().read().bits() - } - } - /// Feeds a slice of bytes into the CRC peripheral. Returns the computed checksum. /// - /// The input is split using [`align_to::`] into: + /// The input is split using `align_to::` into: /// - `prefix`: unaligned leading bytes, /// - `data`: aligned `u32` words, /// - `suffix`: trailing bytes. /// /// This allows efficient 32‑bit writes where possible, falling back to byte writes /// for the remainder. - pub fn feed(&mut self, bytes: &[u8]) -> u16 { - let (prefix, data, suffix) = unsafe { bytes.align_to::() }; - - for b in prefix { - self.feed_byte(*b); - } - - // use 32-bit writes as long as possible - for w in data { - self.feed_word(*w); - } - - for b in suffix { - self.feed_byte(*b); - } - - // read back result. - self.read_crc() - } - - /// Feeds a slice of halfwords into the CRC peripheral. Returns the computed checksum. - pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u16 { - for halfword in halfwords { - self.feed_halfword(*halfword); - } - - self.read_crc() + pub fn feed(&mut self, data: &[W]) -> u16 { + self.feed_inner(data) } } @@ -322,68 +325,51 @@ impl<'d> Crc<'d, Crc32> { Self::new_algorithm32(peri, Algorithm32::Xfer) } - fn read_crc(&mut self) -> u32 { - // Reference manual states: - // - // "After writing all the data, you must wait for at least two - // clock cycles to read the data from CRC Data (DATA) - // register." - cortex_m::asm::delay(2); - Self::regs().data32().read().bits() - } - - /// Feeds a slice of bytes into the CRC peripheral. Returns the computed checksum. + /// Feeds a slice of `Word`s into the CRC peripheral. Returns the + /// computed checksum. /// - /// The input is split using [`align_to::`] into: + /// The input is split using `align_to::` into: /// - `prefix`: unaligned leading bytes, /// - `data`: aligned `u32` words, /// - `suffix`: trailing bytes. /// - /// This allows efficient 32‑bit writes where possible, falling back to byte writes - /// for the remainder. - pub fn feed(&mut self, bytes: &[u8]) -> u32 { - let (prefix, data, suffix) = unsafe { bytes.align_to::() }; + /// This allows efficient 32‑bit writes where possible, falling + /// back to `Word` writes for the remainder. + pub fn feed(&mut self, data: &[W]) -> u32 { + self.feed_inner(data) + } +} - for b in prefix { - self.feed_byte(*b); - } +mod sealed { + pub trait SealedMode {} - // use 32-bit writes as long as possible - for w in data { - self.feed_word(*w); - } + pub trait SealedWord: Copy { + const WIDTH: u8; - for b in suffix { - self.feed_byte(*b); + #[inline] + fn is_8bit() -> bool { + Self::WIDTH == 8 } - // read back result. - self.read_crc() - } - - /// Feeds a slice of halfwords into the CRC peripheral. Returns the computed checksum. - pub fn feed_halfwords(&mut self, halfwords: &[u16]) -> u32 { - for halfword in halfwords { - self.feed_halfword(*halfword); + #[inline] + fn is_16bit() -> bool { + Self::WIDTH == 16 } - self.read_crc() - } - - /// Feeds a slice of words into the CRC peripheral. Returns the computed checksum. - pub fn feed_words(&mut self, words: &[u32]) -> u32 { - for word in words { - self.feed_word(*word); + #[inline] + fn is_32bit() -> bool { + Self::WIDTH == 32 } - self.read_crc() + fn from_u8(x: u8) -> Self; + fn from_u16(x: u16) -> Self; + fn from_u32(x: u32) -> Self; + fn to_u8(self) -> u8; + fn to_u16(self) -> u16; + fn to_u32(self) -> u32; } } -mod sealed { - pub trait SealedMode {} -} - /// Mode of operation: 32 or 16-bit CRC. #[allow(private_bounds)] pub trait Mode: sealed::SealedMode {} @@ -398,6 +384,54 @@ pub struct Crc32; impl sealed::SealedMode for Crc32 {} impl Mode for Crc32 {} +/// Word size for the CRC. +#[allow(private_bounds)] +pub trait Word: sealed::SealedWord {} + +macro_rules! impl_word { + ($t:ty, $width:literal) => { + impl sealed::SealedWord for $t { + const WIDTH: u8 = $width; + + #[inline] + fn from_u8(x: u8) -> Self { + x as $t + } + + #[inline] + fn from_u16(x: u16) -> Self { + x as $t + } + + #[inline] + fn from_u32(x: u32) -> Self { + x as $t + } + + #[inline] + fn to_u8(self) -> u8 { + self as u8 + } + + #[inline] + fn to_u16(self) -> u16 { + self as u16 + } + + #[inline] + fn to_u32(self) -> u32 { + self as u32 + } + } + + impl Word for $t {} + }; +} + +impl_word!(u8, 8); +impl_word!(u16, 16); +impl_word!(u32, 32); + /// CRC configuration. #[derive(Copy, Clone, Debug)] #[non_exhaustive] -- cgit From c48aa4c1a9820d3875995ca707c3aa133f17d19e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 5 Dec 2025 09:58:17 -0800 Subject: mcxa: crc: check all word types Signed-off-by: Felipe Balbi --- examples/mcxa/src/bin/crc.rs | 74 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 11 deletions(-) diff --git a/examples/mcxa/src/bin/crc.rs b/examples/mcxa/src/bin/crc.rs index f963620cc..12c423980 100644 --- a/examples/mcxa/src/bin/crc.rs +++ b/examples/mcxa/src/bin/crc.rs @@ -13,27 +13,79 @@ async fn main(_spawner: Spawner) { defmt::info!("CRC example"); - let buf = b"123456789"; + let buf_u8 = [0x00u8, 0x11, 0x22, 0x33]; + let buf_u16 = [0x0000u16, 0x1111, 0x2222, 0x3333]; + let buf_u32 = [0x0000_0000u32, 0x1111_1111, 0x2222_2222, 0x3333_3333]; + + // CCITT False + + let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); + let sum = crc.feed(&buf_u8); + assert_eq!(sum, 0x9627); let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); - let sum = crc.feed(buf); - assert_eq!(sum, 0x29b1); + let sum = crc.feed(&buf_u16); + assert_eq!(sum, 0xa467); + + let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); + let sum = crc.feed(&buf_u32); + assert_eq!(sum, 0xe5c7); + + // Maxim let mut crc = Crc::new_maxim(p.CRC0.reborrow()); - let sum = crc.feed(buf); - assert_eq!(sum, 0x44c2); + let sum = crc.feed(&buf_u8); + assert_eq!(sum, 0x4ff7); + + let mut crc = Crc::new_maxim(p.CRC0.reborrow()); + let sum = crc.feed(&buf_u16); + assert_eq!(sum, 0x2afe); + + let mut crc = Crc::new_maxim(p.CRC0.reborrow()); + let sum = crc.feed(&buf_u32); + assert_eq!(sum, 0x17d7); + + // Kermit + + let mut crc = Crc::new_kermit(p.CRC0.reborrow()); + let sum = crc.feed(&buf_u8); + assert_eq!(sum, 0xccd2); let mut crc = Crc::new_kermit(p.CRC0.reborrow()); - let sum = crc.feed(buf); - assert_eq!(sum, 0x2189); + let sum = crc.feed(&buf_u16); + assert_eq!(sum, 0x66eb); + + let mut crc = Crc::new_kermit(p.CRC0.reborrow()); + let sum = crc.feed(&buf_u32); + assert_eq!(sum, 0x75ea); + + // ISO HDLC + + let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); + let sum = crc.feed(&buf_u8); + assert_eq!(sum, 0x24c2_316d); + + let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); + let sum = crc.feed(&buf_u16); + assert_eq!(sum, 0x8a61_4178); let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); - let sum = crc.feed(buf); - assert_eq!(sum, 0xcbf4_3926); + let sum = crc.feed(&buf_u32); + assert_eq!(sum, 0xfab5_d04e); + + // POSIX + + let mut crc = Crc::new_posix(p.CRC0.reborrow()); + let sum = crc.feed(&buf_u8); + assert_eq!(sum, 0xba8d_7868); + + let mut crc = Crc::new_posix(p.CRC0.reborrow()); + let sum = crc.feed(&buf_u16); + assert_eq!(sum, 0x6d76_4f58); let mut crc = Crc::new_posix(p.CRC0.reborrow()); - let sum = crc.feed(buf); - assert_eq!(sum, 0x765e_7680); + let sum = crc.feed(&buf_u32); + assert_eq!(sum, 0x2a5b_cb90); defmt::info!("CRC successful"); } -- cgit From cc34871ebef2513f69ce52f8f8f717473e701ec2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Dec 2025 08:44:50 -0800 Subject: review comments --- embassy-mcxa/src/crc.rs | 202 +++++++++++++++++-------------------------- examples/mcxa/Cargo.toml | 1 + examples/mcxa/src/bin/crc.rs | 103 +++++++++++++++++----- 3 files changed, 161 insertions(+), 145 deletions(-) diff --git a/embassy-mcxa/src/crc.rs b/embassy-mcxa/src/crc.rs index 0db36273f..8f3b6b5de 100644 --- a/embassy-mcxa/src/crc.rs +++ b/embassy-mcxa/src/crc.rs @@ -1,9 +1,9 @@ -//! Cyclic Redundandy Check (CRC) +//! Cyclic Redundancy Check (CRC) use core::marker::PhantomData; use embassy_hal_internal::Peri; -use mcxa_pac::crc0::ctrl::{Fxor, Tot, Totr}; +use mcxa_pac::crc0::ctrl::{Fxor, Tcrc, Tot, Totr}; use crate::clocks::enable_and_reset; use crate::clocks::periph_helpers::NoConfig; @@ -27,12 +27,7 @@ impl<'d, M: Mode> Crc<'d, M> { // Configure the underlying peripheral. `f` is expected to set the // operating mode to either 16- or 32-bits. - fn configure(config: Config, f: F) - where - F: FnOnce(), - { - f(); - + fn configure(config: Config, width: Tcrc) { Self::regs().ctrl().modify(|_, w| { w.fxor() .variant(config.complement_out.into()) @@ -42,6 +37,8 @@ impl<'d, M: Mode> Crc<'d, M> { .variant(config.reflect_in.into()) .was() .data() + .tcrc() + .variant(width) }); Self::regs().gpoly32().write(|w| unsafe { w.bits(config.polynomial) }); @@ -56,49 +53,26 @@ impl<'d, M: Mode> Crc<'d, M> { } /// Read the computed CRC value - fn read_crc(&mut self) -> W { + fn finalize_inner(self) -> W { // Reference manual states: // // "After writing all the data, you must wait for at least two // clock cycles to read the data from CRC Data (DATA) // register." cortex_m::asm::delay(2); - - let ctrl = Self::regs().ctrl().read(); - - if W::is_16bit() { - // if transposition is enabled, result sits in the upper 16 bits - if ctrl.totr().is_byts_trnps() || ctrl.totr().is_byts_bts_trnps() { - W::from_u32(Self::regs().data32().read().bits() >> 16) - } else { - W::from_u16(Self::regs().data16().read().bits()) - } - } else { - W::from_u32(Self::regs().data32().read().bits()) - } + W::read(Self::regs()) } fn feed_word(&mut self, word: W) { - if W::is_32bit() { - Self::regs().data32().write(|w| unsafe { w.bits(word.to_u32()) }); - } else if W::is_16bit() { - Self::regs().data16().write(|w| unsafe { w.bits(word.to_u16()) }); - } else { - Self::regs().data8().write(|w| unsafe { w.bits(word.to_u8()) }); - } + W::write(Self::regs(), word); } - /// Feeds a slice of `Word`s into the CRC peripheral. Returns the - /// computed checksum. - /// - /// The input is split using [`align_to::`] into: - /// - `prefix`: unaligned leading data, - /// - `data`: aligned `u32` words, - /// - `suffix`: trailing data. + /// Feeds a slice of `Word`s into the CRC peripheral. Returns the computed + /// checksum. /// - /// This allows efficient 32‑bit writes where possible, falling - /// back to `Word` writes for the remainder. - fn feed_inner(&mut self, data: &[W]) -> R { + /// The input is strided efficiently into as many `u32`s as possible, + /// falling back to smaller writes for the remainder. + fn feed_inner(&mut self, data: &[W]) { let (prefix, aligned, suffix) = unsafe { data.align_to::() }; for w in prefix { @@ -112,8 +86,6 @@ impl<'d, M: Mode> Crc<'d, M> { for w in suffix { self.feed_word(*w); } - - self.read_crc::() } } @@ -121,11 +93,7 @@ impl<'d> Crc<'d, Crc16> { /// Instantiates a new CRC peripheral driver in 16-bit mode pub fn new_crc16(peri: Peri<'d, CRC0>, config: Config) -> Self { let inst = Self::new_inner(peri); - - Self::configure(config, || { - Self::regs().ctrl().modify(|_, w| w.tcrc().b16()); - }); - + Self::configure(config, Tcrc::B16); inst } @@ -249,17 +217,18 @@ impl<'d> Crc<'d, Crc16> { Self::new_algorithm16(peri, Algorithm16::Xmodem) } - /// Feeds a slice of bytes into the CRC peripheral. Returns the computed checksum. - /// - /// The input is split using `align_to::` into: - /// - `prefix`: unaligned leading bytes, - /// - `data`: aligned `u32` words, - /// - `suffix`: trailing bytes. + /// Feeds a slice of `Word`s into the CRC peripheral. /// - /// This allows efficient 32‑bit writes where possible, falling back to byte writes - /// for the remainder. - pub fn feed(&mut self, data: &[W]) -> u16 { - self.feed_inner(data) + /// The input is strided efficiently into as many `u32`s as possible, + /// falling back to smaller writes for the remainder. + pub fn feed(&mut self, data: &[W]) { + self.feed_inner(data); + } + + /// Finalizes the CRC calculation and reads the resulting CRC from the + /// hardware consuming `self`. + pub fn finalize(self) -> u16 { + self.finalize_inner() } } @@ -267,11 +236,7 @@ impl<'d> Crc<'d, Crc32> { /// Instantiates a new CRC peripheral driver in 32-bit mode pub fn new_crc32(peri: Peri<'d, CRC0>, config: Config) -> Self { let inst = Self::new_inner(peri); - - Self::configure(config, || { - Self::regs().ctrl().modify(|_, w| w.tcrc().b32()); - }); - + Self::configure(config, Tcrc::B32); inst } @@ -325,18 +290,18 @@ impl<'d> Crc<'d, Crc32> { Self::new_algorithm32(peri, Algorithm32::Xfer) } - /// Feeds a slice of `Word`s into the CRC peripheral. Returns the - /// computed checksum. - /// - /// The input is split using `align_to::` into: - /// - `prefix`: unaligned leading bytes, - /// - `data`: aligned `u32` words, - /// - `suffix`: trailing bytes. + /// Feeds a slice of `Word`s into the CRC peripheral. /// - /// This allows efficient 32‑bit writes where possible, falling - /// back to `Word` writes for the remainder. - pub fn feed(&mut self, data: &[W]) -> u32 { - self.feed_inner(data) + /// The input is strided efficiently into as many `u32`s as possible, + /// falling back to smaller writes for the remainder. + pub fn feed(&mut self, data: &[W]) { + self.feed_inner(data); + } + + /// Finalizes the CRC calculation and reads the resulting CRC from the + /// hardware consuming `self`. + pub fn finalize(self) -> u32 { + self.finalize_inner() } } @@ -344,29 +309,8 @@ mod sealed { pub trait SealedMode {} pub trait SealedWord: Copy { - const WIDTH: u8; - - #[inline] - fn is_8bit() -> bool { - Self::WIDTH == 8 - } - - #[inline] - fn is_16bit() -> bool { - Self::WIDTH == 16 - } - - #[inline] - fn is_32bit() -> bool { - Self::WIDTH == 32 - } - - fn from_u8(x: u8) -> Self; - fn from_u16(x: u16) -> Self; - fn from_u32(x: u32) -> Self; - fn to_u8(self) -> u8; - fn to_u16(self) -> u16; - fn to_u32(self) -> u32; + fn write(regs: &'static crate::pac::crc0::RegisterBlock, word: Self); + fn read(regs: &'static crate::pac::crc0::RegisterBlock) -> Self; } } @@ -389,38 +333,16 @@ impl Mode for Crc32 {} pub trait Word: sealed::SealedWord {} macro_rules! impl_word { - ($t:ty, $width:literal) => { + ($t:ty, $width:literal, $write:expr, $read:expr) => { impl sealed::SealedWord for $t { - const WIDTH: u8 = $width; - - #[inline] - fn from_u8(x: u8) -> Self { - x as $t - } - - #[inline] - fn from_u16(x: u16) -> Self { - x as $t - } - - #[inline] - fn from_u32(x: u32) -> Self { - x as $t - } - #[inline] - fn to_u8(self) -> u8 { - self as u8 + fn write(regs: &'static crate::pac::crc0::RegisterBlock, word: Self) { + $write(regs, word) } #[inline] - fn to_u16(self) -> u16 { - self as u16 - } - - #[inline] - fn to_u32(self) -> u32 { - self as u32 + fn read(regs: &'static crate::pac::crc0::RegisterBlock) -> Self { + $read(regs) } } @@ -428,9 +350,39 @@ macro_rules! impl_word { }; } -impl_word!(u8, 8); -impl_word!(u16, 16); -impl_word!(u32, 32); +impl_word!( + u8, + 8, + |regs: &'static crate::pac::crc0::RegisterBlock, word| { + regs.data8().write(|w| unsafe { w.bits(word) }); + }, + |regs: &'static crate::pac::crc0::RegisterBlock| { regs.data8().read().bits() } +); +impl_word!( + u16, + 16, + |regs: &'static crate::pac::crc0::RegisterBlock, word| { + regs.data16().write(|w| unsafe { w.bits(word) }); + }, + |regs: &'static crate::pac::crc0::RegisterBlock| { + let ctrl = regs.ctrl().read(); + + // if transposition is enabled, result sits in the upper 16 bits + if ctrl.totr().is_byts_trnps() || ctrl.totr().is_byts_bts_trnps() { + (regs.data32().read().bits() >> 16) as u16 + } else { + regs.data16().read().bits() + } + } +); +impl_word!( + u32, + 32, + |regs: &'static crate::pac::crc0::RegisterBlock, word| { + regs.data32().write(|w| unsafe { w.bits(word) }); + }, + |regs: &'static crate::pac::crc0::RegisterBlock| { regs.data32().read().bits() } +); /// CRC configuration. #[derive(Copy, Clone, Debug)] diff --git a/examples/mcxa/Cargo.toml b/examples/mcxa/Cargo.toml index 4d0459f41..19d8d8657 100644 --- a/examples/mcxa/Cargo.toml +++ b/examples/mcxa/Cargo.toml @@ -8,6 +8,7 @@ publish = false [dependencies] cortex-m = { version = "0.7", features = ["critical-section-single-core"] } cortex-m-rt = { version = "0.7", features = ["set-sp", "set-vtor"] } +crc = "3.4.0" critical-section = "1.2.0" defmt = "1.0" defmt-rtt = "1.0" diff --git a/examples/mcxa/src/bin/crc.rs b/examples/mcxa/src/bin/crc.rs index 12c423980..0125e625c 100644 --- a/examples/mcxa/src/bin/crc.rs +++ b/examples/mcxa/src/bin/crc.rs @@ -6,6 +6,28 @@ use hal::config::Config; use hal::crc::Crc; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; +const CCITT_FALSE: crc::Algorithm = crc::Algorithm { + width: 16, + poly: 0x1021, + init: 0xffff, + refin: false, + refout: false, + xorout: 0, + check: 0x29b1, + residue: 0x0000, +}; + +const POSIX: crc::Algorithm = crc::Algorithm { + width: 32, + poly: 0x04c1_1db7, + init: 0, + refin: false, + refout: false, + xorout: 0xffff_ffff, + check: 0x765e_7680, + residue: 0x0000, +}; + #[embassy_executor::main] async fn main(_spawner: Spawner) { let config = Config::default(); @@ -19,72 +41,113 @@ async fn main(_spawner: Spawner) { // CCITT False + let sw_crc = crc::Crc::::new(&CCITT_FALSE); + let mut digest = sw_crc.digest(); + digest.update(&buf_u8); + let sw_sum = digest.finalize(); + let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u8); - assert_eq!(sum, 0x9627); + crc.feed(&buf_u8); + let sum = crc.finalize(); + assert_eq!(sum, sw_sum); let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u16); + crc.feed(&buf_u16); + let sum = crc.finalize(); assert_eq!(sum, 0xa467); let mut crc = Crc::new_ccitt_false(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u32); + crc.feed(&buf_u32); + let sum = crc.finalize(); assert_eq!(sum, 0xe5c7); // Maxim + let sw_crc = crc::Crc::::new(&crc::CRC_16_MAXIM_DOW); + let mut digest = sw_crc.digest(); + digest.update(&buf_u8); + let sw_sum = digest.finalize(); + let mut crc = Crc::new_maxim(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u8); - assert_eq!(sum, 0x4ff7); + crc.feed(&buf_u8); + let sum = crc.finalize(); + assert_eq!(sum, sw_sum); let mut crc = Crc::new_maxim(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u16); + crc.feed(&buf_u16); + let sum = crc.finalize(); assert_eq!(sum, 0x2afe); let mut crc = Crc::new_maxim(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u32); + crc.feed(&buf_u32); + let sum = crc.finalize(); assert_eq!(sum, 0x17d7); // Kermit + let sw_crc = crc::Crc::::new(&crc::CRC_16_KERMIT); + let mut digest = sw_crc.digest(); + digest.update(&buf_u8); + let sw_sum = digest.finalize(); + let mut crc = Crc::new_kermit(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u8); - assert_eq!(sum, 0xccd2); + crc.feed(&buf_u8); + let sum = crc.finalize(); + assert_eq!(sum, sw_sum); let mut crc = Crc::new_kermit(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u16); + crc.feed(&buf_u16); + let sum = crc.finalize(); assert_eq!(sum, 0x66eb); let mut crc = Crc::new_kermit(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u32); + crc.feed(&buf_u32); + let sum = crc.finalize(); assert_eq!(sum, 0x75ea); // ISO HDLC + let sw_crc = crc::Crc::::new(&crc::CRC_32_ISO_HDLC); + let mut digest = sw_crc.digest(); + digest.update(&buf_u8); + let sw_sum = digest.finalize(); + let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u8); - assert_eq!(sum, 0x24c2_316d); + crc.feed(&buf_u8); + let sum = crc.finalize(); + assert_eq!(sum, sw_sum); let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u16); + crc.feed(&buf_u16); + let sum = crc.finalize(); assert_eq!(sum, 0x8a61_4178); let mut crc = Crc::new_iso_hdlc(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u32); + crc.feed(&buf_u32); + let sum = crc.finalize(); assert_eq!(sum, 0xfab5_d04e); // POSIX + let sw_crc = crc::Crc::::new(&POSIX); + let mut digest = sw_crc.digest(); + digest.update(&buf_u8); + let sw_sum = digest.finalize(); + let mut crc = Crc::new_posix(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u8); - assert_eq!(sum, 0xba8d_7868); + crc.feed(&buf_u8); + let sum = crc.finalize(); + + assert_eq!(sum, sw_sum); let mut crc = Crc::new_posix(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u16); + crc.feed(&buf_u16); + let sum = crc.finalize(); assert_eq!(sum, 0x6d76_4f58); let mut crc = Crc::new_posix(p.CRC0.reborrow()); - let sum = crc.feed(&buf_u32); + crc.feed(&buf_u32); + let sum = crc.finalize(); assert_eq!(sum, 0x2a5b_cb90); defmt::info!("CRC successful"); -- cgit From d9193fb77730110d37e17271b1836273594b5706 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 8 Dec 2025 09:38:57 -0800 Subject: Fix more review comments --- embassy-mcxa/src/crc.rs | 68 ++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/embassy-mcxa/src/crc.rs b/embassy-mcxa/src/crc.rs index 8f3b6b5de..753a59122 100644 --- a/embassy-mcxa/src/crc.rs +++ b/embassy-mcxa/src/crc.rs @@ -25,8 +25,7 @@ impl<'d, M: Mode> Crc<'d, M> { } } - // Configure the underlying peripheral. `f` is expected to set the - // operating mode to either 16- or 32-bits. + // Configure the underlying peripheral according to the reference manual. fn configure(config: Config, width: Tcrc) { Self::regs().ctrl().modify(|_, w| { w.fxor() @@ -350,39 +349,40 @@ macro_rules! impl_word { }; } -impl_word!( - u8, - 8, - |regs: &'static crate::pac::crc0::RegisterBlock, word| { - regs.data8().write(|w| unsafe { w.bits(word) }); - }, - |regs: &'static crate::pac::crc0::RegisterBlock| { regs.data8().read().bits() } -); -impl_word!( - u16, - 16, - |regs: &'static crate::pac::crc0::RegisterBlock, word| { - regs.data16().write(|w| unsafe { w.bits(word) }); - }, - |regs: &'static crate::pac::crc0::RegisterBlock| { - let ctrl = regs.ctrl().read(); - - // if transposition is enabled, result sits in the upper 16 bits - if ctrl.totr().is_byts_trnps() || ctrl.totr().is_byts_bts_trnps() { - (regs.data32().read().bits() >> 16) as u16 - } else { - regs.data16().read().bits() - } +impl_word!(u8, 8, write_u8, read_u8); +impl_word!(u16, 16, write_u16, read_u16); +impl_word!(u32, 32, write_u32, read_u32); + +fn write_u8(regs: &'static crate::pac::crc0::RegisterBlock, word: u8) { + regs.data8().write(|w| unsafe { w.bits(word) }); +} + +fn read_u8(regs: &'static crate::pac::crc0::RegisterBlock) -> u8 { + regs.data8().read().bits() +} + +fn write_u16(regs: &'static crate::pac::crc0::RegisterBlock, word: u16) { + regs.data16().write(|w| unsafe { w.bits(word) }); +} + +fn read_u16(regs: &'static crate::pac::crc0::RegisterBlock) -> u16 { + let ctrl = regs.ctrl().read(); + + // if transposition is enabled, result sits in the upper 16 bits + if ctrl.totr().is_byts_trnps() || ctrl.totr().is_byts_bts_trnps() { + (regs.data32().read().bits() >> 16) as u16 + } else { + regs.data16().read().bits() } -); -impl_word!( - u32, - 32, - |regs: &'static crate::pac::crc0::RegisterBlock, word| { - regs.data32().write(|w| unsafe { w.bits(word) }); - }, - |regs: &'static crate::pac::crc0::RegisterBlock| { regs.data32().read().bits() } -); +} + +fn write_u32(regs: &'static crate::pac::crc0::RegisterBlock, word: u32) { + regs.data32().write(|w| unsafe { w.bits(word) }); +} + +fn read_u32(regs: &'static crate::pac::crc0::RegisterBlock) -> u32 { + regs.data32().read().bits() +} /// CRC configuration. #[derive(Copy, Clone, Debug)] -- cgit