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