From b1c04c9c2d7a109d898ecd51239b3f531493b447 Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Sat, 17 Jan 2026 18:02:33 -0500 Subject: [PATCH 1/3] PCLK source ID can be either type-checked or runtime-checked --- boards/atsame54_xpro/examples/mcan.rs | 2 +- boards/examples/m4-adc.rs | 8 +- boards/metro_m4/examples/async_adc.rs | 8 +- hal/src/clock/v2/gclk.rs | 27 +++++ hal/src/clock/v2/pclk.rs | 148 +++++++++++++++++++++++--- hal/src/clock/v2/reset_thumbv6m.rs | 9 +- hal/src/peripherals/adc/builder.rs | 4 +- hal/src/peripherals/adc/mod.rs | 29 +++-- 8 files changed, 202 insertions(+), 33 deletions(-) diff --git a/boards/atsame54_xpro/examples/mcan.rs b/boards/atsame54_xpro/examples/mcan.rs index d79541b3561c..d2f9ad8fb671 100644 --- a/boards/atsame54_xpro/examples/mcan.rs +++ b/boards/atsame54_xpro/examples/mcan.rs @@ -83,7 +83,7 @@ type Aux = mcan::bus::Aux< clock::types::Can1, hal::can::Dependencies< clock::types::Can1, - clock::gclk::Gclk0Id, + clock::pclk::PclkSource, bsp::Ata6561Rx, bsp::Ata6561Tx, bsp::pac::Can1, diff --git a/boards/examples/m4-adc.rs b/boards/examples/m4-adc.rs index f6278c5fadf7..91cc17d6505f 100644 --- a/boards/examples/m4-adc.rs +++ b/boards/examples/m4-adc.rs @@ -44,20 +44,20 @@ fn main() -> ! { let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); // ...and enable the ADC0 PCLK. Both of these are required for the // ADC to run. - let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); + let (pclk_adc0, _gclk0) = Pclk::enable_dyn(tokens.pclks.adc0, clocks.gclk0); let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) .with_clock_cycles_per_sample(5) // Overruns if clock divider < 32 in debug mode .with_clock_divider(Prescaler::Div32) .with_vref(atsamd_hal::adc::Reference::Arefa) - .enable(peripherals.adc0, apb_adc0, &pclk_adc0) + .enable(peripherals.adc0, apb_adc0, pclk_adc0) .unwrap(); let mut adc_pin = pins.a0.into_alternate(); loop { - let res = adc.read(&mut adc_pin); + let _res = adc.read(&mut adc_pin); #[cfg(feature = "use_semihosting")] - let _ = cortex_m_semihosting::hprintln!("ADC value: {}", res); + let _ = cortex_m_semihosting::hprintln!("ADC value: {}", _res); } } diff --git a/boards/metro_m4/examples/async_adc.rs b/boards/metro_m4/examples/async_adc.rs index a9fe0763369f..a4121ba277bf 100644 --- a/boards/metro_m4/examples/async_adc.rs +++ b/boards/metro_m4/examples/async_adc.rs @@ -42,21 +42,21 @@ async fn main(_s: embassy_executor::Spawner) -> ! { let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); // ...and enable the ADC0 PCLK. Both of these are required for the // ADC to run. - let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); + let (pclk_adc0, _gclk0) = Pclk::enable_dyn(tokens.pclks.adc0, clocks.gclk0); let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) .with_clock_cycles_per_sample(5) .with_clock_divider(Prescaler::Div32) .with_vref(atsamd_hal::adc::Reference::Arefa) - .enable(peripherals.adc0, apb_adc0, &pclk_adc0) + .enable(peripherals.adc0, apb_adc0, pclk_adc0) .unwrap() .into_future(Irqs); let mut adc_pin = pins.a0.into_alternate(); loop { - let res = adc.read(&mut adc_pin).await; + let _res = adc.read(&mut adc_pin).await; #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); + cortex_m_semihosting::hprintln!("ADC Result: {}", _res).unwrap(); } } diff --git a/hal/src/clock/v2/gclk.rs b/hal/src/clock/v2/gclk.rs index 09037dd0f24e..af07992cd9b6 100644 --- a/hal/src/clock/v2/gclk.rs +++ b/hal/src/clock/v2/gclk.rs @@ -462,6 +462,7 @@ impl GclkToken { /// The variants of this enum identify one generic clock generator. /// /// `DynGclkId` is the value-level equivalent of [`GclkId`]. +#[derive(Clone, Copy, PartialEq, Eq)] #[hal_macro_helper] pub enum DynGclkId { Gclk0, @@ -484,6 +485,32 @@ pub enum DynGclkId { Gclk11, } +impl core::fmt::Debug for DynGclkId { + #[hal_macro_helper] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Gclk0 => write!(f, "Gclk0"), + Self::Gclk1 => write!(f, "Gclk1"), + Self::Gclk2 => write!(f, "Gclk2"), + Self::Gclk3 => write!(f, "Gclk3"), + Self::Gclk4 => write!(f, "Gclk4"), + Self::Gclk5 => write!(f, "Gclk5"), + #[hal_cfg("gclk6")] + Self::Gclk6 => write!(f, "Gclk6"), + #[hal_cfg("gclk7")] + Self::Gclk7 => write!(f, "Gclk7"), + #[hal_cfg("gclk8")] + Self::Gclk8 => write!(f, "Gclk8"), + #[hal_cfg("gclk9")] + Self::Gclk9 => write!(f, "Gclk9"), + #[hal_cfg("gclk10")] + Self::Gclk10 => write!(f, "Gclk10"), + #[hal_cfg("gclk11")] + Self::Gclk11 => write!(f, "Gclk11"), + } + } +} + //============================================================================== // GclkId //============================================================================== diff --git a/hal/src/clock/v2/pclk.rs b/hal/src/clock/v2/pclk.rs index ace79da62e7a..02a04286dd95 100644 --- a/hal/src/clock/v2/pclk.rs +++ b/hal/src/clock/v2/pclk.rs @@ -547,6 +547,28 @@ impl From for Genselect { } } +impl PclkSourceId for DynPclkSourceId { + fn source_id(&self) -> DynGclkId { + *self + } +} + +impl Sealed for DynPclkSourceId {} + +//============================================================================== +// PclkSourceId +//============================================================================== + +pub struct PclkSource { + _src: PhantomData, +} + +impl GclkId for PclkSource { + const DYN: DynGclkId = G::DYN; + const NUM: usize = G::NUM; + type Divider = G::Divider; +} + //============================================================================== // PclkSourceId //============================================================================== @@ -566,9 +588,19 @@ impl From for Genselect { /// [`Gclk`]: super::gclk::Gclk /// [type-level programming]: crate::typelevel /// [type-level enums]: crate::typelevel#type-level-enums -pub trait PclkSourceId: GclkId {} +pub trait PclkSourceId: Sealed { + fn source_id(&self) -> DynGclkId; +} -impl PclkSourceId for G {} +// PclkSource implements PclkSourceId via its GclkId implementation +impl PclkSourceId for G { + #[inline] + fn source_id(&self) -> DynGclkId { + Self::DYN + } +} + +impl Sealed for PclkSource {} //============================================================================== // Pclk @@ -606,24 +638,41 @@ where I: PclkSourceId, { token: PclkToken

