Skip to content
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
14 changes: 14 additions & 0 deletions boards/nucleo_u545re_q/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct NucleoU545RE {
stm32u545::tim::Tim2<'static>,
>,
>,
crc: &'static capsules_extra::crc::CrcDriver<'static, stm32u545::crc::CRC<'static>>,
}

impl SyscallDriverLookup for NucleoU545RE {
Expand All @@ -64,6 +65,7 @@ impl SyscallDriverLookup for NucleoU545RE {
capsules_core::led::DRIVER_NUM => f(Some(self.led)),
capsules_core::button::DRIVER_NUM => f(Some(self.button)),
capsules_core::alarm::DRIVER_NUM => f(Some(self.alarm)),
capsules_extra::crc::DRIVER_NUM => f(Some(self.crc)),
_ => f(None),
}
}
Expand Down Expand Up @@ -233,6 +235,17 @@ unsafe fn start() -> (
)
.finalize(components::button_component_static!(stm32u545::gpio::Pin));

kernel::deferred_call::DeferredCallClient::register(&periphs.crc);

let crc = components::crc::CrcComponent::new(
board_kernel,
capsules_extra::crc::DRIVER_NUM,
&periphs.crc,
)
.finalize(components::crc_component_static!(
stm32u545::crc::CRC<'static>
));

// Platform and Interrupts
let platform = static_init!(
NucleoU545RE,
Expand All @@ -244,6 +257,7 @@ unsafe fn start() -> (
led,
button,
alarm,
crc,
}
);

Expand Down
2 changes: 1 addition & 1 deletion chips/stm32u545/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#![no_std]

pub use stm32u5xx::{chip, dma, exti, gpio, rcc, tim, usart};
pub use stm32u5xx::{chip, crc, dma, exti, gpio, rcc, tim, usart};

use cortexm33::{CortexM33, CortexMVariant};

Expand Down
4 changes: 4 additions & 0 deletions chips/stm32u5xx/src/chip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Copyright Tock Contributors 2024.
// Copyright OxidOS Automotive 2026.

use crate::crc::{self, CRC_BASE};
use crate::dma::{ChannelId, Dma};
use crate::exti;
use crate::gpio;
Expand Down Expand Up @@ -34,6 +35,7 @@ pub struct Stm32u5xxDefaultPeripherals<'a> {
pub dma1: &'a Dma,
pub gpio_a: gpio::Port<'a>,
pub gpio_c: gpio::Port<'a>,
pub crc: crc::CRC<'a>,
}

fn enable_tim2_clock() {
Expand All @@ -51,6 +53,7 @@ impl<'a> Stm32u5xxDefaultPeripherals<'a> {
dma1,
gpio_a: gpio::Port::new(gpio::GPIO_A_BASE, exti, gpio::GpioPort::PortA),
gpio_c: gpio::Port::new(gpio::GPIO_C_BASE, exti, gpio::GpioPort::PortC),
crc: crc::CRC::new(CRC_BASE),
}
}

Expand All @@ -62,6 +65,7 @@ impl<'a> Stm32u5xxDefaultPeripherals<'a> {
self.rcc.enable_usart1();
self.rcc.enable_syscfg();
self.rcc.set_usart1_source_pclk();
self.rcc.enable_crc();
// Link DMA to USART1
let usart1_channel_tx = self.dma1.request_channel();
let usart1_channel_rx = self.dma1.request_channel();
Expand Down
301 changes: 301 additions & 0 deletions chips/stm32u5xx/src/crc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2026.

use core::cell::Cell;
use kernel::deferred_call::{DeferredCall, DeferredCallClient};
use kernel::hil::crc::{Client, Crc, CrcAlgorithm, CrcOutput};
use kernel::utilities::cells::OptionalCell;
use kernel::utilities::leasable_buffer::SubSliceMut;
use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite, WriteOnly};
use kernel::utilities::StaticRef;
use kernel::ErrorCode;

register_structs! {
pub CrcRegisters {
/// Data register
(0x00 => pub dr: ReadWrite<u32, DR::Register>),
/// Independent data register
(0x04 => pub idr: ReadWrite<u32, IDR::Register>),
/// Control register
(0x08 => pub cr: ReadWrite<u32, CR::Register>),
/// Padding
(0x0C => reserved),
/// Initial value
(0x10 => pub init: ReadWrite<u32, INIT::Register>),
/// Polynomial
(0x14 => pub pol: ReadWrite<u32, POL::Register>),
(0x18 => @END),
}
}

