diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 6694aab66..ea5e9362b 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -38,7 +38,7 @@ //! //! ## Examples //! -//! ### Standard 125MHz configuration +//! ### Standard 125MHz (rp2040) or 150Mhz (rp235x) configuration //! ```rust,ignore //! let config = ClockConfig::crystal(12_000_000); //! ``` @@ -136,43 +136,152 @@ pub enum PeriClkSrc { // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ , } -/// Core voltage regulator settings for RP2040. +/// Core voltage regulator settings. /// -/// The RP2040 voltage regulator can be configured for different output voltages. +/// The voltage regulator can be configured for different output voltages. /// Higher voltages allow for higher clock frequencies but increase power consumption and heat. -#[cfg(feature = "rp2040")] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(u8)] pub enum CoreVoltage { - /// 0.80V - Suitable for lower frequencies + // RP2040 voltage levels + #[cfg(feature = "rp2040")] + /// RP2040: 0.80V V0_80 = 0b0000, - /// 0.85V + #[cfg(feature = "rp2040")] + /// RP2040: 0.85V V0_85 = 0b0110, - /// 0.90V + #[cfg(feature = "rp2040")] + /// RP2040: 0.90V V0_90 = 0b0111, - /// 0.95V + #[cfg(feature = "rp2040")] + /// RP2040: 0.95V V0_95 = 0b1000, - /// 1.00V + #[cfg(feature = "rp2040")] + /// RP2040: 1.00V V1_00 = 0b1001, - /// 1.05V + #[cfg(feature = "rp2040")] + /// RP2040: 1.05V V1_05 = 0b1010, - /// 1.10V - Default voltage level + #[cfg(feature = "rp2040")] + /// RP2040: 1.10V - Default voltage level V1_10 = 0b1011, - /// 1.15V - Required for overclocking to 133-200MHz + #[cfg(feature = "rp2040")] + /// RP2040: 1.15V - Required for overclocking to 133-200MHz V1_15 = 0b1100, - /// 1.20V + #[cfg(feature = "rp2040")] + /// RP2040: 1.20V V1_20 = 0b1101, - /// 1.25V + #[cfg(feature = "rp2040")] + /// RP2040: 1.25V V1_25 = 0b1110, - /// 1.30V + #[cfg(feature = "rp2040")] + /// RP2040: 1.30V V1_30 = 0b1111, + + // RP235x voltage levels + #[cfg(feature = "_rp235x")] + /// RP235x: 0.55V + V0_55 = 0b00000, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.60V + V0_60 = 0b00001, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.65V + V0_65 = 0b00010, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.70V + V0_70 = 0b00011, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.75V + V0_75 = 0b00100, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.80V + V0_80 = 0b00101, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.85V + V0_85 = 0b00110, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.90V + V0_90 = 0b00111, + #[cfg(feature = "_rp235x")] + /// RP235x: 0.95V + V0_95 = 0b01000, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.00V + V1_00 = 0b01001, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.05V + V1_05 = 0b01010, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.10V - Default voltage level + V1_10 = 0b01011, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.15V + V1_15 = 0b01100, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.20V + V1_20 = 0b01101, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.25V + V1_25 = 0b01110, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.30V + V1_30 = 0b01111, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.35V + V1_35 = 0b10000, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.40V + V1_40 = 0b10001, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.50V + V1_50 = 0b10010, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.60V + V1_60 = 0b10011, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.65V + V1_65 = 0b10100, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.70V + V1_70 = 0b10101, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.80V + V1_80 = 0b10110, + #[cfg(feature = "_rp235x")] + /// RP235x: 1.90V + V1_90 = 0b10111, + #[cfg(feature = "_rp235x")] + /// RP235x: 2.00V + V2_00 = 0b11000, + #[cfg(feature = "_rp235x")] + /// RP235x: 2.35V + V2_35 = 0b11001, + #[cfg(feature = "_rp235x")] + /// RP235x: 2.50V + V2_50 = 0b11010, + #[cfg(feature = "_rp235x")] + /// RP235x: 2.65V + V2_65 = 0b11011, + #[cfg(feature = "_rp235x")] + /// RP235x: 2.80V + V2_80 = 0b11100, + #[cfg(feature = "_rp235x")] + /// RP235x: 3.00V + V3_00 = 0b11101, + #[cfg(feature = "_rp235x")] + /// RP235x: 3.15V + V3_15 = 0b11110, + #[cfg(feature = "_rp235x")] + /// RP235x: 3.30V + V3_30 = 0b11111, } -#[cfg(feature = "rp2040")] impl CoreVoltage { /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. /// Sets the BOD threshold to approximately 80% of the core voltage. fn recommended_bod(self) -> u8 { + #[cfg(feature = "rp2040")] match self { CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V) CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V) @@ -180,12 +289,38 @@ impl CoreVoltage { CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V) CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V) CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V) - CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V) + CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V), the default CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V) CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V) CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V) CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V) } + #[cfg(feature = "_rp235x")] + match self { + CoreVoltage::V0_55 => 0b00001, // 0.516V (~94% of 0.55V) + CoreVoltage::V0_60 => 0b00010, // 0.559V (~93% of 0.60V) + CoreVoltage::V0_65 => 0b00011, // 0.602V (~93% of 0.65V) + CoreVoltage::V0_70 => 0b00011, // 0.602V (~86% of 0.70V) + CoreVoltage::V0_75 => 0b00100, // 0.645V (~86% of 0.75V) + CoreVoltage::V0_80 => 0b00101, // 0.688V (~86% of 0.80V) + CoreVoltage::V0_85 => 0b00110, // 0.731V (~86% of 0.85V) + CoreVoltage::V0_90 => 0b00110, // 0.731V (~81% of 0.90V) + CoreVoltage::V0_95 => 0b00111, // 0.774V (~81% of 0.95V) + CoreVoltage::V1_00 => 0b01000, // 0.817V (~82% of 1.00V) + CoreVoltage::V1_05 => 0b01000, // 0.817V (~78% of 1.05V) + CoreVoltage::V1_10 => 0b01001, // 0.860V (~78% of 1.10V), the default + CoreVoltage::V1_15 => 0b01001, // 0.860V (~75% of 1.15V) + CoreVoltage::V1_20 => 0b01010, // 0.903V (~75% of 1.20V) + CoreVoltage::V1_25 => 0b01010, // 0.903V (~72% of 1.25V) + CoreVoltage::V1_30 => 0b01011, // 0.946V (~73% of 1.30V) + CoreVoltage::V1_35 => 0b01011, // 0.946V (~70% of 1.35V) + CoreVoltage::V1_40 => 0b01100, // 0.989V (~71% of 1.40V) + CoreVoltage::V1_50 => 0b01101, // 1.032V (~69% of 1.50V) + CoreVoltage::V1_60 => 0b01110, // 1.075V (~67% of 1.60V) + CoreVoltage::V1_65 => 0b01110, // 1.075V (~65% of 1.65V) + CoreVoltage::V1_70 => 0b01111, // 1.118V (~66% of 1.70V) + _ => 0b10000, // the rp2350 datasheet repeats this value for all other core voltages + } } } @@ -209,12 +344,10 @@ pub struct ClockConfig { /// RTC clock configuration. #[cfg(feature = "rp2040")] pub rtc_clk: Option, - /// Core voltage scaling (RP2040 only). Defaults to 1.10V. - #[cfg(feature = "rp2040")] + /// Core voltage scaling. Defaults to 1.10V. pub core_voltage: CoreVoltage, /// Voltage stabilization delay in microseconds. /// If not set, defaults will be used based on voltage level. - #[cfg(feature = "rp2040")] pub voltage_stabilization_delay_us: Option, // See above re gpin handling being commented out // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, @@ -250,9 +383,7 @@ impl Default for ClockConfig { adc_clk: None, #[cfg(feature = "rp2040")] rtc_clk: None, - #[cfg(feature = "rp2040")] core_voltage: CoreVoltage::V1_10, - #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, // See above re gpin handling being commented out // gpin0: None, @@ -323,9 +454,7 @@ impl ClockConfig { div_frac: 0, phase: 0, }), - #[cfg(feature = "rp2040")] core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) - #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, // See above re gpin handling being commented out // gpin0: None, @@ -368,9 +497,7 @@ impl ClockConfig { div_frac: 171, phase: 0, }), - #[cfg(feature = "rp2040")] core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) - #[cfg(feature = "rp2040")] voltage_stabilization_delay_us: None, // See above re gpin handling being commented out // gpin0: None, @@ -394,12 +521,17 @@ impl ClockConfig { /// the usual 12Mhz crystal, or panic if no valid parameters can be found. /// /// # Note on core voltage: + /// + /// **For RP2040**: /// To date the only officially documented core voltages (see Datasheet section 2.15.3.1. Instances) are: /// - Up to 133MHz: V1_10 (default) /// - Above 133MHz: V1_15, but in the context of the datasheet covering reaching up to 200Mhz /// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here. /// In case You want to go below 133MHz or above 200MHz and want a different voltage, You will have to set that manually and with caution. - #[cfg(feature = "rp2040")] + /// + /// **For RP235x**: + /// At this point in time there is no official manufacturer endorsement for running the chip on other core voltages and/or other clock speeds than the defaults. + /// Using this function is experimental and may not work as expected or even damage the chip. pub fn system_freq(hz: u32) -> Self { // Start with the standard configuration from crystal() const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; @@ -407,9 +539,14 @@ impl ClockConfig { // No need to modify anything if target frequency is already 125MHz // (which is what crystal() configures by default) + #[cfg(feature = "rp2040")] if hz == 125_000_000 { return config; } + #[cfg(feature = "_rp235x")] + if hz == 150_000_000 { + return config; + } // Find optimal PLL parameters for the requested frequency let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz) @@ -429,6 +566,14 @@ impl ClockConfig { _ => CoreVoltage::V1_10, // Use default voltage (V1_10) }; } + #[cfg(feature = "_rp235x")] + { + config.core_voltage = match hz { + // There is no official support for running the chip on other core voltages and/or other clock speeds than the defaults. + // So for now we have not way of knowing what the voltage should be. Change this if the manufacturer provides more information. + _ => CoreVoltage::V1_10, // Use default voltage (V1_10) + }; + } config } @@ -791,7 +936,6 @@ pub struct RtcClkConfig { /// // Find parameters for 133MHz system clock from 12MHz crystal /// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); /// ``` -#[cfg(feature = "rp2040")] fn find_pll_params(input_hz: u32, target_hz: u32) -> Option { // Fixed reference divider for system PLL const PLL_SYS_REFDIV: u8 = 1; @@ -925,18 +1069,59 @@ pub(crate) unsafe fn init(config: ClockConfig) { }; CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); - // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default - #[cfg(feature = "rp2040")] + // Set Core Voltage, if we have config for it and we're not using the default { let voltage = config.core_voltage; + + #[cfg(feature = "rp2040")] let vreg = pac::VREG_AND_CHIP_RESET; + #[cfg(feature = "_rp235x")] + let vreg = pac::POWMAN; + let current_vsel = vreg.vreg().read().vsel(); let target_vsel = voltage as u8; // If the target voltage is different from the current one, we need to change it if target_vsel != current_vsel { - // Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage - vreg.vreg().modify(|w| w.set_vsel(target_vsel)); + #[cfg(feature = "rp2040")] + { + // Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage + vreg.vreg().modify(|w| w.set_vsel(target_vsel)); + } + #[cfg(feature = "_rp235x")] + { + // The rp235x has a different way of controlling the voltage regulator + // Changes to the voltage regulator are protected by a password, see datasheet section 6.4 + // The password is "5AFE" (0x5AFE), it must be set in the top 16 bits of the register + + // The rp235x by default locks the voltage regulator control, so we need to unlock it first + // See datasheet section 6.3.2. Software Control + vreg.vreg_ctrl().modify(|w| { + // Add password to top 16 bits, preserving the rest, repeat below for other registers + w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); + w.set_unlock(true); + *w + }); + + // Set the voltage + vreg.vreg().modify(|w| { + w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); + w.set_vsel(target_vsel); + *w + }); + + // The rp235x has two more registers to set the voltage for low power mode + vreg.vreg_lp_entry().modify(|w| { + w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); + w.set_vsel(target_vsel); + *w + }); + vreg.vreg_lp_exit().modify(|w| { + w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); + w.set_vsel(target_vsel); + *w + }); + } // Wait for the voltage to stabilize. Use the provided delay or default based on voltage let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { @@ -959,6 +1144,19 @@ pub(crate) unsafe fn init(config: ClockConfig) { w.set_vsel(voltage.recommended_bod()); w.set_en(true); // Enable brownout detection }); + + #[cfg(feature = "_rp235x")] + { + // The rp235x has a separate register for the BOD level in low power mode + vreg.bod_lp_entry().write(|w| { + w.set_vsel(voltage.recommended_bod()); + w.set_en(true); // Enable brownout detection + }); + vreg.bod_lp_exit().write(|w| { + w.set_vsel(voltage.recommended_bod()); + w.set_en(true); // Enable brownout detection + }); + } } } @@ -1283,6 +1481,73 @@ pub fn clk_rtc_freq() -> u16 { CLOCKS.rtc.load(Ordering::Relaxed) } +/// The core voltage of the chip. +/// +/// Returns the current core voltage or an error if the voltage register +/// contains an unknown value. +pub fn core_voltage() -> Result { + #[cfg(feature = "rp2040")] + { + let vreg = pac::VREG_AND_CHIP_RESET; + let vsel = vreg.vreg().read().vsel(); + match vsel { + 0b0000 => Ok(CoreVoltage::V0_80), + 0b0110 => Ok(CoreVoltage::V0_85), + 0b0111 => Ok(CoreVoltage::V0_90), + 0b1000 => Ok(CoreVoltage::V0_95), + 0b1001 => Ok(CoreVoltage::V1_00), + 0b1010 => Ok(CoreVoltage::V1_05), + 0b1011 => Ok(CoreVoltage::V1_10), + 0b1100 => Ok(CoreVoltage::V1_15), + 0b1101 => Ok(CoreVoltage::V1_20), + 0b1110 => Ok(CoreVoltage::V1_25), + 0b1111 => Ok(CoreVoltage::V1_30), + _ => Err("Unexpected value in register"), + } + } + + #[cfg(feature = "_rp235x")] + { + let vreg = pac::POWMAN; + let vsel = vreg.vreg().read().vsel(); + match vsel { + 0b00000 => Ok(CoreVoltage::V0_55), + 0b00001 => Ok(CoreVoltage::V0_60), + 0b00010 => Ok(CoreVoltage::V0_65), + 0b00011 => Ok(CoreVoltage::V0_70), + 0b00100 => Ok(CoreVoltage::V0_75), + 0b00101 => Ok(CoreVoltage::V0_80), + 0b00110 => Ok(CoreVoltage::V0_85), + 0b00111 => Ok(CoreVoltage::V0_90), + 0b01000 => Ok(CoreVoltage::V0_95), + 0b01001 => Ok(CoreVoltage::V1_00), + 0b01010 => Ok(CoreVoltage::V1_05), + 0b01011 => Ok(CoreVoltage::V1_10), + 0b01100 => Ok(CoreVoltage::V1_15), + 0b01101 => Ok(CoreVoltage::V1_20), + 0b01110 => Ok(CoreVoltage::V1_25), + 0b01111 => Ok(CoreVoltage::V1_30), + 0b10000 => Ok(CoreVoltage::V1_35), + 0b10001 => Ok(CoreVoltage::V1_40), + 0b10010 => Ok(CoreVoltage::V1_50), + 0b10011 => Ok(CoreVoltage::V1_60), + 0b10100 => Ok(CoreVoltage::V1_65), + 0b10101 => Ok(CoreVoltage::V1_70), + 0b10110 => Ok(CoreVoltage::V1_80), + 0b10111 => Ok(CoreVoltage::V1_90), + 0b11000 => Ok(CoreVoltage::V2_00), + 0b11001 => Ok(CoreVoltage::V2_35), + 0b11010 => Ok(CoreVoltage::V2_50), + 0b11011 => Ok(CoreVoltage::V2_65), + 0b11100 => Ok(CoreVoltage::V2_80), + 0b11101 => Ok(CoreVoltage::V3_00), + 0b11110 => Ok(CoreVoltage::V3_15), + 0b11111 => Ok(CoreVoltage::V3_30), + _ => Err("Unexpected value in register"), + } + } +} + fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256; pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16)); diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index 9c78e0c9d..89147ba42 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::clocks::{clk_sys_freq, ClockConfig}; +use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig}; use embassy_rp::config::Config; use embassy_rp::gpio::{Level, Output}; use embassy_time::{Duration, Instant, Timer}; @@ -20,15 +20,15 @@ async fn main(_spawner: Spawner) -> ! { // Set up for clock frequency of 200 MHz, setting all necessary defaults. let config = Config::new(ClockConfig::system_freq(200_000_000)); - // Show the voltage scale for verification - info!("System core voltage: {}", Debug2Format(&config.clocks.core_voltage)); - // Initialize the peripherals let p = embassy_rp::init(config); // Show CPU frequency for verification let sys_freq = clk_sys_freq(); info!("System clock frequency: {} MHz", sys_freq / 1_000_000); + // Show core voltage for verification + let core_voltage = core_voltage().unwrap(); + info!("Core voltage: {}", Debug2Format(&core_voltage)); // LED to indicate the system is running let mut led = Output::new(p.PIN_25, Level::Low); diff --git a/examples/rp/src/bin/overclock_manual.rs b/examples/rp/src/bin/overclock_manual.rs index 35160b250..88ef26a7a 100644 --- a/examples/rp/src/bin/overclock_manual.rs +++ b/examples/rp/src/bin/overclock_manual.rs @@ -7,8 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::clocks; -use embassy_rp::clocks::{ClockConfig, CoreVoltage, PllConfig}; +use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage, PllConfig}; use embassy_rp::config::Config; use embassy_rp::gpio::{Level, Output}; use embassy_time::{Duration, Instant, Timer}; @@ -41,9 +40,12 @@ async fn main(_spawner: Spawner) -> ! { // Initialize with our manual overclock configuration let p = embassy_rp::init(configure_manual_overclock()); - // Verify the actual system clock frequency - let sys_freq = clocks::clk_sys_freq(); + // Show CPU frequency for verification + let sys_freq = clk_sys_freq(); info!("System clock frequency: {} MHz", sys_freq / 1_000_000); + // Show core voltage for verification + let core_voltage = core_voltage().unwrap(); + info!("Core voltage: {}", Debug2Format(&core_voltage)); // LED to indicate the system is running let mut led = Output::new(p.PIN_25, Level::Low); diff --git a/examples/rp235x/src/bin/overclock.rs b/examples/rp235x/src/bin/overclock.rs new file mode 100644 index 000000000..8713df688 --- /dev/null +++ b/examples/rp235x/src/bin/overclock.rs @@ -0,0 +1,74 @@ +//! # Overclocking the RP2350 to 200 MHz +//! +//! This example demonstrates how to configure the RP2350 to run at 200 MHz instead of the default 150 MHz. +//! +//! ## Note +//! +//! As of yet there is no official support for running the RP235x at higher clock frequencies and/or other core voltages than the default. +//! Doing so may cause unexpected behavior and/or damage the chip. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage}; +use embassy_rp::config::Config; +use embassy_rp::gpio::{Level, Output}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +const COUNT_TO: i64 = 10_000_000; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + // Set up for clock frequency of 200 MHz, setting all necessary defaults. + let mut config = Config::new(ClockConfig::system_freq(200_000_000)); + + // since for the rp235x there is no official support for higher clock frequencies, `system_freq()` will not set a voltage for us. + // We need to guess the core voltage, that is needed for the higher clock frequency. Going with a small increase from the default 1.1V here, based on + // what we know about the RP2040. This is not guaranteed to be correct. + config.clocks.core_voltage = CoreVoltage::V1_15; + + // Initialize the peripherals + let p = embassy_rp::init(config); + + // Show CPU frequency for verification + let sys_freq = clk_sys_freq(); + info!("System clock frequency: {} MHz", sys_freq / 1_000_000); + // Show core voltage for verification + let core_voltage = core_voltage().unwrap(); + info!("Core voltage: {}", Debug2Format(&core_voltage)); + + // LED to indicate the system is running + let mut led = Output::new(p.PIN_25, Level::Low); + + loop { + // Reset the counter at the start of measurement period + let mut counter = 0; + + // Turn LED on while counting + led.set_high(); + + let start = Instant::now(); + + // This is a busy loop that will take some time to complete + while counter < COUNT_TO { + counter += 1; + } + + let elapsed = Instant::now() - start; + + // Report the elapsed time + led.set_low(); + info!( + "At {}Mhz: Elapsed time to count to {}: {}ms", + sys_freq / 1_000_000, + counter, + elapsed.as_millis() + ); + + // Wait 2 seconds before starting the next measurement + Timer::after(Duration::from_secs(2)).await; + } +} diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs index be8e85a3f..a568d7fed 100644 --- a/tests/rp/src/bin/overclock.rs +++ b/tests/rp/src/bin/overclock.rs @@ -7,14 +7,8 @@ teleprobe_meta::target!(b"rpi-pico"); teleprobe_meta::target!(b"pimoroni-pico-plus-2"); use defmt::info; -#[cfg(feature = "rp2040")] -use defmt::{assert, assert_eq}; use embassy_executor::Spawner; -use embassy_rp::clocks; -#[cfg(feature = "rp2040")] -use embassy_rp::clocks::ClockConfig; -#[cfg(feature = "rp2040")] -use embassy_rp::clocks::CoreVoltage; +use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage}; use embassy_rp::config::Config; use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; @@ -23,23 +17,26 @@ const COUNT_TO: i64 = 10_000_000; #[embassy_executor::main] async fn main(_spawner: Spawner) { - #[cfg(feature = "rp2040")] let mut config = Config::default(); - #[cfg(not(feature = "rp2040"))] - let config = Config::default(); - // Initialize with 200MHz clock configuration for RP2040, other chips will use default clock - #[cfg(feature = "rp2040")] + // Initialize with 200MHz clock configuration + config.clocks = ClockConfig::system_freq(200_000_000); + + // if we are rp235x, we need to manually set the core voltage. rp2040 should do this automatically + #[cfg(feature = "rp235xb")] { - config.clocks = ClockConfig::system_freq(200_000_000); - let voltage = config.clocks.core_voltage; - assert!(matches!(voltage, CoreVoltage::V1_15), "Expected voltage scale V1_15"); + config.clocks.core_voltage = CoreVoltage::V1_15; } let _p = embassy_rp::init(config); + // We should be at core voltage of 1.15V + assert_eq!(core_voltage().unwrap(), CoreVoltage::V1_15, "Core voltage is not 1.15V"); + // We should be at 200MHz + assert_eq!(clk_sys_freq(), 200_000_000, "System clock frequency is not 200MHz"); + // Test the system speed - let (time_elapsed, clk_sys_freq) = { + let time_elapsed = { let mut counter = 0; let start = Instant::now(); while counter < COUNT_TO { @@ -47,24 +44,26 @@ async fn main(_spawner: Spawner) { } let elapsed = Instant::now() - start; - (elapsed.as_millis(), clocks::clk_sys_freq()) + elapsed.as_millis() }; - // Report the elapsed time, so that the compiler doesn't optimize it away for chips other than RP2040 + // Tests will fail if unused variables are detected: + // Report the elapsed time, so that the compiler doesn't optimize it away for the chip not on test info!( "At {}Mhz: Elapsed time to count to {}: {}ms", - clk_sys_freq / 1_000_000, + clk_sys_freq() / 1_000_000, COUNT_TO, time_elapsed ); + // Check if the elapsed time is within expected limits + // for rp2040 we expect about 600ms #[cfg(feature = "rp2040")] - { - // we should be at 200MHz - assert_eq!(clk_sys_freq, 200_000_000, "System clock frequency is not 200MHz"); - // At 200MHz, the time to count to 10_000_000 should be at 600ms, testing with 1% margin - assert!(time_elapsed <= 606, "Elapsed time is too long"); - } + // allow 1% error + assert!(time_elapsed < 606, "Elapsed time is too long"); + // for rp235x we expect about 450ms + #[cfg(feature = "rp235xb")] + assert!(time_elapsed < 455, "Elapsed time is too long"); cortex_m::asm::bkpt(); }