, - src: PhantomData, + src: I, freq: Hertz, } -impl Pclk +/// [`Pclk`] with a dynamic source ID ([`DynPclkSourceId`]). +pub type DynPclk

= Pclk; + +impl From>> for DynPclk

where P: PclkId, - I: PclkSourceId, + G: GclkId, +{ + fn from(value: Pclk>) -> Self { + Pclk { + token: value.token, + freq: value.freq, + src: G::DYN, + } + } +} + +impl Pclk> +where + P: PclkId, + G: GclkId, { pub(super) fn new(token: PclkToken

, freq: Hertz) -> Self { Self { token, - src: PhantomData, + src: PclkSource { _src: PhantomData }, freq, } } - /// Create and enable a [`Pclk`] + /// Create and enable a [`Pclk`] with a type-checked source ID. /// /// Creating a [`Pclk`] immediately enables the corresponding peripheral /// channel clock. It also [`Increment`]s the [`Source`]'s [`Enabled`] @@ -636,15 +685,15 @@ where #[inline] pub fn enable(mut token: PclkToken

, gclk: S) -> (Self, S::Inc) where - S: Source + Increment, + S: Source + Increment, { let freq = gclk.freq(); - token.enable(I::DYN); - let pclk = Pclk::new(token, freq); + token.enable(G::DYN); + let pclk = Self::new(token, freq); (pclk, gclk.inc()) } - /// Disable and destroy a [`Pclk`] + /// Disable and destroy a [`Pclk`]. /// /// Consume the [`Pclk`], release the [`PclkToken`], and [`Decrement`] the /// [`EnabledGclk`]'s counter @@ -654,12 +703,87 @@ where #[inline] pub fn disable(mut self, gclk: S) -> (PclkToken

