From b0cf9b4626e7fa99d1da82a5eb745e9ed14508de Mon Sep 17 00:00:00 2001 From: James Munns Date: Thu, 18 Dec 2025 14:53:33 +0100 Subject: First basic test works --- embassy-mcxa/src/clocks/config.rs | 15 +++---- embassy-mcxa/src/clocks/mod.rs | 83 +++++++++++++++++++++++++++++++++++---- examples/mcxa/src/bin/clkout.rs | 25 ++++++++++-- 3 files changed, 103 insertions(+), 20 deletions(-) diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs index 0f362ad0a..3f1729d00 100644 --- a/embassy-mcxa/src/clocks/config.rs +++ b/embassy-mcxa/src/clocks/config.rs @@ -157,6 +157,8 @@ pub struct SpllConfig { pub source: SpllSource, pub mode: SpllMode, pub power: PoweredClock, + /// Is the "pll1_clk_div" clock enabled? + pub pll1_clk_div: Option, } pub enum SpllSource { @@ -168,7 +170,6 @@ pub enum SpllSource { Firc, /// S Internal Oscillator (12M) Sirc, - // TODO: the reference manual hints that ROSC is possible, // however the minimum input frequency is 32K, but ROSC is 16K. // Some diagrams show this option, and some diagrams omit it. @@ -182,9 +183,7 @@ pub enum SpllSource { /// P: 1..=31 pub enum SpllMode { /// Fout = M x Fin - Mode1a { - m_mult: u16, - }, + Mode1a { m_mult: u16 }, /// if !bypass_p2_div: Fout = (M / (2 x P)) x Fin /// if bypass_p2_div: Fout = (M / P ) x Fin Mode1b { @@ -193,10 +192,7 @@ pub enum SpllMode { bypass_p2_div: bool, }, /// Fout = (M / N) x Fin - Mode1c { - m_mult: u16, - n_div: u8, - }, + Mode1c { m_mult: u16, n_div: u8 }, /// if !bypass_p2_div: Fout = (M / (N x 2 x P)) x Fin /// if bypass_p2_div: Fout = (M / ( N x P )) x Fin Mode1d { @@ -204,10 +200,9 @@ pub enum SpllMode { n_div: u8, p_div: u8, bypass_p2_div: bool, - } + }, } - // FIRC/FRO180M /// ```text diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs index 98f7075eb..866e78bf2 100644 --- a/embassy-mcxa/src/clocks/mod.rs +++ b/embassy-mcxa/src/clocks/mod.rs @@ -200,6 +200,9 @@ pub struct Clocks { /// `pll1_clk` is the output of the main system PLL, `pll1`. pub pll1_clk: Option, + + /// `pll1_clk_div` is a configurable frequency clock, sourced from `pll1_clk` + pub pll1_clk_div: Option, } /// `ClockError` is the main error returned when configuring or checking clock state @@ -548,13 +551,37 @@ impl Clocks { } /// Ensure the `pll1_clk` clock is active and valid at the given power state. - pub fn ensure_pll1_clk_active(&self, _at_level: &PoweredClock) -> Result { - Err(ClockError::NotImplemented { clock: "pll1_clk" }) + pub fn ensure_pll1_clk_active(&self, at_level: &PoweredClock) -> Result { + let Some(clk) = self.pll1_clk.as_ref() else { + return Err(ClockError::BadConfig { + clock: "spll", + reason: "required but not active", + }); + }; + if !clk.power.meets_requirement_of(at_level) { + return Err(ClockError::BadConfig { + clock: "spll", + reason: "not low power active", + }); + } + Ok(clk.frequency) } /// Ensure the `pll1_clk_div` clock is active and valid at the given power state. - pub fn ensure_pll1_clk_div_active(&self, _at_level: &PoweredClock) -> Result { - Err(ClockError::NotImplemented { clock: "pll1_clk_div" }) + pub fn ensure_pll1_clk_div_active(&self, at_level: &PoweredClock) -> Result { + let Some(clk) = self.pll1_clk_div.as_ref() else { + return Err(ClockError::BadConfig { + clock: "spll", + reason: "required but not active", + }); + }; + if !clk.power.meets_requirement_of(at_level) { + return Err(ClockError::BadConfig { + clock: "spll", + reason: "not low power active", + }); + } + Ok(clk.frequency) } /// Ensure the `CPU_CLK` or `SYSTEM_CLK` is active @@ -1031,7 +1058,10 @@ impl ClockOperator<'_> { reason: s, })?; if !clk.power.meets_requirement_of(&cfg.power) { - return Err(ClockError::BadConfig { clock: "spll", reason: "needs low power source" }); + return Err(ClockError::BadConfig { + clock: "spll", + reason: "needs low power source", + }); } // Bandwidth calc @@ -1118,9 +1148,20 @@ impl ClockOperator<'_> { fout = (f_in / div).checked_mul(m_mult as u32); } }; + + defmt::debug!("f_in: {:?}", f_in); + defmt::debug!("bp_pre: {:?}", bp_pre); + defmt::debug!("bp_post: {:?}", bp_post); + defmt::debug!("bp_post2: {:?}", bp_post2); + defmt::debug!("m: {:?}", m); + defmt::debug!("p: {:?}", p); + defmt::debug!("n: {:?}", n); + defmt::debug!("fout: {:?}", fout); + defmt::debug!("fcco: {:?}", fcco); + let fcco = fcco.ok_or(ClockError::BadConfig { clock: "spll", - reason: "fcco invalid", + reason: "fcco invalid1", })?; let fout = fout.ok_or(ClockError::BadConfig { clock: "spll", @@ -1130,7 +1171,7 @@ impl ClockOperator<'_> { if !(275_000_000..=550_000_000).contains(&fcco) { return Err(ClockError::BadConfig { clock: "spll", - reason: "fcco invalid", + reason: "fcco invalid2", }); } // Fout: 4.3MHz to 2x Max CPU Frequency @@ -1149,6 +1190,7 @@ impl ClockOperator<'_> { assert!(m >= 1); if let Some(p) = p.as_ref() { assert!(*p >= 1); + assert!(*p <= 31); } if let Some(n) = n.as_ref() { assert!(*n >= 1); @@ -1254,11 +1296,38 @@ impl ClockOperator<'_> { // Re-lock SPLL CSR self.scg0.spllcsr().modify(|_r, w| w.lk().write_disabled()); + // Store clock state self.clocks.pll1_clk = Some(Clock { frequency: fout, power: cfg.power, }); + // Do we enable the `pll1_clk_div` output? + if let Some(d) = cfg.pll1_clk_div.as_ref() { + // Halt and reset the div; then set our desired div. + self.syscon.pll1clkdiv().write(|w| { + w.halt().halt(); + w.reset().asserted(); + unsafe { w.div().bits(d.into_bits()) }; + w + }); + // Then unhalt it, and reset it + self.syscon.pll1clkdiv().write(|w| { + w.halt().run(); + w.reset().released(); + w + }); + + // Wait for clock to stabilize + while self.syscon.pll1clkdiv().read().unstab().is_ongoing() {} + + // Store off the clock info + self.clocks.pll1_clk_div = Some(Clock { + frequency: fout / d.into_divisor(), + power: cfg.power, + }); + } + Ok(()) } } diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs index e6e6a2d3d..94f7d7bbf 100644 --- a/examples/mcxa/src/bin/clkout.rs +++ b/examples/mcxa/src/bin/clkout.rs @@ -4,7 +4,7 @@ use embassy_executor::Spawner; use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; use embassy_mcxa::clocks::PoweredClock; -use embassy_mcxa::clocks::config::{SoscConfig, SoscMode}; +use embassy_mcxa::clocks::config::{SoscConfig, SoscMode, SpllConfig, SpllMode, SpllSource}; use embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate}; use embassy_time::Timer; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -18,6 +18,18 @@ async fn main(_spawner: Spawner) { frequency: 8_000_000, power: PoweredClock::NormalEnabledDeepSleepDisabled, }); + cfg.clock_cfg.spll = Some(SpllConfig { + source: SpllSource::Sirc, // 12MHz + // 12 x 32 => 384MHz + // 384 / (16 x 2) => 12.0MHz + mode: SpllMode::Mode1b { + m_mult: 32, + p_div: 16, + bypass_p2_div: false, + }, + power: PoweredClock::NormalEnabledDeepSleepDisabled, + pll1_clk_div: None, + }); let p = hal::init(cfg); @@ -39,11 +51,18 @@ async fn main(_spawner: Spawner) { div: const { Div4::from_divisor(16).unwrap() }, level: PoweredClock::NormalEnabledDeepSleepDisabled, }; + const M1_CONFIG: Config = Config { + sel: ClockOutSel::Pll1Clk, + div: const { Div4::from_divisor(12).unwrap() }, + level: PoweredClock::NormalEnabledDeepSleepDisabled, + }; + #[rustfmt::skip] let configs = [ - ("16K -> /1 = 16K", K16_CONFIG), - ("12M -> /3 = 4M", M4_CONFIG), + ("16K -> /1 = 16K", K16_CONFIG), + ("12M -> /3 = 4M", M4_CONFIG), ("8M -> /16 = 512K", K512_CONFIG), + ("12M-> /12 = 1M", M1_CONFIG), ]; loop { -- cgit