atsamd_hal/peripherals/clock/d5x/v2/dpll.rs
1//! # Digital Phase-Locked Loop
2//!
3//! ## Overview
4//!
5//! The `dpll` module provides access to the two digital phase-locked loops
6//! (DPLLs) within the `OSCCTRL` peripheral.
7//!
8//! A DPLL is used to multiply clock frequencies. It takes a lower-frequency
9//! input clock and produces a higher-frequency output clock. It works by taking
10//! the output clock, dividing it down to the same frequency as the input clock,
11//! comparing phase between the two signals, and locking that phase difference
12//! to zero. Consequently, the clock divider within the feedback loop sets the
13//! frequency multiplication factor.
14//!
15//! The DPLLs operate over a large range of frequencies, but their operating
16//! region is not infinite. Specifically, they can only accept input frequencies
17//! between 32 kHz and 3.2 MHz, and they can only output frequencies in the
18//! range of 96 MHz to 200 MHz.
19//!
20//! Creating and configuring a [`Dpll`] proceeds according to the principles
21//! outlined in the [`clock` module documentation]. It is best shown with an
22//! example.
23//!
24//! ## Example
25//!
26//! Suppose we start with the default clock tree after power-on reset.
27//!
28//! ```text
29//! DFLL (48 MHz)
30//! └── GCLK0 (48 MHz)
31//! └── Master clock (48 MHz)
32//! ```
33//!
34//! We would like to transform it to a clock tree like this:
35//!
36//! ```text
37//! DFLL (48 MHz)
38//! └── GCLK1 (2 MHz)
39//! └── DPLL (200 MHz)
40//! └── GCLK0 (200 MHz)
41//! └── Master clock (200 MHz)
42//! ```
43//!
44//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking
45//! structs.
46//!
47//! ```no_run
48//! use atsamd_hal::{
49//! clock::v2::{
50//! clock_system_at_reset,
51//! dpll::Dpll,
52//! gclk::{Gclk, GclkDiv16},
53//! pclk::Pclk,
54//! },
55//! pac::Peripherals,
56//! };
57//! let mut pac = Peripherals::take().unwrap();
58//! let (buses, clocks, tokens) = clock_system_at_reset(
59//! pac.oscctrl,
60//! pac.osc32kctrl,
61//! pac.gclk,
62//! pac.mclk,
63//! &mut pac.nvmctrl,
64//! );
65//! ```
66//!
67//! First, we would like to divide down the 48 MHz output of the [`Dfll`] to
68//! produce a valid input frequency for the [`Dpll`]. We start by feeding the
69//! already-[`Enabled`] [`Dfll`] to [`Gclk1`] with a [`GclkDivider`] of 24,
70//! producing a 2 MHz output frequency. This has the side effect of
71//! [`Increment`]ing the counter for [`EnabledDfll`].
72//!
73//! ```no_run
74//! # use atsamd_hal::{
75//! # clock::v2::{
76//! # clock_system_at_reset,
77//! # dpll::Dpll,
78//! # gclk::{Gclk, GclkDiv16},
79//! # pclk::Pclk,
80//! # },
81//! # pac::Peripherals,
82//! # };
83//! # let mut pac = Peripherals::take().unwrap();
84//! # let (buses, clocks, tokens) = clock_system_at_reset(
85//! # pac.oscctrl,
86//! # pac.osc32kctrl,
87//! # pac.gclk,
88//! # pac.mclk,
89//! # &mut pac.nvmctrl,
90//! # );
91//! let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll);
92//! let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable();
93//! ```
94//!
95//! Next, we use the output of [`Gclk1`] to enable the peripheral channel clock
96//! ([`Pclk`]) for [`Dpll0`]. This [`Increment`]s the counter for
97//! [`EnabledGclk1`].
98//!
99//! ```no_run
100//! # use atsamd_hal::{
101//! # clock::v2::{
102//! # clock_system_at_reset,
103//! # dpll::Dpll,
104//! # gclk::{Gclk, GclkDiv16},
105//! # pclk::Pclk,
106//! # },
107//! # pac::Peripherals,
108//! # };
109//! # let mut pac = Peripherals::take().unwrap();
110//! # let (buses, clocks, tokens) = clock_system_at_reset(
111//! # pac.oscctrl,
112//! # pac.osc32kctrl,
113//! # pac.gclk,
114//! # pac.mclk,
115//! # &mut pac.nvmctrl,
116//! # );
117//! # let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll);
118//! # let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable();
119//! let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1);
120//! ```
121//!
122//! Now we use [`Dpll::from_pclk`], which consumes the [`Pclk`] and returns an
123//! instance of [`Dpll0`]. We use builder API functions to set the loop divider
124//! to 100 and enable the [`Dpll`]. This will multiply the 2 MHz input clock to
125//! produce a 200 MHz output clock.
126//!
127//! ```no_run
128//! # use atsamd_hal::{
129//! # clock::v2::{
130//! # clock_system_at_reset,
131//! # dpll::Dpll,
132//! # gclk::{Gclk, GclkDiv16},
133//! # pclk::Pclk,
134//! # },
135//! # pac::Peripherals,
136//! # };
137//! # let mut pac = Peripherals::take().unwrap();
138//! # let (buses, clocks, tokens) = clock_system_at_reset(
139//! # pac.oscctrl,
140//! # pac.osc32kctrl,
141//! # pac.gclk,
142//! # pac.mclk,
143//! # &mut pac.nvmctrl,
144//! # );
145//! # let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll);
146//! # let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable();
147//! # let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1);
148//! let dpll0 = Dpll::from_pclk(tokens.dpll0, pclk_dpll0)
149//! .loop_div(100, 0)
150//! .enable();
151//! ```
152//!
153//! There are two things to note at this point.
154//!
155//! First, the loop divider has both an integer part and a fractional part.
156//! However, users should generally avoid using fractional division, if
157//! possible, because it increases the jitter of the output clock. See the
158//! [`Dpll::loop_div`] documentation for more details.
159//!
160//! Second, because the input clock frequency and loop division factors are
161//! run-time values, the [`Dpll`] cannot verify at compile time that the input
162//! and output frequencies satisfy the requirements specified in the
163//! [overview](self#overview). Instead, these values are checked at run-time. If
164//! either frequency violates its requirement, the call to [`Dpll::enable`] will
165//! panic.
166//!
167//! Finally, we wait until the [`EnabledDpll0`] output is ready, and then we
168//! swap the [`EnabledGclk0`], which feeds the processor master clock, from the
169//! 48 MHz [`EnabledDfll`] to the 200 MHz [`EnabledDpll0`].
170//!
171//! ```no_run
172//! # use atsamd_hal::{
173//! # clock::v2::{
174//! # clock_system_at_reset,
175//! # dpll::Dpll,
176//! # gclk::{Gclk, GclkDiv16},
177//! # pclk::Pclk,
178//! # },
179//! # pac::Peripherals,
180//! # };
181//! # let mut pac = Peripherals::take().unwrap();
182//! # let (buses, clocks, tokens) = clock_system_at_reset(
183//! # pac.oscctrl,
184//! # pac.osc32kctrl,
185//! # pac.gclk,
186//! # pac.mclk,
187//! # &mut pac.nvmctrl,
188//! # );
189//! # let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll);
190//! # let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable();
191//! # let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1);
192//! # let dpll0 = Dpll::from_pclk(tokens.dpll0, pclk_dpll0)
193//! # .loop_div(100, 0)
194//! # .enable();
195//! while !dpll0.is_ready() {}
196//! let (gclk0, dfll, dpll0) = clocks.gclk0.swap_sources(dfll, dpll0);
197//! ```
198//!
199//! We have now achieved the disired clock tree. The complete example is
200//! provided below.
201//!
202//! ```no_run
203//! use atsamd_hal::{
204//! clock::v2::{
205//! clock_system_at_reset,
206//! dpll::Dpll,
207//! gclk::{Gclk, GclkDiv16},
208//! pclk::Pclk,
209//! },
210//! pac::Peripherals,
211//! };
212//! let mut pac = Peripherals::take().unwrap();
213//! let (buses, clocks, tokens) = clock_system_at_reset(
214//! pac.oscctrl,
215//! pac.osc32kctrl,
216//! pac.gclk,
217//! pac.mclk,
218//! &mut pac.nvmctrl,
219//! );
220//! let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll);
221//! let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable();
222//! let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1);
223//! let dpll0 = Dpll::from_pclk(tokens.dpll0, pclk_dpll0)
224//! .loop_div(100, 0)
225//! .enable();
226//! while !dpll0.is_ready() {}
227//! let (gclk0, dfll, dpll0) = clocks.gclk0.swap_sources(dfll, dpll0);
228//! ```
229//!
230//! [`clock` module documentation]: super
231//! [`clock_system_at_reset`]: super::clock_system_at_reset
232//! [`Dfll`]: super::dfll::Dfll
233//! [`EnabledDfll`]: super::dfll::EnabledDfll
234//! [`EnabledGclk0`]: super::gclk::EnabledGclk0
235//! [`Gclk1`]: super::gclk::Gclk1
236//! [`EnabledGclk1`]: super::gclk::EnabledGclk1
237//! [`GclkDivider`]: super::gclk::GclkDivider
238//! [`Pclk`]: super::pclk::Pclk
239
240use core::marker::PhantomData;
241
242use fugit::RateExtU32;
243use typenum::U0;
244
245use crate::pac::oscctrl;
246use crate::pac::oscctrl::dpll::{dpllstatus, dpllsyncbusy, Dpllctrla, Dpllctrlb, Dpllratio};
247
248use crate::pac::oscctrl::dpll::dpllctrlb::Refclkselect;
249
250use crate::time::Hertz;
251use crate::typelevel::{Decrement, Increment, Sealed};
252
253use super::gclk::GclkId;
254use super::pclk::{Pclk, PclkId};
255use super::xosc::{Xosc0Id, Xosc1Id, XoscId};
256use super::xosc32k::Xosc32kId;
257use super::{Enabled, Source};
258
259//==============================================================================
260// DpllToken
261//==============================================================================
262
263/// Singleton token that can be exchanged for a [`Dpll`]
264///
265/// As explained in the [`clock` module documentation](super), instances of
266/// various `Token` types can be exchanged for actual clock types. They
267/// typically represent clocks that are disabled at power-on reset.
268///
269/// [`DpllToken`]s are no different. Both [`Dpll`]s are disabled at power-on
270/// reset. To use a [`Dpll`], you must first exchange the token for an actual
271/// clock with [`Dpll::from_pclk`], [`Dpll::from_xosc`] or
272/// [`Dpll::from_xosc32k`].
273///
274/// [`DpllToken`] is generic over the [`DpllId`], where each corresponding token
275/// represents one of the two respective [`Dpll`]s.
276pub struct DpllToken<D: DpllId> {
277 dpll: PhantomData<D>,
278}
279
280impl<D: DpllId> DpllToken<D> {
281 /// Create a new instance of [`DpllToken`]
282 ///
283 /// # Safety
284 ///
285 /// Each `DpllToken`s is a singleton. There must never be two simulatenous
286 /// instances with the same [`DpllId`]. See the notes on `Token` types and
287 /// memory safety in the root of the `clock` module for more details.
288 #[inline]
289 pub(super) unsafe fn new() -> Self {
290 Self { dpll: PhantomData }
291 }
292
293 /// Access the corresponding PAC `DPLL` struct
294 #[inline]
295 fn dpll(&self) -> &oscctrl::Dpll {
296 // Safety: Each `DpllToken` only has access to a mutually exclusive set
297 // of registers for the corresponding `DpllId`, and we use a shared
298 // reference to the register block. See the notes on `Token` types and
299 // memory safety in the root of the `clock` module for more details.
300 unsafe { (*crate::pac::Oscctrl::PTR).dpll(D::NUM) }
301 }
302
303 /// Access the corresponding Dpllctrla register
304 #[inline]
305 fn ctrla(&self) -> &Dpllctrla {
306 self.dpll().dpllctrla()
307 }
308
309 /// Access the corresponding Dpllctrlb register
310 #[inline]
311 fn ctrlb(&self) -> &Dpllctrlb {
312 self.dpll().dpllctrlb()
313 }
314
315 /// Access the corresponding Dpllratio register
316 #[inline]
317 fn ratio(&self) -> &Dpllratio {
318 self.dpll().dpllratio()
319 }
320
321 /// Access the corresponding DPLLSYNCBUSY register for reading only
322 #[inline]
323 fn syncbusy(&self) -> dpllsyncbusy::R {
324 self.dpll().dpllsyncbusy().read()
325 }
326
327 /// Access the corresponding DPLLSTATUS register for reading only
328 #[inline]
329 fn status(&self) -> dpllstatus::R {
330 self.dpll().dpllstatus().read()
331 }
332
333 #[inline]
334 fn configure(&mut self, id: DynDpllSourceId, settings: Settings, prediv: u16) {
335 // Convert the actual predivider to the `div` register field value
336 let div = match id {
337 DynDpllSourceId::Xosc0 | DynDpllSourceId::Xosc1 => prediv / 2 - 1,
338 _ => 0,
339 };
340 self.ctrlb().modify(|_, w| {
341 // Safety: The value is masked to the correct bit width by the PAC.
342 // An invalid value could produce an invalid clock frequency, but
343 // that does not break memory safety.
344 unsafe { w.div().bits(div) };
345 w.refclk().variant(id.into());
346 w.lbypass().bit(settings.lock_bypass);
347 w.wuf().bit(settings.wake_up_fast);
348 if let Some(cap) = settings.dco_filter {
349 w.dcoen().bit(true);
350 unsafe { w.dcofilter().bits(cap as u8); }
351 } else {
352 w.dcoen().bit(false);
353 }
354 unsafe { w.filter().bits(settings.filter as u8) }
355 });
356 // Safety: The values are masked to the correct bit width by the PAC.
357 // Invalid values here could produce invalid clock frequencies, but that
358 // does not break memory safety.
359 self.ratio().write(|w| unsafe {
360 w.ldr().bits(settings.mult - 1);
361 w.ldrfrac().bits(settings.frac)
362 });
363 while self.syncbusy().dpllratio().bit_is_set() {}
364 self.ctrla().modify(|_, w| {
365 w.ondemand().bit(settings.on_demand);
366 w.runstdby().bit(settings.run_standby)
367 });
368 }
369
370 /// Enable the [`Dpll`]
371 #[inline]
372 fn enable(&mut self) {
373 self.ctrla().modify(|_, w| w.enable().set_bit());
374 while self.syncbusy().enable().bit_is_set() {}
375 }
376
377 /// Disable the [`Dpll`]
378 #[inline]
379 fn disable(&mut self) {
380 self.ctrla().modify(|_, w| w.enable().clear_bit());
381 while self.syncbusy().enable().bit_is_set() {}
382 }
383
384 /// Check the STATUS register to see if the clock is locked
385 #[inline]
386 fn is_locked(&self) -> bool {
387 self.status().lock().bit()
388 }
389
390 /// Check the STATUS register to see if the clock is ready
391 #[inline]
392 fn is_ready(&self) -> bool {
393 self.status().clkrdy().bit()
394 }
395}
396
397//==============================================================================
398// DynDpllId
399//==============================================================================
400
401/// Value-level enum identifying one of two possible [`Dpll`]s
402///
403/// The variants of this enum identify one of the two possible digital
404/// phase-locked loops.
405///
406/// `DynDpllId` is the value-level equivalent of [`DpllId`].
407pub enum DynDpllId {
408 Dpll0,
409 Dpll1,
410}
411
412//==============================================================================
413// DpllId
414//==============================================================================
415
416/// Type-level enum identifying one of two possible [`Dpll`]s
417///
418/// The types implementing this trait, i.e. [`Dpll0Id`] and [`Dpll1Id`], are
419/// type-level variants of `DpllId`, and they identify one of the two possible
420/// digital phase-locked loops.
421///
422/// `DpllId` is the type-level equivalent of [`DynDpllId`]. See the
423/// documentation on [type-level programming] and specifically
424/// [type-level enums] for more details.
425///
426/// [type-level programming]: crate::typelevel
427/// [type-level enums]: crate::typelevel#type-level-enums
428pub trait DpllId: Sealed + PclkId {
429 /// Corresponding variant of [`DynDpllId`]
430 const DYN: DynDpllId;
431 /// Corresponding numeric index
432 const NUM: usize;
433}
434
435/// Type-level variant of [`DpllId`] representing the identity of DPLL0
436///
437/// See the documentation on [type-level programming] and specifically
438/// [type-level enums] for more details.
439///
440/// [type-level programming]: crate::typelevel
441/// [type-level enums]: crate::typelevel#type-level-enums
442pub enum Dpll0Id {}
443
444impl Sealed for Dpll0Id {}
445
446impl DpllId for Dpll0Id {
447 const DYN: DynDpllId = DynDpllId::Dpll0;
448 const NUM: usize = 0;
449}
450
451/// Type-level variant of [`DpllId`] representing the identity of DPLL1
452///
453/// See the documentation on [type-level programming] and specifically
454/// [type-level enums] for more details.
455///
456/// [type-level programming]: crate::typelevel
457/// [type-level enums]: crate::typelevel#type-level-enums
458pub enum Dpll1Id {}
459
460impl Sealed for Dpll1Id {}
461
462impl DpllId for Dpll1Id {
463 const DYN: DynDpllId = DynDpllId::Dpll1;
464 const NUM: usize = 1;
465}
466
467//==============================================================================
468// DynDpllSourceId
469//==============================================================================
470
471/// Value-level enum of possible clock sources for a [`Dpll`]
472///
473/// The variants of this enum identify one of four possible clock sources for
474/// a given [`Dpll`].
475///
476/// `DynDpllSourceId` is the value-level equivalent of [`DpllSourceId`].
477#[derive(Copy, Clone, PartialEq, Eq, Debug)]
478pub enum DynDpllSourceId {
479 /// The DPLL is driven by a [`Pclk`]
480 Pclk,
481 /// The DPLL is driven by [`Xosc0`](super::xosc::Xosc0)
482 Xosc0,
483 /// The DPLL is driven by [`Xosc1`](super::xosc::Xosc1)
484 Xosc1,
485 /// The DPLL is driven by [`Xosc32k`](super::xosc32k::Xosc32k)
486 Xosc32k,
487}
488
489impl From<DynDpllSourceId> for Refclkselect {
490 fn from(source: DynDpllSourceId) -> Self {
491 match source {
492 DynDpllSourceId::Pclk => Refclkselect::Gclk,
493 DynDpllSourceId::Xosc0 => Refclkselect::Xosc0,
494 DynDpllSourceId::Xosc1 => Refclkselect::Xosc1,
495 DynDpllSourceId::Xosc32k => Refclkselect::Xosc32,
496 }
497 }
498}
499
500//==============================================================================
501// DpllSourceId
502//==============================================================================
503
504/// Type-level enum of possible clock [`Source`]s for a [`Dpll`]
505///
506/// The types implementing this trait are type-level variants of `DpllSourceId`,
507/// and they identify one of four possible clock [`Source`]s for a given
508/// [`Dpll`]. All implementers of this trait are `Id` types, which are described
509/// in more detail in the [`clock` module documentation](super).
510///
511/// `DpllSourceId` is the type-level equivalent of [`DynDpllSourceId`]. See the
512/// documentation on [type-level programming] and specifically
513/// [type-level enums] for more details.
514///
515/// [type-level programming]: crate::typelevel
516/// [type-level enums]: crate::typelevel#type-level-enums
517pub trait DpllSourceId {
518 /// Corresponding variant of [`DynDpllSourceId`]
519 const DYN: DynDpllSourceId;
520
521 /// Reference-specific settings type
522 #[doc(hidden)]
523 type Reference<D: DpllId>: settings::Reference;
524}
525
526impl<G: GclkId> DpllSourceId for G {
527 const DYN: DynDpllSourceId = DynDpllSourceId::Pclk;
528 type Reference<D: DpllId> = settings::Pclk<D, G>;
529}
530impl DpllSourceId for Xosc0Id {
531 const DYN: DynDpllSourceId = DynDpllSourceId::Xosc0;
532 type Reference<D: DpllId> = settings::Xosc;
533}
534impl DpllSourceId for Xosc1Id {
535 const DYN: DynDpllSourceId = DynDpllSourceId::Xosc1;
536 type Reference<D: DpllId> = settings::Xosc;
537}
538impl DpllSourceId for Xosc32kId {
539 const DYN: DynDpllSourceId = DynDpllSourceId::Xosc32k;
540 type Reference<D: DpllId> = settings::Xosc32k;
541}
542
543//==============================================================================
544// Settings
545//==============================================================================
546
547/// [`Dpll`] Proportional Integral Filter
548///
549/// Filter settings affect PLL stability and jitter. The datasheet suggests a
550/// good compromise is automatically selected, however this API allows manual
551/// selection.
552#[derive(Copy, Clone)]
553pub enum PiFilter {
554 /// PLL Bandwidth 23.2kHz, Damping Factor 0.75
555 Bw23p2kHzDf0p75 = 0xA,
556 /// PLL Bandwidth 32.8kHz, Damping Factor 0.53
557 Bw32p8kHzDf0p53 = 0xE,
558 /// PLL Bandwidth 32.8kHz, Damping Factor 1.06
559 Bw32p8kHzDf1p06 = 0xB,
560 /// PLL Bandwidth 46.4kHz, Damping Factor 0.38
561 Bw46p4kHzDf0p38 = 0x2,
562 /// PLL Bandwidth 46.4kHz, Damping Factor 0.75
563 Bw46p4kHzDf0p75 = 0xF,
564 /// PLL Bandwidth 46.4kHz, Damping Factor 1.49
565 Bw46p4kHzDf1p49 = 0x8,
566 /// PLL Bandwidth 65.6kHz, Damping Factor 0.28
567 Bw65p6kHzDf0p28 = 0x6,
568 /// PLL Bandwidth 65.6kHz, Damping Factor 0.54
569 Bw65p6kHzDf0p54 = 0x3,
570 /// PLL Bandwidth 65.6kHz, Damping Factor 1.07
571 Bw65p6kHzDf1p07 = 0xC,
572 /// PLL Bandwidth 65.6kHz, Damping Factor 2.11
573 Bw65p6kHzDf2p11 = 0x9,
574 /// PLL Bandwidth 92.7kHz, Damping Factor 0.39
575 Bw92p7kHzDf0p39 = 0x7,
576 /// PLL Bandwidth 92.7kHz, Damping Factor 0.76
577 Bw92p7kHzDf0p76 = 0x0,
578 /// PLL Bandwidth 92.7kHz, Damping Factor 1.51
579 Bw92p7kHzDf1p51 = 0xD,
580 /// PLL Bandwidth 131kHz, Damping Factor 0.56
581 Bw131kHzDf0p56 = 0x4,
582 /// PLL Bandwidth 131kHz, Damping Factor 1.08
583 Bw131kHzDf1p08 = 0x1,
584 /// PLL Bandwidth 185kHz, Damping Factor 0.79
585 Bw185kHzDf0p79 = 0x5,
586}
587
588/// Capacitor choice for DCO filter
589#[derive(Copy, Clone)]
590pub enum DcoFilter {
591 /// 0.5pF, Bandwidth Fn 3.21MHz
592 C0p5pF = 0,
593 /// 1pF, Bandwidth Fn 1.6MHz
594 C1pF = 1,
595 /// 1.5pF, Bandwidth Fn 1.1MHz
596 C1p5pF = 2,
597 /// 2pF, Bandwidth Fn 0.8MHz
598 C2pF = 3,
599 /// 2.5pF, Bandwidth Fn 0.64MHz
600 C2p5pF = 4,
601 /// 3pF, Bandwidth Fn 0.55MHz
602 C3pF = 5,
603 /// 3.5pF, Bandwidth Fn 0.45MHz
604 C3p5pF = 6,
605 /// 4pF, Bandwidth Fn 0.4MHz
606 C4pF = 7,
607}
608
609
610/// [`Dpll`] settings relevant to all reference clocks
611#[derive(Copy, Clone)]
612struct Settings {
613 mult: u16,
614 frac: u8,
615 lock_bypass: bool,
616 wake_up_fast: bool,
617 on_demand: bool,
618 run_standby: bool,
619 filter: PiFilter,
620 dco_filter: Option<DcoFilter>,
621}
622
623/// Store and retrieve [`Dpll`] settings for different reference clocks
624mod settings {
625 use super::super::pclk;
626 use super::RateExtU32;
627 use super::{DpllId, GclkId, Hertz};
628
629 /// [`Dpll`] settings when referenced to a [`Pclk`]
630 ///
631 /// [`Dpll`]: super::Dpll
632 /// [`Pclk`]: pclk::Pclk
633 pub struct Pclk<D: DpllId, G: GclkId> {
634 pub pclk: pclk::Pclk<D, G>,
635 }
636
637 /// [`Dpll`] settings when referenced to an [`Xosc`]
638 ///
639 /// [`Dpll`]: super::Dpll
640 /// [`Xosc`]: super::super::xosc::Xosc
641 pub struct Xosc {
642 pub freq: Hertz,
643 pub prediv: u16,
644 }
645
646 /// [`Dpll`] settings when referenced to an [`Xosc32k`]
647 ///
648 /// [`Dpll`]: super::Dpll
649 /// [`Xosc32k`]: super::super::xosc32k::Xosc32k
650 pub struct Xosc32k;
651
652 /// Generic interface for the frequency and predivider of a reference clock
653 pub trait Reference {
654 fn freq(&self) -> Hertz;
655 fn prediv(&self) -> u16;
656 }
657
658 impl<D: DpllId, G: GclkId> Reference for Pclk<D, G> {
659 #[inline]
660 fn freq(&self) -> Hertz {
661 self.pclk.freq()
662 }
663 #[inline]
664 fn prediv(&self) -> u16 {
665 1
666 }
667 }
668
669 impl Reference for Xosc {
670 #[inline]
671 fn freq(&self) -> Hertz {
672 self.freq
673 }
674 #[inline]
675 fn prediv(&self) -> u16 {
676 self.prediv
677 }
678 }
679
680 impl Reference for Xosc32k {
681 #[inline]
682 fn freq(&self) -> Hertz {
683 32_768.Hz()
684 }
685 #[inline]
686 fn prediv(&self) -> u16 {
687 1
688 }
689 }
690}
691
692//==============================================================================
693// Dpll
694//==============================================================================
695
696/// Digital phase-locked loop used to multiply clock frequencies
697///
698/// A DPLL is used to multiply clock frequencies, taking a lower-frequency input
699/// clock and producing a higher-frequency output clock.
700///
701/// The type parameter `D` is a [`DpllId`] that determines which of the two
702/// instances this `Dpll` represents ([`Dpll0`] or [`Dpll1`]). The type
703/// parameter `I` represents the `Id` type for the clock [`Source`] driving this
704/// `Dpll`. It must be one of the valid [`DpllSourceId`]s. See the
705/// [`clock` module documentation](super) for more detail on
706/// [`Id` types](super#id-types).
707///
708/// On its own, an instance of `Dpll` does not represent an enabled DPLL.
709/// Instead, it must first be wrapped with [`Enabled`], which implements
710/// compile-time safety of the clock tree.
711///
712/// Because the terminal call to [`enable`] consumes the `Dpll` and returns an
713/// [`EnabledDpll`], the remaining API uses the builder pattern, where each
714/// method takes and returns `self` by value, allowing them to be easily
715/// chained.
716///
717/// See the [module-level documentation](self) for an example of creating,
718/// configuring and using a `Dpll`.
719///
720/// [`enable`]: Dpll::enable
721pub struct Dpll<D, I>
722where
723 D: DpllId,
724 I: DpllSourceId,
725{
726 token: DpllToken<D>,
727 reference: I::Reference<D>,
728 settings: Settings,
729}
730
731/// Type alias for the corresponding [`Dpll`]
732pub type Dpll0<M> = Dpll<Dpll0Id, M>;
733
734/// Type alias for the corresponding [`Dpll`]
735pub type Dpll1<M> = Dpll<Dpll1Id, M>;
736
737impl<D, I> Dpll<D, I>
738where
739 D: DpllId,
740 I: DpllSourceId,
741{
742 fn new(token: DpllToken<D>, reference: I::Reference<D>) -> Self {
743 let settings = Settings {
744 mult: 1,
745 frac: 0,
746 lock_bypass: false,
747 wake_up_fast: false,
748 on_demand: true,
749 run_standby: false,
750 filter: PiFilter::Bw92p7kHzDf0p76,
751 dco_filter: None,
752 };
753 Self {
754 token,
755 reference,
756 settings,
757 }
758 }
759}
760
761impl<D, G> Dpll<D, G>
762where
763 D: DpllId,
764 G: GclkId,
765{
766 /// Create a [`Dpll`] from a [`Pclk`]
767 ///
768 /// Creating a [`Dpll`] does not modify any of the hardware registers. It
769 /// only creates a struct to track the DPLL configuration.
770 ///
771 /// The configuration data is stored until the user calls [`enable`]. At
772 /// that point, all of the registers are written according to the
773 /// initialization procedures specified in the datasheet, and an
774 /// [`EnabledDpll`] is returned. The `Dpll` is not active or useful until
775 /// that point.
776 ///
777 /// [`enable`]: Dpll::enable
778 #[inline]
779 pub fn from_pclk(token: DpllToken<D>, pclk: Pclk<D, G>) -> Self {
780 let reference = settings::Pclk { pclk };
781 Dpll::new(token, reference)
782 }
783
784 /// Consume the [`Dpll`], release the [`DpllToken`], and return the [`Pclk`]
785 #[inline]
786 pub fn free_pclk(self) -> (DpllToken<D>, Pclk<D, G>) {
787 (self.token, self.reference.pclk)
788 }
789}
790
791impl<D, X> Dpll<D, X>
792where
793 D: DpllId,
794 X: XoscId + DpllSourceId<Reference<D> = settings::Xosc>,
795{
796 /// Create a [`Dpll`] from an [`Xosc`]
797 ///
798 /// Note that, when the [`Dpll`] is driven by an [`Xosc`], there is an extra
799 /// clock divider between the `Xosc` output and the input to the actual
800 /// phase-locked loop. This allows the [`Xosc`] frequency to be above the
801 /// maximum DPLL input frequency of 3.2 MHz.
802 ///
803 /// The `Xosc` pre-divider can be set to any *even* value in the range
804 /// `[2, 4096]`. It defaults to the minimum value of 2, but it can be
805 /// changed with the [`Dpll::prediv`] method.
806 ///
807 /// Creating a [`Dpll`] does not modify any of the hardware registers. It
808 /// only creates a struct to track the DPLL configuration and [`Increment`]s
809 /// the [`Source`] [`Enabled`] counter.
810 ///
811 /// The configuration data is stored until the user calls [`enable`]. At
812 /// that point, all of the registers are written according to the
813 /// initialization procedures specified in the datasheet, and an
814 /// [`EnabledDpll`] is returned. The `Dpll` is not active or useful until
815 /// that point.
816 ///
817 /// [`Xosc`]: super::xosc::Xosc
818 /// [`enable`]: Dpll::enable
819 #[inline]
820 pub fn from_xosc<S>(token: DpllToken<D>, source: S) -> (Self, S::Inc)
821 where
822 S: Source<Id = X> + Increment,
823 {
824 let reference = settings::Xosc {
825 freq: source.freq(),
826 prediv: 2,
827 };
828 let dpll = Dpll::new(token, reference);
829 (dpll, source.inc())
830 }
831
832 /// Consume the [`Dpll`], release the [`DpllToken`], and [`Decrement`] the
833 /// [`EnabledXosc`] consumer count
834 ///
835 /// [`EnabledXosc`]: super::xosc::EnabledXosc
836 #[inline]
837 pub fn free_xosc<S>(self, source: S) -> (DpllToken<D>, S::Dec)
838 where
839 S: Source<Id = X> + Decrement,
840 {
841 (self.token, source.dec())
842 }
843
844 /// Set the [`Xosc`] pre-division factor
845 ///
846 /// The [`Xosc`] output frequency is divided down before it enters the
847 /// actual phase-locked loop. This function will panic if the pre-division
848 /// factor is not an *even* number in the range `[2, 4096]`.
849 ///
850 /// [`Xosc`]: super::xosc::Xosc
851 #[inline]
852 pub fn prediv(mut self, prediv: u16) -> Self {
853 if prediv % 2 != 0 || prediv < 2 || prediv > 4096 {
854 panic!("DPLL prediv must be an even integer in the range [2, 4096]")
855 }
856 self.reference.prediv = prediv;
857 self
858 }
859}
860
861impl<D: DpllId> Dpll<D, Xosc32kId> {
862 /// Create a [`Dpll`] from an [`Xosc32k`]
863 ///
864 /// Creating a [`Dpll`] does not modify any of the hardware registers. It
865 /// only creates a struct to track the DPLL configuration and [`Increment`]s
866 /// the [`Source`] [`Enabled`] counter.
867 ///
868 /// The configuration data is stored until the user calls [`enable`]. At
869 /// that point, all of the registers are written according to the
870 /// initialization procedures specified in the datasheet, and an
871 /// [`EnabledDpll`] is returned. The `Dpll` is not active or useful until
872 /// that point.
873 ///
874 /// [`Xosc32k`]: super::xosc32k::Xosc32k
875 /// [`enable`]: Dpll::enable
876 #[inline]
877 pub fn from_xosc32k<S>(token: DpllToken<D>, source: S) -> (Self, S::Inc)
878 where
879 S: Source<Id = Xosc32kId> + Increment,
880 {
881 let dpll = Dpll::new(token, settings::Xosc32k);
882 (dpll, source.inc())
883 }
884
885 /// Consume the [`Dpll`], release the [`DpllToken`], and [`Decrement`] the
886 /// [`EnabledXosc32k`] consumer count
887 ///
888 /// [`EnabledXosc32k`]: super::xosc32k::EnabledXosc32k
889 /// d`] consumer count
890 pub fn free_xosc32k<S>(self, source: S) -> (DpllToken<D>, S::Dec)
891 where
892 S: Source<Id = Xosc32kId> + Decrement,
893 {
894 (self.token, source.dec())
895 }
896}
897
898impl<D, I> Dpll<D, I>
899where
900 D: DpllId,
901 I: DpllSourceId,
902{
903 /// Set the [`Dpll`] loop divider, which is also the frequency
904 /// multiplication factor
905 ///
906 /// The inputs to this function are the natural integer and fractional
907 /// parts of the division factor, i.e. the division factor is:
908 ///
909 /// ```text
910 /// int + frac / 32
911 /// ```
912 ///
913 /// This function will confirm that the `int` and `frac` values convert to
914 /// valid `LDR` and `LDRFRAC` register fields, panicking otherwise.
915 #[inline]
916 pub fn loop_div(mut self, int: u16, frac: u8) -> Self {
917 if int < 1 || int > 0x2000 {
918 panic!("Invalid integer part of the DPLL loop divider")
919 }
920 if frac > 31 {
921 panic!("Invalid fractional part of the DPLL loop divider")
922 }
923 self.settings.mult = int;
924 self.settings.frac = frac;
925 self
926 }
927
928 /// Bypass the [`Dpll`] lock
929 ///
930 /// If `true`, the [`Dpll`] will output its clock regardless of whether it
931 /// is locked.
932 #[inline]
933 pub fn lock_bypass(mut self, bypass: bool) -> Self {
934 self.settings.lock_bypass = bypass;
935 self
936 }
937
938 /// Output the [`Dpll`] clock immediately, without waiting for various
939 /// conditions
940 ///
941 /// See the datasheet for complete details.
942 #[inline]
943 pub fn wake_up_fast(mut self, wuf: bool) -> Self {
944 self.settings.wake_up_fast = wuf;
945 self
946 }
947
948 /// Set digital PI Filter coefficients
949 ///
950 /// Filter settings affect PLL stability and jitter. The datasheet suggests
951 /// a good compromise is automatically selected, however this API allows
952 /// manual selection.
953 #[inline]
954 pub fn filter(mut self, filter: PiFilter) -> Self {
955 self.settings.filter = filter;
956 self
957 }
958
959 /// Enable sigma-delta DAC low pass filter
960 #[inline]
961 pub fn dco_filter(mut self, capacitor: DcoFilter) -> Self {
962 self.settings.dco_filter = Some(capacitor);
963 self
964 }
965
966 /// Set on-demand mode
967 ///
968 /// See the datasheet for complete details.
969 #[inline]
970 pub fn on_demand(mut self, on_demand: bool) -> Self {
971 self.settings.on_demand = on_demand;
972 self
973 }
974
975 /// Set run-in-standby mode
976 ///
977 /// See the datasheet for complete details.
978 #[inline]
979 pub fn run_standby(mut self, run_standby: bool) -> Self {
980 self.settings.run_standby = run_standby;
981 self
982 }
983
984 #[inline]
985 fn input_freq(&self) -> Hertz {
986 use settings::Reference;
987 self.reference.freq() / self.reference.prediv() as u32
988 }
989
990 #[inline]
991 fn output_freq(&self) -> Hertz {
992 self.input_freq() * (self.settings.mult as u32 + self.settings.frac as u32 / 32)
993 }
994
995 /// Return the output frequency of the [`Dpll`]
996 #[inline]
997 pub fn freq(&self) -> Hertz {
998 self.output_freq()
999 }
1000
1001 /// Enable the [`Dpll`], so that it can be used as a clock [`Source`]
1002 ///
1003 /// As mentioned when creating a new `Dpll`, no hardware registers are
1004 /// actually modified until this call. Rather, the desired configuration is
1005 /// stored internally, and the [`Dpll`] is initialized and configured here
1006 /// according to the datasheet.
1007 ///
1008 /// The returned value is an [`EnabledDpll`] that can be used as a clock
1009 /// [`Source`] for other clocks.
1010 ///
1011 /// # Panics
1012 ///
1013 /// This function will also check that the input and output clock
1014 /// frequencies fall within the valid ranges specified in the datasheet.
1015 /// Specifically, the input frequency must be between 32 kHz and 3.2 MHz,
1016 /// while the output frequency must be between 96 MHz and 200 MHz. If either
1017 /// frequency is invalid, this call will panic.
1018 #[inline]
1019 pub fn enable(self) -> EnabledDpll<D, I> {
1020 let input_freq = self.input_freq().to_Hz();
1021 let output_freq = self.output_freq().to_Hz();
1022 if input_freq < 32_000 || input_freq > 3_200_000 {
1023 panic!("Invalid DPLL input frequency");
1024 }
1025 if output_freq < 96_000_000 || output_freq > 200_000_000 {
1026 panic!("Invalid DPLL output frequency");
1027 }
1028 self.enable_unchecked()
1029 }
1030
1031 /// Enable the [`Dpll`] without validating the input & output frequencies
1032 ///
1033 /// This is equivalent to calling [`Dpll::enable`] but without the checks on
1034 /// input and output frequencies. Using frequencies outside the ranges
1035 /// specified in the datasheet may not work and could cause clocking
1036 /// problems.
1037 #[inline]
1038 pub fn enable_unchecked(mut self) -> EnabledDpll<D, I> {
1039 use settings::Reference;
1040 let prediv = self.reference.prediv();
1041 self.token.configure(I::DYN, self.settings, prediv);
1042 self.token.enable();
1043 Enabled::new(self)
1044 }
1045}
1046
1047//==============================================================================
1048// EnabledDpll
1049//==============================================================================
1050
1051/// An [`Enabled`] [`Dpll`]
1052///
1053/// As described in the [`clock` module documentation](super), the [`Enabled`]
1054/// wrapper implements compile-time clock tree safety by tracking the number of
1055/// consumer clocks and restricting access to the underlying [`Dpll`] to prevent
1056/// modification while in use.
1057///
1058/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified,
1059/// the counter is assumed to be zero.
1060pub type EnabledDpll<D, I, N = U0> = Enabled<Dpll<D, I>, N>;
1061
1062/// Type alias for the corresponding [`EnabledDpll`]
1063pub type EnabledDpll0<I, N = U0> = EnabledDpll<Dpll0Id, I, N>;
1064
1065/// Type alias for the corresponding [`EnabledDpll`]
1066pub type EnabledDpll1<I, N = U0> = EnabledDpll<Dpll1Id, I, N>;
1067
1068impl<D, I> EnabledDpll<D, I>
1069where
1070 D: DpllId,
1071 I: DpllSourceId,
1072{
1073 /// Disable the [`Dpll`]
1074 ///
1075 /// This method is only implemented for `N = U0`, which means the clock can
1076 /// only be disabled when no other clocks consume this [`Dpll`].
1077 #[inline]
1078 pub fn disable(mut self) -> Dpll<D, I> {
1079 self.0.token.disable();
1080 self.0
1081 }
1082}
1083
1084impl<D, I, N> EnabledDpll<D, I, N>
1085where
1086 D: DpllId,
1087 I: DpllSourceId,
1088{
1089 /// Test whether the [`Dpll`] is locked
1090 #[inline]
1091 pub fn is_locked(&self) -> bool {
1092 self.0.token.is_locked()
1093 }
1094
1095 /// Test whether the [`Dpll`] is ready
1096 #[inline]
1097 pub fn is_ready(&self) -> bool {
1098 self.0.token.is_ready()
1099 }
1100}
1101
1102//==============================================================================
1103// Source
1104//==============================================================================
1105
1106impl<D, I, N> Source for EnabledDpll<D, I, N>
1107where
1108 D: DpllId,
1109 I: DpllSourceId,
1110{
1111 type Id = D;
1112
1113 #[inline]
1114 fn freq(&self) -> Hertz {
1115 self.0.freq()
1116 }
1117}