aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2025-05-13 21:45:22 +0000
committerGitHub <[email protected]>2025-05-13 21:45:22 +0000
commit5a19b64fec396db1ababa6d3e7a71f4b3c6bab18 (patch)
tree897805aa66eb7997ea24f6ab3e7b817f45e5a7e6
parent5caa4ac51bacb8444ca6b3caafb7d0ba66e39310 (diff)
parent981ef20f83ec88601818d8c55f69a1037d57b0cb (diff)
Merge pull request #4187 from 1-rafael-1/rp235x-overclocking
RP235x overclocking
-rw-r--r--embassy-rp/src/clocks.rs244
-rw-r--r--examples/rp/src/bin/overclock.rs10
-rw-r--r--examples/rp/src/bin/overclock_manual.rs10
-rw-r--r--examples/rp235x/src/bin/overclock.rs74
-rw-r--r--tests/rp/src/bin/overclock.rs49
5 files changed, 316 insertions, 71 deletions
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 6694aab66..857877680 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -38,7 +38,7 @@
38//! 38//!
39//! ## Examples 39//! ## Examples
40//! 40//!
41//! ### Standard 125MHz configuration 41//! ### Standard 125MHz (rp2040) or 150Mhz (rp235x) configuration
42//! ```rust,ignore 42//! ```rust,ignore
43//! let config = ClockConfig::crystal(12_000_000); 43//! let config = ClockConfig::crystal(12_000_000);
44//! ``` 44//! ```
@@ -82,6 +82,18 @@ use crate::{pac, reset, Peri};
82// be very useful until we have runtime clock reconfiguration. once this 82// be very useful until we have runtime clock reconfiguration. once this
83// happens we can resurrect the commented-out gpin bits. 83// happens we can resurrect the commented-out gpin bits.
84 84
85/// Clock error types.
86#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87#[cfg_attr(feature = "defmt", derive(defmt::Format))]
88pub enum ClockError {
89 /// PLL failed to lock within the timeout period.
90 PllLockTimedOut,
91 /// Could not find valid PLL parameters for system clock.
92 InvalidPllParameters,
93 /// Reading the core voltage failed due to an unexpected value in the register.
94 UnexpectedCoreVoltageRead,
95}
96
85struct Clocks { 97struct Clocks {
86 xosc: AtomicU32, 98 xosc: AtomicU32,
87 sys: AtomicU32, 99 sys: AtomicU32,
@@ -136,15 +148,16 @@ pub enum PeriClkSrc {
136 // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ , 148 // Gpin1 = ClkPeriCtrlAuxsrc::CLKSRC_GPIN1 as _ ,
137} 149}
138 150
139/// Core voltage regulator settings for RP2040. 151/// Core voltage regulator settings.
140/// 152///
141/// The RP2040 voltage regulator can be configured for different output voltages. 153/// The voltage regulator can be configured for different output voltages.
142/// Higher voltages allow for higher clock frequencies but increase power consumption and heat. 154/// Higher voltages allow for higher clock frequencies but increase power consumption and heat.
143#[cfg(feature = "rp2040")] 155#[cfg(feature = "rp2040")]
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
145#[repr(u8)] 156#[repr(u8)]
157#[derive(Clone, Copy, Debug, PartialEq, Eq)]
158#[cfg_attr(feature = "defmt", derive(defmt::Format))]
146pub enum CoreVoltage { 159pub enum CoreVoltage {
147 /// 0.80V - Suitable for lower frequencies 160 /// 0.80V
148 V0_80 = 0b0000, 161 V0_80 = 0b0000,
149 /// 0.85V 162 /// 0.85V
150 V0_85 = 0b0110, 163 V0_85 = 0b0110,
@@ -168,11 +181,58 @@ pub enum CoreVoltage {
168 V1_30 = 0b1111, 181 V1_30 = 0b1111,
169} 182}
170 183
171#[cfg(feature = "rp2040")] 184/// Core voltage regulator settings.
185///
186/// The voltage regulator can be configured for different output voltages.
187/// Higher voltages allow for higher clock frequencies but increase power consumption and heat.
188///
189/// **Note**: The maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit
190/// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this
191/// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now.
192#[cfg(feature = "_rp235x")]
193#[repr(u8)]
194#[derive(Clone, Copy, Debug, PartialEq, Eq)]
195#[cfg_attr(feature = "defmt", derive(defmt::Format))]
196pub enum CoreVoltage {
197 /// 0.55V
198 V0_55 = 0b00000,
199 /// 0.60V
200 V0_60 = 0b00001,
201 /// 0.65V
202 V0_65 = 0b00010,
203 /// 0.70V
204 V0_70 = 0b00011,
205 /// 0.75V
206 V0_75 = 0b00100,
207 /// 0.80V
208 V0_80 = 0b00101,
209 /// 0.85V
210 V0_85 = 0b00110,
211 /// 0.90V
212 V0_90 = 0b00111,
213 /// 0.95V
214 V0_95 = 0b01000,
215 /// 1.00V
216 V1_00 = 0b01001,
217 /// 1.05V
218 V1_05 = 0b01010,
219 /// 1.10V - Default voltage level
220 V1_10 = 0b01011,
221 /// 1.15V
222 V1_15 = 0b01100,
223 /// 1.20V
224 V1_20 = 0b01101,
225 /// 1.25V
226 V1_25 = 0b01110,
227 /// 1.30V
228 V1_30 = 0b01111,
229}
230
172impl CoreVoltage { 231impl CoreVoltage {
173 /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. 232 /// Get the recommended Brown-Out Detection (BOD) setting for this voltage.
174 /// Sets the BOD threshold to approximately 80% of the core voltage. 233 /// Sets the BOD threshold to approximately 80% of the core voltage.
175 fn recommended_bod(self) -> u8 { 234 fn recommended_bod(self) -> u8 {
235 #[cfg(feature = "rp2040")]
176 match self { 236 match self {
177 CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V) 237 CoreVoltage::V0_80 => 0b0100, // 0.645V (~81% of 0.80V)
178 CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V) 238 CoreVoltage::V0_85 => 0b0101, // 0.688V (~81% of 0.85V)
@@ -180,12 +240,32 @@ impl CoreVoltage {
180 CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V) 240 CoreVoltage::V0_95 => 0b0111, // 0.774V (~81% of 0.95V)
181 CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V) 241 CoreVoltage::V1_00 => 0b1000, // 0.817V (~82% of 1.00V)
182 CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V) 242 CoreVoltage::V1_05 => 0b1000, // 0.817V (~78% of 1.05V)
183 CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V) 243 CoreVoltage::V1_10 => 0b1001, // 0.860V (~78% of 1.10V), the default
184 CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V) 244 CoreVoltage::V1_15 => 0b1010, // 0.903V (~79% of 1.15V)
185 CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V) 245 CoreVoltage::V1_20 => 0b1011, // 0.946V (~79% of 1.20V)
186 CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V) 246 CoreVoltage::V1_25 => 0b1100, // 0.989V (~79% of 1.25V)
187 CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V) 247 CoreVoltage::V1_30 => 0b1101, // 1.032V (~79% of 1.30V)
188 } 248 }
249 #[cfg(feature = "_rp235x")]
250 match self {
251 CoreVoltage::V0_55 => 0b00001, // 0.516V (~94% of 0.55V)
252 CoreVoltage::V0_60 => 0b00010, // 0.559V (~93% of 0.60V)
253 CoreVoltage::V0_65 => 0b00011, // 0.602V (~93% of 0.65V)
254 CoreVoltage::V0_70 => 0b00011, // 0.602V (~86% of 0.70V)
255 CoreVoltage::V0_75 => 0b00100, // 0.645V (~86% of 0.75V)
256 CoreVoltage::V0_80 => 0b00101, // 0.688V (~86% of 0.80V)
257 CoreVoltage::V0_85 => 0b00110, // 0.731V (~86% of 0.85V)
258 CoreVoltage::V0_90 => 0b00110, // 0.731V (~81% of 0.90V)
259 CoreVoltage::V0_95 => 0b00111, // 0.774V (~81% of 0.95V)
260 CoreVoltage::V1_00 => 0b01000, // 0.817V (~82% of 1.00V)
261 CoreVoltage::V1_05 => 0b01000, // 0.817V (~78% of 1.05V)
262 CoreVoltage::V1_10 => 0b01001, // 0.860V (~78% of 1.10V), the default
263 CoreVoltage::V1_15 => 0b01001, // 0.860V (~75% of 1.15V)
264 CoreVoltage::V1_20 => 0b01010, // 0.903V (~75% of 1.20V)
265 CoreVoltage::V1_25 => 0b01010, // 0.903V (~72% of 1.25V)
266 CoreVoltage::V1_30 => 0b01011, // 0.946V (~73% of 1.30V)
267 // all others: 0.946V (see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point)
268 }
189 } 269 }
190} 270}
191 271
@@ -209,12 +289,10 @@ pub struct ClockConfig {
209 /// RTC clock configuration. 289 /// RTC clock configuration.
210 #[cfg(feature = "rp2040")] 290 #[cfg(feature = "rp2040")]
211 pub rtc_clk: Option<RtcClkConfig>, 291 pub rtc_clk: Option<RtcClkConfig>,
212 /// Core voltage scaling (RP2040 only). Defaults to 1.10V. 292 /// Core voltage scaling. Defaults to 1.10V.
213 #[cfg(feature = "rp2040")]
214 pub core_voltage: CoreVoltage, 293 pub core_voltage: CoreVoltage,
215 /// Voltage stabilization delay in microseconds. 294 /// Voltage stabilization delay in microseconds.
216 /// If not set, defaults will be used based on voltage level. 295 /// If not set, defaults will be used based on voltage level.
217 #[cfg(feature = "rp2040")]
218 pub voltage_stabilization_delay_us: Option<u32>, 296 pub voltage_stabilization_delay_us: Option<u32>,
219 // See above re gpin handling being commented out 297 // See above re gpin handling being commented out
220 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, 298 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>,
@@ -250,9 +328,7 @@ impl Default for ClockConfig {
250 adc_clk: None, 328 adc_clk: None,
251 #[cfg(feature = "rp2040")] 329 #[cfg(feature = "rp2040")]
252 rtc_clk: None, 330 rtc_clk: None,
253 #[cfg(feature = "rp2040")]
254 core_voltage: CoreVoltage::V1_10, 331 core_voltage: CoreVoltage::V1_10,
255 #[cfg(feature = "rp2040")]
256 voltage_stabilization_delay_us: None, 332 voltage_stabilization_delay_us: None,
257 // See above re gpin handling being commented out 333 // See above re gpin handling being commented out
258 // gpin0: None, 334 // gpin0: None,
@@ -323,9 +399,7 @@ impl ClockConfig {
323 div_frac: 0, 399 div_frac: 0,
324 phase: 0, 400 phase: 0,
325 }), 401 }),
326 #[cfg(feature = "rp2040")]
327 core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) 402 core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V)
328 #[cfg(feature = "rp2040")]
329 voltage_stabilization_delay_us: None, 403 voltage_stabilization_delay_us: None,
330 // See above re gpin handling being commented out 404 // See above re gpin handling being commented out
331 // gpin0: None, 405 // gpin0: None,
@@ -368,9 +442,7 @@ impl ClockConfig {
368 div_frac: 171, 442 div_frac: 171,
369 phase: 0, 443 phase: 0,
370 }), 444 }),
371 #[cfg(feature = "rp2040")]
372 core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V) 445 core_voltage: CoreVoltage::V1_10, // Use hardware default (1.10V)
373 #[cfg(feature = "rp2040")]
374 voltage_stabilization_delay_us: None, 446 voltage_stabilization_delay_us: None,
375 // See above re gpin handling being commented out 447 // See above re gpin handling being commented out
376 // gpin0: None, 448 // gpin0: None,
@@ -391,29 +463,42 @@ impl ClockConfig {
391 /// # Returns 463 /// # Returns
392 /// 464 ///
393 /// A ClockConfig configured to achieve the requested system frequency using the 465 /// A ClockConfig configured to achieve the requested system frequency using the
394 /// the usual 12Mhz crystal, or panic if no valid parameters can be found. 466 /// the usual 12Mhz crystal, or an error if no valid parameters can be found.
395 /// 467 ///
396 /// # Note on core voltage: 468 /// # Note on core voltage:
469 ///
470 /// **For RP2040**:
397 /// To date the only officially documented core voltages (see Datasheet section 2.15.3.1. Instances) are: 471 /// To date the only officially documented core voltages (see Datasheet section 2.15.3.1. Instances) are:
398 /// - Up to 133MHz: V1_10 (default) 472 /// - Up to 133MHz: V1_10 (default)
399 /// - Above 133MHz: V1_15, but in the context of the datasheet covering reaching up to 200Mhz 473 /// - Above 133MHz: V1_15, but in the context of the datasheet covering reaching up to 200Mhz
400 /// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here. 474 /// That way all other frequencies below 133MHz or above 200MHz are not explicitly documented and not covered here.
401 /// 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. 475 /// 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.
402 #[cfg(feature = "rp2040")] 476 ///
403 pub fn system_freq(hz: u32) -> Self { 477 /// **For RP235x**:
478 /// 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.
479 /// Using this function is experimental and may not work as expected or even damage the chip.
480 ///
481 /// # Returns
482 ///
483 /// A Result containing either the configured ClockConfig or a ClockError.
484 pub fn system_freq(hz: u32) -> Result<Self, ClockError> {
404 // Start with the standard configuration from crystal() 485 // Start with the standard configuration from crystal()
405 const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; 486 const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000;
406 let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ); 487 let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ);
407 488
408 // No need to modify anything if target frequency is already 125MHz 489 // No need to modify anything if target frequency is already 125MHz
409 // (which is what crystal() configures by default) 490 // (which is what crystal() configures by default)
491 #[cfg(feature = "rp2040")]
410 if hz == 125_000_000 { 492 if hz == 125_000_000 {
411 return config; 493 return Ok(config);
494 }
495 #[cfg(feature = "_rp235x")]
496 if hz == 150_000_000 {
497 return Ok(config);
412 } 498 }
413 499
414 // Find optimal PLL parameters for the requested frequency 500 // Find optimal PLL parameters for the requested frequency
415 let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz) 501 let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz).ok_or(ClockError::InvalidPllParameters)?;
416 .unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock"));
417 502
418 // Replace the sys_pll configuration with our custom parameters 503 // Replace the sys_pll configuration with our custom parameters
419 if let Some(xosc) = &mut config.xosc { 504 if let Some(xosc) = &mut config.xosc {
@@ -429,8 +514,16 @@ impl ClockConfig {
429 _ => CoreVoltage::V1_10, // Use default voltage (V1_10) 514 _ => CoreVoltage::V1_10, // Use default voltage (V1_10)
430 }; 515 };
431 } 516 }
517 #[cfg(feature = "_rp235x")]
518 {
519 config.core_voltage = match hz {
520 // There is no official support for running the chip on other core voltages and/or other clock speeds than the defaults.
521 // So for now we have not way of knowing what the voltage should be. Change this if the manufacturer provides more information.
522 _ => CoreVoltage::V1_10, // Use default voltage (V1_10)
523 };
524 }
432 525
433 config 526 Ok(config)
434 } 527 }
435 528
436 /// Configure with manual PLL settings for full control over system clock 529 /// Configure with manual PLL settings for full control over system clock
@@ -525,6 +618,7 @@ impl ClockConfig {
525#[repr(u16)] 618#[repr(u16)]
526#[non_exhaustive] 619#[non_exhaustive]
527#[derive(Clone, Copy, Debug, PartialEq, Eq)] 620#[derive(Clone, Copy, Debug, PartialEq, Eq)]
621#[cfg_attr(feature = "defmt", derive(defmt::Format))]
528pub enum RoscRange { 622pub enum RoscRange {
529 /// Low range. 623 /// Low range.
530 Low = pac::rosc::vals::FreqRange::LOW.0, 624 Low = pac::rosc::vals::FreqRange::LOW.0,
@@ -631,6 +725,7 @@ pub struct RefClkConfig {
631/// Reference clock source. 725/// Reference clock source.
632#[non_exhaustive] 726#[non_exhaustive]
633#[derive(Clone, Copy, Debug, PartialEq, Eq)] 727#[derive(Clone, Copy, Debug, PartialEq, Eq)]
728#[cfg_attr(feature = "defmt", derive(defmt::Format))]
634pub enum RefClkSrc { 729pub enum RefClkSrc {
635 /// XOSC. 730 /// XOSC.
636 Xosc, 731 Xosc,
@@ -646,6 +741,7 @@ pub enum RefClkSrc {
646/// SYS clock source. 741/// SYS clock source.
647#[non_exhaustive] 742#[non_exhaustive]
648#[derive(Clone, Copy, Debug, PartialEq, Eq)] 743#[derive(Clone, Copy, Debug, PartialEq, Eq)]
744#[cfg_attr(feature = "defmt", derive(defmt::Format))]
649pub enum SysClkSrc { 745pub enum SysClkSrc {
650 /// REF. 746 /// REF.
651 Ref, 747 Ref,
@@ -684,6 +780,7 @@ pub struct SysClkConfig {
684#[repr(u8)] 780#[repr(u8)]
685#[non_exhaustive] 781#[non_exhaustive]
686#[derive(Clone, Copy, Debug, PartialEq, Eq)] 782#[derive(Clone, Copy, Debug, PartialEq, Eq)]
783#[cfg_attr(feature = "defmt", derive(defmt::Format))]
687pub enum UsbClkSrc { 784pub enum UsbClkSrc {
688 /// PLL USB. 785 /// PLL USB.
689 PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _, 786 PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _,
@@ -712,6 +809,7 @@ pub struct UsbClkConfig {
712#[repr(u8)] 809#[repr(u8)]
713#[non_exhaustive] 810#[non_exhaustive]
714#[derive(Clone, Copy, Debug, PartialEq, Eq)] 811#[derive(Clone, Copy, Debug, PartialEq, Eq)]
812#[cfg_attr(feature = "defmt", derive(defmt::Format))]
715pub enum AdcClkSrc { 813pub enum AdcClkSrc {
716 /// PLL USB. 814 /// PLL USB.
717 PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _, 815 PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _,
@@ -740,6 +838,7 @@ pub struct AdcClkConfig {
740#[repr(u8)] 838#[repr(u8)]
741#[non_exhaustive] 839#[non_exhaustive]
742#[derive(Clone, Copy, Debug, PartialEq, Eq)] 840#[derive(Clone, Copy, Debug, PartialEq, Eq)]
841#[cfg_attr(feature = "defmt", derive(defmt::Format))]
743#[cfg(feature = "rp2040")] 842#[cfg(feature = "rp2040")]
744pub enum RtcClkSrc { 843pub enum RtcClkSrc {
745 /// PLL USB. 844 /// PLL USB.
@@ -791,7 +890,6 @@ pub struct RtcClkConfig {
791/// // Find parameters for 133MHz system clock from 12MHz crystal 890/// // Find parameters for 133MHz system clock from 12MHz crystal
792/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); 891/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap();
793/// ``` 892/// ```
794#[cfg(feature = "rp2040")]
795fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> { 893fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
796 // Fixed reference divider for system PLL 894 // Fixed reference divider for system PLL
797 const PLL_SYS_REFDIV: u8 = 1; 895 const PLL_SYS_REFDIV: u8 = 1;
@@ -925,18 +1023,31 @@ pub(crate) unsafe fn init(config: ClockConfig) {
925 }; 1023 };
926 CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); 1024 CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed);
927 1025
928 // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default 1026 // Set Core Voltage, if we have config for it and we're not using the default
929 #[cfg(feature = "rp2040")]
930 { 1027 {
931 let voltage = config.core_voltage; 1028 let voltage = config.core_voltage;
1029
1030 #[cfg(feature = "rp2040")]
932 let vreg = pac::VREG_AND_CHIP_RESET; 1031 let vreg = pac::VREG_AND_CHIP_RESET;
1032 #[cfg(feature = "_rp235x")]
1033 let vreg = pac::POWMAN;
1034
933 let current_vsel = vreg.vreg().read().vsel(); 1035 let current_vsel = vreg.vreg().read().vsel();
934 let target_vsel = voltage as u8; 1036 let target_vsel = voltage as u8;
935 1037
936 // If the target voltage is different from the current one, we need to change it 1038 // If the target voltage is different from the current one, we need to change it
937 if target_vsel != current_vsel { 1039 if target_vsel != current_vsel {
938 // Use modify() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage 1040 // Set the voltage regulator to the target voltage
1041 #[cfg(feature = "rp2040")]
939 vreg.vreg().modify(|w| w.set_vsel(target_vsel)); 1042 vreg.vreg().modify(|w| w.set_vsel(target_vsel));
1043 #[cfg(feature = "_rp235x")]
1044 // For rp235x changes to the voltage regulator are protected by a password, see datasheet section 6.4 Power Management (POWMAN) Registers
1045 // The password is "5AFE" (0x5AFE), it must be set in the top 16 bits of the register
1046 vreg.vreg().modify(|w| {
1047 w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); // Set the password
1048 w.set_vsel(target_vsel);
1049 *w
1050 });
940 1051
941 // Wait for the voltage to stabilize. Use the provided delay or default based on voltage 1052 // Wait for the voltage to stabilize. Use the provided delay or default based on voltage
942 let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { 1053 let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| {
@@ -955,7 +1066,14 @@ pub(crate) unsafe fn init(config: ClockConfig) {
955 } 1066 }
956 1067
957 // Only now set the BOD level. At this point the voltage is considered stable. 1068 // Only now set the BOD level. At this point the voltage is considered stable.
1069 #[cfg(feature = "rp2040")]
1070 vreg.bod().write(|w| {
1071 w.set_vsel(voltage.recommended_bod());
1072 w.set_en(true); // Enable brownout detection
1073 });
1074 #[cfg(feature = "_rp235x")]
958 vreg.bod().write(|w| { 1075 vreg.bod().write(|w| {
1076 w.0 = (w.0 & 0x0000FFFF) | (0x5AFE << 16); // Set the password
959 w.set_vsel(voltage.recommended_bod()); 1077 w.set_vsel(voltage.recommended_bod());
960 w.set_en(true); // Enable brownout detection 1078 w.set_en(true); // Enable brownout detection
961 }); 1079 });
@@ -970,14 +1088,14 @@ pub(crate) unsafe fn init(config: ClockConfig) {
970 let pll_sys_freq = match config.sys_pll { 1088 let pll_sys_freq = match config.sys_pll {
971 Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) { 1089 Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) {
972 Ok(freq) => freq, 1090 Ok(freq) => freq,
973 Err(e) => panic!("Failed to configure PLL_SYS: {}", e), 1091 Err(e) => panic!("Failed to configure PLL_SYS: {:?}", e),
974 }, 1092 },
975 None => 0, 1093 None => 0,
976 }; 1094 };
977 let pll_usb_freq = match config.usb_pll { 1095 let pll_usb_freq = match config.usb_pll {
978 Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) { 1096 Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) {
979 Ok(freq) => freq, 1097 Ok(freq) => freq,
980 Err(e) => panic!("Failed to configure PLL_USB: {}", e), 1098 Err(e) => panic!("Failed to configure PLL_USB: {:?}", e),
981 }, 1099 },
982 None => 0, 1100 None => 0,
983 }; 1101 };
@@ -1283,6 +1401,58 @@ pub fn clk_rtc_freq() -> u16 {
1283 CLOCKS.rtc.load(Ordering::Relaxed) 1401 CLOCKS.rtc.load(Ordering::Relaxed)
1284} 1402}
1285 1403
1404/// The core voltage of the chip.
1405///
1406/// Returns the current core voltage or an error if the voltage register
1407/// contains an unknown value.
1408pub fn core_voltage() -> Result<CoreVoltage, ClockError> {
1409 #[cfg(feature = "rp2040")]
1410 {
1411 let vreg = pac::VREG_AND_CHIP_RESET;
1412 let vsel = vreg.vreg().read().vsel();
1413 match vsel {
1414 0b0000 => Ok(CoreVoltage::V0_80),
1415 0b0110 => Ok(CoreVoltage::V0_85),
1416 0b0111 => Ok(CoreVoltage::V0_90),
1417 0b1000 => Ok(CoreVoltage::V0_95),
1418 0b1001 => Ok(CoreVoltage::V1_00),
1419 0b1010 => Ok(CoreVoltage::V1_05),
1420 0b1011 => Ok(CoreVoltage::V1_10),
1421 0b1100 => Ok(CoreVoltage::V1_15),
1422 0b1101 => Ok(CoreVoltage::V1_20),
1423 0b1110 => Ok(CoreVoltage::V1_25),
1424 0b1111 => Ok(CoreVoltage::V1_30),
1425 _ => Err(ClockError::UnexpectedCoreVoltageRead),
1426 }
1427 }
1428
1429 #[cfg(feature = "_rp235x")]
1430 {
1431 let vreg = pac::POWMAN;
1432 let vsel = vreg.vreg().read().vsel();
1433 match vsel {
1434 0b00000 => Ok(CoreVoltage::V0_55),
1435 0b00001 => Ok(CoreVoltage::V0_60),
1436 0b00010 => Ok(CoreVoltage::V0_65),
1437 0b00011 => Ok(CoreVoltage::V0_70),
1438 0b00100 => Ok(CoreVoltage::V0_75),
1439 0b00101 => Ok(CoreVoltage::V0_80),
1440 0b00110 => Ok(CoreVoltage::V0_85),
1441 0b00111 => Ok(CoreVoltage::V0_90),
1442 0b01000 => Ok(CoreVoltage::V0_95),
1443 0b01001 => Ok(CoreVoltage::V1_00),
1444 0b01010 => Ok(CoreVoltage::V1_05),
1445 0b01011 => Ok(CoreVoltage::V1_10),
1446 0b01100 => Ok(CoreVoltage::V1_15),
1447 0b01101 => Ok(CoreVoltage::V1_20),
1448 0b01110 => Ok(CoreVoltage::V1_25),
1449 0b01111 => Ok(CoreVoltage::V1_30),
1450 _ => Err(ClockError::UnexpectedCoreVoltageRead),
1451 // see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point
1452 }
1453 }
1454}
1455
1286fn start_xosc(crystal_hz: u32, delay_multiplier: u32) { 1456fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
1287 let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256; 1457 let startup_delay = (((crystal_hz / 1000) * delay_multiplier) + 128) / 256;
1288 pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16)); 1458 pac::XOSC.startup().write(|w| w.set_delay(startup_delay as u16));
@@ -1295,7 +1465,7 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
1295 1465
1296/// PLL (Phase-Locked Loop) configuration 1466/// PLL (Phase-Locked Loop) configuration
1297#[inline(always)] 1467#[inline(always)]
1298fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, &'static str> { 1468fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, ClockError> {
1299 // Calculate reference frequency 1469 // Calculate reference frequency
1300 let ref_freq = input_freq / config.refdiv as u32; 1470 let ref_freq = input_freq / config.refdiv as u32;
1301 1471
@@ -1366,7 +1536,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result
1366 timeout -= 1; 1536 timeout -= 1;
1367 if timeout == 0 { 1537 if timeout == 0 {
1368 // PLL failed to lock, return 0 to indicate failure 1538 // PLL failed to lock, return 0 to indicate failure
1369 return Err("PLL failed to lock"); 1539 return Err(ClockError::PllLockTimedOut);
1370 } 1540 }
1371 } 1541 }
1372 1542
@@ -1937,21 +2107,21 @@ mod tests {
1937 { 2107 {
1938 // Test automatic voltage scaling based on frequency 2108 // Test automatic voltage scaling based on frequency
1939 // Under 133 MHz should use default voltage (V1_10) 2109 // Under 133 MHz should use default voltage (V1_10)
1940 let config = ClockConfig::system_freq(125_000_000); 2110 let config = ClockConfig::system_freq(125_000_000).unwrap();
1941 assert_eq!(config.core_voltage, CoreVoltage::V1_10); 2111 assert_eq!(config.core_voltage, CoreVoltage::V1_10);
1942 2112
1943 // 133-200 MHz should use V1_15 2113 // 133-200 MHz should use V1_15
1944 let config = ClockConfig::system_freq(150_000_000); 2114 let config = ClockConfig::system_freq(150_000_000).unwrap();
1945 assert_eq!(config.core_voltage, CoreVoltage::V1_15); 2115 assert_eq!(config.core_voltage, CoreVoltage::V1_15);
1946 let config = ClockConfig::system_freq(200_000_000); 2116 let config = ClockConfig::system_freq(200_000_000).unwrap();
1947 assert_eq!(config.core_voltage, CoreVoltage::V1_15); 2117 assert_eq!(config.core_voltage, CoreVoltage::V1_15);
1948 2118
1949 // Above 200 MHz should use V1_25 2119 // Above 200 MHz should use V1_15
1950 let config = ClockConfig::system_freq(250_000_000); 2120 let config = ClockConfig::system_freq(250_000_000).unwrap();
1951 assert_eq!(config.core_voltage, CoreVoltage::V1_15); 2121 assert_eq!(config.core_voltage, CoreVoltage::V1_15);
1952 2122
1953 // Below 125 MHz should use V1_10 2123 // Below 125 MHz should use V1_10
1954 let config = ClockConfig::system_freq(100_000_000); 2124 let config = ClockConfig::system_freq(100_000_000).unwrap();
1955 assert_eq!(config.core_voltage, CoreVoltage::V1_10); 2125 assert_eq!(config.core_voltage, CoreVoltage::V1_10);
1956 } 2126 }
1957 } 2127 }
diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs
index 9c78e0c9d..83b17308b 100644
--- a/examples/rp/src/bin/overclock.rs
+++ b/examples/rp/src/bin/overclock.rs
@@ -7,7 +7,7 @@
7 7
8use defmt::*; 8use defmt::*;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_rp::clocks::{clk_sys_freq, ClockConfig}; 10use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig};
11use embassy_rp::config::Config; 11use embassy_rp::config::Config;
12use embassy_rp::gpio::{Level, Output}; 12use embassy_rp::gpio::{Level, Output};
13use embassy_time::{Duration, Instant, Timer}; 13use embassy_time::{Duration, Instant, Timer};
@@ -18,10 +18,7 @@ const COUNT_TO: i64 = 10_000_000;
18#[embassy_executor::main] 18#[embassy_executor::main]
19async fn main(_spawner: Spawner) -> ! { 19async fn main(_spawner: Spawner) -> ! {
20 // Set up for clock frequency of 200 MHz, setting all necessary defaults. 20 // Set up for clock frequency of 200 MHz, setting all necessary defaults.
21 let config = Config::new(ClockConfig::system_freq(200_000_000)); 21 let config = Config::new(ClockConfig::system_freq(200_000_000).unwrap());
22
23 // Show the voltage scale for verification
24 info!("System core voltage: {}", Debug2Format(&config.clocks.core_voltage));
25 22
26 // Initialize the peripherals 23 // Initialize the peripherals
27 let p = embassy_rp::init(config); 24 let p = embassy_rp::init(config);
@@ -29,6 +26,9 @@ async fn main(_spawner: Spawner) -> ! {
29 // Show CPU frequency for verification 26 // Show CPU frequency for verification
30 let sys_freq = clk_sys_freq(); 27 let sys_freq = clk_sys_freq();
31 info!("System clock frequency: {} MHz", sys_freq / 1_000_000); 28 info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
29 // Show core voltage for verification
30 let core_voltage = core_voltage().unwrap();
31 info!("Core voltage: {}", core_voltage);
32 32
33 // LED to indicate the system is running 33 // LED to indicate the system is running
34 let mut led = Output::new(p.PIN_25, Level::Low); 34 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..dea5cfb3c 100644
--- a/examples/rp/src/bin/overclock_manual.rs
+++ b/examples/rp/src/bin/overclock_manual.rs
@@ -7,8 +7,7 @@
7 7
8use defmt::*; 8use defmt::*;
9use embassy_executor::Spawner; 9use embassy_executor::Spawner;
10use embassy_rp::clocks; 10use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage, PllConfig};
11use embassy_rp::clocks::{ClockConfig, CoreVoltage, PllConfig};
12use embassy_rp::config::Config; 11use embassy_rp::config::Config;
13use embassy_rp::gpio::{Level, Output}; 12use embassy_rp::gpio::{Level, Output};
14use embassy_time::{Duration, Instant, Timer}; 13use embassy_time::{Duration, Instant, Timer};
@@ -41,9 +40,12 @@ async fn main(_spawner: Spawner) -> ! {
41 // Initialize with our manual overclock configuration 40 // Initialize with our manual overclock configuration
42 let p = embassy_rp::init(configure_manual_overclock()); 41 let p = embassy_rp::init(configure_manual_overclock());
43 42
44 // Verify the actual system clock frequency 43 // Show CPU frequency for verification
45 let sys_freq = clocks::clk_sys_freq(); 44 let sys_freq = clk_sys_freq();
46 info!("System clock frequency: {} MHz", sys_freq / 1_000_000); 45 info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
46 // Show core voltage for verification
47 let core_voltage = core_voltage().unwrap();
48 info!("Core voltage: {}", core_voltage);
47 49
48 // LED to indicate the system is running 50 // LED to indicate the system is running
49 let mut led = Output::new(p.PIN_25, Level::Low); 51 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..5fd97ef97
--- /dev/null
+++ b/examples/rp235x/src/bin/overclock.rs
@@ -0,0 +1,74 @@
1//! # Overclocking the RP2350 to 200 MHz
2//!
3//! This example demonstrates how to configure the RP2350 to run at 200 MHz instead of the default 150 MHz.
4//!
5//! ## Note
6//!
7//! As of yet there is no official support for running the RP235x at higher clock frequencies and/or other core voltages than the default.
8//! Doing so may cause unexpected behavior and/or damage the chip.
9
10#![no_std]
11#![no_main]
12
13use defmt::*;
14use embassy_executor::Spawner;
15use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage};
16use embassy_rp::config::Config;
17use embassy_rp::gpio::{Level, Output};
18use embassy_time::{Duration, Instant, Timer};
19use {defmt_rtt as _, panic_probe as _};
20
21const COUNT_TO: i64 = 10_000_000;
22
23#[embassy_executor::main]
24async fn main(_spawner: Spawner) -> ! {
25 // Set up for clock frequency of 200 MHz, setting all necessary defaults.
26 let mut config = Config::new(ClockConfig::system_freq(200_000_000).unwrap());
27
28 // since for the rp235x there is no official support for higher clock frequencies, `system_freq()` will not set a voltage for us.
29 // 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
30 // what we know about the RP2040. This is not guaranteed to be correct.
31 config.clocks.core_voltage = CoreVoltage::V1_15;
32
33 // Initialize the peripherals
34 let p = embassy_rp::init(config);
35
36 // Show CPU frequency for verification
37 let sys_freq = clk_sys_freq();
38 info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
39 // Show core voltage for verification
40 let core_voltage = core_voltage().unwrap();
41 info!("Core voltage: {}", core_voltage);
42
43 // LED to indicate the system is running
44 let mut led = Output::new(p.PIN_25, Level::Low);
45
46 loop {
47 // Reset the counter at the start of measurement period
48 let mut counter = 0;
49
50 // Turn LED on while counting
51 led.set_high();
52
53 let start = Instant::now();
54
55 // This is a busy loop that will take some time to complete
56 while counter < COUNT_TO {
57 counter += 1;
58 }
59
60 let elapsed = Instant::now() - start;
61
62 // Report the elapsed time
63 led.set_low();
64 info!(
65 "At {}Mhz: Elapsed time to count to {}: {}ms",
66 sys_freq / 1_000_000,
67 counter,
68 elapsed.as_millis()
69 );
70
71 // Wait 2 seconds before starting the next measurement
72 Timer::after(Duration::from_secs(2)).await;
73 }
74}
diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs
index be8e85a3f..167a26eb2 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");
7teleprobe_meta::target!(b"pimoroni-pico-plus-2"); 7teleprobe_meta::target!(b"pimoroni-pico-plus-2");
8 8
9use defmt::info; 9use defmt::info;
10#[cfg(feature = "rp2040")]
11use defmt::{assert, assert_eq};
12use embassy_executor::Spawner; 10use embassy_executor::Spawner;
13use embassy_rp::clocks; 11use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage};
14#[cfg(feature = "rp2040")]
15use embassy_rp::clocks::ClockConfig;
16#[cfg(feature = "rp2040")]
17use embassy_rp::clocks::CoreVoltage;
18use embassy_rp::config::Config; 12use embassy_rp::config::Config;
19use embassy_time::Instant; 13use embassy_time::Instant;
20use {defmt_rtt as _, panic_probe as _}; 14use {defmt_rtt as _, panic_probe as _};
@@ -23,23 +17,26 @@ const COUNT_TO: i64 = 10_000_000;
23 17
24#[embassy_executor::main] 18#[embassy_executor::main]
25async fn main(_spawner: Spawner) { 19async fn main(_spawner: Spawner) {
26 #[cfg(feature = "rp2040")]
27 let mut config = Config::default(); 20 let mut config = Config::default();
28 #[cfg(not(feature = "rp2040"))]
29 let config = Config::default();
30 21
31 // Initialize with 200MHz clock configuration for RP2040, other chips will use default clock 22 // Initialize with 200MHz clock configuration
32 #[cfg(feature = "rp2040")] 23 config.clocks = ClockConfig::system_freq(200_000_000).unwrap();
24
25 // if we are rp235x, we need to manually set the core voltage. rp2040 should do this automatically
26 #[cfg(feature = "rp235xb")]
33 { 27 {
34 config.clocks = ClockConfig::system_freq(200_000_000); 28 config.clocks.core_voltage = CoreVoltage::V1_15;
35 let voltage = config.clocks.core_voltage;
36 assert!(matches!(voltage, CoreVoltage::V1_15), "Expected voltage scale V1_15");
37 } 29 }
38 30
39 let _p = embassy_rp::init(config); 31 let _p = embassy_rp::init(config);
40 32
33 // We should be at core voltage of 1.15V
34 assert_eq!(core_voltage().unwrap(), CoreVoltage::V1_15, "Core voltage is not 1.15V");
35 // We should be at 200MHz
36 assert_eq!(clk_sys_freq(), 200_000_000, "System clock frequency is not 200MHz");
37
41 // Test the system speed 38 // Test the system speed
42 let (time_elapsed, clk_sys_freq) = { 39 let time_elapsed = {
43 let mut counter = 0; 40 let mut counter = 0;
44 let start = Instant::now(); 41 let start = Instant::now();
45 while counter < COUNT_TO { 42 while counter < COUNT_TO {
@@ -47,24 +44,26 @@ async fn main(_spawner: Spawner) {
47 } 44 }
48 let elapsed = Instant::now() - start; 45 let elapsed = Instant::now() - start;
49 46
50 (elapsed.as_millis(), clocks::clk_sys_freq()) 47 elapsed.as_millis()
51 }; 48 };
52 49
53 // Report the elapsed time, so that the compiler doesn't optimize it away for chips other than RP2040 50 // Tests will fail if unused variables are detected:
51 // Report the elapsed time, so that the compiler doesn't optimize it away for the chip not on test
54 info!( 52 info!(
55 "At {}Mhz: Elapsed time to count to {}: {}ms", 53 "At {}Mhz: Elapsed time to count to {}: {}ms",
56 clk_sys_freq / 1_000_000, 54 clk_sys_freq() / 1_000_000,
57 COUNT_TO, 55 COUNT_TO,
58 time_elapsed 56 time_elapsed
59 ); 57 );
60 58
59 // Check if the elapsed time is within expected limits
60 // for rp2040 we expect about 600ms
61 #[cfg(feature = "rp2040")] 61 #[cfg(feature = "rp2040")]
62 { 62 // allow 1% error
63 // we should be at 200MHz 63 assert!(time_elapsed < 606, "Elapsed time is too long");
64 assert_eq!(clk_sys_freq, 200_000_000, "System clock frequency is not 200MHz"); 64 // for rp235x we expect about 450ms
65 // At 200MHz, the time to count to 10_000_000 should be at 600ms, testing with 1% margin 65 #[cfg(feature = "rp235xb")]
66 assert!(time_elapsed <= 606, "Elapsed time is too long"); 66 assert!(time_elapsed < 455, "Elapsed time is too long");
67 }
68 67
69 cortex_m::asm::bkpt(); 68 cortex_m::asm::bkpt();
70} 69}