Skip to main content

sam4l/
crccu.rs

1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Implementation of the SAM4L CRCCU.
6//!
7//! See datasheet section "41. Cyclic Redundancy Check Calculation Unit (CRCCU)".
8//!
9//! The SAM4L can compute CRCs using three different polynomials:
10//!
11//!   * `0x04C11DB7` (as used in "CRC-32"; Atmel calls this "CCIT8023")
12//!   * `0x1EDC6F41` (as used in "CRC-32C"; Atmel calls this "CASTAGNOLI")
13//!   * `0x1021`     (as used in "CRC-16-CCITT"; Atmel calls this "CCIT16")
14//!
15//! (The integers above give each polynomial from most-significant to least-significant
16//! bit, except that the most significant bit is omitted because it is always 1.)
17//!
18//! In all cases, the unit consumes each input byte from LSB to MSB.
19//!
20//! Note that the chip's behavior differs from some "standard" CRC algorithms,
21//! which may do some of these things:
22//!
23//!   * Consume input from MSB to LSB (CRC-16-CCITT?)
24//!   * Bit-reverse and then bit-invert the output (CRC-32)
25//!
26//! # Notes
27//!
28//! This [calculator](http://www.zorc.breitbandkatze.de/crc.html) may be used to
29//! generate CRC values.  To match the output of the SAM4L, the parameters must
30//! be set as follows:
31//!
32//!   * Final XOR value: 0  (equivalent to no final XOR)
33//!   * reverse data bytes: yes
34//!   * reverse CRC result before Final XOR: no
35//!
36//! For one example, the SAM4L calculates 0x1541 for "ABCDEFG" when using
37//! polynomial 0x1021.
38
39// Infelicities:
40//
41// - As much as 512 bytes of RAM is wasted to allow runtime alignment of the
42//   CRCCU Descriptor.  Reliable knowledge of kernel alignment might allow this
43//   to be done statically.
44//
45// - CRC performance would be improved by using transfer-widths larger than Byte,
46//   but it is not clear in what cases that is possible.
47
48// TODO:
49//
50// - Chain computations to permit arbitrary-size computations, or at least
51//   publish the max buffer size the unit can handle.
52//
53// - Support continuous-mode CRC
54
55use crate::pm::{Clock, HSBClock, PBBClock, disable_clock, enable_clock};
56use core::cell::Cell;
57use kernel::ErrorCode;
58use kernel::deferred_call::{DeferredCall, DeferredCallClient};
59use kernel::hil::crc::{Client, Crc, CrcAlgorithm, CrcOutput};
60use kernel::utilities::StaticRef;
61use kernel::utilities::cells::OptionalCell;
62use kernel::utilities::leasable_buffer::SubSliceMut;
63use kernel::utilities::registers::interfaces::{Readable, Writeable};
64use kernel::utilities::registers::{
65    FieldValue, InMemoryRegister, ReadOnly, ReadWrite, WriteOnly, register_bitfields,
66    register_structs,
67};
68
69// Base address of CRCCU registers.  See "7.1 Product Mapping"
70pub const BASE_ADDRESS: StaticRef<CrccuRegisters> =
71    unsafe { StaticRef::new(0x400A4000 as *const CrccuRegisters) };
72
73register_structs! {
74    pub CrccuRegisters {
75        (0x000 => dscr: ReadWrite<u32, DescriptorBaseAddress::Register>),
76        (0x004 => _reserved0: u32),
77        (0x008 => dmaen: WriteOnly<u32, DmaEnable::Register>),
78        (0x00C => dmadis: WriteOnly<u32, DmaDisable::Register>),
79        (0x010 => dmasr: ReadOnly<u32, DmaStatus::Register>),
80        (0x014 => dmaier: WriteOnly<u32, DmaInterrupt::Register>),
81        (0x018 => dmaidr: WriteOnly<u32, DmaInterrupt::Register>),
82        (0x01C => dmaimr: ReadOnly<u32, DmaInterrupt::Register>),
83        (0x020 => dmaisr: ReadOnly<u32, DmaInterrupt::Register>),
84        (0x024 => _reserved1: [u32; 4]),
85        (0x034 => cr: WriteOnly<u32, Control::Register>),
86        (0x038 => mr: ReadWrite<u32, Mode::Register>),
87        (0x03C => sr: ReadOnly<u32, Status::Register>),
88        (0x040 => ier: WriteOnly<u32, Interrupt::Register>),
89        (0x044 => idr: WriteOnly<u32, Interrupt::Register>),
90        (0x048 => imr: ReadOnly<u32, Interrupt::Register>),
91        (0x04C => isr: ReadOnly<u32, Interrupt::Register>),
92        (0x050 => @END),
93    }
94}
95
96register_bitfields![u32,
97    DescriptorBaseAddress [
98        /// Description Base Address
99        DSCR OFFSET(9) NUMBITS(23) []
100    ],
101
102    DmaEnable [
103        /// DMA Enable
104        DMAEN 0
105    ],
106
107    DmaDisable [
108        /// DMA Disable
109        DMADIS 0
110    ],
111
112    DmaStatus [
113        /// DMA Channel Status
114        DMASR 0
115    ],
116
117    DmaInterrupt [
118        /// DMA Interrupt
119        DMA 0
120    ],
121
122    Control [
123        /// Reset CRC Computation
124        RESET 0
125    ],
126
127    Mode [
128        /// Bandwidth Divider
129        DIVIDER OFFSET(4) NUMBITS(4) [],
130        /// Polynomial Type
131        PTYPE OFFSET(2) NUMBITS(2) [
132            Ccit8023 = 0,
133            Castagnoli = 1,
134            Ccit16 = 2
135        ],
136        /// CRC Compare
137        COMPARE OFFSET(1) NUMBITS(1) [],
138        /// CRC Computation Enable
139        ENABLE OFFSET(0) NUMBITS(1) [
140            Enabled = 1,
141            Disabled = 0
142        ]
143    ],
144
145    Status [
146        /// Cyclic Redundancy Check Value
147        CRC OFFSET(0) NUMBITS(32)
148    ],
149
150    Interrupt [
151        /// CRC Error Interrupt Status
152        ERR 0
153    ]
154];
155
156#[repr(C)]
157#[repr(align(512))]
158struct Descriptor {
159    /// Transfer Address Register (RW): Address of memory block to compute
160    addr: InMemoryRegister<u32>,
161    /// Transfer Control Register (RW): IEN, TRWIDTH, BTSIZE
162    ctrl: InMemoryRegister<u32>,
163    _res: [u32; 2],
164    /// Transfer Reference Register (RW): Reference CRC (for compare mode)
165    crc: InMemoryRegister<u32>,
166}
167
168impl Descriptor {
169    pub fn new() -> Descriptor {
170        Descriptor {
171            addr: InMemoryRegister::new(0),
172            ctrl: InMemoryRegister::new(TCR::default().0),
173            _res: [0; 2],
174            crc: InMemoryRegister::new(0),
175        }
176    }
177}
178
179// Transfer Control Register (see Section 41.6.18)
180#[derive(Copy, Clone)]
181#[repr(C)]
182struct TCR(u32);
183
184impl TCR {
185    const fn new(enable_interrupt: bool, trwidth: TrWidth, btsize: u16) -> Self {
186        TCR((!enable_interrupt as u32) << 27 | (trwidth as u32) << 24 | (btsize as u32))
187    }
188
189    const fn default() -> Self {
190        Self::new(false, TrWidth::Byte, 0)
191    }
192
193    fn interrupt_enabled(self) -> bool {
194        (self.0 & (1 << 27)) == 0
195    }
196
197    #[allow(dead_code)]
198    fn get_btsize(self) -> u16 {
199        (self.0 & 0xffff) as u16
200    }
201}
202
203fn poly_for_alg(alg: CrcAlgorithm) -> FieldValue<u32, Mode::Register> {
204    match alg {
205        CrcAlgorithm::Crc32 => Mode::PTYPE::Ccit8023,
206        CrcAlgorithm::Crc32C => Mode::PTYPE::Castagnoli,
207        CrcAlgorithm::Crc16CCITT => Mode::PTYPE::Ccit16,
208        // CrcAlg::Sam4L32 => Mode::PTYPE::Ccit8023,
209        // CrcAlg::Sam4L32C => Mode::PTYPE::Castagnoli,
210    }
211}
212
213fn post_process(result: u32, alg: CrcAlgorithm) -> CrcOutput {
214    match alg {
215        CrcAlgorithm::Crc32 => CrcOutput::Crc32(reverse_and_invert(result)),
216        CrcAlgorithm::Crc32C => CrcOutput::Crc32C(reverse_and_invert(result)),
217        CrcAlgorithm::Crc16CCITT => CrcOutput::Crc16CCITT(result as u16),
218        // CrcAlg::Sam4L32 => result,
219        // CrcAlg::Sam4L32C => result,
220    }
221}
222
223fn reverse_and_invert(n: u32) -> u32 {
224    let mut out: u32 = 0;
225
226    // Bit-reverse
227    for j in 0..32 {
228        let i = 31 - j;
229        out |= ((n & (1 << i)) >> i) << j;
230    }
231
232    // Bit-invert
233    out ^ 0xffffffff
234}
235
236/// Transfer width for DMA
237#[allow(dead_code)]
238enum TrWidth {
239    Byte,
240    HalfWord,
241    Word,
242}
243
244#[derive(Copy, Clone, PartialEq)]
245enum State {
246    Invalid,
247    Initialized,
248    Enabled,
249}
250
251/// State for managing the CRCCU
252pub struct Crccu<'a> {
253    registers: StaticRef<CrccuRegisters>,
254    client: OptionalCell<&'a dyn Client>,
255    state: Cell<State>,
256    algorithm: OptionalCell<CrcAlgorithm>,
257
258    // This store the full leasable-buffer boundaries for
259    // reconstruction when a call to [`Crc::input`] finishes
260    current_full_buffer: Cell<(*mut u8, usize)>,
261
262    // Marker whether a "computation" (pending deferred call) is in
263    // progress
264    compute_requested: Cell<bool>,
265
266    // CRC DMA descriptor
267    //
268    // Must be aligned to a 512-byte boundary, which is guaranteed by
269    // the struct definition.
270    descriptor: Descriptor,
271
272    deferred_call: DeferredCall,
273}
274
275impl Crccu<'_> {
276    pub fn new(base_addr: StaticRef<CrccuRegisters>) -> Self {
277        Self {
278            registers: base_addr,
279            client: OptionalCell::empty(),
280            state: Cell::new(State::Invalid),
281            algorithm: OptionalCell::empty(),
282            current_full_buffer: Cell::new((core::ptr::null_mut::<u8>(), 0)),
283            compute_requested: Cell::new(false),
284            descriptor: Descriptor::new(),
285            deferred_call: DeferredCall::new(),
286        }
287    }
288
289    fn init(&self) {
290        if self.state.get() == State::Invalid {
291            self.descriptor.addr.set(0);
292            self.descriptor.ctrl.set(TCR::default().0);
293            self.descriptor.crc.set(0);
294            self.state.set(State::Initialized);
295        }
296    }
297
298    /// Enable the CRCCU's clocks and interrupt
299    fn enable(&self) {
300        if self.state.get() != State::Enabled {
301            // see "10.7.4 Clock Mask"
302            enable_clock(Clock::HSB(HSBClock::CRCCU));
303            enable_clock(Clock::PBB(PBBClock::CRCCU));
304            self.state.set(State::Enabled);
305        }
306    }
307
308    /// Disable the CRCCU's clocks and interrupt
309    fn disable(&self) {
310        if self.state.get() == State::Enabled {
311            disable_clock(Clock::PBB(PBBClock::CRCCU));
312            disable_clock(Clock::HSB(HSBClock::CRCCU));
313            self.state.set(State::Initialized);
314        }
315    }
316
317    /// Handle an interrupt from the CRCCU
318    pub fn handle_interrupt(&self) {
319        if self.registers.isr.is_set(Interrupt::ERR) {
320            // A CRC error has occurred
321        }
322
323        if self.registers.dmaisr.is_set(DmaInterrupt::DMA) {
324            // A DMA transfer has completed
325
326            if TCR(self.descriptor.ctrl.get()).interrupt_enabled() {
327                // We have the current temporary result ready, but
328                // wait for the client to finish the CRC computation
329                // by calling [`Crc::compute`]
330
331                // self.client.map(|client| {
332                //     let result = post_process(self.registers.sr.read(Status::CRC), self.alg.get());
333                //     client.receive_result(result);
334                // });
335
336                // Disable the unit
337                self.registers.mr.write(Mode::ENABLE::Disabled);
338
339                // Recover the window into the SubSliceMut
340                let window_addr = self.descriptor.addr.get();
341                let window_len = TCR(self.descriptor.ctrl.get()).get_btsize() as usize;
342
343                // Reset CTRL.IEN (for our own statekeeping)
344                self.descriptor.addr.set(0);
345                self.descriptor.ctrl.set(TCR::default().0);
346                self.descriptor.crc.set(0);
347
348                // Disable DMA interrupt
349                self.registers.dmaidr.write(DmaInterrupt::DMA::SET);
350
351                // Disable DMA channel
352                self.registers.dmadis.write(DmaDisable::DMADIS::SET);
353
354                // Reconstruct the leasable buffer from stored
355                // information and slice into the proper window
356                let (full_buffer_addr, full_buffer_len) = self.current_full_buffer.get();
357                let mut data = SubSliceMut::<'static, u8>::new(unsafe {
358                    core::slice::from_raw_parts_mut(full_buffer_addr, full_buffer_len)
359                });
360
361                // Must be strictly positive or zero
362                let start_offset = (window_addr as usize) - (full_buffer_addr as usize);
363                data.slice(start_offset..(start_offset + window_len));
364
365                // Pass the properly sliced and reconstructed buffer
366                // back to the client
367                self.client.map(move |client| {
368                    client.input_done(Ok(()), data);
369                });
370            }
371        }
372    }
373}
374impl DeferredCallClient for Crccu<'_> {
375    fn handle_deferred_call(&self) {
376        // A deferred call is currently only issued on a call to
377        // compute, in which case we need to provide the CRC to the
378        // client
379        let result = post_process(
380            self.registers.sr.read(Status::CRC),
381            self.algorithm.unwrap_or_panic(), // Unwrap fail = crccu deferred call: no algorithm
382        );
383
384        // Reset the internal CRC state such that the next call to
385        // input will start a new CRC
386        self.registers.cr.write(Control::RESET::SET);
387        self.descriptor.ctrl.set(TCR::default().0);
388        self.compute_requested.set(false);
389
390        self.client.map(|client| {
391            client.crc_done(Ok(result));
392        });
393    }
394
395    fn register(&'static self) {
396        self.deferred_call.register(self);
397    }
398}
399
400// Implement the generic CRC interface with the CRCCU
401impl<'a> Crc<'a> for Crccu<'a> {
402    /// Set a client to receive results from the CRCCU
403    fn set_client(&self, client: &'a dyn Client) {
404        self.client.set(client);
405    }
406
407    fn algorithm_supported(&self, algorithm: CrcAlgorithm) -> bool {
408        // Deliberately has an exhaustive list here to avoid
409        // advertising support for added variants to CrcAlgorithm
410        match algorithm {
411            CrcAlgorithm::Crc32 => true,
412            CrcAlgorithm::Crc32C => true,
413            CrcAlgorithm::Crc16CCITT => true,
414        }
415    }
416
417    fn set_algorithm(&self, algorithm: CrcAlgorithm) -> Result<(), ErrorCode> {
418        // If there currently is a DMA operation in progress, refuse
419        // to set the algorithm.
420        if TCR(self.descriptor.ctrl.get()).interrupt_enabled() || self.compute_requested.get() {
421            // A computation is already in progress
422            return Err(ErrorCode::BUSY);
423        }
424
425        self.init();
426        // Clear the descriptor contents
427        self.descriptor.addr.set(0);
428        self.descriptor.ctrl.set(TCR::default().0);
429        self.descriptor.crc.set(0);
430        self.algorithm.set(algorithm);
431
432        // Reset intermediate CRC value
433        self.registers.cr.write(Control::RESET::SET);
434
435        Ok(())
436    }
437
438    fn input(
439        &self,
440        mut data: SubSliceMut<'static, u8>,
441    ) -> Result<(), (ErrorCode, SubSliceMut<'static, u8>)> {
442        let algorithm = if let Some(algorithm) = self.algorithm.get() {
443            algorithm
444        } else {
445            return Err((ErrorCode::RESERVE, data));
446        };
447
448        if TCR(self.descriptor.ctrl.get()).interrupt_enabled() || self.compute_requested.get() {
449            // A computation is already in progress
450            return Err((ErrorCode::BUSY, data));
451        }
452
453        // Need to initialize after checking business, because init will
454        // clear out interrupt state.
455        self.init();
456
457        // Initialize the descriptor, since it is used to track business
458        let len = data.len() as u16;
459        let ctrl = TCR::new(true, TrWidth::Byte, len);
460
461        // Make sure we don't try to process more data than the CRC
462        // DMA operation supports.
463        if data.len() > u16::MAX as usize {
464            // Restore the full slice, calculate the current
465            // window's start offset.
466            let window_ptr = data.as_ptr();
467            data.reset();
468            let start_ptr = data.as_ptr();
469            // Must be strictly positive or zero
470            let start_offset = unsafe { window_ptr.offset_from(start_ptr) } as usize;
471
472            // Reslice the buffer such that it start at the same
473            // position as the old window, but fits the size
474            // constraints
475            data.slice(start_offset..=(start_offset + u16::MAX as usize));
476        }
477
478        self.enable();
479
480        // Enable DMA interrupt
481        self.registers.dmaier.write(DmaInterrupt::DMA::SET);
482
483        // Enable error interrupt
484        self.registers.ier.write(Interrupt::ERR::SET);
485
486        // Configure the data transfer descriptor
487        //
488        // The data length is guaranteed to be <= u16::MAX by the
489        // above SubSliceMut resizing mechanism
490        self.descriptor.addr.set(data.as_ptr() as u32);
491        self.descriptor.ctrl.set(ctrl.0);
492        self.descriptor.crc.set(0); // this is the CRC compare field, not used
493
494        // Prior to starting the DMA operation, drop the
495        // SubSlice slice. Otherwise we violate Rust's mutable
496        // aliasing rules.
497        let full_slice = data.take();
498        let full_slice_ptr_len = (full_slice.as_mut_ptr(), full_slice.len());
499        self.current_full_buffer.set(full_slice_ptr_len);
500
501        // Ensure the &'static mut slice reference goes out of scope
502        //
503        // We can't use mem::drop on a reference here, clippy will
504        // complain, even though it would be effective at making this
505        // 'static mut buffer inaccessible. For now, just make sure to
506        // not reference it below.
507        //
508        // TODO: this needs a proper type and is a broader issue. See
509        // tock/tock#2637 for more information.
510        //
511        // core::mem::drop(full_slice);
512
513        // Set the descriptor memory address accordingly
514        self.registers
515            .dscr
516            .set(core::ptr::addr_of!(self.descriptor) as u32);
517
518        // Configure the unit to compute a checksum
519        self.registers.mr.write(
520            Mode::DIVIDER.val(0)
521                + poly_for_alg(algorithm)
522                + Mode::COMPARE::CLEAR
523                + Mode::ENABLE::Enabled,
524        );
525
526        // Enable DMA channel
527        self.registers.dmaen.write(DmaEnable::DMAEN::SET);
528
529        Ok(())
530    }
531
532    fn compute(&self) -> Result<(), ErrorCode> {
533        // In this hardware implementation, we compute the CRC in
534        // parallel to the DMA operations. Thus this can simply
535        // request the CR and reset the state in a deferred call.
536
537        if TCR(self.descriptor.ctrl.get()).interrupt_enabled() || self.compute_requested.get() {
538            // A computation is already in progress
539            return Err(ErrorCode::BUSY);
540        }
541
542        // Mark the device as busy
543        self.compute_requested.set(true);
544
545        // Request a deferred call such that we can provide the result
546        // back to the client
547        self.deferred_call.set();
548
549        Ok(())
550    }
551
552    fn disable(&self) {
553        Crccu::disable(self);
554    }
555}