From 4addd5d6e4b52568c73d1df59c3fd8386739be78 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Fri, 22 Apr 2022 13:34:23 +0200 Subject: [PATCH 01/44] manifest: increase internal feature flag granularity Closes #11 --- hal/Cargo.toml | 150 ++++++++++++++++++++++++++-------------------- hal/src/lib.rs | 4 +- hal/src/serial.rs | 87 +++++---------------------- 3 files changed, 102 insertions(+), 139 deletions(-) diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 01d4b063..08ee1a0c 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -93,136 +93,154 @@ atsamv71q21 = { version = "0.21.0", path = "../pac/atsamv71q21", optional = tru atsamv71q21b = { version = "0.21.0", path = "../pac/atsamv71q21b", optional = true } [features] +# Internal-only feature flags; do not set directly. +# Refer to §2 in the data sheet. +v71 = [] +v70 = [] +e70 = [] +s70 = [] +pins-64 = [] # J variants +pins-100 = [] # N variants +pins-144 = [] # Q variants +flash-2M = [] # "21"-suffix +flash-1M = [] # "20"-suffix +flash-512K = [] # "19"-suffix +rev-a = [] # No suffix after flash memory density number +rev-b = ["device-selected"] # "b"-suffix device-selected = [] -same70j19 = ["atsame70j19", "device-selected"] + +same70j19 = ["atsame70j19", "e70", "pins-64", "flash-512K", "rev-a"] same70j19-rt = ["same70j19", "atsame70j19/rt"] -same70j19b = ["atsame70j19b", "device-selected"] +same70j19b = ["atsame70j19b", "e70", "pins-64", "flash-512K", "rev-b"] same70j19b-rt = ["same70j19b", "atsame70j19b/rt"] -same70j20 = ["atsame70j20", "device-selected"] +same70j20 = ["atsame70j20", "e70", "pins-64", "flash-1M", "rev-a"] same70j20-rt = ["same70j20", "atsame70j20/rt"] -same70j20b = ["atsame70j20b", "device-selected"] +same70j20b = ["atsame70j20b", "e70", "pins-64", "flash-1M", "rev-b"] same70j20b-rt = ["same70j20b", "atsame70j20b/rt"] -same70j21 = ["atsame70j21", "device-selected"] +same70j21 = ["atsame70j21", "e70", "pins-64", "flash-2M", "rev-a"] same70j21-rt = ["same70j21", "atsame70j21/rt"] -same70j21b = ["atsame70j21b", "device-selected"] +same70j21b = ["atsame70j21b", "e70", "pins-64", "flash-2M", "rev-b"] same70j21b-rt = ["same70j21b", "atsame70j21b/rt"] -same70n19 = ["atsame70n19", "device-selected"] +same70n19 = ["atsame70n19", "e70", "pins-100", "flash-512K", "rev-a"] same70n19-rt = ["same70n19", "atsame70n19/rt"] -same70n19b = ["atsame70n19b", "device-selected"] +same70n19b = ["atsame70n19b", "e70", "pins-100", "flash-512K", "rev-b"] same70n19b-rt = ["same70n19b", "atsame70n19b/rt"] -same70n20 = ["atsame70n20", "device-selected"] +same70n20 = ["atsame70n20", "e70", "pins-100", "flash-1M", "rev-a"] same70n20-rt = ["same70n20", "atsame70n20/rt"] -same70n20b = ["atsame70n20b", "device-selected"] +same70n20b = ["atsame70n20b", "e70", "pins-100", "flash-1M", "rev-b"] same70n20b-rt = ["same70n20b", "atsame70n20b/rt"] -same70n21 = ["atsame70n21", "device-selected"] +same70n21 = ["atsame70n21", "e70", "pins-100", "flash-2M", "rev-a"] same70n21-rt = ["same70n21", "atsame70n21/rt"] -same70n21b = ["atsame70n21b", "device-selected"] +same70n21b = ["atsame70n21b", "e70", "pins-100", "flash-2M", "rev-b"] same70n21b-rt = ["same70n21b", "atsame70n21b/rt"] -same70q19 = ["atsame70q19", "device-selected"] +same70q19 = ["atsame70q19", "e70", "pins-144", "flash-512K", "rev-a"] same70q19-rt = ["same70q19", "atsame70q19/rt"] -same70q19b = ["atsame70q19b", "device-selected"] +same70q19b = ["atsame70q19b", "e70", "pins-144", "flash-512K", "rev-b"] same70q19b-rt = ["same70q19b", "atsame70q19b/rt"] -same70q20 = ["atsame70q20", "device-selected"] +same70q20 = ["atsame70q20", "e70", "pins-144", "flash-1M", "rev-a"] same70q20-rt = ["same70q20", "atsame70q20/rt"] -same70q20b = ["atsame70q20b", "device-selected"] +same70q20b = ["atsame70q20b", "e70", "pins-144", "flash-1M", "rev-b"] same70q20b-rt = ["same70q20b", "atsame70q20b/rt"] -same70q21 = ["atsame70q21", "device-selected"] +same70q21 = ["atsame70q21", "e70", "pins-144", "flash-2M", "rev-a"] same70q21-rt = ["same70q21", "atsame70q21/rt"] -same70q21b = ["atsame70q21b", "device-selected"] +same70q21b = ["atsame70q21b", "e70", "pins-144", "flash-2M", "rev-b"] same70q21b-rt = ["same70q21b", "atsame70q21b/rt"] -sams70j19 = ["atsams70j19", "device-selected"] + +sams70j19 = ["atsams70j19", "s70", "pins-64", "flash-512K", "rev-a"] sams70j19-rt = ["sams70j19", "atsams70j19/rt"] -sams70j19b = ["atsams70j19b", "device-selected"] +sams70j19b = ["atsams70j19b", "s70", "pins-64", "flash-512K", "rev-b"] sams70j19b-rt = ["sams70j19b", "atsams70j19b/rt"] -sams70j20 = ["atsams70j20", "device-selected"] +sams70j20 = ["atsams70j20", "s70", "pins-64", "flash-1M", "rev-a"] sams70j20-rt = ["sams70j20", "atsams70j20/rt"] -sams70j20b = ["atsams70j20b", "device-selected"] +sams70j20b = ["atsams70j20b", "s70", "pins-64", "flash-1M", "rev-b"] sams70j20b-rt = ["sams70j20b", "atsams70j20b/rt"] -sams70j21 = ["atsams70j21", "device-selected"] +sams70j21 = ["atsams70j21", "s70", "pins-64", "flash-2M", "rev-a"] sams70j21-rt = ["sams70j21", "atsams70j21/rt"] -sams70j21b = ["atsams70j21b", "device-selected"] +sams70j21b = ["atsams70j21b", "s70", "pins-64", "flash-2M", "rev-b"] sams70j21b-rt = ["sams70j21b", "atsams70j21b/rt"] -sams70n19 = ["atsams70n19", "device-selected"] +sams70n19 = ["atsams70n19", "s70", "pins-100", "flash-512K", "rev-a"] sams70n19-rt = ["sams70n19", "atsams70n19/rt"] -sams70n19b = ["atsams70n19b", "device-selected"] +sams70n19b = ["atsams70n19b", "s70", "pins-100", "flash-512K", "rev-a"] sams70n19b-rt = ["sams70n19b", "atsams70n19b/rt"] -sams70n20 = ["atsams70n20", "device-selected"] +sams70n20 = ["atsams70n20", "s70", "pins-100", "flash-1M", "rev-a"] sams70n20-rt = ["sams70n20", "atsams70n20/rt"] -sams70n20b = ["atsams70n20b", "device-selected"] +sams70n20b = ["atsams70n20b", "s70", "pins-100", "flash-1M", "rev-b"] sams70n20b-rt = ["sams70n20b", "atsams70n20b/rt"] -sams70n21 = ["atsams70n21", "device-selected"] +sams70n21 = ["atsams70n21", "s70", "pins-100", "flash-2M", "rev-a"] sams70n21-rt = ["sams70n21", "atsams70n21/rt"] -sams70n21b = ["atsams70n21b", "device-selected"] +sams70n21b = ["atsams70n21b", "s70", "pins-100", "flash-2M", "rev-b"] sams70n21b-rt = ["sams70n21b", "atsams70n21b/rt"] -sams70q19 = ["atsams70q19", "device-selected"] +sams70q19 = ["atsams70q19", "s70", "pins-144", "flash-512K", "rev-a"] sams70q19-rt = ["sams70q19", "atsams70q19/rt"] -sams70q19b = ["atsams70q19b", "device-selected"] +sams70q19b = ["atsams70q19b", "s70", "pins-144", "flash-512K", "rev-b"] sams70q19b-rt = ["sams70q19b", "atsams70q19b/rt"] -sams70q20 = ["atsams70q20", "device-selected"] +sams70q20 = ["atsams70q20", "s70", "pins-144", "flash-1M", "rev-a"] sams70q20-rt = ["sams70q20", "atsams70q20/rt"] -sams70q20b = ["atsams70q20b", "device-selected"] +sams70q20b = ["atsams70q20b", "s70", "pins-144", "flash-1M", "rev-b"] sams70q20b-rt = ["sams70q20b", "atsams70q20b/rt"] -sams70q21 = ["atsams70q21", "device-selected"] +sams70q21 = ["atsams70q21", "s70", "pins-144", "flash-2M", "rev-a"] sams70q21-rt = ["sams70q21", "atsams70q21/rt"] -sams70q21b = ["atsams70q21b", "device-selected"] +sams70q21b = ["atsams70q21b", "s70", "pins-144", "flash-2M", "rev-b"] sams70q21b-rt = ["sams70q21b", "atsams70q21b/rt"] -samv70j19 = ["atsamv70j19", "device-selected"] + +samv70j19 = ["atsamv70j19", "v70", "pins-64", "flash-512K", "rev-a"] samv70j19-rt = ["samv70j19", "atsamv70j19/rt"] -samv70j19b = ["atsamv70j19b", "device-selected"] +samv70j19b = ["atsamv70j19b", "v70", "pins-64", "flash-512K", "rev-b"] samv70j19b-rt = ["samv70j19b", "atsamv70j19b/rt"] -samv70j20 = ["atsamv70j20", "device-selected"] +samv70j20 = ["atsamv70j20", "v70", "pins-64", "flash-1M", "rev-a"] samv70j20-rt = ["samv70j20", "atsamv70j20/rt"] -samv70j20b = ["atsamv70j20b", "device-selected"] +samv70j20b = ["atsamv70j20b", "v70", "pins-64", "flash-1M", "rev-b"] samv70j20b-rt = ["samv70j20b", "atsamv70j20b/rt"] -samv70n19 = ["atsamv70n19", "device-selected"] +samv70n19 = ["atsamv70n19", "v70", "pins-100", "flash-512K", "rev-a"] samv70n19-rt = ["samv70n19", "atsamv70n19/rt"] -samv70n19b = ["atsamv70n19b", "device-selected"] +samv70n19b = ["atsamv70n19b", "v70", "pins-100", "flash-512K", "rev-b"] samv70n19b-rt = ["samv70n19b", "atsamv70n19b/rt"] -samv70n20 = ["atsamv70n20", "device-selected"] +samv70n20 = ["atsamv70n20", "v70", "pins-100", "flash-1M", "rev-a"] samv70n20-rt = ["samv70n20", "atsamv70n20/rt"] -samv70n20b = ["atsamv70n20b", "device-selected"] +samv70n20b = ["atsamv70n20b", "v70", "pins-100", "flash-1M", "rev-b"] samv70n20b-rt = ["samv70n20b", "atsamv70n20b/rt"] -samv70q19 = ["atsamv70q19", "device-selected"] +samv70q19 = ["atsamv70q19", "v70", "pins-144", "flash-512K", "rev-a"] samv70q19-rt = ["samv70q19", "atsamv70q19/rt"] -samv70q19b = ["atsamv70q19b", "device-selected"] +samv70q19b = ["atsamv70q19b", "v70", "pins-144", "flash-512K", "rev-b"] samv70q19b-rt = ["samv70q19b", "atsamv70q19b/rt"] -samv70q20 = ["atsamv70q20", "device-selected"] +samv70q20 = ["atsamv70q20", "v70", "pins-144", "flash-1M", "rev-a"] samv70q20-rt = ["samv70q20", "atsamv70q20/rt"] -samv70q20b = ["atsamv70q20b", "device-selected"] +samv70q20b = ["atsamv70q20b", "v70", "pins-144", "flash-1M", "rev-b"] samv70q20b-rt = ["samv70q20b", "atsamv70q20b/rt"] -samv71j19 = ["atsamv71j19", "device-selected"] + +samv71j19 = ["atsamv71j19", "v71", "pins-64", "flash-512K", "rev-a"] samv71j19-rt = ["samv71j19", "atsamv71j19/rt"] -samv71j19b = ["atsamv71j19b", "device-selected"] +samv71j19b = ["atsamv71j19b", "v71", "pins-64", "flash-512K", "rev-b"] samv71j19b-rt = ["samv71j19b", "atsamv71j19b/rt"] -samv71j20 = ["atsamv71j20", "device-selected"] +samv71j20 = ["atsamv71j20", "v71", "pins-64", "flash-1M", "rev-a"] samv71j20-rt = ["samv71j20", "atsamv71j20/rt"] -samv71j20b = ["atsamv71j20b", "device-selected"] +samv71j20b = ["atsamv71j20b", "v71", "pins-64", "flash-1M", "rev-b"] samv71j20b-rt = ["samv71j20b", "atsamv71j20b/rt"] -samv71j21 = ["atsamv71j21", "device-selected"] +samv71j21 = ["atsamv71j21", "v71", "pins-64", "flash-2M", "rev-a"] samv71j21-rt = ["samv71j21", "atsamv71j21/rt"] -samv71j21b = ["atsamv71j21b", "device-selected"] +samv71j21b = ["atsamv71j21b", "v71", "pins-64", "flash-2M", "rev-b"] samv71j21b-rt = ["samv71j21b", "atsamv71j21b/rt"] -samv71n19 = ["atsamv71n19", "device-selected"] +samv71n19 = ["atsamv71n19", "v71", "pins-100", "flash-512K", "rev-a"] samv71n19-rt = ["samv71n19", "atsamv71n19/rt"] -samv71n19b = ["atsamv71n19b", "device-selected"] +samv71n19b = ["atsamv71n19b", "v71", "pins-100", "flash-512K", "rev-b"] samv71n19b-rt = ["samv71n19b", "atsamv71n19b/rt"] -samv71n20 = ["atsamv71n20", "device-selected"] +samv71n20 = ["atsamv71n20", "v71", "pins-100", "flash-1M", "rev-a"] samv71n20-rt = ["samv71n20", "atsamv71n20/rt"] -samv71n20b = ["atsamv71n20b", "device-selected"] +samv71n20b = ["atsamv71n20b", "v71", "pins-100", "flash-1M", "rev-b"] samv71n20b-rt = ["samv71n20b", "atsamv71n20b/rt"] -samv71n21 = ["atsamv71n21", "device-selected"] +samv71n21 = ["atsamv71n21", "v71", "pins-100", "flash-2M", "rev-a"] samv71n21-rt = ["samv71n21", "atsamv71n21/rt"] -samv71n21b = ["atsamv71n21b", "device-selected"] +samv71n21b = ["atsamv71n21b", "v71", "pins-100", "flash-2M", "rev-b"] samv71n21b-rt = ["samv71n21b", "atsamv71n21b/rt"] -samv71q19 = ["atsamv71q19", "device-selected"] +samv71q19 = ["atsamv71q19", "v71", "pins-144", "flash-512K", "rev-a"] samv71q19-rt = ["samv71q19", "atsamv71q19/rt"] -samv71q19b = ["atsamv71q19b", "device-selected"] +samv71q19b = ["atsamv71q19b", "v71", "pins-144", "flash-512K", "rev-b"] samv71q19b-rt = ["samv71q19b", "atsamv71q19b/rt"] -samv71q20 = ["atsamv71q20", "device-selected"] +samv71q20 = ["atsamv71q20", "v71", "pins-144", "flash-1M", "rev-a"] samv71q20-rt = ["samv71q20", "atsamv71q20/rt"] -samv71q20b = ["atsamv71q20b", "device-selected"] +samv71q20b = ["atsamv71q20b", "v71", "pins-144", "flash-1M", "rev-b"] samv71q20b-rt = ["samv71q20b", "atsamv71q20b/rt"] -samv71q21 = ["atsamv71q21", "device-selected"] +samv71q21 = ["atsamv71q21", "v71", "pins-144", "flash-2M", "rev-a"] samv71q21-rt = ["samv71q21", "atsamv71q21/rt"] -samv71q21b = ["atsamv71q21b", "device-selected"] +samv71q21b = ["atsamv71q21b", "v71", "pins-144", "flash-2M", "rev-b"] samv71q21b-rt = ["samv71q21b", "atsamv71q21b/rt"] diff --git a/hal/src/lib.rs b/hal/src/lib.rs index 1b8888d5..b49db4ea 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs @@ -141,7 +141,7 @@ pub use atsamv71q21 as target_device; #[cfg(feature = "samv71q21b")] pub use atsamv71q21b as target_device; -#[cfg(feature = "device-selected")] +#[cfg(feature = "rev-b")] pub mod serial; -#[cfg(feature = "device-selected")] +#[cfg(feature = "rev-b")] pub mod watchdog; diff --git a/hal/src/serial.rs b/hal/src/serial.rs index 2eccf881..a678ab11 100644 --- a/hal/src/serial.rs +++ b/hal/src/serial.rs @@ -2,35 +2,15 @@ use crate::{ehal, nb}; // Smaller part have 3x UART & 2x USART use crate::target_device::{UART0, UART1, UART2, USART0, USART1}; -#[cfg(any( - feature = "sams70n19b", - feature = "sams70n20b", - feature = "sams70n21b", - feature = "sams70q19b", - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70n19b", - feature = "same70n20b", - feature = "same70n21b", - feature = "same70q19b", - feature = "same70q20b", - feature = "same70q21b", +#[cfg(all( + any(feature = "e70", feature = "s70"), + any(feature = "pins-100", feature = "pins-144") ))] use crate::target_device::{UART3, USART2}; -#[cfg(any( - feature = "sams70n19b", - feature = "sams70n20b", - feature = "sams70n21b", - feature = "sams70q19b", - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70n19b", - feature = "same70n20b", - feature = "same70n21b", - feature = "same70q19b", - feature = "same70q20b", - feature = "same70q21b", +#[cfg(all( + any(feature = "e70", feature = "s70"), + any(feature = "pins-100", feature = "pins-144") ))] use crate::target_device::UART4; @@ -46,54 +26,24 @@ pub type Serial0 = Serial; pub type Serial1 = Serial; pub type Serial2 = Serial; -#[cfg(any( - feature = "sams70n19b", - feature = "sams70n20b", - feature = "sams70n21b", - feature = "sams70q19b", - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70n19b", - feature = "same70n20b", - feature = "same70n21b", - feature = "same70q19b", - feature = "same70q20b", - feature = "same70q21b", +#[cfg(all( + any(feature = "e70", feature = "s70"), + any(feature = "pins-100", feature = "pins-144") ))] pub type Serial3 = Serial; -#[cfg(any( - feature = "sams70n19b", - feature = "sams70n20b", - feature = "sams70n21b", - feature = "sams70q19b", - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70n19b", - feature = "same70n20b", - feature = "same70n21b", - feature = "same70q19b", - feature = "same70q20b", - feature = "same70q21b", +#[cfg(all( + any(feature = "e70", feature = "s70"), + any(feature = "pins-100", feature = "pins-144") ))] pub type Serial4 = Serial; pub type Serial5 = Serial; pub type Serial6 = Serial; -#[cfg(any( - feature = "sams70n19b", - feature = "sams70n20b", - feature = "sams70n21b", - feature = "sams70q19b", - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70n19b", - feature = "same70n20b", - feature = "same70n21b", - feature = "same70q19b", - feature = "same70q20b", - feature = "same70q21b", +#[cfg(all( + any(feature = "e70", feature = "s70"), + any(feature = "pins-100", feature = "pins-144") ))] pub type Serial7 = Serial; @@ -132,12 +82,7 @@ impl ehal::serial::Write for Serial { } } -#[cfg(any( - feature = "sams70q20b", - feature = "sams70q21b", - feature = "same70q20b", - feature = "same70q21b", -))] +#[cfg(all(any(feature = "e70", feature = "s70"), feature = "pins-100"))] impl ehal::serial::Write for Serial { type Error = Error; From c552e479b3309656a0c8c64837ac9459e1071de9 Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 22 Apr 2022 13:54:42 +0200 Subject: [PATCH 02/44] add pmc, efc modules from @jamesmunns's work Sourced from [0]; /vendor/asamx7x-hal. [0] https://github.com/jamesmunns/same70-experiments/commit/58a3c34512f7b7877833edc3a1cc9b28f9d8941d --- hal/src/efc.rs | 68 ++++++ hal/src/lib.rs | 4 + hal/src/pmc.rs | 553 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 625 insertions(+) create mode 100644 hal/src/efc.rs create mode 100644 hal/src/pmc.rs diff --git a/hal/src/efc.rs b/hal/src/efc.rs new file mode 100644 index 00000000..8549dfeb --- /dev/null +++ b/hal/src/efc.rs @@ -0,0 +1,68 @@ +//! Flash block configuration + +use crate::target_device::EFC; +use crate::pmc::PmcError; + +pub struct Efc { + pub(crate) periph: EFC, +} + +impl Efc { + pub fn new(periph: EFC) -> Self { + periph.eefc_wpmr.modify(|_r, w| { + w.wpkey().passwd(); + w.wpen().clear_bit(); + w + }); + + Self { periph } + } + + pub fn set_wait_states(&mut self, fws: FlashWaitStates) { + let fws_bits = fws as u8; + + self.periph + .eefc_fmr + .modify(|_r, w| unsafe { w.fws().bits(fws_bits) }); + } +} + +/// The number of flash wait states for a read operation. +/// +/// Note: The number of cycles a read takes is 1 + FWS. +#[derive(Debug, PartialEq, Copy, Clone)] +#[repr(u8)] +pub enum FlashWaitStates { + Zero, + One, + Two, + Three, + Four, + Five, + Six, +} + +impl FlashWaitStates { + /// Calculate the lowest possible number of flash wait states from a given + /// master clock frequency in MHz. + /// + /// The max mck frequency supported is 150MHz. This is *not* the CPU frequency, + /// which may go up to 300MHz. + /// + /// Note: This is probably only valid at VDDIO = 3.0V + pub fn from_mck_mhz(freq: u8) -> Result { + // Reference: Table 58-51 Embedded Flash Wait States for Worst-Case Conditions + let fws = match freq { + 0..=23 => Self::Zero, + 24..=46 => Self::One, + 47..=69 => Self::Two, + 70..=92 => Self::Three, + 93..=115 => Self::Four, + 116..=138 => Self::Five, + 139..=150 => Self::Six, + _ => return Err(PmcError::InvalidConfiguration), + }; + + Ok(fws) + } +} diff --git a/hal/src/lib.rs b/hal/src/lib.rs index b49db4ea..999c1418 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs @@ -145,3 +145,7 @@ pub use atsamv71q21b as target_device; pub mod serial; #[cfg(feature = "rev-b")] pub mod watchdog; +#[cfg(feature = "rev-b")] +pub mod pmc; +#[cfg(feature = "rev-b")] +pub mod efc; diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs new file mode 100644 index 00000000..5a22f5ae --- /dev/null +++ b/hal/src/pmc.rs @@ -0,0 +1,553 @@ +//! Clock hierarchy configuration + +use crate::efc::Efc; +use crate::efc::FlashWaitStates; +use crate::target_device::PMC; + +pub use crate::target_device::pmc::pmc_mckr::MDIV_A as MckDivider; +pub use crate::target_device::pmc::pmc_mckr::PRES_A as MckPrescaler; + +pub struct Pmc { + periph: PMC, + settings: Option, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum PmcError { + ClockingError(PeripheralIdentifier), + InvalidConfiguration, + UnimplementedError, + InternalError, +} + +/// The selected "Main Clock Oscillator" source +/// +/// TODO/NOTE: At the moment, we only support the internal trimmed 12MHz +/// oscillator. Some driver behavior is hardcoded on this assumption for +/// the sake of simplicity. +/// +/// This corresponds to CKGR_MOR.MOSCSEL +pub enum MainClockOscillatorSource { + MainCk12MHz, +} + +/// The selected "Master Clock" source +/// +/// TODO/NOTE: At the moment, we only support the PLLA Clock. +/// Some driver behavior is hardcoded on this assumption for +/// the sake of simplicity. +/// +/// This corresponds to PMC_MCKR.CSS +pub enum MasterClockSource { + PllaClock, +} + +pub struct ClockSettings { + // "Main Clock" is abbreviated "MAINCK" + /// Main Clock Oscillator Source + pub main_clk_osc_src: MainClockOscillatorSource, + + // "Master Clock" is abbreviated "MCK" + /// Master Clock Prescaler + pub mck_pres: MckPrescaler, + /// Master Clock Source + pub mck_src: MasterClockSource, + /// Master Clock Divider + pub mck_div: MckDivider, + + /// PLLA Multiplier - 10 bits + /// + /// Resulting change is (1 + MULA) * INPUT + /// + /// A value of zero disables the PLLA. + pub multiplier_a: u16, + /// PLLA Divider - 8 bits + /// + /// Result change is INPUT / DIVA + /// + /// A value of zero disables the PLLA. + pub divider_a: u8, +} + +impl ClockSettings { + pub fn calc_master_clk_mhz(&self) -> Result { + // NOTE: This is based on Figure 31-1 - "General Clock Distribution Block Diagram" + + // PLLACK is currently the (only) choice for driving the Master Clock Controller + let mck_input = match self.mck_src { + MasterClockSource::PllaClock => { + // The internal 12MHz osc is currently the (only) choice for driving the PLLACK + let main_ck: f32 = match self.main_clk_osc_src { + MainClockOscillatorSource::MainCk12MHz => 12.0, + }; + + // These values disable the PLLA + if (self.multiplier_a == 0) || (self.divider_a == 0) { + return Err(PmcError::InvalidConfiguration); + } + + let plla_ck = main_ck * ((self.multiplier_a + 1) as f32); + let plla_ck = plla_ck / (self.divider_a as f32); + + plla_ck + } + }; + + // MCK input first passes through a prescaler... + let presc: f32 = match self.mck_pres { + MckPrescaler::CLK_1 => 1.0, + MckPrescaler::CLK_2 => 2.0, + MckPrescaler::CLK_3 => 3.0, + MckPrescaler::CLK_4 => 4.0, + MckPrescaler::CLK_8 => 8.0, + MckPrescaler::CLK_16 => 16.0, + MckPrescaler::CLK_32 => 32.0, + MckPrescaler::CLK_64 => 64.0, + }; + + let post_pres = mck_input / presc; + + // ... then the output of the prescaler is passed through a divider + let div: f32 = match self.mck_div { + MckDivider::EQ_PCK => 1.0, + MckDivider::PCK_DIV2 => 2.0, + MckDivider::PCK_DIV3 => 3.0, + MckDivider::PCK_DIV4 => 4.0, + }; + let mck = post_pres / div; + + // Make sure we're still in a reasonable range + // + // TODO: The datasheet says: + // + // > Master Clock (MCK), programmable from a few hundred Hz to the maximum operating frequency of + // > the device. It is available to the modules running permanently, such as the Enhanced Embedded + // > Flash Controller + // + // However I currently assume that it never exceeds 150MHz. This could probably be changed. + // This is mostly because the datasheet only lists calculations for flash wait states up to + // a master clock of 150MHz. + if mck > 255.0 { + return Err(PmcError::InvalidConfiguration); + } else { + // Note, this is a "floor" operation, while we should probably + // be using "ceil", though that requires libm (or a similar float library). + // + // For now, this is probably close enough. + let mck_u8 = mck as u8; + Ok(mck_u8) + } + } +} + +impl Pmc { + pub fn new(periph: PMC) -> Self { + periph.pmc_wpmr.modify(|_r, w| { + w.wpkey().passwd(); + w.wpen().clear_bit(); + w + }); + + Self { + periph, + + // TODO: I could probably figure out the default settings... + // this is fine for now. + settings: None, + } + } + + pub fn settings(&self) -> Option<&ClockSettings> { + self.settings.as_ref() + } + + pub fn enable_peripherals(&mut self, pids: &[PeripheralIdentifier]) -> Result<(), PmcError> { + if pids.is_empty() { + return Ok(()); + } + + let pcsr0 = self.periph.pmc_pcsr0.read().bits(); + let pcsr1 = self.periph.pmc_pcsr1.read().bits(); + + let mut pcr0 = 0; + let mut pcr1 = 0; + + for pid in pids { + // Check if this supports PMC clocking + pid.supports_pmc_clocking() + .map_err(|_| PmcError::ClockingError(*pid))?; + + let pid_val: u32 = (*pid) as u32; + + match pid_val { + 7..=31 => { + let mask = 1 << pid_val; + let pre_set = (pcsr0 & mask) != 0; + let dup_set = (pcr0 & mask) != 0; + + if pre_set || dup_set { + // defmt::warn!("[PMC] Duplicate Clock Enable: {}", pid); + } + + pcr0 |= mask; + } + 32..=63 => { + let mask = 1 << (pid_val - 32); + + let pre_set = (pcsr1 & mask) != 0; + let dup_set = (pcr1 & mask) != 0; + + if pre_set || dup_set { + // defmt::warn!("[PMC] Duplicate Clock Enable: {}", pid); + } + + pcr1 |= mask; + } + 69 | 70 => { + // I don't know how to enable these peripherals yet + return Err(PmcError::UnimplementedError); + } + _ => { + // This should be impossible, and probably means there is an + // error in the `supports_pmc_clocking()` function + return Err(PmcError::InternalError); + } + } + } + + // Enable the newly set peripherals + self.periph.pmc_pcer0.write(|w| unsafe { w.bits(pcr0) }); + self.periph.pmc_pcer1.write(|w| unsafe { w.bits(pcr1) }); + + Ok(()) + } + + pub fn set_clocks(&mut self, efc: &mut Efc, cfg: ClockSettings) -> Result<(), PmcError> { + // Calculate the master clock to determine the number of flash wait states. + // This must be done BEFORE increasing the clock speed, in case the current number + // of wait states is insufficient for the new speed. + // + // The flash controller (EEFC) is driven from the master clock (stated in section 31.2) + let mck_new = cfg.calc_master_clk_mhz()?; + let fws = FlashWaitStates::from_mck_mhz(mck_new)?; + + efc.periph.eefc_wpmr.modify(|_r, w| { + w.wpkey().passwd(); + w.wpen().clear_bit(); + w + }); + efc.periph + .eefc_fmr + .modify(|_r, w| unsafe { w.fws().bits(fws as u8) }); + + // Note: This follows Datasheet 31.17 "Recommendeded Programming Sequence" + // + // Steps 1-5 skipped, using the internal osc + + // The Main RC oscillator. Three output frequencies can be selected: 4/8/12 MHz. By default 12 MHz is + // selected. 8 MHz and 12 MHz are factory-trimmed. The Main RC Oscillator is the default choice, and + // requires no further configuration. + // + // TODO: This is the only supported variant. This code will fail if we add another option. + // You should implement the logic of steps 1-5 here if you are adding more MCO source + // options to the public interface! + let MainClockOscillatorSource::MainCk12MHz = cfg.main_clk_osc_src; + + // # Step 6 + // + // All parameters needed to configure PLLA and the divider are located in CKGR_PLLAR. + // CKGR_PLLAR.DIVA is used to control the divider. This parameter can be programmed between 0 + // and 127. Divider output is divider input divided by DIVA parameter. By default, DIVA field is cleared + // which means that the divider and PLLA are turned off. + // + // CKGR_PLLAR.MULA is the PLLA multiplier factor. This parameter can be programmed between 0 + // and 62. If MULA is cleared, PLLA will be turned off, otherwise the PLLA output frequency is PLLA + // input frequency multiplied by (MULA + 1). + // + // CKGR_PLLAR.PLLACOUNT specifies the number of SLCK cycles before PMC_SR.LOCKA is set + // after CKGR_PLLAR has been written. + + // TODO: Since we have a fixed input clock of 12MHz, mul_a cannot exceed 24 (25x12 = 300MHz) + if cfg.multiplier_a > 24 { + return Err(PmcError::InvalidConfiguration); + } + + if cfg.divider_a == 0 || cfg.divider_a > 127 { + return Err(PmcError::InvalidConfiguration); + } + + self.periph.ckgr_pllar.modify(|_r, w| { + w.one().set_bit(); + unsafe { + w.mula().bits(cfg.multiplier_a.into()); + + // This is the reset value? + w.pllacount().bits(0b111111); + w.diva().bits(cfg.divider_a); + } + w + }); + + // Once CKGR_PLLAR has been written, the user must wait for PMC_SR.LOCKA to be set. This can + // be done either by polling PMC_SR.LOCKA or by waiting for the interrupt line to be raised if the + // associated interrupt source (LOCKA) has been enabled in PMC_IER. All fields in CKGR_PLLAR + // can be programmed in a single write operation. If MULA or DIVA is modified, the LOCKA bit goes + // low to indicate that PLLA is not yet ready. When PLLA is locked, LOCKA is set again. The user + // must wait for the LOCKA bit to be set before using the PLLA output clock. + while self.periph.pmc_sr.read().locka().bit_is_clear() {} + + // # Step 7 + // Select MCK and HCLK: + // MCK and HCLK are configurable via PMC_MCKR. + // + // CSS is used to select the clock source of MCK and HCLK. By default, the selected clock source is + // MAINCK. + // + // PRES is used to define the HCLK and MCK prescalers The user can choose between different + // values (1, 2, 3, 4, 8, 16, 32, 64). Prescaler output is the selected clock source frequency divided by + // the PRES value. + // + // MDIV is used to define the MCK divider. It is possible to choose between different values (0, 1, 2, + // 3). MCK output is the HCLK frequency divided by 1, 2, 3 or 4, depending on the value programmed + // in MDIV. + // + // By default, MDIV is cleared, which indicates that the HCLK is equal to MCK. + // Once the PMC_MCKR has been written, the user must wait for PMC_SR.MCKRDY to be set. This + // can be done either by polling PMC_SR.MCKRDY or by waiting for the interrupt line to be raised if + // the associated interrupt source (MCKRDY) has been enabled in PMC_IER. PMC_MCKR must not + // be programmed in a single write operation. The programming sequence for PMC_MCKR is as + // follows: + // + // If a new value for PMC_MCKR.CSS corresponds to any of the available PLL clocks: + // a. Program PMC_MCKR.PRES. + // b. Wait for PMC_SR.MCKRDY to be set. + self.periph + .pmc_mckr + .modify(|_r, w| w.pres().variant(cfg.mck_pres)); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + + // c. Program PMC_MCKR.MDIV. + // d. Wait for PMC_SR.MCKRDY to be set. + self.periph + .pmc_mckr + .modify(|_r, w| w.mdiv().variant(cfg.mck_div)); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + + // TODO: Again: a hardcoded compile time check + let MasterClockSource::PllaClock = cfg.mck_src; + + // e. Program PMC_MCKR.CSS. + // f. Wait for PMC_SR.MCKRDY to be set. + self.periph.pmc_mckr.modify(|_r, w| w.css().plla_clk()); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + + // If a new value for PMC_MCKR.CSS corresponds to MAINCK or SLCK: + // a. Program PMC_MCKR.CSS. + // b. Wait for PMC_SR.MCKRDY to be set. + // c. Program PMC_MCKR.PRES. + // d. Wait for PMC_SR.MCKRDY to be set. + // + // If CSS, MDIV or PRES are modified at any stage, the MCKRDY bit goes low to indicate that MCK + // and HCLK are not yet ready. The user must wait for MCKRDY bit to be set again before using MCK + // and HCLK. + // + // Note: If PLLA clock was selected as MCK and the user decides to modify it by writing a new value + // into CKGR_PLLAR, the MCKRDY flag will go low while PLLA is unlocked. Once PLLA is locked + // again, LOCKA goes high and MCKRDY is set. + // + // While PLLA is unlocked, MCK selection is automatically changed to SLCK for PLLA. For further + // information, see "Clock Switching Waveforms". + // + // MCK is MAINCK divided by 2. + self.settings = Some(cfg); + Ok(()) + } +} + +#[allow(non_camel_case_types)] +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[repr(u32)] +pub enum PeripheralIdentifier { + /// 0 (NVIC + !PMC CC) Supply Controller + SUPC = 0, + /// 1 (NVIC + !PMC CC) Reset Controller + RSTC = 1, + /// 2 (NVIC + !PMC CC) Real Time Clock + RTC = 2, + /// 3 (NVIC + !PMC CC) Real Time Timer + RTT = 3, + /// 4 (NVIC + !PMC CC) Watchdog Timer + WDT = 4, + /// 5 (NVIC + !PMC CC) Power Management Controller + PMC = 5, + /// 6 (NVIC + !PMC CC) Enhanced Embedded Flash Controller + EFC = 6, + /// 7 (NVIC + PMC CC) Universal Asynchronous Receiver/Transmitter + UART0 = 7, + /// 8 (NVIC + PMC CC) Universal Asynchronous Receiver/Transmitter + UART1 = 8, + /// 9 (!NVIC + PMC CC) Static Memory Controller + SMC = 9, + /// 10 (NVIC + PMC CC) Parallel I/O Controller A + PIOA = 10, + /// 11 (NVIC + PMC CC) Parallel I/O Controller B + PIOB = 11, + /// 12 (NVIC + PMC CC) Parallel I/O Controller C + PIOC = 12, + /// 13 (NVIC + PMC CC) Universal Synchronous/Asynchronous Receiver/Transmitter + USART0 = 13, + /// 14 (NVIC + PMC CC) Universal Synchronous/Asynchronous Receiver/Transmitter + USART1 = 14, + /// 15 (NVIC + PMC CC) Universal Synchronous/Asynchronous Receiver/Transmitter + USART2 = 15, + /// 16 (NVIC + PMC CC) Parallel I/O Controller D + PIOD = 16, + /// 17 (NVIC + PMC CC) Parallel I/O Controller E + PIOE = 17, + /// 18 (NVIC + PMC CC) Multimedia Card Interface + HSMCI = 18, + /// 19 (NVIC + PMC CC) Two-wire Interface (I2C-compatible) + TWIHS0 = 19, + /// 20 (NVIC + PMC CC) Two-wire Interface (I2C-compatible) + TWIHS1 = 20, + /// 21 (NVIC + PMC CC) Serial Peripheral Interface + SPI0 = 21, + /// 22 (NVIC + PMC CC) Synchronous Serial Controller + SSC = 22, + /// 23 (NVIC + PMC CC) 16-bit Timer Counter 0, Channel 0 + TC0_CHANNEL0 = 23, + /// 24 (NVIC + PMC CC) 16-bit Timer Counter 0, Channel 1 + TC0_CHANNEL1 = 24, + /// 25 (NVIC + PMC CC) 16-bit Timer Counter 0, Channel 2 + TC0_CHANNEL2 = 25, + /// 26 (NVIC + PMC CC) 16-bit Timer Counter 1, Channel 0 + TC1_CHANNEL0 = 26, + /// 27 (NVIC + PMC CC) 16-bit Timer Counter 1, Channel 1 + TC1_CHANNEL1 = 27, + /// 28 (NVIC + PMC CC) 16-bit Timer Counter 1, Channel 2 + TC1_CHANNEL2 = 28, + /// 29 (NVIC + PMC CC) Analog Front-End Controller + AFEC0 = 29, + /// 30 (NVIC + PMC CC) Digital-to-Analog Converter + DACC = 30, + /// 31 (NVIC + PMC CC) Pulse Width Modulation Controller + PWM0 = 31, + /// 32 (NVIC + PMC CC) Integrity Check Monitor + ICM = 32, + /// 33 (NVIC + PMC CC) Analog Comparator Controller + ACC = 33, + /// 34 (NVIC + PMC CC) USB Host / Device Controller + USBHS = 34, + /// 35 (NVIC + PMC CC) CAN IRQ Line 0 + MCAN0 = 35, + /// 36 (NVIC + !PMC CC) CAN IRQ Line 1 + MCAN0INT1 = 36, + /// 37 (NVIC + PMC CC) CAN IRQ Line 0 + MCAN1 = 37, + /// 38 (NVIC + !PMC CC) CAN IRQ Line 1 + MCAN1INT1 = 38, + /// 39 (NVIC + PMC CC) Ethernet MAC + GMAC = 39, + /// 40 (NVIC + PMC CC) Analog Front End Controller + AFEC1 = 40, + /// 41 (NVIC + PMC CC) Two-wire Interface + TWIHS2 = 41, + /// 42 (NVIC + PMC CC) Serial Peripheral Interface + SPI1 = 42, + /// 43 (NVIC + PMC CC) Quad I/O Serial Peripheral Interface + QSPI = 43, + /// 44 (NVIC + PMC CC) Universal Asynchronous Receiver/ Transmitter + UART2 = 44, + /// 45 (NVIC + PMC CC) Universal Asynchronous Receiver/ Transmitter + UART3 = 45, + /// 46 (NVIC + PMC CC) Universal Asynchronous Receiver/ Transmitter + UART4 = 46, + /// 47 (NVIC + PMC CC) 16-bit Timer Counter 2, Channel 0 + TC2_CHANNEL0 = 47, + /// 48 (NVIC + PMC CC) 16-bit Timer Counter 2, Channel 1 + TC2_CHANNEL1 = 48, + /// 49 (NVIC + PMC CC) 16-bit Timer Counter 2, Channel 2 + TC2_CHANNEL2 = 49, + /// 50 (NVIC + PMC CC) 16-bit Timer Counter 3, Channel 0 + TC3_CHANNEL0 = 50, + /// 51 (NVIC + PMC CC) 16-bit Timer Counter 3, Channel 1 + TC3_CHANNEL1 = 51, + /// 52 (NVIC + PMC CC) 16-bit Timer Counter 3, Channel 2 + TC3_CHANNEL2 = 52, + /// 53 (NVIC + PMC CC) MediaLB IRQ 0 + MLB_IRQ0 = 53, + /// 54 (NVIC + !PMC CC) MediaLB IRQ 1 + MLB_IRQ1 = 54, + /// 55 (NVIC + !PMC CC) Reserved + _RESERVED = 55, + /// 56 (NVIC + PMC CC) Advanced Encryption Standard + AES = 56, + /// 57 (NVIC + PMC CC) True Random Number Generator + TRNG = 57, + /// 58 (NVIC + PMC CC) DMA Controller + XDMAC = 58, + /// 59 (NVIC + PMC CC) Image Sensor Interface + ISI = 59, + /// 60 (NVIC + PMC CC) Pulse Width Modulation Controller + PWM1 = 60, + /// 61 (NVIC:FPU + !PMC CC) ARM Floating Point Unit interrupt associated with OFC, UFC, IOC, DZC and IDC bits + ARM = 61, + /// 62 (NVIC + !PMC CC) SDRAM Controller + SDRAMC = 62, + /// 63 (NVIC + !PMC CC) Reinforced Safety Watchdog Timer + RSWDT = 63, + /// 64 (NVIC:CCW + !PMC CC) ARM Cache ECC Warning + ARM_CACHE_ECC_WARNING = 64, + /// 65 (NVIC:CCF + !PMC CC) ARM Cache ECC Fault + ARM_CACHE_ECC_FAULT = 65, + /// 66 (NVIC:Q1 + !PMC CC) GMAC Queue 1 Interrupt signal toggled on a DMA write to the first word of each DMA data buffer associated with queue 1 + GMAC_Q1 = 66, + /// 67 (NVIC:Q2 + !PMC CC) GMAC Queue 2 Interrupt signal toggled on a DMA write to the first word of each DMA data buffer associated with queue 2 + GMAC_Q2 = 67, + /// 68 (NVIC:IX + !PMC CC) –Floating Point Unit Interrupt IXC associated with FPU cumulative exception bit + ARM_FPU_IXC_FPU = 68, + /// 69 (NVIC + PMC CC) Inter-IC Sound Controller + // TODO: How does this get enabled with PMC? The PMC bits only go up to 63... + I2SC0 = 69, + /// 70 (NVIC + PMC CC) Inter-IC Sound Controller + // TODO: How does this get enabled with PMC? The PMC bits only go up to 63... + I2SC1 = 70, + /// 71 (NVIC:Q3 + !PMC CC) GMAC Queue 3 Interrupt signal toggled on a DMA write to the first word of each DMA data buffer associated with queue 3 + GMAC_Q3 = 71, + /// 72 (NVIC:Q4 + !PMC CC) GMAC Queue 4 Interrupt signal toggled on a DMA write to the first word of each DMA data buffer associated with queue 4 + GMAC_Q4 = 72, + /// 73 (NVIC:Q5 + !PMC CC) GMAC Queue 5 Interrupt signal toggled on a DMA write to the first word of each DMA data buffer associated with queue 5 + GMAC_Q5 = 73, +} + +impl PeripheralIdentifier { + pub fn supports_pmc_clocking(&self) -> Result<(), ()> { + use PeripheralIdentifier::*; + // These pids don't support PMC clocking + match self { + SUPC => Err(()), + RSTC => Err(()), + RTC => Err(()), + RTT => Err(()), + WDT => Err(()), + PMC => Err(()), + EFC => Err(()), + MCAN0INT1 => Err(()), + MCAN1INT1 => Err(()), + MLB_IRQ1 => Err(()), + _RESERVED => Err(()), + ARM => Err(()), + SDRAMC => Err(()), + RSWDT => Err(()), + ARM_CACHE_ECC_WARNING => Err(()), + ARM_CACHE_ECC_FAULT => Err(()), + GMAC_Q1 => Err(()), + GMAC_Q2 => Err(()), + ARM_FPU_IXC_FPU => Err(()), + GMAC_Q3 => Err(()), + GMAC_Q4 => Err(()), + GMAC_Q5 => Err(()), + _ => Ok(()), + } + } +} From 1c8fcd298fe2238597034e3fc314fb22989ccd7c Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 25 Apr 2022 14:26:29 +0200 Subject: [PATCH 03/44] move feature checking to a build.rs instead More features will be added in future commits that require explicit configuration. Moved to build.rs to remove noise from HAL implementation. --- hal/build.rs | 21 +++++++++++++++++++++ hal/src/lib.rs | 5 ----- 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 hal/build.rs diff --git a/hal/build.rs b/hal/build.rs new file mode 100644 index 00000000..0286feba --- /dev/null +++ b/hal/build.rs @@ -0,0 +1,21 @@ +use std::env; + +fn main() -> Result<(), &'static str> { + // Refer to + // + fn feat(f: &str) -> bool { + env::var(format!( + "CARGO_FEATURE_{}", + f.to_ascii_uppercase().replace("-", "_") + )) + .is_ok() + } + + if !feat("device-selected") { + return Err( + "The HAL is built for a specific target device selected using a feature, but no such a feature was selected." + ); + } + + Ok(()) +} diff --git a/hal/src/lib.rs b/hal/src/lib.rs index 999c1418..a8099424 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs @@ -3,11 +3,6 @@ pub use embedded_hal as ehal; pub use nb; -#[cfg(not(feature = "device-selected"))] -compile_error!( - "The HAL is built for a specific target device selected using a feature, but no such a feature was selected." -); - #[cfg(feature = "same70j19")] pub use atsame70j19 as target_device; #[cfg(feature = "same70j19b")] From d7e0f5756eae0fc13e69e47b29fce158f7a2d54f Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 25 Apr 2022 14:30:43 +0200 Subject: [PATCH 04/44] efc: find FWS via TryFrom impls --- hal/Cargo.toml | 9 +++++++-- hal/src/efc.rs | 55 +++++++++++++++++++++++++++++++++++--------------- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 08ee1a0c..55a1106c 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -95,8 +95,13 @@ atsamv71q21b = { version = "0.21.0", path = "../pac/atsamv71q21b", optional = tr [features] # Internal-only feature flags; do not set directly. # Refer to §2 in the data sheet. -v71 = [] -v70 = [] +## Different electrical characteristics for different VDDIO values +## Refer to Tables 58-3 and 58-9 +vddio-3v = [] # Minimum value of 3.3V +vddio-1v = [] # Minimum value of 1.7V +## Refer to §2. +v71 = ["vddio-3v"] +v70 = ["vddio-3v"] e70 = [] s70 = [] pins-64 = [] # J variants diff --git a/hal/src/efc.rs b/hal/src/efc.rs index 8549dfeb..639ea4ef 100644 --- a/hal/src/efc.rs +++ b/hal/src/efc.rs @@ -1,7 +1,7 @@ -//! Flash block configuration +//! Flash controller configuration -use crate::target_device::EFC; use crate::pmc::PmcError; +use crate::target_device::EFC; pub struct Efc { pub(crate) periph: EFC, @@ -42,17 +42,15 @@ pub enum FlashWaitStates { Six, } -impl FlashWaitStates { - /// Calculate the lowest possible number of flash wait states from a given - /// master clock frequency in MHz. - /// - /// The max mck frequency supported is 150MHz. This is *not* the CPU frequency, - /// which may go up to 300MHz. - /// - /// Note: This is probably only valid at VDDIO = 3.0V - pub fn from_mck_mhz(freq: u8) -> Result { - // Reference: Table 58-51 Embedded Flash Wait States for Worst-Case Conditions - let fws = match freq { +impl TryFrom for FlashWaitStates { + type Error = PmcError; + + #[cfg(feature = "vddio-3v")] + fn try_from(freq: u8) -> Result { + // References: + // - Table 58-50 (p. 1804) Embedded Flash Wait States for Worst-Case Conditions (V70/V71) + // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 3.0V) + match freq { 0..=23 => Self::Zero, 24..=46 => Self::One, 47..=69 => Self::Two, @@ -60,9 +58,34 @@ impl FlashWaitStates { 93..=115 => Self::Four, 116..=138 => Self::Five, 139..=150 => Self::Six, - _ => return Err(PmcError::InvalidConfiguration), - }; + _ => Err(PmcError::InvalidConfiguration), + } + } + + #[cfg(feature = "vddio-1v")] + fn try_from(freq: u8) -> Result { + // References: + // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 1.7V) + match freq { + 0..=21 => Self::Zero, + 22..=42 => Self::One, + 43..=63 => Self::Two, + 64..=84 => Self::Three, + 85..=106 => Self::Four, + 107..=125 => Self::Five, + 126..=137 => Self::Six, + _ => Err(PmcError::InvalidConfiguration), + } + } +} - Ok(fws) +impl FlashWaitStates { + /// Calculate the lowest possible number of flash wait states from a given + /// master clock frequency in MHz. + /// + /// The max mck frequency supported is 150MHz. This is *not* the CPU frequency, + /// which may go up to 300MHz. + pub fn from_mck_mhz(freq: u8) -> Result { + freq.try_into()? } } From 7c5d8b68ebcec7ea366fcb104d2c3128ef47ede1 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 25 Apr 2022 14:32:36 +0200 Subject: [PATCH 05/44] hal: apply fmt --- hal/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hal/src/lib.rs b/hal/src/lib.rs index a8099424..8798a1e1 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs @@ -137,10 +137,10 @@ pub use atsamv71q21 as target_device; pub use atsamv71q21b as target_device; #[cfg(feature = "rev-b")] -pub mod serial; -#[cfg(feature = "rev-b")] -pub mod watchdog; +pub mod efc; #[cfg(feature = "rev-b")] pub mod pmc; #[cfg(feature = "rev-b")] -pub mod efc; +pub mod serial; +#[cfg(feature = "rev-b")] +pub mod watchdog; From ecb51b5f79af975f1d563ba78776042428a49597 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 25 Apr 2022 16:12:52 +0200 Subject: [PATCH 06/44] features: separate device-selected master feature from MCU info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit device-selected is a meta-feature that is not described in §2. --- hal/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 55a1106c..516b6a1a 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -112,6 +112,7 @@ flash-1M = [] # "20"-suffix flash-512K = [] # "19"-suffix rev-a = [] # No suffix after flash memory density number rev-b = ["device-selected"] # "b"-suffix + device-selected = [] same70j19 = ["atsame70j19", "e70", "pins-64", "flash-512K", "rev-a"] From d77f43ee0c7fdaa796333ae5813e581b3a64ba1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6rtsell?= Date: Tue, 26 Apr 2022 07:27:29 -0700 Subject: [PATCH 07/44] Add Voltage Level Checks To build.rs --- hal/build.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hal/build.rs b/hal/build.rs index 0286feba..96ba44f9 100644 --- a/hal/build.rs +++ b/hal/build.rs @@ -17,5 +17,15 @@ fn main() -> Result<(), &'static str> { ); } + if feat("vddio-3v") && feat("vddio-1v") { + return Err( + "\"vddio-3v\" and \"vddio-1v\" are mutually exclusive features, try building with one, not both." + ); + } else if !(feat("vddio-3v") || feat("vddio-1v")) { + return Err( + "The HAL is built for a specific voltage level using a feature, but no such feature was selected." + ); + } + Ok(()) } From 83de6117f0f097f0370e6a267e64179aa60bb8ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6rtsell?= Date: Tue, 26 Apr 2022 07:51:26 -0700 Subject: [PATCH 08/44] Fix Return Types --- hal/src/efc.rs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hal/src/efc.rs b/hal/src/efc.rs index 639ea4ef..77768932 100644 --- a/hal/src/efc.rs +++ b/hal/src/efc.rs @@ -46,10 +46,11 @@ impl TryFrom for FlashWaitStates { type Error = PmcError; #[cfg(feature = "vddio-3v")] - fn try_from(freq: u8) -> Result { + fn try_from(freq: u8) -> Result { // References: // - Table 58-50 (p. 1804) Embedded Flash Wait States for Worst-Case Conditions (V70/V71) // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 3.0V) + Ok( match freq { 0..=23 => Self::Zero, 24..=46 => Self::One, @@ -58,14 +59,15 @@ impl TryFrom for FlashWaitStates { 93..=115 => Self::Four, 116..=138 => Self::Five, 139..=150 => Self::Six, - _ => Err(PmcError::InvalidConfiguration), - } + _ => return Err(PmcError::InvalidConfiguration), + }) } #[cfg(feature = "vddio-1v")] - fn try_from(freq: u8) -> Result { + fn try_from(freq: u8) -> Result { // References: // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 1.7V) + Ok( match freq { 0..=21 => Self::Zero, 22..=42 => Self::One, @@ -74,8 +76,9 @@ impl TryFrom for FlashWaitStates { 85..=106 => Self::Four, 107..=125 => Self::Five, 126..=137 => Self::Six, - _ => Err(PmcError::InvalidConfiguration), + _ => return Err(PmcError::InvalidConfiguration), } + ) } } @@ -86,6 +89,6 @@ impl FlashWaitStates { /// The max mck frequency supported is 150MHz. This is *not* the CPU frequency, /// which may go up to 300MHz. pub fn from_mck_mhz(freq: u8) -> Result { - freq.try_into()? + Ok(freq.try_into()?) } } From 3f1f25f444f4f7e7437905982adb2030a4af5b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6rtsell?= Date: Tue, 26 Apr 2022 14:22:00 -0700 Subject: [PATCH 09/44] Add get_ Methods For All The Clocks (Except SLCK) --- hal/src/pmc.rs | 117 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 6 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 5a22f5ae..9d1b248b 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -22,15 +22,51 @@ pub enum PmcError { /// The selected "Main Clock Oscillator" source /// -/// TODO/NOTE: At the moment, we only support the internal trimmed 12MHz -/// oscillator. Some driver behavior is hardcoded on this assumption for -/// the sake of simplicity. +/// MainCrystalOsc should have a frequency type, I think /// /// This corresponds to CKGR_MOR.MOSCSEL +#[derive(Debug, PartialEq, Clone)] pub enum MainClockOscillatorSource { - MainCk12MHz, + MainRcOsc(MainRcFreq), + MainCrystalOsc(MegaHertz), +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum MainRcFreq { + MHz4 = 0, + MHz8 = 1, + MHz12 = 2, } +#[derive(Debug, PartialEq, Clone)] +pub struct MegaHertz(pub u32); + +pub struct MainClock { + source: MainClockOscillatorSource, +} + +// Slow Clock Oscillator Source is set in SUPC +pub enum SlowClockOscillatorSource { + SlowRcOsc, + SlowCrystalOsc, +} + +pub struct SlowClock { + source: SlowClockOscillatorSource, +} + +pub struct PllaConfig<'a> { + pub source: &'a MainClock, + pub div: u8, + pub mult: u8, +} + +pub struct PllaClock<'a> { + config: PllaConfig<'a>, +} + +pub struct UpllClock; + /// The selected "Master Clock" source /// /// TODO/NOTE: At the moment, we only support the PLLA Clock. @@ -78,7 +114,8 @@ impl ClockSettings { MasterClockSource::PllaClock => { // The internal 12MHz osc is currently the (only) choice for driving the PLLACK let main_ck: f32 = match self.main_clk_osc_src { - MainClockOscillatorSource::MainCk12MHz => 12.0, + MainClockOscillatorSource::MainRcOsc(MainRcFreq::MHz12) => 12.0, + _ => panic!("Only Internal 12MHz source is accepted here"), }; // These values disable the PLLA @@ -153,10 +190,78 @@ impl Pmc { // TODO: I could probably figure out the default settings... // this is fine for now. + // TODO: If the get_mainck() etc. API is to be used pmc needs to have ownership over + // the clock structs at startup to prevent multiple instances of MainClock to be + // constructed. Watchucallit, Singletons. settings: None, } } + pub fn get_mainck(&mut self, source: MainClockOscillatorSource) -> Result { + match source { + MainClockOscillatorSource::MainRcOsc(ref freq) => { + let freq_bits = *freq as u8; + self.periph.ckgr_mor.modify( |_,w| { + w.moscsel().clear_bit(); + w.moscrcen().set_bit(); + unsafe { w.moscrcf().bits(freq_bits); } + w + }); + }, + MainClockOscillatorSource::MainCrystalOsc(ref freq) => { + // Crystal Frequency needs to be between 3 and 20MHz (30.2) + if freq.0 < 3 || freq.0 > 20 { + return Err(PmcError::InvalidConfiguration); + } + self.periph.ckgr_mor.modify( |_,w| { + w.moscxten().set_bit(); + w + }); + // loop until main crystal oscillator has stabilised + while self.periph.pmc_sr.read().moscxts().bit_is_clear() {} + self.periph.ckgr_mor.modify( |_,w| { + w.moscsel().set_bit(); + w + }); + // loop until source switch has completed + while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} + } + } + Ok(MainClock{ source }) + } + + pub fn get_pllack<'a>(&mut self, config: PllaConfig<'a>) -> Result, PmcError> { + if config.mult > 63 && config.mult < 2 { + return Err(PmcError::InvalidConfiguration); + } + if config.div == 0 || config.div > 127 { + return Err(PmcError::InvalidConfiguration); + } + // NOTE: Maximum frequency is not checked her + + self.periph.ckgr_pllar.modify( |_,w| { + w.one().set_bit(); + unsafe { + w.mula().bits(config.mult as u16 - 1); + w.diva().bits(config.div); + } + w + }); + // loop until PLLA Lock Status + while self.periph.pmc_sr.read().locka().bit_is_clear() {} + Ok(PllaClock{ config }) + } + + pub fn get_upllck(&mut self) -> Result { + self.periph.ckgr_uckr.modify(|_,w| w.upllen().set_bit()); + // loop until UPLL Lock Status + while self.periph.pmc_sr.read().locku().bit_is_clear() {} + + Ok(UpllClock) + } + + + pub fn settings(&self) -> Option<&ClockSettings> { self.settings.as_ref() } @@ -251,7 +356,7 @@ impl Pmc { // TODO: This is the only supported variant. This code will fail if we add another option. // You should implement the logic of steps 1-5 here if you are adding more MCO source // options to the public interface! - let MainClockOscillatorSource::MainCk12MHz = cfg.main_clk_osc_src; + // let MainClockOscillatorSource::MainRcOsc(MHz12) = cfg.main_clk_osc_src; // # Step 6 // From 762e919bbf644113496becf1e15a2909a8410e4d Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Wed, 27 Apr 2022 16:02:11 +0200 Subject: [PATCH 10/44] efc: set VDDIO level via ctor instead of feature This change supports the theoretical case of non-constant VDDIO (for E70/S70). An enum is also a much stronger association, rather than an environmental variable (feature). --- hal/Cargo.toml | 8 +--- hal/build.rs | 10 ----- hal/src/efc.rs | 114 +++++++++++++++++++++++++++---------------------- hal/src/pmc.rs | 12 +----- 4 files changed, 67 insertions(+), 77 deletions(-) diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 516b6a1a..285f1cf0 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -95,13 +95,9 @@ atsamv71q21b = { version = "0.21.0", path = "../pac/atsamv71q21b", optional = tr [features] # Internal-only feature flags; do not set directly. # Refer to §2 in the data sheet. -## Different electrical characteristics for different VDDIO values -## Refer to Tables 58-3 and 58-9 -vddio-3v = [] # Minimum value of 3.3V -vddio-1v = [] # Minimum value of 1.7V ## Refer to §2. -v71 = ["vddio-3v"] -v70 = ["vddio-3v"] +v71 = [] +v70 = [] e70 = [] s70 = [] pins-64 = [] # J variants diff --git a/hal/build.rs b/hal/build.rs index 96ba44f9..0286feba 100644 --- a/hal/build.rs +++ b/hal/build.rs @@ -17,15 +17,5 @@ fn main() -> Result<(), &'static str> { ); } - if feat("vddio-3v") && feat("vddio-1v") { - return Err( - "\"vddio-3v\" and \"vddio-1v\" are mutually exclusive features, try building with one, not both." - ); - } else if !(feat("vddio-3v") || feat("vddio-1v")) { - return Err( - "The HAL is built for a specific voltage level using a feature, but no such feature was selected." - ); - } - Ok(()) } diff --git a/hal/src/efc.rs b/hal/src/efc.rs index 77768932..d055d90d 100644 --- a/hal/src/efc.rs +++ b/hal/src/efc.rs @@ -3,27 +3,46 @@ use crate::pmc::PmcError; use crate::target_device::EFC; +/// The voltage which drives the MCU. +/// +/// Refer to §58 and §59. +#[derive(PartialEq)] +pub enum VddioLevel { + /// VDDIO = 3.3V, typical + V3, + /// VDDIO = 1.7V, minimal + V1, +} + pub struct Efc { pub(crate) periph: EFC, + vddio: VddioLevel, } impl Efc { - pub fn new(periph: EFC) -> Self { + pub fn new(periph: EFC, vddio: VddioLevel) -> Self { periph.eefc_wpmr.modify(|_r, w| { w.wpkey().passwd(); w.wpen().clear_bit(); w }); - Self { periph } + Self { periph, vddio } } - pub fn set_wait_states(&mut self, fws: FlashWaitStates) { - let fws_bits = fws as u8; + /// Calculates and sets the lowest possible number of flash wait + /// states from a given master clock frequency in MHz. + /// + /// The max mck frequency supported is 150MHz. This is *not* the CPU frequency, + /// which may go up to 300MHz. + pub fn set_wait_states(&mut self, freq: u8) -> Result<(), PmcError> { + let fws = FlashWaitStates::calculate(freq, &self.vddio)?; self.periph .eefc_fmr - .modify(|_r, w| unsafe { w.fws().bits(fws_bits) }); + .modify(|_r, w| unsafe { w.fws().bits(fws as u8) }); + + Ok(()) } } @@ -32,7 +51,7 @@ impl Efc { /// Note: The number of cycles a read takes is 1 + FWS. #[derive(Debug, PartialEq, Copy, Clone)] #[repr(u8)] -pub enum FlashWaitStates { +enum FlashWaitStates { Zero, One, Two, @@ -42,53 +61,48 @@ pub enum FlashWaitStates { Six, } -impl TryFrom for FlashWaitStates { - type Error = PmcError; +impl FlashWaitStates { + pub fn calculate(freq: u8, vddio: &VddioLevel) -> Result { + #[cfg(any(feature = "v70", feature = "v71"))] + if vddio == &VddioLevel::V1 { + // V70/V71 must be driven with VDDIO = 3.3V, typical + return Err(PmcError::InvalidConfiguration); + } - #[cfg(feature = "vddio-3v")] - fn try_from(freq: u8) -> Result { - // References: - // - Table 58-50 (p. 1804) Embedded Flash Wait States for Worst-Case Conditions (V70/V71) - // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 3.0V) - Ok( - match freq { - 0..=23 => Self::Zero, - 24..=46 => Self::One, - 47..=69 => Self::Two, - 70..=92 => Self::Three, - 93..=115 => Self::Four, - 116..=138 => Self::Five, - 139..=150 => Self::Six, - _ => return Err(PmcError::InvalidConfiguration), - }) + Self::fws_from_freq(freq, vddio) } - #[cfg(feature = "vddio-1v")] - fn try_from(freq: u8) -> Result { - // References: - // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 1.7V) - Ok( - match freq { - 0..=21 => Self::Zero, - 22..=42 => Self::One, - 43..=63 => Self::Two, - 64..=84 => Self::Three, - 85..=106 => Self::Four, - 107..=125 => Self::Five, - 126..=137 => Self::Six, - _ => return Err(PmcError::InvalidConfiguration), + fn fws_from_freq(freq: u8, vddio: &VddioLevel) -> Result { + match vddio { + VddioLevel::V1 => { + // References: + // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 1.7V) + Ok(match freq { + 0..=21 => Self::Zero, + 22..=42 => Self::One, + 43..=63 => Self::Two, + 64..=84 => Self::Three, + 85..=106 => Self::Four, + 107..=125 => Self::Five, + 126..=137 => Self::Six, + _ => return Err(PmcError::InvalidConfiguration), + }) + } + VddioLevel::V3 => { + // References: + // - Table 58-50 (p. 1804) Embedded Flash Wait States for Worst-Case Conditions (V70/V71) + // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 3.0V) + Ok(match freq { + 0..=23 => Self::Zero, + 24..=46 => Self::One, + 47..=69 => Self::Two, + 70..=92 => Self::Three, + 93..=115 => Self::Four, + 116..=138 => Self::Five, + 139..=150 => Self::Six, + _ => return Err(PmcError::InvalidConfiguration), + }) + } } - ) - } -} - -impl FlashWaitStates { - /// Calculate the lowest possible number of flash wait states from a given - /// master clock frequency in MHz. - /// - /// The max mck frequency supported is 150MHz. This is *not* the CPU frequency, - /// which may go up to 300MHz. - pub fn from_mck_mhz(freq: u8) -> Result { - Ok(freq.try_into()?) } } diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 9d1b248b..3e901b6e 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -1,7 +1,6 @@ //! Clock hierarchy configuration use crate::efc::Efc; -use crate::efc::FlashWaitStates; use crate::target_device::PMC; pub use crate::target_device::pmc::pmc_mckr::MDIV_A as MckDivider; @@ -334,16 +333,7 @@ impl Pmc { // // The flash controller (EEFC) is driven from the master clock (stated in section 31.2) let mck_new = cfg.calc_master_clk_mhz()?; - let fws = FlashWaitStates::from_mck_mhz(mck_new)?; - - efc.periph.eefc_wpmr.modify(|_r, w| { - w.wpkey().passwd(); - w.wpen().clear_bit(); - w - }); - efc.periph - .eefc_fmr - .modify(|_r, w| unsafe { w.fws().bits(fws as u8) }); + efc.set_wait_states(mck_new)?; // Note: This follows Datasheet 31.17 "Recommendeded Programming Sequence" // From be1950103cfe71d88321bfef54b485b37e7e6045 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Wed, 27 Apr 2022 16:09:26 +0200 Subject: [PATCH 11/44] build: ensure a chip feature is enabled That is, so the crate does not build if only device-selected is enabled. --- hal/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hal/build.rs b/hal/build.rs index 0286feba..f8f900c8 100644 --- a/hal/build.rs +++ b/hal/build.rs @@ -11,7 +11,7 @@ fn main() -> Result<(), &'static str> { .is_ok() } - if !feat("device-selected") { + if !feat("device-selected") || ["e70", "s70", "v70", "v71"].iter().all(|&f| !feat(f)) { return Err( "The HAL is built for a specific target device selected using a feature, but no such a feature was selected." ); From e42837e250edbaf22da140f3f150a1bda2a85920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6rtsell?= Date: Wed, 27 Apr 2022 15:06:13 -0700 Subject: [PATCH 12/44] pmc: Add Methods For Configuring Plla And Mck --- hal/src/pmc.rs | 86 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 85 insertions(+), 1 deletion(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 9d1b248b..dd664ce9 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -67,6 +67,29 @@ pub struct PllaClock<'a> { pub struct UpllClock; +pub enum Clock<'a> { + Slck(&'a SlowClock), + Mainck(&'a MainClock), + Pllack(&'a PllaClock<'a>), + Upllck(&'a UpllClock), + Mck(&'a HostClock<'a>), +} + + +pub struct HostClockConfig<'a> { + pub clock: Clock<'a>, + pub pres: u8, + pub div: u8, +} + +pub struct HostClock<'a> { + pub config: &'a HostClockConfig<'a>, +} + +pub struct ProcessorClock<'a> { + pub config: &'a HostClockConfig<'a>, +} + /// The selected "Master Clock" source /// /// TODO/NOTE: At the moment, we only support the PLLA Clock. @@ -208,6 +231,11 @@ impl Pmc { w }); }, + // NOTE Only the RC oscillator works for now. + // I misunderstood how the Crystal Oscillator/SAME70 XPLAINED board works. + // The devkit should be used with the crystal oscillator in standby mode, + // so I cannot test the crystal oscillator as it stands now. I tried to make bypass + // mode work, but I failed. MainClockOscillatorSource::MainCrystalOsc(ref freq) => { // Crystal Frequency needs to be between 3 and 20MHz (30.2) if freq.0 < 3 || freq.0 > 20 { @@ -215,6 +243,7 @@ impl Pmc { } self.periph.ckgr_mor.modify( |_,w| { w.moscxten().set_bit(); + unsafe{ w.moscxtst().bits(255);} w }); // loop until main crystal oscillator has stabilised @@ -231,7 +260,7 @@ impl Pmc { } pub fn get_pllack<'a>(&mut self, config: PllaConfig<'a>) -> Result, PmcError> { - if config.mult > 63 && config.mult < 2 { + if config.mult > 63 || config.mult < 2 { return Err(PmcError::InvalidConfiguration); } if config.div == 0 || config.div > 127 { @@ -260,6 +289,61 @@ impl Pmc { Ok(UpllClock) } + pub fn get_hclk<'a>(&mut self, config: &'a HostClockConfig<'a>) -> Result<(ProcessorClock<'a>, HostClock<'a>), PmcError> { + let pres_bits = match config.pres { + 1 => 0, + 2 => 1, + 4 => 2, + 8 => 3, + 16 => 4, + 32 => 5, + 64 => 6, + 3 => 7, + _ => return Err(PmcError::UnimplementedError), + }; + let div_bits = match config.div { + 1 => 0, + 2 => 1, + 3 => 2, + 4 => 3, + _ => return Err(PmcError::InvalidConfiguration), + }; + + match config.clock { + Clock::Slck(clock) => { + self.periph.pmc_mckr.modify(|_,w| w.css().slow_clk()); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + self.periph.pmc_mckr.modify(|_,w| w.pres().bits(pres_bits)); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + self.periph.pmc_mckr.modify( |_,w| w.mdiv().bits(div_bits)); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + }, + Clock::Mainck(clock) => { + self.periph.pmc_mckr.modify(|_,w| w.css().main_clk()); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + self.periph.pmc_mckr.modify(|_,w| w.pres().bits(pres_bits)); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + self.periph.pmc_mckr.modify( |_,w| { w.mdiv().bits(div_bits)}); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + }, + Clock::Pllack(clock) => { + self.periph.pmc_mckr.modify(|_,w| w.pres().bits(pres_bits)); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + self.periph.pmc_mckr.modify( |_,w| w.mdiv().bits(div_bits)); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + self.periph.pmc_mckr.modify(|_,w| w.css().plla_clk()); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + }, + // Upllck passes through a divider, so it should be Upllckdiv but that's not + // implemented yet + Clock::Upllck(clock) => {return Err(PmcError::InvalidConfiguration)}, + // Mck cannot be used as a source for mck + Clock::Mck(clock) => {return Err(PmcError::InvalidConfiguration);}, + } + Ok((ProcessorClock{ config: &config }, HostClock{ config: &config } )) + + } + pub fn settings(&self) -> Option<&ClockSettings> { From d8edfe11ba61676af6225773591e3e00041f6fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6rtsell?= Date: Wed, 27 Apr 2022 15:35:27 -0700 Subject: [PATCH 13/44] pmc: Add Non-functional External Oscillator Support --- hal/src/pmc.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 18d99360..79e226b9 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -28,6 +28,7 @@ pub enum PmcError { pub enum MainClockOscillatorSource { MainRcOsc(MainRcFreq), MainCrystalOsc(MegaHertz), + MainExternalOsc(MegaHertz), } #[derive(Debug, PartialEq, Clone, Copy)] @@ -254,6 +255,19 @@ impl Pmc { // loop until source switch has completed while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} } + MainClockOscillatorSource::MainExternalOsc(ref freq) => { + // Oscillator Frequency needs to be between 3 and 20MHz (30.2) + if freq.0 < 3 || freq.0 > 20 { + return Err(PmcError::InvalidConfiguration); + } + self.periph.ckgr_mor.modify( |_,w| { + w.moscxtby().set_bit(); + w + }); + // loop until source switch has completed + while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} + + } } Ok(MainClock{ source }) } From daa8a2c6dde20261f90682fb0fcbed453dc40362 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6rtsell?= Date: Tue, 3 May 2022 15:47:58 -0700 Subject: [PATCH 14/44] pmc: Fix Main Clock Selection --- Cargo.lock | 25 +++++++++++++++++++++++++ hal/Cargo.toml | 1 + hal/src/pmc.rs | 25 +++++++++++++++++++++---- 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 206d1cfc..04a2ccd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -668,6 +668,7 @@ dependencies = [ "atsamv71q21b", "cortex-m", "embedded-hal", + "fugit", "nb 0.1.3", ] @@ -728,6 +729,24 @@ dependencies = [ "void", ] +[[package]] +name = "fugit" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6d8595783d5ca52f0e9830036b3d24f359fae0fcc6bb5fde41f2dd82997cb58" +dependencies = [ + "gcd", +] + +[[package]] +name = "gcd" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37978dab2ca789938a83b2f8bc1ef32db6633af9051a6cd409eff72cbaaa79a" +dependencies = [ + "paste", +] + [[package]] name = "nb" version = "0.1.3" @@ -743,6 +762,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" +[[package]] +name = "paste" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" + [[package]] name = "proc-macro2" version = "1.0.18" diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 285f1cf0..d6e45bf4 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -24,6 +24,7 @@ edition = "2021" cortex-m = "0.7" embedded-hal = { version = "0.2.7", features = ["unproven"] } nb = "0.1.2" +fugit = "0.3.5" atsame70j19 = { version = "0.21.0", path = "../pac/atsame70j19", optional = true } atsame70j19b = { version = "0.21.0", path = "../pac/atsame70j19b", optional = true } diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 79e226b9..47ce6fbd 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -2,6 +2,7 @@ use crate::efc::Efc; use crate::target_device::PMC; +use fugit::Rate; pub use crate::target_device::pmc::pmc_mckr::MDIV_A as MckDivider; pub use crate::target_device::pmc::pmc_mckr::PRES_A as MckPrescaler; @@ -27,8 +28,8 @@ pub enum PmcError { #[derive(Debug, PartialEq, Clone)] pub enum MainClockOscillatorSource { MainRcOsc(MainRcFreq), - MainCrystalOsc(MegaHertz), - MainExternalOsc(MegaHertz), + MainCrystalOsc(Rate), + MainExternalOsc(Rate), } #[derive(Debug, PartialEq, Clone, Copy)] @@ -225,6 +226,7 @@ impl Pmc { MainClockOscillatorSource::MainRcOsc(ref freq) => { let freq_bits = *freq as u8; self.periph.ckgr_mor.modify( |_,w| { + w.key().passwd(); w.moscsel().clear_bit(); w.moscrcen().set_bit(); unsafe { w.moscrcf().bits(freq_bits); } @@ -238,10 +240,11 @@ impl Pmc { // mode work, but I failed. MainClockOscillatorSource::MainCrystalOsc(ref freq) => { // Crystal Frequency needs to be between 3 and 20MHz (30.2) - if freq.0 < 3 || freq.0 > 20 { + if freq.to_MHz() < 3 || freq.to_MHz() > 20 { return Err(PmcError::InvalidConfiguration); } self.periph.ckgr_mor.modify( |_,w| { + w.key().passwd(); w.moscxten().set_bit(); unsafe{ w.moscxtst().bits(255);} w @@ -249,7 +252,9 @@ impl Pmc { // loop until main crystal oscillator has stabilised while self.periph.pmc_sr.read().moscxts().bit_is_clear() {} self.periph.ckgr_mor.modify( |_,w| { + w.key().passwd(); w.moscsel().set_bit(); + unsafe{ w.moscxtst().bits(255);} w }); // loop until source switch has completed @@ -257,15 +262,27 @@ impl Pmc { } MainClockOscillatorSource::MainExternalOsc(ref freq) => { // Oscillator Frequency needs to be between 3 and 20MHz (30.2) - if freq.0 < 3 || freq.0 > 20 { + if freq.to_MHz() < 3 || freq.to_MHz() > 20 { return Err(PmcError::InvalidConfiguration); } self.periph.ckgr_mor.modify( |_,w| { + w.key().passwd(); w.moscxtby().set_bit(); w }); // loop until source switch has completed while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} + self.periph.ckgr_mor.modify( |_,w| { + w.key().passwd(); + w.moscsel().set_bit(); + w + }); + while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} + self.periph.ckgr_mor.modify( |_,w| { + w.key().passwd(); + w.moscrcen().clear_bit(); + w + }); } } From 9faca53d061b51c5f4b3067b4830e997c53b7444 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6rtsell?= Date: Thu, 5 May 2022 14:00:49 -0700 Subject: [PATCH 15/44] pmc: Add PCK Configuration --- hal/src/pmc.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 47ce6fbd..c3d70cc4 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -102,6 +102,33 @@ pub enum MasterClockSource { PllaClock, } + +pub struct Pck { + id: PckId, + +} + + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum PckId { + Pck0, + Pck1, + Pck2, + Pck3, + Pck4, + Pck5, + Pck6, + Pck7, +} + +pub enum PckSource { + Sclk, + Mainck, + Pllack, + UpllCk, + Mck, +} + pub struct ClockSettings { // "Main Clock" is abbreviated "MAINCK" /// Main Clock Oscillator Source @@ -374,6 +401,18 @@ impl Pmc { } + pub fn get_pck(&mut self, source: PckSource, pres: u8, id: PckId) -> Result { + + self.periph.pmc_pck[id as usize].write(|w| unsafe { + w.pres().bits(pres); + w.css().bits(source as u8) + }); + self.periph.pmc_scer.write( |w|unsafe{ w.bits(1<<(id as u8+8))}); + while (self.periph.pmc_scsr.read().bits() & (1<< (id as u8+8))) == 0 {} + Ok(Pck {id}) + + } + pub fn settings(&self) -> Option<&ClockSettings> { From 14660e7ce7b3a79104677a64f65ddec0b1a5a202 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6rtsell?= Date: Fri, 6 May 2022 14:03:21 -0700 Subject: [PATCH 16/44] Update Documentation --- hal/src/pmc.rs | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index c3d70cc4..1ebde52a 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -1,4 +1,8 @@ //! Clock hierarchy configuration +//! +//! This module allows the user to fully control the various clock sources available on ATSAMx7x +//! chips. +//! use crate::efc::Efc; use crate::target_device::PMC; @@ -21,10 +25,6 @@ pub enum PmcError { } /// The selected "Main Clock Oscillator" source -/// -/// MainCrystalOsc should have a frequency type, I think -/// -/// This corresponds to CKGR_MOR.MOSCSEL #[derive(Debug, PartialEq, Clone)] pub enum MainClockOscillatorSource { MainRcOsc(MainRcFreq), @@ -32,6 +32,7 @@ pub enum MainClockOscillatorSource { MainExternalOsc(Rate), } +/// RC Source can be configured to run at 4, 8, or 12MHz #[derive(Debug, PartialEq, Clone, Copy)] pub enum MainRcFreq { MHz4 = 0, @@ -39,9 +40,7 @@ pub enum MainRcFreq { MHz12 = 2, } -#[derive(Debug, PartialEq, Clone)] -pub struct MegaHertz(pub u32); - +/// MAINCK Token pub struct MainClock { source: MainClockOscillatorSource, } @@ -52,6 +51,7 @@ pub enum SlowClockOscillatorSource { SlowCrystalOsc, } +/// SCLK Token pub struct SlowClock { source: SlowClockOscillatorSource, } @@ -62,6 +62,7 @@ pub struct PllaConfig<'a> { pub mult: u8, } +/// PLLA Token pub struct PllaClock<'a> { config: PllaConfig<'a>, } @@ -77,16 +78,19 @@ pub enum Clock<'a> { } +/// HCLK/MCK Config pub struct HostClockConfig<'a> { pub clock: Clock<'a>, pub pres: u8, pub div: u8, } +/// MCK Token pub struct HostClock<'a> { pub config: &'a HostClockConfig<'a>, } +/// HCLK Token pub struct ProcessorClock<'a> { pub config: &'a HostClockConfig<'a>, } @@ -102,13 +106,12 @@ pub enum MasterClockSource { PllaClock, } - +/// PCK token pub struct Pck { id: PckId, } - #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum PckId { Pck0, @@ -248,6 +251,8 @@ impl Pmc { } } + /// Configures MAINCk and returns a corresponding Clock Token. + /// This Method corresponds to Steps 2-4 in 31.17 Recommended Programming Sequence. pub fn get_mainck(&mut self, source: MainClockOscillatorSource) -> Result { match source { MainClockOscillatorSource::MainRcOsc(ref freq) => { @@ -260,11 +265,6 @@ impl Pmc { w }); }, - // NOTE Only the RC oscillator works for now. - // I misunderstood how the Crystal Oscillator/SAME70 XPLAINED board works. - // The devkit should be used with the crystal oscillator in standby mode, - // so I cannot test the crystal oscillator as it stands now. I tried to make bypass - // mode work, but I failed. MainClockOscillatorSource::MainCrystalOsc(ref freq) => { // Crystal Frequency needs to be between 3 and 20MHz (30.2) if freq.to_MHz() < 3 || freq.to_MHz() > 20 { @@ -316,6 +316,8 @@ impl Pmc { Ok(MainClock{ source }) } + /// Configures PLLACK and returns a corresponding clock token. + /// This method corresponds to Step 6 of 31.17 Recommended Programming Sequence. pub fn get_pllack<'a>(&mut self, config: PllaConfig<'a>) -> Result, PmcError> { if config.mult > 63 || config.mult < 2 { return Err(PmcError::InvalidConfiguration); @@ -338,6 +340,8 @@ impl Pmc { Ok(PllaClock{ config }) } + /// Configures UPLLCK + /// TODO: There's the UPLLDIV2 that is not touched right now. Should be toggleable. pub fn get_upllck(&mut self) -> Result { self.periph.ckgr_uckr.modify(|_,w| w.upllen().set_bit()); // loop until UPLL Lock Status @@ -346,6 +350,8 @@ impl Pmc { Ok(UpllClock) } + /// Configures HCLK and MCK and returns corresponding Clock Tokens. + /// This method corresponds to Step 7 in 31.17. pub fn get_hclk<'a>(&mut self, config: &'a HostClockConfig<'a>) -> Result<(ProcessorClock<'a>, HostClock<'a>), PmcError> { let pres_bits = match config.pres { 1 => 0, @@ -401,6 +407,8 @@ impl Pmc { } + /// Configures PCKx and returns a token. + /// Corresponds to Step 8 in 31.17 pub fn get_pck(&mut self, source: PckSource, pres: u8, id: PckId) -> Result { self.periph.pmc_pck[id as usize].write(|w| unsafe { From 4ce66d68c48e99cf42a1d0acb969751de25ffb60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6rtsell?= Date: Mon, 9 May 2022 14:53:34 -0700 Subject: [PATCH 17/44] pmc: Change Clock Source From Enum To Trait --- hal/src/pmc.rs | 386 ++++++++----------------------------------------- 1 file changed, 62 insertions(+), 324 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 1ebde52a..9098da81 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -4,16 +4,17 @@ //! chips. //! -use crate::efc::Efc; use crate::target_device::PMC; use fugit::Rate; pub use crate::target_device::pmc::pmc_mckr::MDIV_A as MckDivider; pub use crate::target_device::pmc::pmc_mckr::PRES_A as MckPrescaler; +pub use crate::target_device::pmc::pmc_mckr::CSS_A as HCC_CSS; +pub use crate::target_device::pmc::pmc_pck::CSS_A as PCK_CSS; +pub use crate::target_device::pmc::ckgr_mor::MOSCRCF_A as MainRcFreq; pub struct Pmc { periph: PMC, - settings: Option, } #[derive(Debug, PartialEq, Clone)] @@ -32,14 +33,6 @@ pub enum MainClockOscillatorSource { MainExternalOsc(Rate), } -/// RC Source can be configured to run at 4, 8, or 12MHz -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum MainRcFreq { - MHz4 = 0, - MHz8 = 1, - MHz12 = 2, -} - /// MAINCK Token pub struct MainClock { source: MainClockOscillatorSource, @@ -56,43 +49,29 @@ pub struct SlowClock { source: SlowClockOscillatorSource, } -pub struct PllaConfig<'a> { - pub source: &'a MainClock, +pub struct PllaConfig { pub div: u8, pub mult: u8, } /// PLLA Token -pub struct PllaClock<'a> { - config: PllaConfig<'a>, +pub struct PllaClock { } pub struct UpllClock; -pub enum Clock<'a> { - Slck(&'a SlowClock), - Mainck(&'a MainClock), - Pllack(&'a PllaClock<'a>), - Upllck(&'a UpllClock), - Mck(&'a HostClock<'a>), -} - - /// HCLK/MCK Config -pub struct HostClockConfig<'a> { - pub clock: Clock<'a>, - pub pres: u8, - pub div: u8, +pub struct HostClockConfig { + pub pres: MckPrescaler, + pub div: MckDivider, } /// MCK Token -pub struct HostClock<'a> { - pub config: &'a HostClockConfig<'a>, +pub struct HostClock { } /// HCLK Token -pub struct ProcessorClock<'a> { - pub config: &'a HostClockConfig<'a>, +pub struct ProcessorClock { } /// The selected "Master Clock" source @@ -124,111 +103,46 @@ pub enum PckId { Pck7, } -pub enum PckSource { - Sclk, - Mainck, - Pllack, - UpllCk, - Mck, +pub trait PllaSource {} + +impl PllaSource for MainClock {} + +pub trait HostClockSource { + const HCC_CSS: HCC_CSS; } -pub struct ClockSettings { - // "Main Clock" is abbreviated "MAINCK" - /// Main Clock Oscillator Source - pub main_clk_osc_src: MainClockOscillatorSource, - - // "Master Clock" is abbreviated "MCK" - /// Master Clock Prescaler - pub mck_pres: MckPrescaler, - /// Master Clock Source - pub mck_src: MasterClockSource, - /// Master Clock Divider - pub mck_div: MckDivider, - - /// PLLA Multiplier - 10 bits - /// - /// Resulting change is (1 + MULA) * INPUT - /// - /// A value of zero disables the PLLA. - pub multiplier_a: u16, - /// PLLA Divider - 8 bits - /// - /// Result change is INPUT / DIVA - /// - /// A value of zero disables the PLLA. - pub divider_a: u8, +impl HostClockSource for SlowClock { + const HCC_CSS: HCC_CSS = HCC_CSS::SLOW_CLK; } +impl HostClockSource for MainClock { + const HCC_CSS: HCC_CSS = HCC_CSS::MAIN_CLK; +} +impl HostClockSource for PllaClock { -impl ClockSettings { - pub fn calc_master_clk_mhz(&self) -> Result { - // NOTE: This is based on Figure 31-1 - "General Clock Distribution Block Diagram" - - // PLLACK is currently the (only) choice for driving the Master Clock Controller - let mck_input = match self.mck_src { - MasterClockSource::PllaClock => { - // The internal 12MHz osc is currently the (only) choice for driving the PLLACK - let main_ck: f32 = match self.main_clk_osc_src { - MainClockOscillatorSource::MainRcOsc(MainRcFreq::MHz12) => 12.0, - _ => panic!("Only Internal 12MHz source is accepted here"), - }; - - // These values disable the PLLA - if (self.multiplier_a == 0) || (self.divider_a == 0) { - return Err(PmcError::InvalidConfiguration); - } + const HCC_CSS: HCC_CSS = HCC_CSS::PLLA_CLK; +} +impl HostClockSource for UpllClock { + const HCC_CSS: HCC_CSS = HCC_CSS::UPLL_CLK; +} - let plla_ck = main_ck * ((self.multiplier_a + 1) as f32); - let plla_ck = plla_ck / (self.divider_a as f32); +pub trait PckSource { + const PCK_CSS: PCK_CSS; +} - plla_ck - } - }; - - // MCK input first passes through a prescaler... - let presc: f32 = match self.mck_pres { - MckPrescaler::CLK_1 => 1.0, - MckPrescaler::CLK_2 => 2.0, - MckPrescaler::CLK_3 => 3.0, - MckPrescaler::CLK_4 => 4.0, - MckPrescaler::CLK_8 => 8.0, - MckPrescaler::CLK_16 => 16.0, - MckPrescaler::CLK_32 => 32.0, - MckPrescaler::CLK_64 => 64.0, - }; - - let post_pres = mck_input / presc; - - // ... then the output of the prescaler is passed through a divider - let div: f32 = match self.mck_div { - MckDivider::EQ_PCK => 1.0, - MckDivider::PCK_DIV2 => 2.0, - MckDivider::PCK_DIV3 => 3.0, - MckDivider::PCK_DIV4 => 4.0, - }; - let mck = post_pres / div; - - // Make sure we're still in a reasonable range - // - // TODO: The datasheet says: - // - // > Master Clock (MCK), programmable from a few hundred Hz to the maximum operating frequency of - // > the device. It is available to the modules running permanently, such as the Enhanced Embedded - // > Flash Controller - // - // However I currently assume that it never exceeds 150MHz. This could probably be changed. - // This is mostly because the datasheet only lists calculations for flash wait states up to - // a master clock of 150MHz. - if mck > 255.0 { - return Err(PmcError::InvalidConfiguration); - } else { - // Note, this is a "floor" operation, while we should probably - // be using "ceil", though that requires libm (or a similar float library). - // - // For now, this is probably close enough. - let mck_u8 = mck as u8; - Ok(mck_u8) - } - } +impl PckSource for SlowClock { + const PCK_CSS: PCK_CSS = PCK_CSS::SLOW_CLK; +} +impl PckSource for MainClock { + const PCK_CSS: PCK_CSS = PCK_CSS::MAIN_CLK; +} +impl PckSource for UpllClock { + const PCK_CSS: PCK_CSS = PCK_CSS::UPLL_CLK; +} +impl PckSource for PllaClock { + const PCK_CSS: PCK_CSS = PCK_CSS::PLLA_CLK; +} +impl PckSource for HostClock { + const PCK_CSS: PCK_CSS = PCK_CSS::MCK; } impl Pmc { @@ -247,11 +161,11 @@ impl Pmc { // TODO: If the get_mainck() etc. API is to be used pmc needs to have ownership over // the clock structs at startup to prevent multiple instances of MainClock to be // constructed. Watchucallit, Singletons. - settings: None, + // settings: None, } } - /// Configures MAINCk and returns a corresponding Clock Token. + /// Configures MAINCK and returns a corresponding Clock Token. /// This Method corresponds to Steps 2-4 in 31.17 Recommended Programming Sequence. pub fn get_mainck(&mut self, source: MainClockOscillatorSource) -> Result { match source { @@ -318,7 +232,7 @@ impl Pmc { /// Configures PLLACK and returns a corresponding clock token. /// This method corresponds to Step 6 of 31.17 Recommended Programming Sequence. - pub fn get_pllack<'a>(&mut self, config: PllaConfig<'a>) -> Result, PmcError> { + pub fn get_pllack(&mut self, config: PllaConfig, source: &SRC) -> Result { if config.mult > 63 || config.mult < 2 { return Err(PmcError::InvalidConfiguration); } @@ -337,7 +251,7 @@ impl Pmc { }); // loop until PLLA Lock Status while self.periph.pmc_sr.read().locka().bit_is_clear() {} - Ok(PllaClock{ config }) + Ok(PllaClock{}) } /// Configures UPLLCK @@ -352,68 +266,28 @@ impl Pmc { /// Configures HCLK and MCK and returns corresponding Clock Tokens. /// This method corresponds to Step 7 in 31.17. - pub fn get_hclk<'a>(&mut self, config: &'a HostClockConfig<'a>) -> Result<(ProcessorClock<'a>, HostClock<'a>), PmcError> { - let pres_bits = match config.pres { - 1 => 0, - 2 => 1, - 4 => 2, - 8 => 3, - 16 => 4, - 32 => 5, - 64 => 6, - 3 => 7, - _ => return Err(PmcError::UnimplementedError), - }; - let div_bits = match config.div { - 1 => 0, - 2 => 1, - 3 => 2, - 4 => 3, - _ => return Err(PmcError::InvalidConfiguration), - }; - - match config.clock { - Clock::Slck(clock) => { - self.periph.pmc_mckr.modify(|_,w| w.css().slow_clk()); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - self.periph.pmc_mckr.modify(|_,w| w.pres().bits(pres_bits)); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - self.periph.pmc_mckr.modify( |_,w| w.mdiv().bits(div_bits)); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - }, - Clock::Mainck(clock) => { - self.periph.pmc_mckr.modify(|_,w| w.css().main_clk()); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - self.periph.pmc_mckr.modify(|_,w| w.pres().bits(pres_bits)); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - self.periph.pmc_mckr.modify( |_,w| { w.mdiv().bits(div_bits)}); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - }, - Clock::Pllack(clock) => { - self.periph.pmc_mckr.modify(|_,w| w.pres().bits(pres_bits)); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - self.periph.pmc_mckr.modify( |_,w| w.mdiv().bits(div_bits)); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - self.periph.pmc_mckr.modify(|_,w| w.css().plla_clk()); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - }, - // Upllck passes through a divider, so it should be Upllckdiv but that's not - // implemented yet - Clock::Upllck(clock) => {return Err(PmcError::InvalidConfiguration)}, - // Mck cannot be used as a source for mck - Clock::Mck(clock) => {return Err(PmcError::InvalidConfiguration);}, - } - Ok((ProcessorClock{ config: &config }, HostClock{ config: &config } )) + pub fn get_hclk(&mut self, config: &HostClockConfig, source: &SRC) -> Result<(ProcessorClock, HostClock), PmcError> { + let pres_bits = config.pres as u8; + let div_bits = config.div as u8; + + self.periph.pmc_mckr.modify(|_,w| w.css().bits(SRC::HCC_CSS as u8)); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + self.periph.pmc_mckr.modify(|_,w| w.pres().bits(pres_bits)); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + self.periph.pmc_mckr.modify( |_,w| w.mdiv().bits(div_bits)); + while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + + Ok((ProcessorClock{}, HostClock{} )) } /// Configures PCKx and returns a token. /// Corresponds to Step 8 in 31.17 - pub fn get_pck(&mut self, source: PckSource, pres: u8, id: PckId) -> Result { + pub fn get_pck(&mut self, source: &SRC, pres: u8, id: PckId) -> Result { self.periph.pmc_pck[id as usize].write(|w| unsafe { w.pres().bits(pres); - w.css().bits(source as u8) + w.css().bits(SRC::PCK_CSS as u8) }); self.periph.pmc_scer.write( |w|unsafe{ w.bits(1<<(id as u8+8))}); while (self.periph.pmc_scsr.read().bits() & (1<< (id as u8+8))) == 0 {} @@ -422,11 +296,6 @@ impl Pmc { } - - pub fn settings(&self) -> Option<&ClockSettings> { - self.settings.as_ref() - } - pub fn enable_peripherals(&mut self, pids: &[PeripheralIdentifier]) -> Result<(), PmcError> { if pids.is_empty() { return Ok(()); @@ -488,137 +357,6 @@ impl Pmc { Ok(()) } - pub fn set_clocks(&mut self, efc: &mut Efc, cfg: ClockSettings) -> Result<(), PmcError> { - // Calculate the master clock to determine the number of flash wait states. - // This must be done BEFORE increasing the clock speed, in case the current number - // of wait states is insufficient for the new speed. - // - // The flash controller (EEFC) is driven from the master clock (stated in section 31.2) - let mck_new = cfg.calc_master_clk_mhz()?; - efc.set_wait_states(mck_new)?; - - // Note: This follows Datasheet 31.17 "Recommendeded Programming Sequence" - // - // Steps 1-5 skipped, using the internal osc - - // The Main RC oscillator. Three output frequencies can be selected: 4/8/12 MHz. By default 12 MHz is - // selected. 8 MHz and 12 MHz are factory-trimmed. The Main RC Oscillator is the default choice, and - // requires no further configuration. - // - // TODO: This is the only supported variant. This code will fail if we add another option. - // You should implement the logic of steps 1-5 here if you are adding more MCO source - // options to the public interface! - // let MainClockOscillatorSource::MainRcOsc(MHz12) = cfg.main_clk_osc_src; - - // # Step 6 - // - // All parameters needed to configure PLLA and the divider are located in CKGR_PLLAR. - // CKGR_PLLAR.DIVA is used to control the divider. This parameter can be programmed between 0 - // and 127. Divider output is divider input divided by DIVA parameter. By default, DIVA field is cleared - // which means that the divider and PLLA are turned off. - // - // CKGR_PLLAR.MULA is the PLLA multiplier factor. This parameter can be programmed between 0 - // and 62. If MULA is cleared, PLLA will be turned off, otherwise the PLLA output frequency is PLLA - // input frequency multiplied by (MULA + 1). - // - // CKGR_PLLAR.PLLACOUNT specifies the number of SLCK cycles before PMC_SR.LOCKA is set - // after CKGR_PLLAR has been written. - - // TODO: Since we have a fixed input clock of 12MHz, mul_a cannot exceed 24 (25x12 = 300MHz) - if cfg.multiplier_a > 24 { - return Err(PmcError::InvalidConfiguration); - } - - if cfg.divider_a == 0 || cfg.divider_a > 127 { - return Err(PmcError::InvalidConfiguration); - } - - self.periph.ckgr_pllar.modify(|_r, w| { - w.one().set_bit(); - unsafe { - w.mula().bits(cfg.multiplier_a.into()); - - // This is the reset value? - w.pllacount().bits(0b111111); - w.diva().bits(cfg.divider_a); - } - w - }); - - // Once CKGR_PLLAR has been written, the user must wait for PMC_SR.LOCKA to be set. This can - // be done either by polling PMC_SR.LOCKA or by waiting for the interrupt line to be raised if the - // associated interrupt source (LOCKA) has been enabled in PMC_IER. All fields in CKGR_PLLAR - // can be programmed in a single write operation. If MULA or DIVA is modified, the LOCKA bit goes - // low to indicate that PLLA is not yet ready. When PLLA is locked, LOCKA is set again. The user - // must wait for the LOCKA bit to be set before using the PLLA output clock. - while self.periph.pmc_sr.read().locka().bit_is_clear() {} - - // # Step 7 - // Select MCK and HCLK: - // MCK and HCLK are configurable via PMC_MCKR. - // - // CSS is used to select the clock source of MCK and HCLK. By default, the selected clock source is - // MAINCK. - // - // PRES is used to define the HCLK and MCK prescalers The user can choose between different - // values (1, 2, 3, 4, 8, 16, 32, 64). Prescaler output is the selected clock source frequency divided by - // the PRES value. - // - // MDIV is used to define the MCK divider. It is possible to choose between different values (0, 1, 2, - // 3). MCK output is the HCLK frequency divided by 1, 2, 3 or 4, depending on the value programmed - // in MDIV. - // - // By default, MDIV is cleared, which indicates that the HCLK is equal to MCK. - // Once the PMC_MCKR has been written, the user must wait for PMC_SR.MCKRDY to be set. This - // can be done either by polling PMC_SR.MCKRDY or by waiting for the interrupt line to be raised if - // the associated interrupt source (MCKRDY) has been enabled in PMC_IER. PMC_MCKR must not - // be programmed in a single write operation. The programming sequence for PMC_MCKR is as - // follows: - // - // If a new value for PMC_MCKR.CSS corresponds to any of the available PLL clocks: - // a. Program PMC_MCKR.PRES. - // b. Wait for PMC_SR.MCKRDY to be set. - self.periph - .pmc_mckr - .modify(|_r, w| w.pres().variant(cfg.mck_pres)); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - - // c. Program PMC_MCKR.MDIV. - // d. Wait for PMC_SR.MCKRDY to be set. - self.periph - .pmc_mckr - .modify(|_r, w| w.mdiv().variant(cfg.mck_div)); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - - // TODO: Again: a hardcoded compile time check - let MasterClockSource::PllaClock = cfg.mck_src; - - // e. Program PMC_MCKR.CSS. - // f. Wait for PMC_SR.MCKRDY to be set. - self.periph.pmc_mckr.modify(|_r, w| w.css().plla_clk()); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - - // If a new value for PMC_MCKR.CSS corresponds to MAINCK or SLCK: - // a. Program PMC_MCKR.CSS. - // b. Wait for PMC_SR.MCKRDY to be set. - // c. Program PMC_MCKR.PRES. - // d. Wait for PMC_SR.MCKRDY to be set. - // - // If CSS, MDIV or PRES are modified at any stage, the MCKRDY bit goes low to indicate that MCK - // and HCLK are not yet ready. The user must wait for MCKRDY bit to be set again before using MCK - // and HCLK. - // - // Note: If PLLA clock was selected as MCK and the user decides to modify it by writing a new value - // into CKGR_PLLAR, the MCKRDY flag will go low while PLLA is unlocked. Once PLLA is locked - // again, LOCKA goes high and MCKRDY is set. - // - // While PLLA is unlocked, MCK selection is automatically changed to SLCK for PLLA. For further - // information, see "Clock Switching Waveforms". - // - // MCK is MAINCK divided by 2. - self.settings = Some(cfg); - Ok(()) - } } #[allow(non_camel_case_types)] From 745c86a6c76ffed2b91cffcf065e6fc3aefb2858 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=B6rtsell?= Date: Fri, 13 May 2022 13:05:03 -0700 Subject: [PATCH 18/44] Add get_slck() Method --- hal/src/pmc.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 9098da81..acadd2e3 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -5,6 +5,7 @@ //! use crate::target_device::PMC; +use crate::target_device::SUPC; use fugit::Rate; pub use crate::target_device::pmc::pmc_mckr::MDIV_A as MckDivider; @@ -42,6 +43,7 @@ pub struct MainClock { pub enum SlowClockOscillatorSource { SlowRcOsc, SlowCrystalOsc, + SlowExternalOsc, } /// SCLK Token @@ -165,6 +167,28 @@ impl Pmc { } } + /// Configures SLCK and returns a corresponding Clock Token. + /// + /// Note: Clearing xtalset or oscbypass has no effect. + /// Setting oscbypass after setting xtalset has no effect. + /// Changes to The SLCK source cannot be unmade in software. + pub fn get_slck(&mut self, supc: &mut SUPC, source: SlowClockOscillatorSource) -> Result { + match source { + SlowClockOscillatorSource::SlowRcOsc => (), + SlowClockOscillatorSource::SlowCrystalOsc => { + supc.supc_cr.write(|w| { w.xtalsel().set_bit(); + w.key().passwd()}); + }, + SlowClockOscillatorSource::SlowExternalOsc =>{ + supc.supc_mr.modify(|_,w| { w.oscbypass().set_bit(); + w.key().passwd()}); + supc.supc_cr.write(|w| { w.xtalsel().set_bit(); + w.key().passwd()}); + }, + } + Ok(SlowClock{ source }) + } + /// Configures MAINCK and returns a corresponding Clock Token. /// This Method corresponds to Steps 2-4 in 31.17 Recommended Programming Sequence. pub fn get_mainck(&mut self, source: MainClockOscillatorSource) -> Result { From 3d8af21b4dee45bbc3f68363e15f6e14d92bee97 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Tue, 24 May 2022 13:47:43 +0200 Subject: [PATCH 19/44] pmc: apply formatting --- hal/src/pmc.rs | 153 ++++++++++++++++++++++++++++--------------------- 1 file changed, 87 insertions(+), 66 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index acadd2e3..3312f15f 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -2,17 +2,17 @@ //! //! This module allows the user to fully control the various clock sources available on ATSAMx7x //! chips. -//! +//! use crate::target_device::PMC; use crate::target_device::SUPC; use fugit::Rate; +pub use crate::target_device::pmc::ckgr_mor::MOSCRCF_A as MainRcFreq; +pub use crate::target_device::pmc::pmc_mckr::CSS_A as HCC_CSS; pub use crate::target_device::pmc::pmc_mckr::MDIV_A as MckDivider; pub use crate::target_device::pmc::pmc_mckr::PRES_A as MckPrescaler; -pub use crate::target_device::pmc::pmc_mckr::CSS_A as HCC_CSS; pub use crate::target_device::pmc::pmc_pck::CSS_A as PCK_CSS; -pub use crate::target_device::pmc::ckgr_mor::MOSCRCF_A as MainRcFreq; pub struct Pmc { periph: PMC, @@ -30,8 +30,8 @@ pub enum PmcError { #[derive(Debug, PartialEq, Clone)] pub enum MainClockOscillatorSource { MainRcOsc(MainRcFreq), - MainCrystalOsc(Rate), - MainExternalOsc(Rate), + MainCrystalOsc(Rate), + MainExternalOsc(Rate), } /// MAINCK Token @@ -57,8 +57,7 @@ pub struct PllaConfig { } /// PLLA Token -pub struct PllaClock { -} +pub struct PllaClock {} pub struct UpllClock; @@ -69,12 +68,10 @@ pub struct HostClockConfig { } /// MCK Token -pub struct HostClock { -} +pub struct HostClock {} /// HCLK Token -pub struct ProcessorClock { -} +pub struct ProcessorClock {} /// The selected "Master Clock" source /// @@ -90,7 +87,6 @@ pub enum MasterClockSource { /// PCK token pub struct Pck { id: PckId, - } #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -120,7 +116,6 @@ impl HostClockSource for MainClock { const HCC_CSS: HCC_CSS = HCC_CSS::MAIN_CLK; } impl HostClockSource for PllaClock { - const HCC_CSS: HCC_CSS = HCC_CSS::PLLA_CLK; } impl HostClockSource for UpllClock { @@ -157,7 +152,6 @@ impl Pmc { Self { periph, - // TODO: I could probably figure out the default settings... // this is fine for now. // TODO: If the get_mainck() etc. API is to be used pmc needs to have ownership over @@ -172,21 +166,31 @@ impl Pmc { /// Note: Clearing xtalset or oscbypass has no effect. /// Setting oscbypass after setting xtalset has no effect. /// Changes to The SLCK source cannot be unmade in software. - pub fn get_slck(&mut self, supc: &mut SUPC, source: SlowClockOscillatorSource) -> Result { + pub fn get_slck( + &mut self, + supc: &mut SUPC, + source: SlowClockOscillatorSource, + ) -> Result { match source { - SlowClockOscillatorSource::SlowRcOsc => (), + SlowClockOscillatorSource::SlowRcOsc => (), SlowClockOscillatorSource::SlowCrystalOsc => { - supc.supc_cr.write(|w| { w.xtalsel().set_bit(); - w.key().passwd()}); - }, - SlowClockOscillatorSource::SlowExternalOsc =>{ - supc.supc_mr.modify(|_,w| { w.oscbypass().set_bit(); - w.key().passwd()}); - supc.supc_cr.write(|w| { w.xtalsel().set_bit(); - w.key().passwd()}); - }, + supc.supc_cr.write(|w| { + w.xtalsel().set_bit(); + w.key().passwd() + }); + } + SlowClockOscillatorSource::SlowExternalOsc => { + supc.supc_mr.modify(|_, w| { + w.oscbypass().set_bit(); + w.key().passwd() + }); + supc.supc_cr.write(|w| { + w.xtalsel().set_bit(); + w.key().passwd() + }); + } } - Ok(SlowClock{ source }) + Ok(SlowClock { source }) } /// Configures MAINCK and returns a corresponding Clock Token. @@ -195,31 +199,37 @@ impl Pmc { match source { MainClockOscillatorSource::MainRcOsc(ref freq) => { let freq_bits = *freq as u8; - self.periph.ckgr_mor.modify( |_,w| { - w.key().passwd(); - w.moscsel().clear_bit(); - w.moscrcen().set_bit(); - unsafe { w.moscrcf().bits(freq_bits); } - w - }); - }, + self.periph.ckgr_mor.modify(|_, w| { + w.key().passwd(); + w.moscsel().clear_bit(); + w.moscrcen().set_bit(); + unsafe { + w.moscrcf().bits(freq_bits); + } + w + }); + } MainClockOscillatorSource::MainCrystalOsc(ref freq) => { // Crystal Frequency needs to be between 3 and 20MHz (30.2) if freq.to_MHz() < 3 || freq.to_MHz() > 20 { return Err(PmcError::InvalidConfiguration); } - self.periph.ckgr_mor.modify( |_,w| { + self.periph.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscxten().set_bit(); - unsafe{ w.moscxtst().bits(255);} + unsafe { + w.moscxtst().bits(255); + } w }); // loop until main crystal oscillator has stabilised while self.periph.pmc_sr.read().moscxts().bit_is_clear() {} - self.periph.ckgr_mor.modify( |_,w| { + self.periph.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscsel().set_bit(); - unsafe{ w.moscxtst().bits(255);} + unsafe { + w.moscxtst().bits(255); + } w }); // loop until source switch has completed @@ -230,33 +240,36 @@ impl Pmc { if freq.to_MHz() < 3 || freq.to_MHz() > 20 { return Err(PmcError::InvalidConfiguration); } - self.periph.ckgr_mor.modify( |_,w| { + self.periph.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscxtby().set_bit(); w }); // loop until source switch has completed while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} - self.periph.ckgr_mor.modify( |_,w| { + self.periph.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscsel().set_bit(); w }); while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} - self.periph.ckgr_mor.modify( |_,w| { - w.key().passwd(); - w.moscrcen().clear_bit(); - w - }); - + self.periph.ckgr_mor.modify(|_, w| { + w.key().passwd(); + w.moscrcen().clear_bit(); + w + }); } } - Ok(MainClock{ source }) + Ok(MainClock { source }) } /// Configures PLLACK and returns a corresponding clock token. /// This method corresponds to Step 6 of 31.17 Recommended Programming Sequence. - pub fn get_pllack(&mut self, config: PllaConfig, source: &SRC) -> Result { + pub fn get_pllack( + &mut self, + config: PllaConfig, + source: &SRC, + ) -> Result { if config.mult > 63 || config.mult < 2 { return Err(PmcError::InvalidConfiguration); } @@ -265,7 +278,7 @@ impl Pmc { } // NOTE: Maximum frequency is not checked her - self.periph.ckgr_pllar.modify( |_,w| { + self.periph.ckgr_pllar.modify(|_, w| { w.one().set_bit(); unsafe { w.mula().bits(config.mult as u16 - 1); @@ -275,13 +288,13 @@ impl Pmc { }); // loop until PLLA Lock Status while self.periph.pmc_sr.read().locka().bit_is_clear() {} - Ok(PllaClock{}) + Ok(PllaClock {}) } /// Configures UPLLCK /// TODO: There's the UPLLDIV2 that is not touched right now. Should be toggleable. pub fn get_upllck(&mut self) -> Result { - self.periph.ckgr_uckr.modify(|_,w| w.upllen().set_bit()); + self.periph.ckgr_uckr.modify(|_, w| w.upllen().set_bit()); // loop until UPLL Lock Status while self.periph.pmc_sr.read().locku().bit_is_clear() {} @@ -290,36 +303,45 @@ impl Pmc { /// Configures HCLK and MCK and returns corresponding Clock Tokens. /// This method corresponds to Step 7 in 31.17. - pub fn get_hclk(&mut self, config: &HostClockConfig, source: &SRC) -> Result<(ProcessorClock, HostClock), PmcError> { + pub fn get_hclk( + &mut self, + config: &HostClockConfig, + source: &SRC, + ) -> Result<(ProcessorClock, HostClock), PmcError> { let pres_bits = config.pres as u8; let div_bits = config.div as u8; - self.periph.pmc_mckr.modify(|_,w| w.css().bits(SRC::HCC_CSS as u8)); + self.periph + .pmc_mckr + .modify(|_, w| w.css().bits(SRC::HCC_CSS as u8)); while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - self.periph.pmc_mckr.modify(|_,w| w.pres().bits(pres_bits)); + self.periph.pmc_mckr.modify(|_, w| w.pres().bits(pres_bits)); while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - self.periph.pmc_mckr.modify( |_,w| w.mdiv().bits(div_bits)); + self.periph.pmc_mckr.modify(|_, w| w.mdiv().bits(div_bits)); while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - - Ok((ProcessorClock{}, HostClock{} )) - + + Ok((ProcessorClock {}, HostClock {})) } /// Configures PCKx and returns a token. /// Corresponds to Step 8 in 31.17 - pub fn get_pck(&mut self, source: &SRC, pres: u8, id: PckId) -> Result { - + pub fn get_pck( + &mut self, + source: &SRC, + pres: u8, + id: PckId, + ) -> Result { self.periph.pmc_pck[id as usize].write(|w| unsafe { w.pres().bits(pres); w.css().bits(SRC::PCK_CSS as u8) }); - self.periph.pmc_scer.write( |w|unsafe{ w.bits(1<<(id as u8+8))}); - while (self.periph.pmc_scsr.read().bits() & (1<< (id as u8+8))) == 0 {} - Ok(Pck {id}) - + self.periph + .pmc_scer + .write(|w| unsafe { w.bits(1 << (id as u8 + 8)) }); + while (self.periph.pmc_scsr.read().bits() & (1 << (id as u8 + 8))) == 0 {} + Ok(Pck { id }) } - pub fn enable_peripherals(&mut self, pids: &[PeripheralIdentifier]) -> Result<(), PmcError> { if pids.is_empty() { return Ok(()); @@ -380,7 +402,6 @@ impl Pmc { Ok(()) } - } #[allow(non_camel_case_types)] From 41c83aa057cf26bb2892d9f7ea43e97b3400449a Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Tue, 24 May 2022 14:35:58 +0200 Subject: [PATCH 20/44] boards/atsame70_xpro: init example skeleton memory.x tail courtesy of [0]. [0] https://github.com/atsamd-rs/atsamd/commit/d7e054f49ea1efa006a07cc438d49ce6a9217312 --- Cargo.toml | 13 ++++++----- boards/.gitignore | 1 + boards/atsame70_xpro/.cargo/config | 8 +++++++ boards/atsame70_xpro/Cargo.toml | 22 +++++++++++++++++++ boards/atsame70_xpro/README.md | 27 +++++++++++++++++++++++ boards/atsame70_xpro/memory.x | 31 ++++++++++++++++++++++++++ boards/atsame70_xpro/openocd.cfg | 6 +++++ boards/atsame70_xpro/openocd.gdb | 35 ++++++++++++++++++++++++++++++ boards/atsame70_xpro/src/main.rs | 33 ++++++++++++++++++++++++++++ 9 files changed, 171 insertions(+), 5 deletions(-) create mode 100644 boards/.gitignore create mode 100644 boards/atsame70_xpro/.cargo/config create mode 100644 boards/atsame70_xpro/Cargo.toml create mode 100644 boards/atsame70_xpro/README.md create mode 100644 boards/atsame70_xpro/memory.x create mode 100644 boards/atsame70_xpro/openocd.cfg create mode 100644 boards/atsame70_xpro/openocd.gdb create mode 100644 boards/atsame70_xpro/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index f6f47a8b..7fe8abf3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,8 @@ -[workspace] -members = [ - "hal", - "pac/*", -] \ No newline at end of file +[workspace] +members = [ + "hal", + "pac/*", +] +exclude = [ + "boards/atsame70_xpro" +] diff --git a/boards/.gitignore b/boards/.gitignore new file mode 100644 index 00000000..64857759 --- /dev/null +++ b/boards/.gitignore @@ -0,0 +1 @@ +**/Cargo.lock diff --git a/boards/atsame70_xpro/.cargo/config b/boards/atsame70_xpro/.cargo/config new file mode 100644 index 00000000..099801d8 --- /dev/null +++ b/boards/atsame70_xpro/.cargo/config @@ -0,0 +1,8 @@ +[target.thumbv7em-none-eabihf] +runner = "arm-none-eabi-gdb -q -x openocd.gdb" + +[build] +target = "thumbv7em-none-eabihf" +rustflags = [ + "-C", "link-arg=-Tlink.x", +] diff --git a/boards/atsame70_xpro/Cargo.toml b/boards/atsame70_xpro/Cargo.toml new file mode 100644 index 00000000..1b657598 --- /dev/null +++ b/boards/atsame70_xpro/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "atsame70_xpro" +version = "0.1.0" +edition = "2021" +authors = [ + "Viktor Sonesten " +] +license = "0BSD" + +[dependencies] +cortex-m-rtic = "1.0" +cortex-m = "0.7" +panic-halt = "0.2" + +[dependencies.atsamx7x-hal] +version = "0.1.0" +path = "../../hal" +features = ["same70q21b-rt"] + +[profile.dev] +debug = true +lto = true \ No newline at end of file diff --git a/boards/atsame70_xpro/README.md b/boards/atsame70_xpro/README.md new file mode 100644 index 00000000..6959fbba --- /dev/null +++ b/boards/atsame70_xpro/README.md @@ -0,0 +1,27 @@ +# SAM E70 Xplained Pro Evaluation Kit examples + +This crate provides HAL usage examples for working with the [SAM E70 Xplained Pro Evaulation Kit](https://www.microchip.com/en-us/development-tool/DM320113). +The examples are written in [RTIC](https://rtic.rs). + +## Prerequisites +* Install the cross-compilation toolchain: `rustup target add thumbv7em-none-eabihf`. +* Install [openocd `v0.11.0`](https://openocd.org/openocd-0-11-0-released.html) (or above). + +## Flashing an example +First, the General-Purpose Non-Volatile-Memory (GPNVM) boot bit must be set in order to map the flashed firmware to address `0x0`; +required for the interrupts and software resets to work as expected: +1. Connect the board via the "Debug USB" port and run + ```shell + $ openocd -f openocd.cfg -c "atsamv gpnvm set 1" -c exit + ``` +2. Power-cycle the board, and the verify with + ```shell + $ openocd -f openocd.cfg -c "atsamv gpnvm show 1" -c exit + [...] + samv-gpnvm1: 1 + ``` + +The example can now be flashed after first, in a seperate shell, `openocd -f openocd.cfg` +```shell +$ cargo run +``` diff --git a/boards/atsame70_xpro/memory.x b/boards/atsame70_xpro/memory.x new file mode 100644 index 00000000..57560e6c --- /dev/null +++ b/boards/atsame70_xpro/memory.x @@ -0,0 +1,31 @@ +MEMORY +{ + FLASH : ORIGIN = 0x400000, LENGTH = 2M + RAM : ORIGIN = 0x20400000, LENGTH = 256K /* 256K or 384K; p. 54 */ +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* You may want to use this variable to locate the call stack and static + variables in different memory regions. Below is shown the default value */ +/* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ + +/* You can use this symbol to customize the location of the .text section */ +/* If omitted the .text section will be placed right after the .vector_table + section */ +/* This is required only on microcontrollers that store some configuration right + after the vector table */ +/* _stext = ORIGIN(FLASH) + 0x400; */ + +/* Example of putting non-initialized variables into custom RAM locations. */ +/* This assumes you have defined a region RAM2 above, and in the Rust + sources added the attribute `#[link_section = ".ram2bss"]` to the data + you want to place there. */ +/* Note that the section will not be zero-initialized by the runtime! */ +/* SECTIONS { + .ram2bss (NOLOAD) : ALIGN(4) { + *(.ram2bss); + . = ALIGN(4); + } > RAM2 + } INSERT AFTER .bss; +*/ diff --git a/boards/atsame70_xpro/openocd.cfg b/boards/atsame70_xpro/openocd.cfg new file mode 100644 index 00000000..f4ef1c76 --- /dev/null +++ b/boards/atsame70_xpro/openocd.cfg @@ -0,0 +1,6 @@ +source [find interface/cmsis-dap.cfg] +set CHIPNAME atsame70n21 +source [find target/atsamv.cfg] + +init +reset halt diff --git a/boards/atsame70_xpro/openocd.gdb b/boards/atsame70_xpro/openocd.gdb new file mode 100644 index 00000000..7aa22fd7 --- /dev/null +++ b/boards/atsame70_xpro/openocd.gdb @@ -0,0 +1,35 @@ +target extended-remote :3333 + +set print asm-demangle on + +# detect unhandled exceptions, hard faults and panics +break DefaultHandler +break HardFault +break rust_begin_unwind + +# *try* to stop at the user entry point (it might be gone due to inlining) +# break main + +monitor arm semihosting enable +monitor halt + +load + +# start the process but immediately halt the processor +stepi + +# Helpers +define reload + monitor reset halt + continue +end + +define reflash + !cargo build + monitor reset halt + load + continue +end + +alias rl = reload +alias rf = reflash \ No newline at end of file diff --git a/boards/atsame70_xpro/src/main.rs b/boards/atsame70_xpro/src/main.rs new file mode 100644 index 00000000..e5cdd69c --- /dev/null +++ b/boards/atsame70_xpro/src/main.rs @@ -0,0 +1,33 @@ +#![no_std] +#![no_main] + +use panic_halt as _; + +#[rtic::app(device = atsamx7x_hal::target_device, peripherals = true, dispatchers = [IXC])] +mod app { + use atsamx7x_hal as hal; + use hal::ehal::watchdog::WatchdogDisable; + + #[shared] + struct Shared {} + + #[local] + struct Local {} + + #[init] + fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) { + cortex_m::asm::bkpt(); + + // Disable the watchdog. + hal::watchdog::Watchdog::new(ctx.device.WDT).disable(); + + (Shared {}, Local {}, init::Monotonics()) + } + + #[idle] + fn idle(_: idle::Context) -> ! { + loop { + cortex_m::asm::nop(); + } + } +} From 6a614a8583303bd405894ac08ac697273f4728ce Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Tue, 24 May 2022 14:54:22 +0200 Subject: [PATCH 21/44] boards/atsame70_xpro: doc how to erase fauly firmware --- boards/atsame70_xpro/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/boards/atsame70_xpro/README.md b/boards/atsame70_xpro/README.md index 6959fbba..26db2bee 100644 --- a/boards/atsame70_xpro/README.md +++ b/boards/atsame70_xpro/README.md @@ -25,3 +25,7 @@ The example can now be flashed after first, in a seperate shell, `openocd -f ope ```shell $ cargo run ``` + +## Erasing bad firmware +In case the board has been flashed with software that sets it in a bad state before the debugger can attach, bridge the "ERASE" header (J400; north of the MCU) between power cycles, and remove the bridge. +The flash area has now been zeroed. From 465f7d7d7146acc1064065de5ec8469ad6912e52 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Tue, 24 May 2022 17:09:23 +0200 Subject: [PATCH 22/44] pmc: improve get_mainck From my interpretation there there is only one single internal oscillator (not counting the slow clocks) which can be overridden by a single external oscillator, only that there are two switches that must be flipped to use the external. --- hal/src/pmc.rs | 74 ++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 3312f15f..8022192b 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -14,6 +14,8 @@ pub use crate::target_device::pmc::pmc_mckr::MDIV_A as MckDivider; pub use crate::target_device::pmc::pmc_mckr::PRES_A as MckPrescaler; pub use crate::target_device::pmc::pmc_pck::CSS_A as PCK_CSS; +pub type Megahertz = fugit::Megahertz; + pub struct Pmc { periph: PMC, } @@ -26,17 +28,18 @@ pub enum PmcError { InternalError, } -/// The selected "Main Clock Oscillator" source +/// The source of the Main Clock (MAINCK) #[derive(Debug, PartialEq, Clone)] -pub enum MainClockOscillatorSource { - MainRcOsc(MainRcFreq), - MainCrystalOsc(Rate), - MainExternalOsc(Rate), +pub enum MainCkSource { + /// Internal "Main RC" oscillator. + Internal(MainRcFreq), + /// External oscillator, connected to XIN/XOUT. + External(Megahertz), } /// MAINCK Token pub struct MainClock { - source: MainClockOscillatorSource, + source: MainCkSource, } // Slow Clock Oscillator Source is set in SUPC @@ -195,69 +198,52 @@ impl Pmc { /// Configures MAINCK and returns a corresponding Clock Token. /// This Method corresponds to Steps 2-4 in 31.17 Recommended Programming Sequence. - pub fn get_mainck(&mut self, source: MainClockOscillatorSource) -> Result { + pub fn get_mainck(&mut self, source: MainCkSource) -> Result { + // Refer to §31.20.8, §31.20.16 + match source { - MainClockOscillatorSource::MainRcOsc(ref freq) => { - let freq_bits = *freq as u8; + MainCkSource::Internal(freq) => { self.periph.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscsel().clear_bit(); w.moscrcen().set_bit(); - unsafe { - w.moscrcf().bits(freq_bits); - } + w.moscrcf().variant(freq); w }); + + // TODO hande note for MOSCRCF unhandled (p. 276; + // first table, second row) + + // Wait until clock is stable. + while self.periph.pmc_sr.read().moscrcs().bit_is_clear() {} } - MainClockOscillatorSource::MainCrystalOsc(ref freq) => { + MainCkSource::External(freq) => { // Crystal Frequency needs to be between 3 and 20MHz (30.2) if freq.to_MHz() < 3 || freq.to_MHz() > 20 { return Err(PmcError::InvalidConfiguration); } + + // Bypass the main crystal oscillator self.periph.ckgr_mor.modify(|_, w| { w.key().passwd(); - w.moscxten().set_bit(); + w.moscxtby().set_bit(); + w.moscxten().clear_bit(); unsafe { - w.moscxtst().bits(255); + w.moscxtst().bits(u8::MAX); } w }); - // loop until main crystal oscillator has stabilised + + // Wait until oscillator is stable while self.periph.pmc_sr.read().moscxts().bit_is_clear() {} - self.periph.ckgr_mor.modify(|_, w| { - w.key().passwd(); - w.moscsel().set_bit(); - unsafe { - w.moscxtst().bits(255); - } - w - }); - // loop until source switch has completed - while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} - } - MainClockOscillatorSource::MainExternalOsc(ref freq) => { - // Oscillator Frequency needs to be between 3 and 20MHz (30.2) - if freq.to_MHz() < 3 || freq.to_MHz() > 20 { - return Err(PmcError::InvalidConfiguration); - } - self.periph.ckgr_mor.modify(|_, w| { - w.key().passwd(); - w.moscxtby().set_bit(); - w - }); - // loop until source switch has completed - while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} + + // Switch over to the external clock self.periph.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscsel().set_bit(); w }); while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} - self.periph.ckgr_mor.modify(|_, w| { - w.key().passwd(); - w.moscrcen().clear_bit(); - w - }); } } Ok(MainClock { source }) From c08d50f5b078e152d94cd06c91d578b90c5404fc Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Tue, 24 May 2022 17:12:40 +0200 Subject: [PATCH 23/44] hal/pmc: refer to PMC as pmc, instead of periph "pmc" is shorter than "periph", and make it clear what peripheral that is being configured, if we in the future must configure more of them. --- hal/src/pmc.rs | 54 +++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 8022192b..cb9a846a 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -17,7 +17,7 @@ pub use crate::target_device::pmc::pmc_pck::CSS_A as PCK_CSS; pub type Megahertz = fugit::Megahertz; pub struct Pmc { - periph: PMC, + pmc: PMC, } #[derive(Debug, PartialEq, Clone)] @@ -146,15 +146,15 @@ impl PckSource for HostClock { } impl Pmc { - pub fn new(periph: PMC) -> Self { - periph.pmc_wpmr.modify(|_r, w| { + pub fn new(pmc: PMC) -> Self { + pmc.pmc_wpmr.modify(|_r, w| { w.wpkey().passwd(); w.wpen().clear_bit(); w }); Self { - periph, + pmc, // TODO: I could probably figure out the default settings... // this is fine for now. // TODO: If the get_mainck() etc. API is to be used pmc needs to have ownership over @@ -203,7 +203,7 @@ impl Pmc { match source { MainCkSource::Internal(freq) => { - self.periph.ckgr_mor.modify(|_, w| { + self.pmc.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscsel().clear_bit(); w.moscrcen().set_bit(); @@ -215,7 +215,7 @@ impl Pmc { // first table, second row) // Wait until clock is stable. - while self.periph.pmc_sr.read().moscrcs().bit_is_clear() {} + while self.pmc.pmc_sr.read().moscrcs().bit_is_clear() {} } MainCkSource::External(freq) => { // Crystal Frequency needs to be between 3 and 20MHz (30.2) @@ -224,7 +224,7 @@ impl Pmc { } // Bypass the main crystal oscillator - self.periph.ckgr_mor.modify(|_, w| { + self.pmc.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscxtby().set_bit(); w.moscxten().clear_bit(); @@ -235,15 +235,15 @@ impl Pmc { }); // Wait until oscillator is stable - while self.periph.pmc_sr.read().moscxts().bit_is_clear() {} + while self.pmc.pmc_sr.read().moscxts().bit_is_clear() {} // Switch over to the external clock - self.periph.ckgr_mor.modify(|_, w| { + self.pmc.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscsel().set_bit(); w }); - while self.periph.pmc_sr.read().moscsels().bit_is_clear() {} + while self.pmc.pmc_sr.read().moscsels().bit_is_clear() {} } } Ok(MainClock { source }) @@ -264,7 +264,7 @@ impl Pmc { } // NOTE: Maximum frequency is not checked her - self.periph.ckgr_pllar.modify(|_, w| { + self.pmc.ckgr_pllar.modify(|_, w| { w.one().set_bit(); unsafe { w.mula().bits(config.mult as u16 - 1); @@ -273,16 +273,16 @@ impl Pmc { w }); // loop until PLLA Lock Status - while self.periph.pmc_sr.read().locka().bit_is_clear() {} + while self.pmc.pmc_sr.read().locka().bit_is_clear() {} Ok(PllaClock {}) } /// Configures UPLLCK /// TODO: There's the UPLLDIV2 that is not touched right now. Should be toggleable. pub fn get_upllck(&mut self) -> Result { - self.periph.ckgr_uckr.modify(|_, w| w.upllen().set_bit()); + self.pmc.ckgr_uckr.modify(|_, w| w.upllen().set_bit()); // loop until UPLL Lock Status - while self.periph.pmc_sr.read().locku().bit_is_clear() {} + while self.pmc.pmc_sr.read().locku().bit_is_clear() {} Ok(UpllClock) } @@ -297,14 +297,14 @@ impl Pmc { let pres_bits = config.pres as u8; let div_bits = config.div as u8; - self.periph + self.pmc .pmc_mckr .modify(|_, w| w.css().bits(SRC::HCC_CSS as u8)); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - self.periph.pmc_mckr.modify(|_, w| w.pres().bits(pres_bits)); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} - self.periph.pmc_mckr.modify(|_, w| w.mdiv().bits(div_bits)); - while self.periph.pmc_sr.read().mckrdy().bit_is_clear() {} + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + self.pmc.pmc_mckr.modify(|_, w| w.pres().bits(pres_bits)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + self.pmc.pmc_mckr.modify(|_, w| w.mdiv().bits(div_bits)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} Ok((ProcessorClock {}, HostClock {})) } @@ -317,14 +317,14 @@ impl Pmc { pres: u8, id: PckId, ) -> Result { - self.periph.pmc_pck[id as usize].write(|w| unsafe { + self.pmc.pmc_pck[id as usize].write(|w| unsafe { w.pres().bits(pres); w.css().bits(SRC::PCK_CSS as u8) }); - self.periph + self.pmc .pmc_scer .write(|w| unsafe { w.bits(1 << (id as u8 + 8)) }); - while (self.periph.pmc_scsr.read().bits() & (1 << (id as u8 + 8))) == 0 {} + while (self.pmc.pmc_scsr.read().bits() & (1 << (id as u8 + 8))) == 0 {} Ok(Pck { id }) } @@ -333,8 +333,8 @@ impl Pmc { return Ok(()); } - let pcsr0 = self.periph.pmc_pcsr0.read().bits(); - let pcsr1 = self.periph.pmc_pcsr1.read().bits(); + let pcsr0 = self.pmc.pmc_pcsr0.read().bits(); + let pcsr1 = self.pmc.pmc_pcsr1.read().bits(); let mut pcr0 = 0; let mut pcr1 = 0; @@ -383,8 +383,8 @@ impl Pmc { } // Enable the newly set peripherals - self.periph.pmc_pcer0.write(|w| unsafe { w.bits(pcr0) }); - self.periph.pmc_pcer1.write(|w| unsafe { w.bits(pcr1) }); + self.pmc.pmc_pcer0.write(|w| unsafe { w.bits(pcr0) }); + self.pmc.pmc_pcer1.write(|w| unsafe { w.bits(pcr1) }); Ok(()) } From 672c168de2897bba20abd209ba5d2a1cf27d0387 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Wed, 25 May 2022 13:38:00 +0200 Subject: [PATCH 24/44] hal/pmc: add main crystal osc in normal mode back Changes made in 465f7d7 incorrectly assumed that there was only one way to configure the main crystal oscillator. --- hal/src/pmc.rs | 57 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index cb9a846a..c15d7093 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -28,13 +28,20 @@ pub enum PmcError { InternalError, } -/// The source of the Main Clock (MAINCK) +/// The source of the Main Clock (MAINCK). +/// +/// Refer to §60.2.1. #[derive(Debug, PartialEq, Clone)] pub enum MainCkSource { /// Internal "Main RC" oscillator. - Internal(MainRcFreq), - /// External oscillator, connected to XIN/XOUT. - External(Megahertz), + InternalRC(MainRcFreq), + /// External crystal powered by the MCU and connected to XIN and + /// XOUT. + ExternalNormal(Megahertz), + /// External clock signal connected to XIN, XOUT potentially + /// unconnected. Bypasses the oscillator otherwise used when using + /// [ExternalNormal]. + ExternalBypass(Megahertz), } /// MAINCK Token @@ -202,7 +209,7 @@ impl Pmc { // Refer to §31.20.8, §31.20.16 match source { - MainCkSource::Internal(freq) => { + MainCkSource::InternalRC(freq) => { self.pmc.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscsel().clear_bit(); @@ -217,13 +224,43 @@ impl Pmc { // Wait until clock is stable. while self.pmc.pmc_sr.read().moscrcs().bit_is_clear() {} } - MainCkSource::External(freq) => { - // Crystal Frequency needs to be between 3 and 20MHz (30.2) + MainCkSource::ExternalNormal(freq) => { + // Clock signal frequency needs to be between 3 and + // 20MHz (§30.2). + if freq.to_MHz() < 3 || freq.to_MHz() > 20 { + return Err(PmcError::InvalidConfiguration); + } + + // Enable the external oscillator and wait for it to + // stabilize. + self.pmc.ckgr_mor.modify(|_, w| { + w.key().passwd(); + w.moscxten().set_bit(); + unsafe { + w.moscxtst().bits(u8::MAX); + } + w + }); + while self.pmc.pmc_sr.read().moscxts().bit_is_clear() {} + + // Switch over to the main oscillator. + self.pmc.ckgr_mor.modify(|_, w| { + w.key().passwd(); + w.moscsel().set_bit(); + w + }); + while self.pmc.pmc_sr.read().moscsels().bit_is_clear() {} + + // TODO check MAINCK frequency (§31.17; step 5). + } + MainCkSource::ExternalBypass(freq) => { + // Crystal frequency needs to be between 3 and 20MHz + // (§30.2). if freq.to_MHz() < 3 || freq.to_MHz() > 20 { return Err(PmcError::InvalidConfiguration); } - // Bypass the main crystal oscillator + // Bypass the main crystal oscillator and disable it. self.pmc.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscxtby().set_bit(); @@ -234,10 +271,10 @@ impl Pmc { w }); - // Wait until oscillator is stable + // Wait until oscillator is stable. while self.pmc.pmc_sr.read().moscxts().bit_is_clear() {} - // Switch over to the external clock + // Switch over to the external clock. self.pmc.ckgr_mor.modify(|_, w| { w.key().passwd(); w.moscsel().set_bit(); From d2132828c3a3fa3880b119f5e07bf7046a8a791e Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Wed, 25 May 2022 13:50:18 +0200 Subject: [PATCH 25/44] hal/pmc: get_pllack: deconstruct PllaConfig --- hal/src/pmc.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index c15d7093..3cd6584c 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -290,27 +290,28 @@ impl Pmc { /// This method corresponds to Step 6 of 31.17 Recommended Programming Sequence. pub fn get_pllack( &mut self, - config: PllaConfig, + PllaConfig { div, mult }: PllaConfig, source: &SRC, ) -> Result { - if config.mult > 63 || config.mult < 2 { + if mult > 63 || mult < 2 { return Err(PmcError::InvalidConfiguration); } - if config.div == 0 || config.div > 127 { + if div == 0 || div > 127 { return Err(PmcError::InvalidConfiguration); } - // NOTE: Maximum frequency is not checked her + // TODO: Ensure valid requested output frequency. + // Configure PLLA and wait for lock. self.pmc.ckgr_pllar.modify(|_, w| { w.one().set_bit(); unsafe { - w.mula().bits(config.mult as u16 - 1); - w.diva().bits(config.div); + w.mula().bits(mult as u16 - 1); // HW adds 1 + w.diva().bits(div); } w }); - // loop until PLLA Lock Status while self.pmc.pmc_sr.read().locka().bit_is_clear() {} + Ok(PllaClock {}) } From fef7e4949a4aec734cda1adf34821bab434deabb Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Wed, 25 May 2022 14:24:00 +0200 Subject: [PATCH 26/44] hal/pmc: refactor get_hclk The datasheet documents two methods of selecting a clock for HCLK/MCK: one for PLLs and one for the slow and main clock. --- hal/src/pmc.rs | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 3cd6584c..0cb9e9cf 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -73,7 +73,10 @@ pub struct UpllClock; /// HCLK/MCK Config pub struct HostClockConfig { + /// General prescaler that affects HCLK, SysTick, FCLK, MCK and + /// peripheral clocks. pub pres: MckPrescaler, + /// Divider that only affects MCK and peripheral clocks. pub div: MckDivider, } @@ -329,20 +332,35 @@ impl Pmc { /// This method corresponds to Step 7 in 31.17. pub fn get_hclk( &mut self, - config: &HostClockConfig, + HostClockConfig { pres, div }: HostClockConfig, source: &SRC, ) -> Result<(ProcessorClock, HostClock), PmcError> { - let pres_bits = config.pres as u8; - let div_bits = config.div as u8; + let source = SRC::HCC_CSS; - self.pmc - .pmc_mckr - .modify(|_, w| w.css().bits(SRC::HCC_CSS as u8)); - while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} - self.pmc.pmc_mckr.modify(|_, w| w.pres().bits(pres_bits)); - while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} - self.pmc.pmc_mckr.modify(|_, w| w.mdiv().bits(div_bits)); - while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + match source { + HCC_CSS::PLLA_CLK | HCC_CSS::UPLL_CLK => { + self.pmc.pmc_mckr.modify(|_, w| w.pres().variant(pres)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + + self.pmc.pmc_mckr.modify(|_, w| w.mdiv().variant(div)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + + self.pmc.pmc_mckr.modify(|_, w| w.css().variant(source)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + }, + HCC_CSS::MAIN_CLK | HCC_CSS::SLOW_CLK => { + self.pmc.pmc_mckr.modify(|_, w| w.css().variant(source)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + + self.pmc.pmc_mckr.modify(|_, w| w.pres().variant(pres)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + + // XXX: Not a part of the RPS: is it a noop? Or is + // MDIV static? + self.pmc.pmc_mckr.modify(|_, w| w.mdiv().variant(div)); + while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} + } + } Ok((ProcessorClock {}, HostClock {})) } From 0b5694bcb64ef08641a637fa1d4182cdc9d8e673 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Wed, 25 May 2022 16:54:08 +0200 Subject: [PATCH 27/44] hal/pmc: record clock freq for MAINCK, PLLACK, and HCLK We will be utilizing this information later down the line, for example when configuring peripheral baud rates. --- hal/src/pmc.rs | 50 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 8 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 0cb9e9cf..5703ee41 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -47,6 +47,7 @@ pub enum MainCkSource { /// MAINCK Token pub struct MainClock { source: MainCkSource, + freq: Megahertz, } // Slow Clock Oscillator Source is set in SUPC @@ -67,7 +68,9 @@ pub struct PllaConfig { } /// PLLA Token -pub struct PllaClock {} +pub struct PllaClock { + freq: Megahertz, +} pub struct UpllClock; @@ -114,12 +117,22 @@ pub enum PckId { Pck7, } -pub trait PllaSource {} +pub trait PllaSource { + fn freq(&self) -> Megahertz; +} -impl PllaSource for MainClock {} +impl PllaSource for MainClock { + fn freq(&self) -> Megahertz { + self.freq + } +} pub trait HostClockSource { const HCC_CSS: HCC_CSS; + + fn freq(&self) -> Megahertz { + todo!() + } } impl HostClockSource for SlowClock { @@ -127,9 +140,17 @@ impl HostClockSource for SlowClock { } impl HostClockSource for MainClock { const HCC_CSS: HCC_CSS = HCC_CSS::MAIN_CLK; + + fn freq(&self) -> Megahertz { + self.freq + } } impl HostClockSource for PllaClock { const HCC_CSS: HCC_CSS = HCC_CSS::PLLA_CLK; + + fn freq(&self) -> Megahertz { + self.freq + } } impl HostClockSource for UpllClock { const HCC_CSS: HCC_CSS = HCC_CSS::UPLL_CLK; @@ -211,7 +232,7 @@ impl Pmc { pub fn get_mainck(&mut self, source: MainCkSource) -> Result { // Refer to §31.20.8, §31.20.16 - match source { + let freq = match source { MainCkSource::InternalRC(freq) => { self.pmc.ckgr_mor.modify(|_, w| { w.key().passwd(); @@ -226,6 +247,12 @@ impl Pmc { // Wait until clock is stable. while self.pmc.pmc_sr.read().moscrcs().bit_is_clear() {} + + Megahertz::from_raw(match freq { + MainRcFreq::_4_MHZ => 4, + MainRcFreq::_8_MHZ => 8, + MainRcFreq::_12_MHZ => 12, + }) } MainCkSource::ExternalNormal(freq) => { // Clock signal frequency needs to be between 3 and @@ -255,6 +282,8 @@ impl Pmc { while self.pmc.pmc_sr.read().moscsels().bit_is_clear() {} // TODO check MAINCK frequency (§31.17; step 5). + + freq } MainCkSource::ExternalBypass(freq) => { // Crystal frequency needs to be between 3 and 20MHz @@ -284,9 +313,12 @@ impl Pmc { w }); while self.pmc.pmc_sr.read().moscsels().bit_is_clear() {} + + freq } - } - Ok(MainClock { source }) + }; + + Ok(MainClock { source, freq }) } /// Configures PLLACK and returns a corresponding clock token. @@ -315,7 +347,9 @@ impl Pmc { }); while self.pmc.pmc_sr.read().locka().bit_is_clear() {} - Ok(PllaClock {}) + Ok(PllaClock { + freq: (source.freq() / div as u32) * mult as u32, + }) } /// Configures UPLLCK @@ -347,7 +381,7 @@ impl Pmc { self.pmc.pmc_mckr.modify(|_, w| w.css().variant(source)); while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} - }, + } HCC_CSS::MAIN_CLK | HCC_CSS::SLOW_CLK => { self.pmc.pmc_mckr.modify(|_, w| w.css().variant(source)); while self.pmc.pmc_sr.read().mckrdy().bit_is_clear() {} From c47096b2a51c9ad4af1e9cc5c8d233d9258520ec Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Wed, 25 May 2022 16:56:49 +0200 Subject: [PATCH 28/44] hal/pmc: get_hclk: set EFC wait states before switching clocks --- hal/src/efc.rs | 12 ++++++------ hal/src/pmc.rs | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/hal/src/efc.rs b/hal/src/efc.rs index d055d90d..be75ef14 100644 --- a/hal/src/efc.rs +++ b/hal/src/efc.rs @@ -1,6 +1,6 @@ //! Flash controller configuration -use crate::pmc::PmcError; +use crate::pmc::{Megahertz, PmcError}; use crate::target_device::EFC; /// The voltage which drives the MCU. @@ -35,7 +35,7 @@ impl Efc { /// /// The max mck frequency supported is 150MHz. This is *not* the CPU frequency, /// which may go up to 300MHz. - pub fn set_wait_states(&mut self, freq: u8) -> Result<(), PmcError> { + pub fn set_wait_states(&mut self, freq: Megahertz) -> Result<(), PmcError> { let fws = FlashWaitStates::calculate(freq, &self.vddio)?; self.periph @@ -62,7 +62,7 @@ enum FlashWaitStates { } impl FlashWaitStates { - pub fn calculate(freq: u8, vddio: &VddioLevel) -> Result { + pub fn calculate(freq: Megahertz, vddio: &VddioLevel) -> Result { #[cfg(any(feature = "v70", feature = "v71"))] if vddio == &VddioLevel::V1 { // V70/V71 must be driven with VDDIO = 3.3V, typical @@ -72,12 +72,12 @@ impl FlashWaitStates { Self::fws_from_freq(freq, vddio) } - fn fws_from_freq(freq: u8, vddio: &VddioLevel) -> Result { + fn fws_from_freq(freq: Megahertz, vddio: &VddioLevel) -> Result { match vddio { VddioLevel::V1 => { // References: // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 1.7V) - Ok(match freq { + Ok(match freq.to_MHz() { 0..=21 => Self::Zero, 22..=42 => Self::One, 43..=63 => Self::Two, @@ -92,7 +92,7 @@ impl FlashWaitStates { // References: // - Table 58-50 (p. 1804) Embedded Flash Wait States for Worst-Case Conditions (V70/V71) // - Table 59-50 (p. 1850) Embedded Flash Wait States for Worst-Case Conditions (E70/S70; VDDIO = 3.0V) - Ok(match freq { + Ok(match freq.to_MHz() { 0..=23 => Self::Zero, 24..=46 => Self::One, 47..=69 => Self::Two, diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 5703ee41..370558f3 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -4,8 +4,10 @@ //! chips. //! +use crate::efc::Efc; use crate::target_device::PMC; use crate::target_device::SUPC; + use fugit::Rate; pub use crate::target_device::pmc::ckgr_mor::MOSCRCF_A as MainRcFreq; @@ -368,7 +370,24 @@ impl Pmc { &mut self, HostClockConfig { pres, div }: HostClockConfig, source: &SRC, + efc: &mut Efc, ) -> Result<(ProcessorClock, HostClock), PmcError> { + // Ensure we use the correct amount of wait states for flash + // access for the new HCLK frequency. + efc.set_wait_states( + source.freq() + / match pres { + MckPrescaler::CLK_1 => 1, + MckPrescaler::CLK_2 => 2, + MckPrescaler::CLK_3 => 3, + MckPrescaler::CLK_4 => 4, + MckPrescaler::CLK_8 => 8, + MckPrescaler::CLK_16 => 16, + MckPrescaler::CLK_32 => 32, + MckPrescaler::CLK_64 => 64, + }, + )?; + let source = SRC::HCC_CSS; match source { From 451e3c13d724d8bbb1580de0fa3dc54bdda63365 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Wed, 25 May 2022 17:23:37 +0200 Subject: [PATCH 29/44] hal/pmc: correctly configure UPLLCK and record its freq --- hal/src/pmc.rs | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 370558f3..c4cf7c14 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -74,7 +74,9 @@ pub struct PllaClock { freq: Megahertz, } -pub struct UpllClock; +pub struct UpllClock { + freq: Megahertz +} /// HCLK/MCK Config pub struct HostClockConfig { @@ -119,6 +121,16 @@ pub enum PckId { Pck7, } +pub trait UpllSource { + fn freq(&self) -> Megahertz; +} + +impl UpllSource for MainClock { + fn freq(&self) -> Megahertz { + self.freq + } +} + pub trait PllaSource { fn freq(&self) -> Megahertz; } @@ -156,6 +168,10 @@ impl HostClockSource for PllaClock { } impl HostClockSource for UpllClock { const HCC_CSS: HCC_CSS = HCC_CSS::UPLL_CLK; + + fn freq(&self) -> Megahertz { + self.freq + } } pub trait PckSource { @@ -356,12 +372,27 @@ impl Pmc { /// Configures UPLLCK /// TODO: There's the UPLLDIV2 that is not touched right now. Should be toggleable. - pub fn get_upllck(&mut self) -> Result { - self.pmc.ckgr_uckr.modify(|_, w| w.upllen().set_bit()); - // loop until UPLL Lock Status + pub fn get_upllck(&mut self, source: &SRC, utmi: &mut crate::target_device::UTMI) -> Result { + use crate::target_device::utmi::utmi_cktrim::FREQ_A as FREQ; + + let freq = match source.freq().to_MHz() { + 12 => FREQ::XTAL12, + 16 => FREQ::XTAL16, + _ => return Err(PmcError::InvalidConfiguration), + }; + + // Configure the UTMI PLL clock and wait for lock. + utmi.utmi_cktrim.modify(|_, w| w.freq().variant(freq)); + self.pmc.ckgr_uckr.modify(|_, w| { + w.upllen().set_bit(); + unsafe { + w.upllcount().bits(u8::MAX); + } + w + }); while self.pmc.pmc_sr.read().locku().bit_is_clear() {} - Ok(UpllClock) + Ok(UpllClock { freq: Megahertz::from_raw(480) }) } /// Configures HCLK and MCK and returns corresponding Clock Tokens. From 22c9964f7fdb3b9d14b2843b0ac85bf5c24a6443 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 13:32:10 +0200 Subject: [PATCH 30/44] hal/pmc: implement get_upllckdiv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UPLLCKDIV is derived from UPLLCK by either /1 or /2. Only the divider is forwarded to HCLK and PCK, as per §31.3. --- hal/src/pmc.rs | 40 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index c4cf7c14..23940082 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -16,6 +16,17 @@ pub use crate::target_device::pmc::pmc_mckr::MDIV_A as MckDivider; pub use crate::target_device::pmc::pmc_mckr::PRES_A as MckPrescaler; pub use crate::target_device::pmc::pmc_pck::CSS_A as PCK_CSS; +/// Output divider for UPLLCK. +#[derive(Debug, PartialEq, Clone)] +pub enum UpllDivider { + /// UPLLCK is divided by 1: input and output frequencies are + /// equal. + Div1, + /// UPLLCK is diveded by 2: output frequency is half of input + /// frequency. + Div2, +} + pub type Megahertz = fugit::Megahertz; pub struct Pmc { @@ -78,6 +89,10 @@ pub struct UpllClock { freq: Megahertz } +pub struct UpllDivClock { + freq: Megahertz +} + /// HCLK/MCK Config pub struct HostClockConfig { /// General prescaler that affects HCLK, SysTick, FCLK, MCK and @@ -121,6 +136,16 @@ pub enum PckId { Pck7, } +pub trait UpllDivSource { + fn freq(&self) -> Megahertz; +} + +impl UpllDivSource for UpllClock { + fn freq(&self) -> Megahertz { + self.freq + } +} + pub trait UpllSource { fn freq(&self) -> Megahertz; } @@ -166,7 +191,7 @@ impl HostClockSource for PllaClock { self.freq } } -impl HostClockSource for UpllClock { +impl HostClockSource for UpllDivClock { const HCC_CSS: HCC_CSS = HCC_CSS::UPLL_CLK; fn freq(&self) -> Megahertz { @@ -184,7 +209,7 @@ impl PckSource for SlowClock { impl PckSource for MainClock { const PCK_CSS: PCK_CSS = PCK_CSS::MAIN_CLK; } -impl PckSource for UpllClock { +impl PckSource for UpllDivClock { const PCK_CSS: PCK_CSS = PCK_CSS::UPLL_CLK; } impl PckSource for PllaClock { @@ -371,7 +396,6 @@ impl Pmc { } /// Configures UPLLCK - /// TODO: There's the UPLLDIV2 that is not touched right now. Should be toggleable. pub fn get_upllck(&mut self, source: &SRC, utmi: &mut crate::target_device::UTMI) -> Result { use crate::target_device::utmi::utmi_cktrim::FREQ_A as FREQ; @@ -395,6 +419,16 @@ impl Pmc { Ok(UpllClock { freq: Megahertz::from_raw(480) }) } + /// Configures UPLLCKDIV + pub fn get_upllckdiv(&mut self, source: &SRC, div: UpllDivider) -> UpllDivClock { + self.pmc.pmc_mckr.modify(|_, w| w.uplldiv2().bit(div == UpllDivider::Div2)); + + UpllDivClock { freq: source.freq() / match div { + UpllDivider::Div1 => 1, + UpllDivider::Div2 => 2, + }} + } + /// Configures HCLK and MCK and returns corresponding Clock Tokens. /// This method corresponds to Step 7 in 31.17. pub fn get_hclk( From e035fae7b2a6439138e13dfcab4e2d0c5db6cc8f Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 14:08:40 +0200 Subject: [PATCH 31/44] hal/pmc: use Hertz instead of Megahertz when logical Some clocks will emit MHz (UPLLCK, MAINCK) while others will emit Hz (PLLA - depending on dev/mult, and SLCK). --- hal/src/pmc.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 23940082..53bc7eab 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -8,7 +8,7 @@ use crate::efc::Efc; use crate::target_device::PMC; use crate::target_device::SUPC; -use fugit::Rate; +pub use fugit::{HertzU32 as Hertz, MegahertzU32 as Megahertz}; pub use crate::target_device::pmc::ckgr_mor::MOSCRCF_A as MainRcFreq; pub use crate::target_device::pmc::pmc_mckr::CSS_A as HCC_CSS; @@ -27,8 +27,6 @@ pub enum UpllDivider { Div2, } -pub type Megahertz = fugit::Megahertz; - pub struct Pmc { pmc: PMC, } @@ -82,7 +80,7 @@ pub struct PllaConfig { /// PLLA Token pub struct PllaClock { - freq: Megahertz, + freq: Hertz, } pub struct UpllClock { @@ -169,7 +167,7 @@ impl PllaSource for MainClock { pub trait HostClockSource { const HCC_CSS: HCC_CSS; - fn freq(&self) -> Megahertz { + fn freq(&self) -> Hertz { todo!() } } @@ -180,22 +178,22 @@ impl HostClockSource for SlowClock { impl HostClockSource for MainClock { const HCC_CSS: HCC_CSS = HCC_CSS::MAIN_CLK; - fn freq(&self) -> Megahertz { - self.freq + fn freq(&self) -> Hertz { + self.freq.convert() } } impl HostClockSource for PllaClock { const HCC_CSS: HCC_CSS = HCC_CSS::PLLA_CLK; - fn freq(&self) -> Megahertz { - self.freq + fn freq(&self) -> Hertz { + self.freq.convert() } } impl HostClockSource for UpllDivClock { const HCC_CSS: HCC_CSS = HCC_CSS::UPLL_CLK; - fn freq(&self) -> Megahertz { - self.freq + fn freq(&self) -> Hertz { + self.freq.convert() } } @@ -391,7 +389,7 @@ impl Pmc { while self.pmc.pmc_sr.read().locka().bit_is_clear() {} Ok(PllaClock { - freq: (source.freq() / div as u32) * mult as u32, + freq: (source.freq().convert() / div as u32) * mult as u32, }) } @@ -440,7 +438,7 @@ impl Pmc { // Ensure we use the correct amount of wait states for flash // access for the new HCLK frequency. efc.set_wait_states( - source.freq() + source.freq().convert() / match pres { MckPrescaler::CLK_1 => 1, MckPrescaler::CLK_2 => 2, From 2d4389cb4679b3f90f03bced905ba9f038b20c88 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 14:13:07 +0200 Subject: [PATCH 32/44] hal/pmc: record SLCK freq --- hal/src/pmc.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 53bc7eab..7f784689 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -71,6 +71,7 @@ pub enum SlowClockOscillatorSource { /// SCLK Token pub struct SlowClock { source: SlowClockOscillatorSource, + freq: Hertz, } pub struct PllaConfig { @@ -167,13 +168,15 @@ impl PllaSource for MainClock { pub trait HostClockSource { const HCC_CSS: HCC_CSS; - fn freq(&self) -> Hertz { - todo!() - } + fn freq(&self) -> Hertz; } impl HostClockSource for SlowClock { const HCC_CSS: HCC_CSS = HCC_CSS::SLOW_CLK; + + fn freq(&self) -> Hertz { + self.freq + } } impl HostClockSource for MainClock { const HCC_CSS: HCC_CSS = HCC_CSS::MAIN_CLK; @@ -265,7 +268,7 @@ impl Pmc { }); } } - Ok(SlowClock { source }) + Ok(SlowClock { source, freq: Hertz::from_raw(32_768) }) } /// Configures MAINCK and returns a corresponding Clock Token. From 5adcf9f872da26093d7417aa6bff7369ca0c2f5a Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 14:45:20 +0200 Subject: [PATCH 33/44] hal/pmc: remove unnecessary Results --- hal/src/pmc.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 7f784689..af263dad 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -248,7 +248,7 @@ impl Pmc { &mut self, supc: &mut SUPC, source: SlowClockOscillatorSource, - ) -> Result { + ) -> SlowClock { match source { SlowClockOscillatorSource::SlowRcOsc => (), SlowClockOscillatorSource::SlowCrystalOsc => { @@ -268,7 +268,8 @@ impl Pmc { }); } } - Ok(SlowClock { source, freq: Hertz::from_raw(32_768) }) + + SlowClock { source, freq: Hertz::from_raw(32_768) } } /// Configures MAINCK and returns a corresponding Clock Token. @@ -491,7 +492,7 @@ impl Pmc { source: &SRC, pres: u8, id: PckId, - ) -> Result { + ) -> Pck { self.pmc.pmc_pck[id as usize].write(|w| unsafe { w.pres().bits(pres); w.css().bits(SRC::PCK_CSS as u8) @@ -500,7 +501,7 @@ impl Pmc { .pmc_scer .write(|w| unsafe { w.bits(1 << (id as u8 + 8)) }); while (self.pmc.pmc_scsr.read().bits() & (1 << (id as u8 + 8))) == 0 {} - Ok(Pck { id }) + Pck { id } } pub fn enable_peripherals(&mut self, pids: &[PeripheralIdentifier]) -> Result<(), PmcError> { From dbe39b14f7f230263c3502fd92ef78acd3a8faa6 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 14:51:35 +0200 Subject: [PATCH 34/44] hal/pmc: handle unused_variables/dead_code warnings --- hal/src/pmc.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index af263dad..d8ff7b36 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -57,7 +57,6 @@ pub enum MainCkSource { /// MAINCK Token pub struct MainClock { - source: MainCkSource, freq: Megahertz, } @@ -70,7 +69,6 @@ pub enum SlowClockOscillatorSource { /// SCLK Token pub struct SlowClock { - source: SlowClockOscillatorSource, freq: Hertz, } @@ -120,6 +118,7 @@ pub enum MasterClockSource { /// PCK token pub struct Pck { + #[allow(dead_code)] id: PckId, } @@ -269,7 +268,7 @@ impl Pmc { } } - SlowClock { source, freq: Hertz::from_raw(32_768) } + SlowClock { freq: Hertz::from_raw(32_768) } } /// Configures MAINCK and returns a corresponding Clock Token. @@ -363,7 +362,7 @@ impl Pmc { } }; - Ok(MainClock { source, freq }) + Ok(MainClock { freq }) } /// Configures PLLACK and returns a corresponding clock token. @@ -489,7 +488,7 @@ impl Pmc { /// Corresponds to Step 8 in 31.17 pub fn get_pck( &mut self, - source: &SRC, + _source: &SRC, pres: u8, id: PckId, ) -> Pck { From b88ffd6d53dac1d8ee2e134b9a7b65b60f5d3cbb Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 14:52:56 +0200 Subject: [PATCH 35/44] atsame70_xpro: apply example clock hierarchy configuration This will later be moved into a proper examples/*.rs when a GPIO API is available. --- boards/atsame70_xpro/src/main.rs | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/boards/atsame70_xpro/src/main.rs b/boards/atsame70_xpro/src/main.rs index e5cdd69c..d4b5403e 100644 --- a/boards/atsame70_xpro/src/main.rs +++ b/boards/atsame70_xpro/src/main.rs @@ -21,6 +21,39 @@ mod app { // Disable the watchdog. hal::watchdog::Watchdog::new(ctx.device.WDT).disable(); + let mut efc = { + use hal::efc::{Efc, VddioLevel}; + Efc::new(ctx.device.EFC, VddioLevel::V3) + }; + + // Configure the clock hierarchy + { + use hal::pmc::{ + HostClockConfig, MainCkSource, MckDivider, MckPrescaler, Megahertz, PckId, + PllaConfig, Pmc, + }; + + let mut pmc = Pmc::new(ctx.device.PMC); + let mainck = pmc + .get_mainck(MainCkSource::ExternalBypass(Megahertz::from_raw(12))) + .unwrap(); + let plla = pmc + .get_pllack(PllaConfig { div: 1, mult: 8 }, &mainck) + .unwrap(); + let _hclk = pmc + .get_hclk( + HostClockConfig { + pres: MckPrescaler::CLK_1, + div: MckDivider::EQ_PCK, + }, + &mainck, + &mut efc, + ) + .unwrap(); + + let _pck2 = pmc.get_pck(&plla, 0, PckId::Pck2); + } + (Shared {}, Local {}, init::Monotonics()) } From 27f5bc0915d33c6200164cfb884c7419b52b0130 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 15:10:24 +0200 Subject: [PATCH 36/44] hal/pmc: improve struct/enum docs --- hal/src/pmc.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index d8ff7b36..760f3480 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -27,10 +27,12 @@ pub enum UpllDivider { Div2, } +/// Power Management Controller pub struct Pmc { pmc: PMC, } +/// Possible errors that can occur on PMC configuration. #[derive(Debug, PartialEq, Clone)] pub enum PmcError { ClockingError(PeripheralIdentifier), @@ -39,7 +41,7 @@ pub enum PmcError { InternalError, } -/// The source of the Main Clock (MAINCK). +/// The source of the Main Clock (MAINCK) /// /// Refer to §60.2.1. #[derive(Debug, PartialEq, Clone)] @@ -51,46 +53,51 @@ pub enum MainCkSource { ExternalNormal(Megahertz), /// External clock signal connected to XIN, XOUT potentially /// unconnected. Bypasses the oscillator otherwise used when using - /// [ExternalNormal]. + /// [MainCkSource::ExternalNormal]. ExternalBypass(Megahertz), } -/// MAINCK Token +/// MAINCK token pub struct MainClock { freq: Megahertz, } -// Slow Clock Oscillator Source is set in SUPC +/// The source of the Slow Clock (SLCK) +/// +/// Refer to §23.4.2 and §60.2.1. pub enum SlowClockOscillatorSource { SlowRcOsc, SlowCrystalOsc, SlowExternalOsc, } -/// SCLK Token +/// SCLK token pub struct SlowClock { freq: Hertz, } +/// PLLA configuration pub struct PllaConfig { pub div: u8, pub mult: u8, } -/// PLLA Token +/// PLLA token pub struct PllaClock { freq: Hertz, } +/// UPLLCK token pub struct UpllClock { freq: Megahertz } +/// UPLLCKDIV token pub struct UpllDivClock { freq: Megahertz } -/// HCLK/MCK Config +/// HCLK/MCK configuration pub struct HostClockConfig { /// General prescaler that affects HCLK, SysTick, FCLK, MCK and /// peripheral clocks. @@ -99,10 +106,10 @@ pub struct HostClockConfig { pub div: MckDivider, } -/// MCK Token +/// MCK token pub struct HostClock {} -/// HCLK Token +/// HCLK token pub struct ProcessorClock {} /// The selected "Master Clock" source @@ -122,6 +129,7 @@ pub struct Pck { id: PckId, } +/// PCK to configure #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum PckId { Pck0, @@ -134,6 +142,7 @@ pub enum PckId { Pck7, } +#[doc(hidden)] pub trait UpllDivSource { fn freq(&self) -> Megahertz; } @@ -144,6 +153,7 @@ impl UpllDivSource for UpllClock { } } +#[doc(hidden)] pub trait UpllSource { fn freq(&self) -> Megahertz; } @@ -154,6 +164,7 @@ impl UpllSource for MainClock { } } +#[doc(hidden)] pub trait PllaSource { fn freq(&self) -> Megahertz; } @@ -164,6 +175,7 @@ impl PllaSource for MainClock { } } +#[doc(hidden)] pub trait HostClockSource { const HCC_CSS: HCC_CSS; @@ -199,6 +211,7 @@ impl HostClockSource for UpllDivClock { } } +#[doc(hidden)] pub trait PckSource { const PCK_CSS: PCK_CSS; } @@ -565,6 +578,7 @@ impl Pmc { } } +/// Identifiers for peripherals #[allow(non_camel_case_types)] #[derive(Debug, PartialEq, Eq, Copy, Clone)] #[repr(u32)] From 0e6a9c50d0cf04ce9ffc1eca1a9dda913d495af6 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 15:11:11 +0200 Subject: [PATCH 37/44] hal/pmc: remove unused enum --- hal/src/pmc.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 760f3480..76784d3b 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -112,17 +112,6 @@ pub struct HostClock {} /// HCLK token pub struct ProcessorClock {} -/// The selected "Master Clock" source -/// -/// TODO/NOTE: At the moment, we only support the PLLA Clock. -/// Some driver behavior is hardcoded on this assumption for -/// the sake of simplicity. -/// -/// This corresponds to PMC_MCKR.CSS -pub enum MasterClockSource { - PllaClock, -} - /// PCK token pub struct Pck { #[allow(dead_code)] From dd1a9dce59341f98b6fecc56506d0aa1f9851cf4 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 15:12:26 +0200 Subject: [PATCH 38/44] hal/pmc: get_slck: remove impl details from docs --- hal/src/pmc.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 76784d3b..55db8a0a 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -242,9 +242,7 @@ impl Pmc { /// Configures SLCK and returns a corresponding Clock Token. /// - /// Note: Clearing xtalset or oscbypass has no effect. - /// Setting oscbypass after setting xtalset has no effect. - /// Changes to The SLCK source cannot be unmade in software. + /// Note: Changes to The SLCK source cannot be unmade in software. pub fn get_slck( &mut self, supc: &mut SUPC, From 009d47853241e1aed0a412d80b4982a7c24fbf9a Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 15:17:42 +0200 Subject: [PATCH 39/44] hal/pmc: improve top-level docs --- hal/src/pmc.rs | 42 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 55db8a0a..91889ab6 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -1,8 +1,46 @@ //! Clock hierarchy configuration //! -//! This module allows the user to fully control the various clock sources available on ATSAMx7x -//! chips. +//! This module handles the clock hierarchy configuration. It is based +//! upon trait tokens to statically limit which clocks can be +//! connected where, following what hardware supports (c.f. §31.3). //! +//! Below is an example configuration that drives the host clock via +//! MAINCK, and PCK2 with PLLA, clocked at 96 MHz: +//! +//! ```ignore +//! let mut efc = { +//! use hal::efc::{Efc, VddioLevel}; +//! Efc::new(ctx.device.EFC, VddioLevel::V3) +//! }; +//! +//! // Configure the clock hierarchy +//! { +//! use hal::pmc::{ +//! HostClockConfig, MainCkSource, MckDivider, MckPrescaler, Megahertz, PckId, +//! PllaConfig, Pmc, +//! }; +//! +//! let mut pmc = Pmc::new(ctx.device.PMC); +//! let mainck = pmc +//! .get_mainck(MainCkSource::ExternalBypass(Megahertz::from_raw(12))) +//! .unwrap(); +//! let plla = pmc +//! .get_pllack(PllaConfig { div: 1, mult: 8 }, &mainck) +//! .unwrap(); +//! let _hclk = pmc +//! .get_hclk( +//! HostClockConfig { +//! pres: MckPrescaler::CLK_1, +//! div: MckDivider::EQ_PCK, +//! }, +//! &mainck, +//! &mut efc, +//! ) +//! .unwrap(); +//! +//! let _pck2 = pmc.get_pck(&plla, 0, PckId::Pck2); +//! } +//! ``` use crate::efc::Efc; use crate::target_device::PMC; From f74507d3e726bd0bccf01496b513adba5a1425e3 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Mon, 30 May 2022 15:48:59 +0200 Subject: [PATCH 40/44] atsame70_xpro: expose UPLLCKDIV on PCK2, @ 2.4MHz --- boards/atsame70_xpro/src/main.rs | 36 ++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/boards/atsame70_xpro/src/main.rs b/boards/atsame70_xpro/src/main.rs index d4b5403e..3bbfd1e9 100644 --- a/boards/atsame70_xpro/src/main.rs +++ b/boards/atsame70_xpro/src/main.rs @@ -15,7 +15,7 @@ mod app { struct Local {} #[init] - fn init(ctx: init::Context) -> (Shared, Local, init::Monotonics) { + fn init(mut ctx: init::Context) -> (Shared, Local, init::Monotonics) { cortex_m::asm::bkpt(); // Disable the watchdog. @@ -30,14 +30,14 @@ mod app { { use hal::pmc::{ HostClockConfig, MainCkSource, MckDivider, MckPrescaler, Megahertz, PckId, - PllaConfig, Pmc, + PllaConfig, Pmc, UpllDivider, }; let mut pmc = Pmc::new(ctx.device.PMC); let mainck = pmc .get_mainck(MainCkSource::ExternalBypass(Megahertz::from_raw(12))) .unwrap(); - let plla = pmc + let _plla = pmc .get_pllack(PllaConfig { div: 1, mult: 8 }, &mainck) .unwrap(); let _hclk = pmc @@ -51,7 +51,35 @@ mod app { ) .unwrap(); - let _pck2 = pmc.get_pck(&plla, 0, PckId::Pck2); + let upllck = pmc.get_upllck(&mainck, &mut ctx.device.UTMI).unwrap(); + let upllckdiv = pmc.get_upllckdiv(&upllck, UpllDivider::Div2); + let _pck2 = pmc.get_pck(&upllckdiv, 100 - 1, PckId::Pck2); // @ 2.4MHz + } + + // Configure PA03 as PCK2 output + { + let pioa = ctx.device.PIOA; + + // Configure pins for function C: UART4 (0b10) + pioa.pio_abcdsr[1].modify(|_, w| w.p3().set_bit()); + pioa.pio_abcdsr[0].modify(|_, w| w.p3().clear_bit()); + + // Give pins to the peripheral. + pioa.pio_pdr.write(|w| w.p3().set_bit()); + cortex_m::asm::dsb(); + assert!(pioa.pio_psr.read().p3().bit_is_clear()); + + // disable multidrive + pioa.pio_mddr.write(|w| w.p3().set_bit()); + cortex_m::asm::dsb(); + assert!(pioa.pio_mdsr.read().p3().bit_is_clear()); + + // ensure we dont pull the pin up/down + pioa.pio_pudr.write(|w| w.p3().set_bit()); + pioa.pio_ppddr.write(|w| w.p3().set_bit()); + cortex_m::asm::dsb(); + assert!(pioa.pio_pusr.read().p3().bit_is_set()); + assert!(pioa.pio_ppdsr.read().p3().bit_is_set()); } (Shared {}, Local {}, init::Monotonics()) From 82a87de5da2eefffc31031cc3abf26f54ce8d27e Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Tue, 31 May 2022 13:53:49 +0200 Subject: [PATCH 41/44] hal/pmc: globalize magic values --- hal/src/pmc.rs | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 91889ab6..61b27d99 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -260,6 +260,15 @@ impl PckSource for HostClock { } impl Pmc { + /// Time to wait until an observed clock is stable, from the + /// perpective of SLCK (@ 32.768KHz). + /// + /// Refer to §31.20.8, for example. + const WAIT_UNTIL_STABLE_62_MILLISECS: u8 = u8::MAX; + + const SLCK_FREQ: Hertz = Hertz::from_raw(32_768); + const UPLL_FREQ: Megahertz = Megahertz::from_raw(480); + pub fn new(pmc: PMC) -> Self { pmc.pmc_wpmr.modify(|_r, w| { w.wpkey().passwd(); @@ -306,7 +315,9 @@ impl Pmc { } } - SlowClock { freq: Hertz::from_raw(32_768) } + SlowClock { + freq: Self::SLCK_FREQ, + } } /// Configures MAINCK and returns a corresponding Clock Token. @@ -349,7 +360,7 @@ impl Pmc { w.key().passwd(); w.moscxten().set_bit(); unsafe { - w.moscxtst().bits(u8::MAX); + w.moscxtst().bits(Self::WAIT_UNTIL_STABLE_62_MILLISECS); } w }); @@ -380,7 +391,7 @@ impl Pmc { w.moscxtby().set_bit(); w.moscxten().clear_bit(); unsafe { - w.moscxtst().bits(u8::MAX); + w.moscxtst().bits(Self::WAIT_UNTIL_STABLE_62_MILLISECS); } w }); @@ -449,13 +460,15 @@ impl Pmc { self.pmc.ckgr_uckr.modify(|_, w| { w.upllen().set_bit(); unsafe { - w.upllcount().bits(u8::MAX); + w.upllcount().bits(Self::WAIT_UNTIL_STABLE_62_MILLISECS); } w }); while self.pmc.pmc_sr.read().locku().bit_is_clear() {} - Ok(UpllClock { freq: Megahertz::from_raw(480) }) + Ok(UpllClock { + freq: Self::UPLL_FREQ, + }) } /// Configures UPLLCKDIV From 828299f70dae65b2d386e3d7a0829e1606a35dd3 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Tue, 31 May 2022 13:54:47 +0200 Subject: [PATCH 42/44] hal/pmc: apply formatting --- hal/src/pmc.rs | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 61b27d99..59c82ea5 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -127,12 +127,12 @@ pub struct PllaClock { /// UPLLCK token pub struct UpllClock { - freq: Megahertz + freq: Megahertz, } /// UPLLCKDIV token pub struct UpllDivClock { - freq: Megahertz + freq: Megahertz, } /// HCLK/MCK configuration @@ -290,11 +290,7 @@ impl Pmc { /// Configures SLCK and returns a corresponding Clock Token. /// /// Note: Changes to The SLCK source cannot be unmade in software. - pub fn get_slck( - &mut self, - supc: &mut SUPC, - source: SlowClockOscillatorSource, - ) -> SlowClock { + pub fn get_slck(&mut self, supc: &mut SUPC, source: SlowClockOscillatorSource) -> SlowClock { match source { SlowClockOscillatorSource::SlowRcOsc => (), SlowClockOscillatorSource::SlowCrystalOsc => { @@ -446,7 +442,11 @@ impl Pmc { } /// Configures UPLLCK - pub fn get_upllck(&mut self, source: &SRC, utmi: &mut crate::target_device::UTMI) -> Result { + pub fn get_upllck( + &mut self, + source: &SRC, + utmi: &mut crate::target_device::UTMI, + ) -> Result { use crate::target_device::utmi::utmi_cktrim::FREQ_A as FREQ; let freq = match source.freq().to_MHz() { @@ -472,13 +472,22 @@ impl Pmc { } /// Configures UPLLCKDIV - pub fn get_upllckdiv(&mut self, source: &SRC, div: UpllDivider) -> UpllDivClock { - self.pmc.pmc_mckr.modify(|_, w| w.uplldiv2().bit(div == UpllDivider::Div2)); - - UpllDivClock { freq: source.freq() / match div { - UpllDivider::Div1 => 1, - UpllDivider::Div2 => 2, - }} + pub fn get_upllckdiv( + &mut self, + source: &SRC, + div: UpllDivider, + ) -> UpllDivClock { + self.pmc + .pmc_mckr + .modify(|_, w| w.uplldiv2().bit(div == UpllDivider::Div2)); + + UpllDivClock { + freq: source.freq() + / match div { + UpllDivider::Div1 => 1, + UpllDivider::Div2 => 2, + }, + } } /// Configures HCLK and MCK and returns corresponding Clock Tokens. @@ -537,12 +546,7 @@ impl Pmc { /// Configures PCKx and returns a token. /// Corresponds to Step 8 in 31.17 - pub fn get_pck( - &mut self, - _source: &SRC, - pres: u8, - id: PckId, - ) -> Pck { + pub fn get_pck(&mut self, _source: &SRC, pres: u8, id: PckId) -> Pck { self.pmc.pmc_pck[id as usize].write(|w| unsafe { w.pres().bits(pres); w.css().bits(SRC::PCK_CSS as u8) From b89177d94e654e4a10e9f87fe6034baf03c70e0e Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Tue, 31 May 2022 13:58:29 +0200 Subject: [PATCH 43/44] hal/pmc: refactor SCLK configuration to match MAINCK --- hal/src/pmc.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 59c82ea5..7cd42b1f 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -103,10 +103,16 @@ pub struct MainClock { /// The source of the Slow Clock (SLCK) /// /// Refer to §23.4.2 and §60.2.1. -pub enum SlowClockOscillatorSource { - SlowRcOsc, - SlowCrystalOsc, - SlowExternalOsc, +pub enum SlowCkSource { + /// Internal "Slow RC" oscillator. + InternalRC, + /// External crystal powered by the MCU and connected to XIN32 and + /// XOUT32. + ExternalNormal, + /// External clock signal connected to XIN32, XOUT32 potentially + /// unconnected. Bypasses the oscillator otherwise used when using + /// [SlowCkSource::ExternalNormal]. + ExternalBypass, } /// SCLK token @@ -290,16 +296,16 @@ impl Pmc { /// Configures SLCK and returns a corresponding Clock Token. /// /// Note: Changes to The SLCK source cannot be unmade in software. - pub fn get_slck(&mut self, supc: &mut SUPC, source: SlowClockOscillatorSource) -> SlowClock { + pub fn get_slck(&mut self, supc: &mut SUPC, source: SlowCkSource) -> SlowClock { match source { - SlowClockOscillatorSource::SlowRcOsc => (), - SlowClockOscillatorSource::SlowCrystalOsc => { + SlowCkSource::InternalRC => (), + SlowCkSource::ExternalNormal => { supc.supc_cr.write(|w| { w.xtalsel().set_bit(); w.key().passwd() }); } - SlowClockOscillatorSource::SlowExternalOsc => { + SlowCkSource::ExternalBypass => { supc.supc_mr.modify(|_, w| { w.oscbypass().set_bit(); w.key().passwd() From d249979e787c1b87e003fdcd738c039ef69501f2 Mon Sep 17 00:00:00 2001 From: Viktor Sonesten Date: Tue, 31 May 2022 13:59:03 +0200 Subject: [PATCH 44/44] hal/pmc: remove outdated comment --- hal/src/pmc.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/hal/src/pmc.rs b/hal/src/pmc.rs index 7cd42b1f..6783aab7 100644 --- a/hal/src/pmc.rs +++ b/hal/src/pmc.rs @@ -282,15 +282,7 @@ impl Pmc { w }); - Self { - pmc, - // TODO: I could probably figure out the default settings... - // this is fine for now. - // TODO: If the get_mainck() etc. API is to be used pmc needs to have ownership over - // the clock structs at startup to prevent multiple instances of MainClock to be - // constructed. Watchucallit, Singletons. - // settings: None, - } + Self { pmc } } /// Configures SLCK and returns a corresponding Clock Token.