/// Base address for CRC in Secure Alias mode
pub const CRC_BASE: StaticRef<CrcRegisters> =
unsafe { StaticRef::new(0x50023000 as *const CrcRegisters) };

/// Byte-width alias into the CRC data register.
/// The STM32 CRC hardware supports sub-word writes.
/// Byte-wide writes are required when input length isn't a multiple of 4,
/// as writing full 32-bit words would pad with extra bytes and corrupt the
/// resulting checksum.
const CRC_DR_BYTE: StaticRef<WriteOnly<u8>> =
unsafe { StaticRef::new(0x50023000 as *const WriteOnly<u8>) };

register_bitfields![u32,
pub DR [
/// Data register
DR OFFSET(0) NUMBITS(32) [],
],
pub IDR [
/// Temporary 4 byte storage
IDR OFFSET(0) NUMBITS(32) []
],
pub CR [
/// Reset bit, used for initialising and resetting
RESET OFFSET(0) NUMBITS(1) [],
/// Polynomial size
PSIZE OFFSET(3) NUMBITS(2) [],
/// Reverse input data
REVIN OFFSET(5) NUMBITS(2) [],
/// Reverse output data
REVOUT OFFSET(7) NUMBITS(1) []
],
pub INIT [
/// Initial CRC value
INIT OFFSET(0) NUMBITS(32) []
],
pub POL [
/// Polynomial coefficients to be used for CRC computation
POL OFFSET(0) NUMBITS(32) []
],
];

// CRC state checkers, used in all functions
#[derive(Copy, Clone, PartialEq)]
enum State {
Idle,
Processing,
}

/// Checker values for verifying if the algorithm has been set
#[derive(Copy, Clone, PartialEq)]
enum AlgSet {
Uninitialised,
Initialised,
}

/// Checker values for the DeferredCallClient
#[derive(Copy, Clone, PartialEq)]
enum Request {
Input,
Compute,
None,
}

pub struct CRC<'a> {
registers: StaticRef<CrcRegisters>,
client: OptionalCell<&'a dyn Client>,
deferred_call: DeferredCall,
state: Cell<State>,
alg_state: Cell<AlgSet>,
buffer: OptionalCell<SubSliceMut<'static, u8>>,
request: Cell<Request>,
current_algorithm: OptionalCell<CrcAlgorithm>,
}

impl CRC<'_> {
pub fn new(base_addr: StaticRef<CrcRegisters>) -> Self {
Self {
registers: base_addr,
client: OptionalCell::empty(),
deferred_call: DeferredCall::new(),
state: Cell::new(State::Idle),
alg_state: Cell::new(AlgSet::Uninitialised),
buffer: OptionalCell::empty(),
request: Cell::new(Request::None),
current_algorithm: OptionalCell::empty(),
}
}
}

impl<'a> Crc<'a> for CRC<'a> {
fn set_client(&self, client: &'a dyn Client) {
self.client.set(client);
}

fn algorithm_supported(&self, algorithm: CrcAlgorithm) -> bool {
matches!(
algorithm,
CrcAlgorithm::Crc32 | CrcAlgorithm::Crc32C | CrcAlgorithm::Crc16CCITT
)
}

