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 {}