atsamd_hal/sercom/
pad.rs

1//! Type-level tools to configure SERCOM pads
2//!
3//! This module helps configure [`Pin`]s as SERCOM pads. It provides type-level
4//! tools to convert `Pin`s to the correct [`PinMode`] and to enforce type-level
5//! constraints at compile-time.
6//!
7//! # Overview
8//!
9//! A SERCOM pad is defined by two types, its corresponding [`Sercom`] instance
10//! and its [`PadNum`], from [`Pad0`] to [`Pad3`]. However, a given SERCOM pad
11//! can usually be mapped to several possible [`PinId`]s.
12//!
13//! There are two primary traits defined in this module:
14//! - The [`IsPad`] trait is implemented on `Pin` types that are properly
15//!   configured as SERCOM pads, with `PinMode` [`AlternateC`] or
16//!   [`AlternateD`]. It acts as both a [type class] for SERCOM pads and as a
17//!   [type-level function] to recover the corresponding [`Sercom`] and
18//!   [`PadNum`] types from the `Pin`.
19//! - The [`GetPad`] trait maps each [`PinId`] to its corresponding, pad-related
20//!   types. The [`PadMode`] alias uses `GetPad` to recover the corresponding
21//!   `PinMode` for a given SERCOM pad, while the [`Pad`] alias recovers the
22//!   configured [`Pin`] type.
23//!
24//! [`AlternateC`]: crate::gpio::AlternateC
25//! [`AlternateD`]: crate::gpio::AlternateD
26//! [type class]: crate::typelevel#type-classes
27//! [type-level function]: crate::typelevel#type-level-functions
28//! # IOSET (SAMD51/SAME5x only)
29//!
30//! SAMx5x chips do not allow arbitrary combinations of `PinId` for a given
31//! SERCOM. Instead, all `PinId`s must belong to the same IOSET. This module
32//! defines a [type-level enum], [`IoSet`], to enforce this restriction, and the
33//! [`InIoSet`] [type class] is responsible for labeling each `IsPad` type with
34//! its corresponding, valid `IoSet`\(s).
35
36use atsamd_hal_macros::{hal_cfg, hal_module};
37use paste::paste;
38use seq_macro::seq;
39
40use super::Sercom;
41#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
42use crate::gpio::OptionalPinId;
43use crate::gpio::{AnyPin, OptionalPin, Pin, PinId, PinMode};
44use crate::typelevel::{NoneT, Sealed};
45
46#[hal_module(
47    any("sercom0-d11", "sercom0-d21") => "pad/impl_pad_thumbv6m.rs",
48    "sercom0-d5x" => "pad/impl_pad_thumbv7em.rs",
49)]
50mod impl_pad {}
51
52//==============================================================================
53// PadNum
54//==============================================================================
55
56/// Type-level enum representing a SERCOM pad number
57///
58/// It has variants [`Pad0`], [`Pad1`], [`Pad2`] & [`Pad3`]. See the [type-level
59/// enum] documentation for an explanation of the pattern.
60///
61/// [type-level enum]: crate::typelevel#type-level-enum
62pub trait PadNum: Sealed {}
63
64seq!(N in 0..=3 {
65    paste! {
66        #[doc = "Type-level variant of [`PadNum`] representing SERCOM pad " N]
67        ///
68        /// See the [type-level enum] documentation for an explanation of the
69        /// pattern.
70        ///
71        /// [type-level enum]: crate::typelevel#type-level-enum
72        pub enum Pad~N {}
73        impl Sealed for Pad~N {}
74        impl PadNum for Pad~N {}
75    }
76});
77
78//==============================================================================
79// OptionalPadNum
80//==============================================================================
81
82/// Type-level equivalent of `Option<PadNum>`
83///
84/// See the [`OptionalKind`] documentation for more details on the pattern.
85///
86/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern
87pub trait OptionalPadNum: Sealed {}
88
89impl OptionalPadNum for NoneT {}
90
91impl<N: PadNum> OptionalPadNum for N {}
92
93//==============================================================================
94// IsPad
95//==============================================================================
96
97/// Type class for [`Pin`]s configured as SERCOM pads
98///
99/// This trait serves as both a [type class] for `Pin`s configured as SERCOM
100/// pads and as a [type-level function] mapping each `Pin` type to its
101/// corresponding [`Sercom`] and [`PadNum`].
102///
103/// [type class]: crate::typelevel#type-classes
104/// [type-level function]: crate::typelevel#type-level-functions
105pub trait IsPad: AnyPin {
106    type Sercom: Sercom;
107    type PadNum: PadNum;
108}
109
110//==============================================================================
111// IsI2cPad
112//==============================================================================
113
114/// Type class for [`Pin`]s which can be used as I2C pads
115///
116/// This trait serves as a [type class] for `Pin`s configured as I2C pads.
117///
118/// [type class]: crate::typelevel#type-classes
119pub trait IsI2cPad: IsPad {}
120
121//==============================================================================
122// OptionalPad
123//==============================================================================
124
125/// Type-level equivalent of `Option<Pad>`
126///
127/// See the [`OptionalKind`] documentation for more details on the pattern.
128///
129/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern
130pub trait OptionalPad: OptionalPin {
131    type PadNum: OptionalPadNum;
132}
133
134impl OptionalPad for NoneT {
135    type PadNum = NoneT;
136}
137
138impl<P: IsPad> OptionalPad for P {
139    type PadNum = P::PadNum;
140}
141
142/// Type-level equivalent of `Some(Pad)`
143///
144/// See the [`OptionalKind`] documentation for more details on the pattern.
145///
146/// [`OptionalKind`]: crate::typelevel#optionalkind-trait-pattern
147pub trait SomePad: IsPad {}
148
149impl<P: IsPad> SomePad for P {}
150
151//==============================================================================
152// GetPad
153//==============================================================================
154
155/// Type-level function mapping [`PinId`]s to SERCOM-pad-related types
156///
157/// For SAMD21 and SAMx5x chips, a [`Sercom`] and a [`PinId`] is enough
158/// information to uniquely identify a pad, so this trait returns the
159/// corresponding [`PadNum`] and [`PinMode`].
160///
161/// For SAMD11 chips, on the other hand, some `PinId`s can serve as two
162/// different `PadNum`s for the *same* `Sercom`. For these chips, `GetPad`
163/// requires a second type parameter to specify the `PadNum` and only returns
164/// the `PinMode`.
165///
166/// See the documentation on [type-level functions] for more details.
167///
168/// [type-level functions]: crate::typelevel#type-level-functions
169#[hal_cfg("sercom0-d11")]
170pub trait GetPad<S, N>
171where
172    S: Sercom,
173    N: PadNum,
174    Self: PinId,
175{
176    type PinMode: PinMode;
177}
178
179/// Type-level function mapping [`PinId`]s to SERCOM-pad-related types
180///
181/// For SAMD21 and SAMx5x chips, a [`Sercom`] and a [`PinId`] is enough
182/// information to uniquely identify a pad, so this trait returns the
183/// corresponding [`PadNum`] and [`PinMode`].
184///
185/// For SAMD11 chips, on the other hand, some `PinId`s can serve as two
186/// different `PadNum`s for the *same* `Sercom`. For these chips, `GetPad`
187/// requires a second type parameter to specify the `PadNum` and only returns
188/// the `PinMode`.
189///
190/// See the documentation on [type-level functions] for more details.
191///
192/// [type-level functions]: crate::typelevel#type-level-functions
193#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
194pub trait GetPad<S>
195where
196    S: Sercom,
197    Self: PinId,
198{
199    type PadNum: PadNum;
200    type PinMode: PinMode;
201}
202
203//==============================================================================
204// GetPad aliases
205//==============================================================================
206
207/// Type alias using [`GetPad`] to recover the [`PinMode`] for a given SERCOM
208/// pad
209#[hal_cfg("sercom0-d11")]
210pub type PadMode<S, N, I> = <I as GetPad<S, N>>::PinMode;
211
212/// Type alias using [`GetPad`] to recover the [`PinMode`] for a given SERCOM
213/// pad
214#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
215pub type PadMode<S, I> = <I as GetPad<S>>::PinMode;
216
217/// Type alias to recover a [`Pin`] configured as a SERCOM pad in the correct
218/// [`PadMode`]
219#[hal_cfg("sercom0-d11")]
220pub type Pad<S, N, I> = Pin<I, PadMode<S, N, I>>;
221
222/// Type alias to recover a [`Pin`] configured as a SERCOM pad in the correct
223/// [`PadMode`]
224#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
225pub type Pad<S, I> = Pin<I, PadMode<S, I>>;
226
227//==============================================================================
228// GetOptionalPad
229//==============================================================================
230
231/// Type-level function mapping [`OptionalPinId`]s to their corresponding
232/// [`OptionalPad`]s
233///
234/// This trait acts as a [type-level function] mapping `OptionalPinId`s to their
235/// corresponding `OptionalPad`. In pseudo-Rust, it is the type-level equivalent
236/// of starting with `Option<PinId>` and calling `.map(GetPad)` to recover an
237/// `Option<Pad>`.
238///
239/// [type-level functions]: crate::typelevel#type-level-functions
240#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
241pub trait GetOptionalPad<S: Sercom>: OptionalPinId {
242    type PadNum: OptionalPadNum;
243    type Pad: OptionalPad;
244}
245
246#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
247impl<S: Sercom> GetOptionalPad<S> for NoneT {
248    type PadNum = NoneT;
249    type Pad = NoneT;
250}
251
252#[hal_cfg(any("sercom0-d21", "sercom0-d5x"))]
253impl<S, I> GetOptionalPad<S> for I
254where
255    S: Sercom,
256    I: PinId + GetPad<S>,
257    Pad<S, I>: IsPad,
258{
259    type PadNum = I::PadNum;
260    type Pad = Pad<S, I>;
261}
262
263//==============================================================================
264// IoSet
265//==============================================================================
266
267#[hal_cfg("sercom0-d5x")]
268mod ioset {
269
270    use super::*;
271    use sorted_hlist::{HList, Intersect, NonEmptyHList, mk_hlist};
272
273    /// Type-level enum representing a SERCOM IOSET
274    ///
275    /// The SAM D5x/E5x has particular sets of [`Pin`]s that are allowed to be
276    /// used together for each [`Sercom`], and `Pin`s from different sets cannot
277    /// be used together.  The valid combinations of `Pin`s are called IOSETs
278    /// (or IO SETs) in the datasheet.  Additionally, some undocumented sets are
279    /// used in commercially available boards (such as Adafruit's PyGamer and
280    /// Feather M4).  This `IoSet` trait is used to constrain the various `Pads`
281    /// types ([`spi::Pads`], [`uart::Pads`], and [`i2c::Pads`]) to only contain
282    /// valid documented or undocumented sets of `Pin`s.
283    ///
284    /// See the [type-level enum] documentation for more details on the pattern.
285    /// Typenum unsigned integers are used to make IoSets comparable
286    ///
287    /// [`spi::Pads`]: crate::sercom::spi::Pads
288    /// [`uart::Pads`]: crate::sercom::uart::Pads
289    /// [`i2c::Pads`]: crate::sercom::i2c::Pads
290    /// [type-level enum]: crate::typelevel#type-level-enum
291    pub trait IoSet: Sealed {
292        type Order;
293    }
294
295    seq!(N in 1..=6 {
296        paste! {
297            #[doc = "Type-level variant of [`IoSet`] representing SERCOM IOSET " N]
298            ///
299            /// See the [type-level enum] documentation for more details on the
300            /// pattern.
301            ///
302            /// [type-level enum]: crate::typelevel#type-level-enum
303            pub enum IoSet~N {}
304            impl Sealed for IoSet~N {}
305            impl IoSet for IoSet~N {
306                type Order = typenum::U~N;
307            }
308        }
309    });
310
311    // Implement IoSets for NoneT, making it act as a wildcard.
312    seq!(N in 1..=6 {
313        impl IoSets for NoneT {
314            type SetList = mk_hlist! ( #(<IoSet~N as IoSet>::Order, )* <UndocIoSet1 as IoSet>::Order, <UndocIoSet2 as IoSet>::Order );
315        }
316    });
317
318    /// Type-level variant of [`IoSet`] representing an undocumented SERCOM
319    /// IOSET
320    ///
321    /// After implementing `IoSet` type checking, it became clear that some
322    /// existing boards were using a combinations of pins that did not match any
323    /// IOSET in the datasheet. From that, we infer that there must be at least
324    /// two undocumented IOSETs, and we added these new `IoSet`s to account for
325    /// it.
326    ///
327    /// As of writing this documentation, only two undocumented IOSETs have been
328    /// discovered:
329    /// - [`UndocIoSet1`]: PA16, PA17, PB22 & PB23 configured for `Sercom1`.
330    ///   Both the pygamer & feather_m4 uses this combination.
331    /// - [`UndocIoSet2`]: PA00, PA01, PB22 & PB23 configured for `Sercom1`. The
332    ///   itsybitsy_m4 uses this combination.
333    ///
334    /// See the [type-level enum] documentation for more details on type-level
335    /// variants.
336    ///
337    /// [type-level enum]: crate::typelevel#type-level-enum
338    pub enum UndocIoSet1 {}
339    impl Sealed for UndocIoSet1 {}
340    impl IoSet for UndocIoSet1 {
341        type Order = typenum::U8;
342    }
343
344    /// Type-level variant of [`IoSet`] representing an undocumented SERCOM
345    /// IOSET
346    ///
347    /// After implementing `IoSet` type checking, it became clear that some
348    /// existing boards were using a combinations of pins that did not match any
349    /// IOSET in the datasheet. From that, we infer that there must be at least
350    /// two undocumented IOSETs, and we added these new `IoSet`s to account for
351    /// it.
352    ///
353    /// As of writing this documentation, only two undocumented IOSETs have been
354    /// discovered:
355    /// - [`UndocIoSet1`]: PA16, PA17, PB22 & PB23 configured for `Sercom1`.
356    ///   Both the pygamer & feather_m4 uses this combination.
357    /// - [`UndocIoSet2`]: PA00, PA01, PB22 & PB23 configured for `Sercom1`. The
358    ///   itsybitsy_m4 uses this combination.
359    ///
360    /// See the [type-level enum] documentation for more details on type-level
361    /// variants.
362    ///
363    /// [type-level enum]: crate::typelevel#type-level-enum
364    pub enum UndocIoSet2 {}
365    impl Sealed for UndocIoSet2 {}
366    impl IoSet for UndocIoSet2 {
367        type Order = typenum::U9;
368    }
369
370    /// Type class for SERCOM pads in a given [`IoSet`]
371    ///
372    /// This trait is used to label each [`Pin`] implementing [`IsPad`] with its
373    /// corresponding [`IoSet`]\(s). Downstream types can use this trait as a
374    /// [type class] to restrict [`Pin`]s to a given [`IoSet`]. See the [type
375    /// class] documentation for more details on the pattern.
376    ///
377    /// [type class]: crate::typelevel#type-classes
378    pub trait InIoSet<I>
379    where
380        Self: IsPad,
381        I: IoSet,
382    {
383    }
384
385    /// Type class for corresponding IoSet indices for OptionalPads, NoneT
386    /// serves as a wildcard
387    pub trait IoSets: OptionalPad {
388        type SetList: HList;
389    }
390
391    /// Type class for accessing the intersection of IoSets of OptionalPads
392    /// Currently implemented for tuples of 2 and 4 elements
393    pub trait CommonIoSets {
394        type IoSets: HList;
395    }
396
397    impl<P0: IoSets, P1: IoSets> CommonIoSets for (P0, P1)
398    where
399        P0::SetList: Intersect<P1::SetList>,
400    {
401        type IoSets = <P0::SetList as Intersect<P1::SetList>>::Output;
402    }
403
404    impl<P0: IoSets, P1: IoSets, P2: IoSets, P3: IoSets> CommonIoSets for (P0, P1, P2, P3)
405    where
406        P0::SetList: Intersect<P1::SetList>,
407        <P0::SetList as Intersect<P1::SetList>>::Output: Intersect<P2::SetList>,
408        <<P0::SetList as Intersect<P1::SetList>>::Output as Intersect<P2::SetList>>::Output:
409            Intersect<P3::SetList>,
410    {
411        type IoSets = <<<P0::SetList as Intersect<P1::SetList>>::Output as Intersect<
412            P2::SetList,
413        >>::Output as Intersect<P3::SetList>>::Output;
414    }
415
416    /// Shortcut trait for Pad tuples that share at least one IoSet
417    pub trait ShareIoSet {}
418    impl<A> ShareIoSet for A
419    where
420        A: CommonIoSets,
421        <A as CommonIoSets>::IoSets: NonEmptyHList,
422    {
423    }
424}
425
426#[hal_cfg("sercom0-d5x")]
427pub use ioset::*;
428
429#[cfg(doc)]
430#[hal_cfg(not("sercom0-d5x"))]
431/// This trait is not present with the selected feature set, defined for
432/// documentation only
433pub trait IoSet {}
434
435#[cfg(doc)]
436#[hal_cfg(not("sercom0-d5x"))]
437/// This trait is not present with the selected feature set, defined for
438/// documentation only
439pub trait InIoSet {}
440
441#[cfg(doc)]
442#[hal_cfg(not("sercom0-d5x"))]
443/// This type is not present with the selected feature set, defined for
444/// documentation only
445pub enum UndocIoSet1 {}
446
447#[cfg(doc)]
448#[hal_cfg(not("sercom0-d5x"))]
449/// This type is not present with the selected feature set, defined for
450/// documentation only
451pub enum UndocIoSet2 {}