fn set_algorithm(&self, algorithm: CrcAlgorithm) -> Result<(), ErrorCode> {
if !self.algorithm_supported(algorithm) {
return Err(ErrorCode::NOSUPPORT);
}

if self.state.get() == State::Processing {
return Err(ErrorCode::BUSY);
}

// The STM32U5xx features programable parameters, in order to accomodate for
// multiple CRC algorithms, enforceable by the user

// INIT configurees the initial value of the CRC

// PSIZE controls the size of the polynomial.
// 00: 32 byt polynomial
// 10: 8 bit polynomial
// 01: 16 bit polynomial
// 00: 32 bit polynomial

// REVIN controls the reversal of the bit order of the input data.
// 00: Bit order not affected
// 01: Bit reversal done by byte
// 10: Bit reversal done by half-word
// 11: Bit reversal done by word
// as per the CRC32 Ethernet algorithm, this one was set to byte by reversal

// REVOUT controls the reversal of the bit order of the input data.
// This bit controls the reversal of the bit order of the output data.
// 0: Bit order not affected
// 1: Bit-reversed output format

// POL is used to write the coefficients of the polynomial to be used.

match algorithm {
CrcAlgorithm::Crc32 => {
self.registers.init.write(INIT::INIT.val(0xFFFFFFFF));
self.registers.cr.modify(CR::PSIZE.val(0b00));
self.registers.cr.modify(CR::REVIN.val(0b01));
self.registers.cr.modify(CR::REVOUT.val(0b01));
self.registers.pol.write(POL::POL.val(0x4C11DB7));
}

CrcAlgorithm::Crc32C => {
self.registers.init.write(INIT::INIT.val(0xFFFFFFFF));
self.registers.cr.modify(CR::PSIZE.val(0b00));
self.registers.cr.modify(CR::REVIN.val(0b01));
self.registers.cr.modify(CR::REVOUT.val(0b1));
self.registers.pol.write(POL::POL.val(0x1EDC6F41));
}

CrcAlgorithm::Crc16CCITT => {
self.registers.init.write(INIT::INIT.val(0x0000FFFF));
self.registers.cr.modify(CR::PSIZE.val(0b01));
self.registers.cr.modify(CR::REVIN.val(0b01));
self.registers.cr.modify(CR::REVOUT.val(0b0));
self.registers.pol.write(POL::POL.val(0x1021));
}
}

// Initialising the CRC engine as per the manual, by setting the RESET Bit.
self.registers.cr.modify(CR::RESET::SET);
self.state.set(State::Idle);
self.alg_state.set(AlgSet::Initialised);
self.current_algorithm.set(algorithm);

Ok(())
}

fn input(
&self,
data: SubSliceMut<'static, u8>,
) -> Result<(), (ErrorCode, SubSliceMut<'static, u8>)> {
if self.alg_state.get() == AlgSet::Uninitialised {
return Err((ErrorCode::RESERVE, data));
}

if self.state.get() == State::Processing {
return Err((ErrorCode::BUSY, data));
}

self.state.set(State::Processing);

// The DR registers requires 8-bit writes when inputting data.
// Writing the whole 32 bits causes incorrect results, so we cannot use the regular register.
// We need to read it as 32 bits at the end, to retrieve the CRC result.
for &byte in data.as_slice().iter() {
CRC_DR_BYTE.set(byte);
}

// Shrink the buffer window accordingly, as to confirm that the data
// has been completely been completely processed.
let mut consumed_data = data;
let len = consumed_data.len();
consumed_data.slice(len..len);

self.buffer.set(consumed_data);
self.request.set(Request::Input);
self.deferred_call.set();

Ok(())
}

fn compute(&self) -> Result<(), ErrorCode> {
if self.alg_state.get() == AlgSet::Uninitialised {
return Err(ErrorCode::RESERVE);
}

if self.state.get() == State::Processing {
return Err(ErrorCode::BUSY);
}

self.state.set(State::Processing);
self.request.set(Request::Compute);
self.deferred_call.set();

Ok(())
}

fn disable(&self) {
// The STM's CRC has no bit for directly disabling it, we handle it by
// setting the computation state to idle and the algorithm setting as
// uninitialised.
self.state.set(State::Idle);
self.alg_state.set(AlgSet::Uninitialised);
}
}

impl DeferredCallClient for CRC<'_> {
fn handle_deferred_call(&self) {
let current_request = self.request.get();
self.request.set(Request::None);
self.state.set(State::Idle);

// Mapping the client as per the HIL
self.client.map(|client| match current_request {
Request::Input => {
if let Some(data) = self.buffer.take() {
client.input_done(Ok(()), data);
}
}

Request::Compute => {
let unprocessed_result = self.registers.dr.get();
//debug!("CRC: Request::Compute; client.crc_done...");
let result = match self.current_algorithm.get() {
// As the STM's CRC does not offer the option of a final XOR on the value, as some
// CRC algorithms do, we do it in software, when needed.
Some(CrcAlgorithm::Crc32) => CrcOutput::Crc32(unprocessed_result ^ 0xFFFFFFFF),
Some(CrcAlgorithm::Crc32C) => {
CrcOutput::Crc32C(unprocessed_result ^ 0xFFFFFFFF)
}
Some(CrcAlgorithm::Crc16CCITT) => {
CrcOutput::Crc16CCITT((unprocessed_result & 0xFFFF) as u16)
}
None => unreachable!("No algorithm has been set."),
};
client.crc_done(Ok(result));
}

Request::None => {}
});
}

fn register(&'static self) {
self.deferred_call.register(self);
}
}
1 change: 1 addition & 0 deletions chips/stm32u5xx/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![no_std]

pub mod chip;
pub mod crc;
pub mod dma;
pub mod exti;
pub mod gpio;
Expand Down
Loading
Loading