, S::Dec) where - S: Source + Decrement, + S: Source + Decrement, + { + self.token.disable(); + (self.token, gclk.dec()) + } +} + +impl

DynPclk

+where + P: PclkId, +{ + pub(super) fn new(token: PclkToken

, freq: Hertz) -> Self { + Self { + token, + src: G::DYN, + freq, + } + } + + /// Create and enable a [`Pclk`] with an underlying [`DynPclkSourceId`] + /// source ID type. + /// + /// Some peripherals require a dynamic PCLK source ID type parameter; use + /// this method to create a [`Pclk`] where this type parameter is + /// type-erased. + /// + /// Creating a [`Pclk`] immediately enables the corresponding peripheral + /// channel clock. It also [`Increment`]s the [`Source`]'s [`Enabled`] + /// counter. + /// + /// Note that the [`Source`] will always be an [`EnabledGclk`]. + /// + /// [`Enabled`]: super::Enabled + /// [`EnabledGclk`]: super::gclk::EnabledGclk + #[inline] + pub fn enable_dyn(mut token: PclkToken

, gclk: S) -> (Self, S::Inc) + where + S: Source + Increment, + { + let freq = gclk.freq(); + token.enable(G::DYN); + let pclk = Self::new::(token, freq); + (pclk, gclk.inc()) + } + + /// Disable and destroy a [`Pclk`]. + /// + /// Consume the [`Pclk`], release the [`PclkToken`], and [`Decrement`] the + /// [`EnabledGclk`]'s counter. + /// + /// # Panics + /// + /// Panics if the [`Pclk`]'s underlying GCLK source ID does not match the ID + /// of the provided [`Source`]. + /// + /// [`Enabled`]: super::Enabled + /// [`EnabledGclk`]: super::gclk::EnabledGclk + #[inline] + pub fn disable(mut self, gclk: S) -> (PclkToken

