|
65 | 65 | //! In general, there are two classes of clock in ATSAMD chips. Some clocks map |
66 | 66 | //! one-to-one (1:1) to a specific bus or peripheral. This is true for the AHB |
67 | 67 | //! clocks ([`AhbClk`]s), APB clocks ([`ApbClk`]s), GCLK outputs ([`GclkOut`]s), |
68 | | -//! peripheral channel clocks ([`Pclk`]s), and RTC oscillator ([`RtcOsc`]). |
| 68 | +//! peripheral channel clocks ([`Pclk`]s), and RTC oscillator (`RtcOsc`). |
69 | 69 | //! Other clocks form one-to-many (1:N) relationships, like the external crystal |
70 | 70 | //! oscillator ([`Xosc`]), the 48 MHz DFLL ([`Dfll`]) or the two DPLLs |
71 | 71 | //! ([`Dpll`]). |
|
115 | 115 | //! on the movement of `Producer` objects. |
116 | 116 | //! |
117 | 117 | //! Instead, the `clock` module takes a different approach. It uses type-level |
118 | | -//! programming to track, at compile-time, the number of consumer clocks, N, |
119 | | -//! fed by a particular producer clock. With this approach, we can move |
120 | | -//! `Producer` objects while still making them impossible to modify if N > 0. |
| 118 | +//! programming to track, at compile-time, the number of consumer clocks, N, fed |
| 119 | +//! by a particular producer clock. With this approach, we can move `Producer` |
| 120 | +//! objects while still making them impossible to modify if N > 0. |
121 | 121 | //! |
122 | 122 | //! The following sections will describe the implementation of this strategy. |
123 | 123 | //! |
|
175 | 175 | //! can only `Decrement` the same producer it `Increment`ed. Stated differently, |
176 | 176 | //! we need a way to track the identity of each consumer's clock source. |
177 | 177 | //! |
178 | | -//! The [`Source`] trait is designed for this purpose. It marks |
179 | | -//! [`Enabled<T, N>`] producer clocks, and it's associated type, [`Id`], is the |
180 | | -//! identity type that should be stored by consumers. |
| 178 | +//! The [`Source`] trait is designed for this purpose. It marks [`Enabled<T, |
| 179 | +//! N>`] producer clocks, and it's associated type, [`Id`], is the identity type |
| 180 | +//! that should be stored by consumers. |
181 | 181 | //! |
182 | 182 | //! Given that all implementers of `Source` are instances of `Enabled<T, N>`, |
183 | 183 | //! the naïve choice for [`Source::Id`] would be `T`. However, in a moment, we |
|
194 | 194 | //! |
195 | 195 | //! While these type parameters are important and necessary for configuration of |
196 | 196 | //! a given producer clock, they are not relevant to consumer clocks. A consumer |
197 | | -//! clock does not need to know or care which `Mode` the XOSC is using, but |
198 | | -//! it *does* need to track that its clock [`Source`] is XOSC0. |
| 197 | +//! clock does not need to know or care which `Mode` the XOSC is using, but it |
| 198 | +//! *does* need to track that its clock [`Source`] is XOSC0. |
199 | 199 | //! |
200 | 200 | //! From this, we can see that `Enabled<Xosc0<M>, N>` should not implement |
201 | 201 | //! `Source` with `Source::Id = Xosc0<M>`, because that would require consumers |
|
219 | 219 | //! corresponding clock. Moreover, they also fundamentally restructure the way |
220 | 220 | //! registers are accessed relative to the [PAC]. |
221 | 221 | //! |
222 | | -//! Each of the four PAC clocking structs ([`OSCCTRL`], [`OSC32KCTRL`], [`GCLK`] |
223 | | -//! and [`MCLK`]) is a singleton object that controls a set of MMIO registers. |
224 | | -//! It is impossible to create two instances of any PAC object without `unsafe`. |
225 | | -//! However, each object controls a large set of registers that can be further |
226 | | -//! sub-divided into smaller sets for individual clocks. For example, the |
227 | | -//! [`GCLK`] object controls registers for 12 different clock generators and 48 |
228 | | -//! peripheral channel clocks. |
| 222 | +//! Each of the PAC clocking structs (which vary between targets, including |
| 223 | +//! `OSCCTRL`, `SYSCTRL`, `OSC32KCTRL`, [`GCLK`] and `MCLK`) is a singleton object |
| 224 | +//! that controls a set of MMIO registers. It is impossible to create two |
| 225 | +//! instances of any PAC object without `unsafe`. However, each object controls |
| 226 | +//! a large set of registers that can be further sub-divided into smaller sets |
| 227 | +//! for individual clocks. For example, the [`GCLK`] object controls registers |
| 228 | +//! for 12 different clock generators and 48 peripheral channel clocks. |
229 | 229 | //! |
230 | 230 | //! `Token` types serve to break up the large PAC objects into smaller, |
231 | 231 | //! more-targetted pieces. And in the process, they also remove the PAC objects' |
|
238 | 238 | //! Bus clocks are fundamentally different from the other clock types in this |
239 | 239 | //! module, because they do not use mutually exclusive registers for |
240 | 240 | //! configuration. For instance, the registers that control [`Dpll0`] are |
241 | | -//! mutually exclusive to those that control [`Dpll1`], but `ApbClk<Sercom0>` |
242 | | -//! and `ApbClk<Sercom1>` share a single register. |
| 241 | +//! mutually exclusive to those that control `Dpll1`, but `ApbClk<Sercom0>` and |
| 242 | +//! `ApbClk<Sercom1>` share a single register. |
243 | 243 | //! |
244 | 244 | //! This presents a challenge for memory safety, because we need some way to |
245 | 245 | //! guarantee that there are no data races. For example, if both |
|
403 | 403 | //! |
404 | 404 | //! Next, we want to use a DPLL to multiply the 8 MHz crystal clock up to 100 |
405 | 405 | //! MHz. Once again, we need to decide between two instances of a clock, because |
406 | | -//! each chip has two [`Dpll`]s. This time, however, our decision between |
407 | | -//! [`Dpll0`] and [`Dpll1`] is arbitrary. |
| 406 | +//! this chip has two [`Dpll`]s. This time, however, our decision between |
| 407 | +//! [`Dpll0`] and `Dpll1` is arbitrary. |
408 | 408 | //! |
409 | 409 | //! Also note that, like before, `Dpll0<I>` and `Dpll1<I>` are aliases for |
410 | | -//! `Dpll<Dpll0Id, I>` and `Dpll<Dpll1Id, I>`. [`Dpll0Id`] and [`Dpll1Id`] |
| 410 | +//! `Dpll<Dpll0Id, I>` and `Dpll<Dpll1Id, I>`. [`Dpll0Id`] and `Dpll1Id` |
411 | 411 | //! represent the *identity* of the respective DPLL, while `I` represents the |
412 | 412 | //! [`Id` type](self#id-types) for the [`Source`] driving the DPLL. In this |
413 | 413 | //! particular case, we aim to create an instance of `Dpll0<Xosc0Id>`. |
414 | 414 | //! |
415 | 415 | //! Only certain clocks can drive the DPLL, so `I` is constrained by the |
416 | | -//! [`DpllSourceId`] trait. Specifically, only the [`Xosc0Id`], [`Xosc1Id`], |
417 | | -//! [`Xosc32kId`] and [`GclkId`] types implement this trait. |
| 416 | +//! [`DpllSourceId`] trait. Specifically, only the [`Xosc0Id`], `Xosc1Id` (only |
| 417 | +//! some targets), [`Xosc32kId`] and [`GclkId`] types implement this trait. |
418 | 418 | //! |
419 | 419 | //! As before, we access the [`Tokens`] struct and use the corresponding |
420 | 420 | //! [`DpllToken`] when creating an instance of `Dpll`. However, unlike before, |
421 | 421 | //! we are creating a new clock-tree relationship that must be tracked by the |
422 | | -//! type system. Because DPLL0 will now consume XOSC0, we must [`Increment`] |
423 | | -//! the [`Enabled`] counter for [`EnabledXosc0`]. |
| 422 | +//! type system. Because DPLL0 will now consume XOSC0, we must [`Increment`] the |
| 423 | +//! [`Enabled`] counter for [`EnabledXosc0`]. |
424 | 424 | //! |
425 | 425 | //! Thus, to create an instance of `Dpll0<XoscId0>`, we must provide the |
426 | 426 | //! `EnabledXosc0`, so that its `U0` type parameter can be incremented to `U1`. |
|
461 | 461 | //! # ).enable(); |
462 | 462 | //! let (dpll0, xosc0) = Dpll::from_xosc(tokens.dpll0, xosc0); |
463 | 463 | //! ``` |
464 | | -//! Next, we set the DPLL pre-divider and loop divider. We must pre-divide |
465 | | -//! the XOSC clock down from 8 MHz to 2 MHz, so that it is within the valid |
466 | | -//! input frequency range for the DPLL. Then, we set the DPLL loop divider, so |
467 | | -//! that it will multiply the 2 MHz clock by 50 for a 100 MHz output. We do not |
468 | | -//! need fractional mutiplication here, so the fractional loop divider is zero. |
| 464 | +//! Next, we set the DPLL pre-divider and loop divider. We must pre-divide the |
| 465 | +//! XOSC clock down from 8 MHz to 2 MHz, so that it is within the valid input |
| 466 | +//! frequency range for the DPLL. Then, we set the DPLL loop divider, so that it |
| 467 | +//! will multiply the 2 MHz clock by 50 for a 100 MHz output. We do not need |
| 468 | +//! fractional mutiplication here, so the fractional loop divider is zero. |
469 | 469 | //! Finally, we can enable the `Dpll`, yielding an instance of |
470 | 470 | //! `EnabledDpll0<XoscId0>`. |
471 | 471 | //! |
|
515 | 515 | //! [`EnabledGclk0`] to change the base clock without disabling GCLK0 or the |
516 | 516 | //! main clock. |
517 | 517 | //! |
518 | | -//! This time we will be modifying two [`Enabled`] counters simultaneously. |
519 | | -//! We will [`Decrement`] the [`EnabledDfll`] count from `U1` to `U0`, and |
520 | | -//! we will [`Increment`] the [`EnabledDpll0`] count from `U0` to `U1`. |
521 | | -//! Again, we need to provide both the DFLL and DPLL clocks, so that their |
522 | | -//! type parameters can be changed. |
| 518 | +//! This time we will be modifying two [`Enabled`] counters simultaneously. We |
| 519 | +//! will [`Decrement`] the [`EnabledDfll`] count from `U1` to `U0`, and we will |
| 520 | +//! [`Increment`] the [`EnabledDpll0`] count from `U0` to `U1`. Again, we need |
| 521 | +//! to provide both the DFLL and DPLL clocks, so that their type parameters can |
| 522 | +//! be changed. |
523 | 523 | //! |
524 | 524 | //! ```no_run |
525 | 525 | //! # use atsamd_hal::{ |
|
597 | 597 | //! ``` |
598 | 598 | //! |
599 | 599 | //! We have the clocks set up, but we're not using them for anything other than |
600 | | -//! the main clock. Our final steps will create SERCOM APB and peripheral |
601 | | -//! clocks and will output the raw GCLK0 to a GPIO pin. |
| 600 | +//! the main clock. Our final steps will create SERCOM APB and peripheral clocks |
| 601 | +//! and will output the raw GCLK0 to a GPIO pin. |
602 | 602 | //! |
603 | 603 | //! To enable the APB clock for SERCOM0, we must access the [`Apb`] bus struct. |
604 | 604 | //! We provide an [`ApbToken`] to the [`Apb::enable`] method and receive an |
|
768 | 768 | //! ``` |
769 | 769 | //! |
770 | 770 | //! [PAC]: crate::pac |
771 | | -//! [`OSCCTRL`]: crate::pac::Oscctrl |
772 | | -//! [`OSC32KCTRL`]: crate::pac::Osc32kctrl |
773 | 771 | //! [`GCLK`]: crate::pac::Gclk |
774 | | -//! [`MCLK`]: crate::pac::Mclk |
775 | 772 | //! [`Peripherals::steal`]: crate::pac::Peripherals::steal |
776 | 773 | //! |
777 | 774 | //! [`Ahb`]: ahb::Ahb |
|
795 | 792 | //! [`Dpll`]: dpll::Dpll |
796 | 793 | //! [`Dpll<D, I>`]: dpll::Dpll |
797 | 794 | //! [`Dpll0`]: dpll::Dpll0 |
798 | | -//! [`Dpll1`]: dpll::Dpll1 |
799 | 795 | //! [`Dpll0Id`]: dpll::Dpll0Id |
800 | | -//! [`Dpll1Id`]: dpll::Dpll1Id |
801 | 796 | //! [`DpllSourceId`]: dpll::DpllSourceId |
802 | 797 | //! [`DpllToken`]: dpll::DpllToken |
803 | 798 | //! [`EnabledDpll0`]: dpll::EnabledDpll0 |
|
819 | 814 | //! [`PclkSourceId`]: pclk::PclkSourceId |
820 | 815 | //! [`PclkToken`]: pclk::PclkToken |
821 | 816 | //! |
822 | | -//! [`RtcOsc`]: rtcosc::RtcOsc |
823 | | -//! |
824 | 817 | //! [`Xosc`]: xosc::Xosc |
825 | 818 | //! [`Xosc::from_crystal`]: xosc::Xosc::from_crystal |
826 | 819 | //! [`Xosc::enable`]: xosc::Xosc::enable |
827 | 820 | //! [`Xosc0`]: xosc::Xosc0 |
828 | 821 | //! [`Xosc0<M>`]: xosc::Xosc0 |
829 | 822 | //! [`Xosc0Id`]: xosc::Xosc0Id |
830 | | -//! [`Xosc1Id`]: xosc::Xosc1Id |
831 | 823 | //! [`XoscToken`]: xosc::XoscToken |
832 | 824 | //! [`EnabledXosc0`]: xosc::EnabledXosc0 |
833 | 825 | //! [`EnabledXosc0<M, N>`]: xosc::EnabledXosc0 |
|
850 | 842 | //! [`Sub1`]: typenum::Sub1 |
851 | 843 | //! [`Unsigned`]: typenum::Unsigned |
852 | 844 | //! |
853 | | -//! [interior mutability]: https://doc.rust-lang.org/reference/interior-mutability.html |
| 845 | +//! [interior mutability]: |
| 846 | +//! https://doc.rust-lang.org/reference/interior-mutability.html |
854 | 847 |
|
855 | 848 | #![allow(clippy::manual_range_contains)] |
856 | 849 |
|
|
0 commit comments