1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//! Define a trait to track the transaction [`Length`], which represents the
//! [`Config`] [`Size`] for SAMx5x chips
//!
//! This module defines the `Size` trait for SAMx5x chips. These chips always
//! operate in 32-bit extension mode and use the hardware `LENGTH` counter to
//! track the length of each transaction, in bytes. See the [`Length`]
//! documentation for more details.
//!
//! [`Config`]: super::Config
//! [`Size`]: super::Size

use seq_macro::seq;
use typenum::{Unsigned, U0, U1, U2, U3, U4};

use crate::typelevel::Sealed;

//=============================================================================
// Transaction length
//=============================================================================

/// Type-level enum representing the SPI transaction length, in bytes
///
/// This trait acts as both a [type-level enum], forming a type class for
/// transaction lengths, as well as a [type-level function] mapping the `Length`
/// to the corresponding [`Word`] size.
///
/// The SPI transaction length is represented in the type domain using
/// [`Unsigned`] types from the [`typenum`] crate. The length can be set
/// statically, using a length from `U1` to `U255`, or it can be set
/// dynamically, using the [`DynLength`] marker type. All valid `Length` types
/// are re-exported in this module.
///
/// The SPI transaction length affects the word size for the embedded HAL
/// traits, as well as other aspects of the SPI API. Transaction lengths of 1-4
/// only require a single read/write of the `DATA` register, so they have an
/// [`AtomicSize`] behave differently than longer transaction lengths.
///
/// [type-level enum]: crate::typelevel#type-level-enums
/// [type-level function]: crate::typelevel#type-level-functions
/// [`OpMode`]: super::OpMode
/// [`AtomicSize`]: super::AtomicSize
pub trait Length: Sealed + Unsigned + 'static {
    /// Word size for the transaction length
    ///
    /// For lengths 1-4, this type is `u8`, `u16` or `u32`. For longer
    /// transactions, this type is `[u8, Self::USIZE]`.
    type Word: 'static;
}

/// Type alias to recover the [`Word`](Length::Word) type from an
/// implementation of [`Length`]
pub type Word<L> = <L as Length>::Word;

/// Marker type for a run-time dynamic [`Length`]
pub type DynLength = U0;

impl Length for DynLength {
    type Word = ();
}

/// Marker trait for statically known transaction [`Length`]s
pub trait StaticLength: Length {}

impl StaticLength for U1 {}
impl Length for U1 {
    type Word = u8;
}

impl StaticLength for U2 {}
impl Length for U2 {
    type Word = u16;
}

impl StaticLength for U3 {}
impl Length for U3 {
    type Word = u32;
}

impl StaticLength for U4 {}
impl Length for U4 {
    type Word = u32;
}

/// Marker trait for transaction [`Length`]s greater than four
pub trait GreaterThan4: Length {}

seq!(N in 5..=255 {
    impl StaticLength for typenum::U~N {}
    impl GreaterThan4 for typenum::U~N {}
    impl Length for typenum::U~N {
        type Word = [u8; typenum::U~N::USIZE];
    }
});