, S::Dec) + where + S: Source + Decrement, { + // Make sure that we can only decrement the source we are actually using + assert_eq!( + G::DYN, + self.src, + "Expected GCLK ID {:?}, found {:?}", + G::DYN, + self.src + ); + self.token.disable(); (self.token, gclk.dec()) } +} +impl Pclk +where + P: PclkId, + I: PclkSourceId, +{ /// Return the [`Pclk`] frequency #[inline] pub fn freq(&self) -> Hertz { diff --git a/hal/src/clock/v2/reset_thumbv6m.rs b/hal/src/clock/v2/reset_thumbv6m.rs index 1c44f83f3268..a9831beadbcb 100644 --- a/hal/src/clock/v2/reset_thumbv6m.rs +++ b/hal/src/clock/v2/reset_thumbv6m.rs @@ -6,7 +6,10 @@ use atsamd_hal_macros::hal_macro_helper; use typenum::U1; -use crate::pac::{Gclk, Pm, Sysctrl}; +use crate::{ + clock::v2::pclk::PclkSource, + pac::{Gclk, Pm, Sysctrl}, +}; use super::*; @@ -97,7 +100,7 @@ pub struct Clocks { /// Always-enabled OSCULP oscillators pub osculp: OscUlpClocks, /// [`Pclk`](pclk::Pclk) for the watchdog timer, sourced from [`Gclk2`](gclk::Gclk2) - pub wdt: pclk::Pclk, + pub wdt: pclk::Pclk>, } /// Type-level tokens for unused clocks at power-on reset @@ -155,7 +158,7 @@ pub fn clock_system_at_reset(gclk: Gclk, pm: Pm, sysctrl: Sysctrl) -> (Buses, Cl let osculp32k = Enabled::<_, U0>::new(osculp32k::OscUlp32k::new()); let (gclk2, osculp32k) = gclk::Gclk2::from_source(gclk::GclkToken::new(), osculp32k); let gclk2 = Enabled::new(gclk2); - let wdt = pclk::Pclk::new(pclk::PclkToken::new(), gclk2.freq()); + let wdt = pclk::Pclk::<_, PclkSource<_>>::new(pclk::PclkToken::new(), gclk2.freq()); let osculp = OscUlpClocks { base, osculp1k, diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index baaa62e74b83..41c97148d99c 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -247,11 +247,11 @@ impl AdcBuilder { /// Turn the builder into an ADC #[hal_cfg("adc-d5x")] #[inline] - pub fn enable( + pub fn enable( self, adc: I::Instance, clk: crate::clock::v2::apb::ApbClk, - pclk: &crate::clock::v2::pclk::Pclk, + pclk: crate::clock::v2::pclk::Pclk, ) -> Result, BuilderError> { let settings = self.to_settings()?; Adc::new(adc, settings, clk, pclk).map_err(|e| e.into()) diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index c34b560ad113..0f337a3416c5 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -48,7 +48,13 @@ pub use builder::*; #[hal_cfg(any("adc-d11", "adc-d21"))] use crate::pac::adc as adc0; #[hal_cfg("adc-d5x")] -use crate::pac::adc0; +use crate::{ + clock::v2::{ + apb::ApbClk, + pclk::{DynPclkSourceId, Pclk}, + }, + pac::adc0, +}; pub use adc0::refctrl::Refselselect as Reference; @@ -174,7 +180,8 @@ pub struct Adc { #[hal_cfg("adc-d5x")] pub struct Adc { adc: I::Instance, - _apbclk: crate::clock::v2::apb::ApbClk, + _apbclk: ApbClk, + _pclk: Pclk, cfg: AdcSettings, discard: bool, } @@ -203,11 +210,11 @@ impl Adc { /// frequency for the ADC is restricted to 90Mhz for stable performance. #[hal_cfg("adc-d5x")] #[inline] - pub(crate) fn new( + pub(crate) fn new( adc: I::Instance, settings: AdcSettings, - clk: crate::clock::v2::apb::ApbClk, - pclk: &crate::clock::v2::pclk::Pclk, + clk: ApbClk, + pclk: Pclk, ) -> Result { // TODO: Ideally, the ADC struct would take ownership of the Pclk type here. // However, since clock::v2 is not implemented for all chips yet, the @@ -226,6 +233,7 @@ impl Adc { let mut new_adc = Self { adc, _apbclk: clk, + _pclk: pclk, cfg: settings, discard: true, }; @@ -422,10 +430,17 @@ impl Adc { /// Return the underlying ADC PAC object and the enabled APB ADC clock. #[hal_cfg("adc-d5x")] + #[allow(clippy::type_complexity)] #[inline] - pub fn free(mut self) -> (I::Instance, crate::clock::v2::apb::ApbClk) { + pub fn free( + mut self, + ) -> ( + I::Instance, + ApbClk, + Pclk, + ) { self.software_reset(); - (self.adc, self._apbclk) + (self.adc, self._apbclk, self._pclk) } /// Reset the peripheral. From 2b91312a22c508189d176ea72bb512142b57d76c Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Sat, 17 Jan 2026 19:14:49 -0500 Subject: [PATCH 2/3] ADC takes impl Into --- boards/examples/m4-adc.rs | 2 +- hal/src/peripherals/adc/builder.rs | 9 ++++++--- hal/src/peripherals/adc/mod.rs | 18 ++++-------------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/boards/examples/m4-adc.rs b/boards/examples/m4-adc.rs index 91cc17d6505f..0afcc86f8238 100644 --- a/boards/examples/m4-adc.rs +++ b/boards/examples/m4-adc.rs @@ -44,7 +44,7 @@ fn main() -> ! { let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); // ...and enable the ADC0 PCLK. Both of these are required for the // ADC to run. - let (pclk_adc0, _gclk0) = Pclk::enable_dyn(tokens.pclks.adc0, clocks.gclk0); + let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) .with_clock_cycles_per_sample(5) diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index 41c97148d99c..343ebf0301a4 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -244,17 +244,20 @@ impl AdcBuilder { Ok(adc_clk_freq / clocks_per_sample) } - /// Turn the builder into an ADC + /// Turn the builder into an ADC. + /// + /// This function will convert the provided + /// [`Pclk`](crate::clock::v2::pclk::Pclk) into a [`DynPclk`]. #[hal_cfg("adc-d5x")] #[inline] pub fn enable( self, adc: I::Instance, clk: crate::clock::v2::apb::ApbClk, - pclk: crate::clock::v2::pclk::Pclk, + pclk: impl Into>, ) -> Result, BuilderError> { let settings = self.to_settings()?; - Adc::new(adc, settings, clk, pclk).map_err(|e| e.into()) + Adc::new(adc, settings, clk, pclk.into()).map_err(|e| e.into()) } #[hal_cfg(any("adc-d11", "adc-d21"))] diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index 0f337a3416c5..f00e0f1c2bdc 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -49,10 +49,7 @@ pub use builder::*; use crate::pac::adc as adc0; #[hal_cfg("adc-d5x")] use crate::{ - clock::v2::{ - apb::ApbClk, - pclk::{DynPclkSourceId, Pclk}, - }, + clock::v2::{apb::ApbClk, pclk::DynPclk}, pac::adc0, }; @@ -181,7 +178,7 @@ pub struct Adc { pub struct Adc { adc: I::Instance, _apbclk: ApbClk, - _pclk: Pclk, + _pclk: DynPclk, cfg: AdcSettings, discard: bool, } @@ -214,7 +211,7 @@ impl Adc { adc: I::Instance, settings: AdcSettings, clk: ApbClk, - pclk: Pclk, + pclk: DynPclk, ) -> Result { // TODO: Ideally, the ADC struct would take ownership of the Pclk type here. // However, since clock::v2 is not implemented for all chips yet, the @@ -430,15 +427,8 @@ impl Adc { /// Return the underlying ADC PAC object and the enabled APB ADC clock. #[hal_cfg("adc-d5x")] - #[allow(clippy::type_complexity)] #[inline] - pub fn free( - mut self, - ) -> ( - I::Instance, - ApbClk, - Pclk, - ) { + pub fn free(mut self) -> (I::Instance, ApbClk, DynPclk) { self.software_reset(); (self.adc, self._apbclk, self._pclk) } From 774784af3d5b7dfa1321b1c3fa67188430251ebc Mon Sep 17 00:00:00 2001 From: Justin Beaurivage Date: Sat, 24 Jan 2026 22:57:46 -0500 Subject: [PATCH 3/3] Fix docs Signed-off-by: Justin Beaurivage --- hal/src/peripherals/adc/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index 343ebf0301a4..34441bf5e01a 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -247,7 +247,7 @@ impl AdcBuilder { /// Turn the builder into an ADC. /// /// This function will convert the provided - /// [`Pclk`](crate::clock::v2::pclk::Pclk) into a [`DynPclk`]. + /// [`Pclk`](crate::clock::v2::pclk::Pclk) into a [`DynPclk`](crate::clock::v2::pclk::DynPclk). #[hal_cfg("adc-d5x")] #[inline] pub fn enable(