aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author1-rafael-1 <[email protected]>2025-04-28 22:54:15 +0200
committer1-rafael-1 <[email protected]>2025-04-28 22:54:15 +0200
commit3a6dc910ffc66d4a30b89f299432b383271a719f (patch)
treeb053571d597b2030e1069397a93de05cf9939661
parentb0594d16f238f803a0192810833ae2b0c3941ec3 (diff)
first working draft
-rw-r--r--embassy-rp/src/clocks.rs658
-rw-r--r--examples/rp/src/bin/overclock.rs26
2 files changed, 505 insertions, 179 deletions
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index ba7b139a6..b74e90f5e 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -70,43 +70,51 @@ pub enum PeriClkSrc {
70} 70}
71 71
72/// Core voltage scaling options for RP2040. 72/// Core voltage scaling options for RP2040.
73/// See RP2040 Datasheet, Table 18. 73/// See RP2040 Datasheet, Table 189, VREG Register.
74#[cfg(feature = "rp2040")] 74#[cfg(feature = "rp2040")]
75#[derive(Clone, Copy, Debug, PartialEq, Eq)] 75#[derive(Clone, Copy, Debug, PartialEq, Eq)]
76#[repr(u8)] 76#[repr(u8)]
77pub enum VoltageScale { 77pub enum VoltageScale {
78 /// 0.85V 78 /// 0.85V
79 V0_85 = 0b1000, 79 V0_85 = 0b0110,
80 /// 0.90V 80 /// 0.90V
81 V0_90 = 0b1001, 81 V0_90 = 0b0111,
82 /// 0.95V 82 /// 0.95V
83 V0_95 = 0b1010, 83 V0_95 = 0b1000,
84 /// 1.00V 84 /// 1.00V
85 V1_00 = 0b1011, 85 V1_00 = 0b1001,
86 /// 1.05V 86 /// 1.05V
87 V1_05 = 0b1100, 87 V1_05 = 0b1010,
88 /// 1.10V (Default) 88 /// 1.10V (Default)
89 V1_10 = 0b1101, 89 V1_10 = 0b1011,
90 /// 1.15V 90 /// 1.15V
91 V1_15 = 0b1110, 91 V1_15 = 0b1100,
92 /// 1.20V 92 /// 1.20V
93 V1_20 = 0b1111, 93 V1_20 = 0b1101,
94 /// 1.25V
95 V1_25 = 0b1110,
96 /// 1.30V
97 V1_30 = 0b1111,
94} 98}
95 99
96#[cfg(feature = "rp2040")] 100#[cfg(feature = "rp2040")]
97impl VoltageScale { 101impl VoltageScale {
98 /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. 102 /// Get the recommended Brown-Out Detection (BOD) setting for this voltage.
99 /// See RP2040 Datasheet, Table 19. 103 /// Sets the BOD threshold to approximately 90% of the core voltage.
104 /// See RP2040 Datasheet, Table 190, BOD Register
100 fn recommended_bod(self) -> u8 { 105 fn recommended_bod(self) -> u8 {
101 match self { 106 match self {
102 VoltageScale::V0_85 => 0b1000, // BOD recommends VSEL + 1 107 // ~90% of voltage setting based on Table 190 values
103 VoltageScale::V0_90 => 0b1001, 108 VoltageScale::V0_85 => 0b0111, // 0.774V (~91% of 0.85V)
104 VoltageScale::V0_95 => 0b1010, 109 VoltageScale::V0_90 => 0b1000, // 0.817V (~91% of 0.90V)
105 VoltageScale::V1_00 => 0b1011, 110 VoltageScale::V0_95 => 0b1001, // 0.860V (~91% of 0.95V)
106 VoltageScale::V1_05 => 0b1100, 111 VoltageScale::V1_00 => 0b1010, // 0.903V (~90% of 1.00V)
107 VoltageScale::V1_10 => 0b1101, // Default 112 VoltageScale::V1_05 => 0b1011, // 0.946V (~90% of 1.05V)
108 VoltageScale::V1_15 => 0b1110, 113 VoltageScale::V1_10 => 0b1100, // 0.989V (~90% of 1.10V)
109 VoltageScale::V1_20 => 0b1111, 114 VoltageScale::V1_15 => 0b1101, // 1.032V (~90% of 1.15V)
115 VoltageScale::V1_20 => 0b1110, // 1.075V (~90% of 1.20V)
116 VoltageScale::V1_25 => 0b1111, // 1.118V (~89% of 1.25V)
117 VoltageScale::V1_30 => 0b1111, // 1.118V (~86% of 1.30V) - using max available threshold
110 } 118 }
111 } 119 }
112} 120}
@@ -134,6 +142,9 @@ pub struct ClockConfig {
134 /// Core voltage scaling (RP2040 only). Defaults to 1.10V if None. 142 /// Core voltage scaling (RP2040 only). Defaults to 1.10V if None.
135 #[cfg(feature = "rp2040")] 143 #[cfg(feature = "rp2040")]
136 pub voltage_scale: Option<VoltageScale>, 144 pub voltage_scale: Option<VoltageScale>,
145 /// Voltage stabilization delay in microseconds.
146 #[cfg(feature = "rp2040")]
147 pub voltage_stabilization_delay_us: Option<u32>,
137 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, 148 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>,
138 // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, 149 // gpin1: Option<(u32, Gpin<'static, AnyPin>)>,
139} 150}
@@ -199,8 +210,10 @@ impl ClockConfig {
199 }), 210 }),
200 #[cfg(feature = "rp2040")] 211 #[cfg(feature = "rp2040")]
201 voltage_scale: None, // Use hardware default (1.10V) 212 voltage_scale: None, // Use hardware default (1.10V)
202 // gpin0: None, 213 #[cfg(feature = "rp2040")]
203 // gpin1: None, 214 voltage_stabilization_delay_us: None,
215 // gpin0: None,
216 // gpin1: None,
204 } 217 }
205 } 218 }
206 219
@@ -235,8 +248,16 @@ impl ClockConfig {
235 }; 248 };
236 249
237 // Find suitable PLL parameters 250 // Find suitable PLL parameters
238 let pll_params = find_pll_params(crystal_hz, sys_freq_hz) 251 let pll_params = match find_pll_params(crystal_hz, sys_freq_hz) {
239 .expect("Could not find valid PLL parameters for the requested frequency"); 252 Some(params) => params,
253 None => {
254 // If we can't find valid parameters for the requested frequency,
255 // fall back to safe defaults (125 MHz for RP2040)
256 let safe_freq = 125_000_000; // Safe default frequency
257 find_pll_params(crystal_hz, safe_freq)
258 .expect("Could not find valid PLL parameters even for safe default frequency")
259 }
260 };
240 261
241 Self { 262 Self {
242 rosc: Some(RoscConfig { 263 rosc: Some(RoscConfig {
@@ -284,6 +305,8 @@ impl ClockConfig {
284 phase: 0, 305 phase: 0,
285 }), 306 }),
286 voltage_scale: Some(voltage_scale), 307 voltage_scale: Some(voltage_scale),
308 #[cfg(feature = "rp2040")]
309 voltage_stabilization_delay_us: None,
287 // gpin0: None, 310 // gpin0: None,
288 // gpin1: None, 311 // gpin1: None,
289 } 312 }
@@ -326,11 +349,128 @@ impl ClockConfig {
326 }), 349 }),
327 #[cfg(feature = "rp2040")] 350 #[cfg(feature = "rp2040")]
328 voltage_scale: None, // Use hardware default (1.10V) 351 voltage_scale: None, // Use hardware default (1.10V)
329 // gpin0: None, 352 #[cfg(feature = "rp2040")]
330 // gpin1: None, 353 voltage_stabilization_delay_us: None,
354 // gpin0: None,
355 // gpin1: None,
331 } 356 }
332 } 357 }
333 358
359 /// Configure the system clock to a specific frequency in MHz.
360 ///
361 /// This is a more user-friendly way to configure the system clock, similar to
362 /// the Pico SDK's approach. It automatically handles voltage scaling based on the
363 /// requested frequency and uses the standard 12MHz crystal found on most RP2040 boards.
364 ///
365 /// # Arguments
366 ///
367 /// * `sys_freq_mhz` - The target system clock frequency in MHz
368 ///
369 /// # Safety Notes
370 ///
371 /// * Frequencies > 133MHz require increased core voltage and are considered overclocking.
372 /// * Frequencies > 250MHz are extreme overclocking and may not be stable on all chips.
373 ///
374 /// # Example
375 ///
376 /// ```
377 /// // Configure for standard 125MHz clock
378 /// let config = ClockConfig::with_speed_mhz(125);
379 ///
380 /// // Overclock to 200MHz (requires higher voltage)
381 /// let config = ClockConfig::with_speed_mhz(200);
382 /// ```
383 #[cfg(feature = "rp2040")]
384 pub fn with_speed_mhz(sys_freq_mhz: u32) -> Self {
385 // For 125MHz, use exactly the same config as the default to avoid any differences
386 if sys_freq_mhz == 125 {
387 return Self::crystal(12_000_000);
388 }
389
390 // For other frequencies, provide appropriate voltage scaling and PLL configuration
391 // Standard crystal on Raspberry Pi Pico boards is 12MHz
392 const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000;
393
394 let sys_freq_hz = sys_freq_mhz * 1_000_000;
395 let mut config = Self::crystal_freq(DEFAULT_CRYSTAL_HZ, sys_freq_hz);
396
397 // For frequencies above 200MHz, ensure we're using the highest voltage
398 if sys_freq_mhz > 200 {
399 config.voltage_scale = Some(VoltageScale::V1_20);
400 }
401
402 config
403 }
404
405 /// Configure the system clock to a specific frequency in Hz, using a custom crystal frequency.
406 ///
407 /// This more flexible version allows specifying both the crystal frequency and target
408 /// system frequency for boards with non-standard crystals.
409 ///
410 /// # Arguments
411 ///
412 /// * `crystal_hz` - The frequency of the external crystal in Hz
413 /// * `sys_freq_hz` - The target system clock frequency in Hz
414 ///
415 /// # Safety Notes
416 ///
417 /// * Frequencies > 133MHz require increased core voltage and are considered overclocking.
418 /// * Frequencies > 250MHz are extreme overclocking and may not be stable on all chips.
419 ///
420 /// # Example
421 ///
422 /// ```
423 /// // Use a non-standard 16MHz crystal to achieve 250MHz
424 /// let config = ClockConfig::with_custom_crystal(16_000_000, 250_000_000);
425 /// ```
426 #[cfg(feature = "rp2040")]
427 pub fn with_custom_crystal(crystal_hz: u32, sys_freq_hz: u32) -> Self {
428 Self::crystal_freq(crystal_hz, sys_freq_hz)
429 }
430
431 #[cfg(feature = "rp2040")]
432 pub fn with_speed_mhz_test_voltage(sys_freq_mhz: u32, voltage: Option<VoltageScale>) -> Self {
433 // Create a config with the requested frequency
434 let sys_freq_hz = sys_freq_mhz * 1_000_000;
435 let mut config = Self::crystal_freq(12_000_000, sys_freq_hz);
436
437 // Override the voltage setting
438 config.voltage_scale = voltage;
439
440 // For debugging
441 // println!("Debug: Setting freq to {}MHz with voltage {:?}", sys_freq_mhz, voltage);
442
443 config
444 }
445
446 /// Similar to `with_speed_mhz_test_voltage` but with an extended voltage stabilization delay.
447 ///
448 /// This function is useful for testing voltage stability issues where the default delay
449 /// may not be sufficient for the voltage regulator to fully stabilize.
450 ///
451 /// # Arguments
452 ///
453 /// * `sys_freq_mhz` - The target system clock frequency in MHz
454 /// * `voltage` - The desired voltage scale setting
455 /// * `stabilization_delay_us` - Voltage stabilization delay in microseconds (default: 500μs)
456 #[cfg(feature = "rp2040")]
457 pub fn with_speed_mhz_test_voltage_extended_delay(
458 sys_freq_mhz: u32,
459 voltage: Option<VoltageScale>,
460 stabilization_delay_us: Option<u32>,
461 ) -> Self {
462 // Create a config with the requested frequency
463 let sys_freq_hz = sys_freq_mhz * 1_000_000;
464 let mut config = Self::crystal_freq(12_000_000, sys_freq_hz);
465
466 // Override the voltage setting
467 config.voltage_scale = voltage;
468
469 // Add a custom voltage stabilization delay
470 config.voltage_stabilization_delay_us = stabilization_delay_us;
471
472 config
473 }
334 // pub fn bind_gpin<P: GpinPin>(&mut self, gpin: Gpin<'static, P>, hz: u32) { 474 // pub fn bind_gpin<P: GpinPin>(&mut self, gpin: Gpin<'static, P>, hz: u32) {
335 // match P::NR { 475 // match P::NR {
336 // 0 => self.gpin0 = Some((hz, gpin.into())), 476 // 0 => self.gpin0 = Some((hz, gpin.into())),
@@ -385,6 +525,7 @@ pub struct XoscConfig {
385} 525}
386 526
387/// PLL configuration. 527/// PLL configuration.
528#[derive(Clone, Copy, Debug)]
388pub struct PllConfig { 529pub struct PllConfig {
389 /// Reference divisor. 530 /// Reference divisor.
390 pub refdiv: u8, 531 pub refdiv: u8,
@@ -542,67 +683,124 @@ pub struct RtcClkConfig {
542/// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency 683/// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency
543/// based on the input frequency. 684/// based on the input frequency.
544/// 685///
686/// Similar to the Pico SDK's parameter selection approach, prioritizing stability.
545/// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL 687/// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL
546#[cfg(feature = "rp2040")] 688#[cfg(feature = "rp2040")]
547fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> { 689fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
690 // Fixed reference divider for system PLL
691 const PLL_SYS_REFDIV: u8 = 1;
692
548 // Constraints from datasheet: 693 // Constraints from datasheet:
549 // REFDIV: 1..=63 694 // REFDIV: 1..=63
550 // FBDIV: 16..=320 695 // FBDIV: 16..=320
551 // POSTDIV1: 1..=7 696 // POSTDIV1: 1..=7
552 // POSTDIV2: 1..=7 (must be <= POSTDIV1) 697 // POSTDIV2: 1..=7 (must be <= POSTDIV1)
553 // VCO frequency (input_hz / refdiv * fbdiv): 400MHz ..= 1600MHz 698 // VCO frequency (input_hz / refdiv * fbdiv): 750MHz ..= 1800MHz
554 699
555 for refdiv in 1..=63 { 700 // Calculate reference frequency
556 let ref_clk = input_hz / refdiv; 701 let reference_freq = input_hz / PLL_SYS_REFDIV as u32;
557 // Reference clock must be >= 5MHz (implied by VCO min / FBDIV max) 702
558 if ref_clk < 5_000_000 { 703 // Start from highest fbdiv for better stability (like SDK does)
704 for fbdiv in (16..=320).rev() {
705 let vco_freq = reference_freq * fbdiv;
706
707 // Check VCO frequency is within valid range
708 if vco_freq < 750_000_000 || vco_freq > 1_800_000_000 {
559 continue; 709 continue;
560 } 710 }
561 711
562 for fbdiv in (16..=320).rev() { 712 // Try all possible postdiv combinations starting from larger values
563 // Iterate high fbdiv first for better VCO stability 713 // (more conservative/stable approach)
564 let vco_freq = ref_clk * fbdiv; 714 for post_div1 in (1..=7).rev() {
565 if !(400_000_000..=1_600_000_000).contains(&vco_freq) { 715 for post_div2 in (1..=post_div1).rev() {
566 continue; 716 let out_freq = vco_freq / (post_div1 * post_div2) as u32;
717
718 // Check if we get the exact target frequency without remainder
719 if out_freq == target_hz && (vco_freq % (post_div1 * post_div2) as u32 == 0) {
720 return Some(PllConfig {
721 refdiv: PLL_SYS_REFDIV,
722 fbdiv: fbdiv as u16,
723 post_div1: post_div1 as u8,
724 post_div2: post_div2 as u8,
725 });
726 }
567 } 727 }
728 }
729 }
568 730
569 // We want vco_freq / (post_div1 * post_div2) = target_hz 731 // If we couldn't find an exact match, find the closest match
570 // So, post_div1 * post_div2 = vco_freq / target_hz 732 let mut best_config = None;
571 let target_post_div_product = vco_freq as f64 / target_hz as f64; 733 let mut min_diff = u32::MAX;
572 if target_post_div_product < 1.0 || target_post_div_product > 49.0 {
573 // 7*7 = 49
574 continue;
575 }
576 // Manual rounding: floor(x + 0.5)
577 let target_post_div_product_int = (target_post_div_product + 0.5) as u32;
578 if target_post_div_product_int == 0 {
579 continue;
580 }
581 734
582 // Check if the rounded product gives the target frequency 735 for fbdiv in (16..=320).rev() {
583 if vco_freq / target_post_div_product_int != target_hz { 736 let vco_freq = reference_freq * fbdiv;
584 continue;
585 }
586 737
587 for post_div1 in (1..=7).rev() { 738 if vco_freq < 750_000_000 || vco_freq > 1_800_000_000 {
588 // Iterate high post_div1 first 739 continue;
589 if target_post_div_product_int % post_div1 == 0 { 740 }
590 let post_div2 = target_post_div_product_int / post_div1; 741
591 if (1..=7).contains(&post_div2) && post_div2 <= post_div1 { 742 for post_div1 in (1..=7).rev() {
592 // Found a valid combination 743 for post_div2 in (1..=post_div1).rev() {
593 return Some(PllConfig { 744 let out_freq = vco_freq / (post_div1 * post_div2) as u32;
594 refdiv: refdiv as u8, // Cast u32 to u8 (safe: 1..=63) 745 let diff = if out_freq > target_hz {
595 fbdiv: fbdiv as u16, // Cast u32 to u16 (safe: 16..=320) 746 out_freq - target_hz
596 post_div1: post_div1 as u8, 747 } else {
597 post_div2: post_div2 as u8, 748 target_hz - out_freq
598 }); 749 };
599 } 750
751 // If this is closer to the target, save it
752 if diff < min_diff {
753 min_diff = diff;
754 best_config = Some(PllConfig {
755 refdiv: PLL_SYS_REFDIV,
756 fbdiv: fbdiv as u16,
757 post_div1: post_div1 as u8,
758 post_div2: post_div2 as u8,
759 });
600 } 760 }
601 } 761 }
602 } 762 }
603 } 763 }
604 764
605 None // No valid parameters found 765 // Return the closest match if we found one
766 best_config
767}
768
769#[cfg(feature = "rp2040")]
770pub fn compare_pll_params() {
771 // Parameters from default configuration
772 let default_params = PllConfig {
773 refdiv: 1,
774 fbdiv: 125,
775 post_div1: 6,
776 post_div2: 2,
777 };
778
779 // Calculate parameters using our find_pll_params function
780 let crystal_hz = 12_000_000;
781 let target_hz = 125_000_000;
782 let calculated_params = find_pll_params(crystal_hz, target_hz).expect("Failed to find PLL parameters");
783
784 // Check if they're identical
785 let params_match = default_params.refdiv == calculated_params.refdiv
786 && default_params.fbdiv == calculated_params.fbdiv
787 && default_params.post_div1 == calculated_params.post_div1
788 && default_params.post_div2 == calculated_params.post_div2;
789
790 // Here we'd normally print results, but without a console we'll just
791 // use this for debugging in our IDE
792 let _default_output_freq = crystal_hz / default_params.refdiv as u32 * default_params.fbdiv as u32
793 / (default_params.post_div1 * default_params.post_div2) as u32;
794
795 let _calculated_output_freq = crystal_hz / calculated_params.refdiv as u32 * calculated_params.fbdiv as u32
796 / (calculated_params.post_div1 * calculated_params.post_div2) as u32;
797
798 // Parameters: default vs calculated
799 // refdiv: 1 vs {calculated_params.refdiv}
800 // fbdiv: 125 vs {calculated_params.fbdiv}
801 // post_div1: 6 vs {calculated_params.post_div1}
802 // post_div2: 2 vs {calculated_params.post_div2}
803 // params_match: {params_match}
606} 804}
607 805
608/// safety: must be called exactly once at bootup 806/// safety: must be called exactly once at bootup
@@ -640,12 +838,42 @@ pub(crate) unsafe fn init(config: ClockConfig) {
640 #[cfg(feature = "_rp235x")] 838 #[cfg(feature = "_rp235x")]
641 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {} 839 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {}
642 840
643 // Reset the PLLs 841 // Set Core Voltage (RP2040 only) BEFORE doing anything with PLLs
644 let mut peris = reset::Peripherals(0); 842 // This is critical for overclocking - must be done before PLL setup
645 peris.set_pll_sys(true); 843 #[cfg(feature = "rp2040")]
646 peris.set_pll_usb(true); 844 if let Some(voltage) = config.voltage_scale {
647 reset::reset(peris); 845 let vreg = pac::VREG_AND_CHIP_RESET;
648 reset::unreset_wait(peris); 846 let current_vsel = vreg.vreg().read().vsel();
847 let target_vsel = voltage as u8;
848
849 if target_vsel != current_vsel {
850 // IMPORTANT: Use modify() instead of write() to preserve the HIZ and EN bits
851 // This is critical - otherwise we might disable the regulator when changing voltage
852 vreg.vreg().modify(|w| w.set_vsel(target_vsel));
853
854 // For higher voltage settings (overclocking), we need a longer stabilization time
855 // Default to 1000 µs (1ms) like the SDK, but allow user override
856 let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| {
857 match voltage {
858 VoltageScale::V1_15 => 1000, // 1ms for 1.15V (matches SDK default)
859 VoltageScale::V1_20 | VoltageScale::V1_25 | VoltageScale::V1_30 => 2000, // 2ms for higher voltages
860 _ => 500, // 500 µs for standard voltages
861 }
862 });
863
864 // We need a clock that's guaranteed to be running at this point
865 // Use ROSC which should be configured by now
866 let rosc_freq_rough = 6_000_000; // Rough ROSC frequency estimate
867 let cycles_per_us = rosc_freq_rough / 1_000_000;
868 let delay_cycles = settling_time_us * cycles_per_us;
869
870 // Wait for voltage to stabilize
871 cortex_m::asm::delay(delay_cycles);
872
873 // Only NOW set the BOD level after voltage has stabilized
874 vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod()));
875 }
876 }
649 877
650 // Configure ROSC first if present 878 // Configure ROSC first if present
651 let rosc_freq = match config.rosc { 879 let rosc_freq = match config.rosc {
@@ -654,59 +882,91 @@ pub(crate) unsafe fn init(config: ClockConfig) {
654 }; 882 };
655 CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed); 883 CLOCKS.rosc.store(rosc_freq, Ordering::Relaxed);
656 884
657 // Configure XOSC and PLLs if present 885 // Configure XOSC - we'll need this for our temporary stable clock
658 let (xosc_freq, pll_sys_freq, pll_usb_freq) = match config.xosc { 886 let xosc_freq = match &config.xosc {
659 Some(config) => { 887 Some(config) => {
660 // start XOSC
661 // datasheet mentions support for clock inputs into XIN, but doesn't go into
662 // how this is achieved. pico-sdk doesn't support this at all.
663 start_xosc(config.hz, config.delay_multiplier); 888 start_xosc(config.hz, config.delay_multiplier);
664 889 config.hz
665 let pll_sys_freq = match config.sys_pll {
666 Some(sys_pll_config) => configure_pll(pac::PLL_SYS, config.hz, sys_pll_config),
667 None => 0,
668 };
669 let pll_usb_freq = match config.usb_pll {
670 Some(usb_pll_config) => configure_pll(pac::PLL_USB, config.hz, usb_pll_config),
671 None => 0,
672 };
673
674 (config.hz, pll_sys_freq, pll_usb_freq)
675 } 890 }
676 None => (0, 0, 0), 891 None => 0,
677 }; 892 };
678 CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed); 893 CLOCKS.xosc.store(xosc_freq, Ordering::Relaxed);
679 CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed); 894
680 CLOCKS.pll_usb.store(pll_usb_freq, Ordering::Relaxed); 895 // SETUP TEMPORARY STABLE CLOCKS FIRST
681 896 // Configure USB PLL for our stable temporary clock
682 // Configure REF clock source and divider 897 // This follows the SDK's approach of using USB PLL as a stable intermediate clock
683 let (ref_src, ref_aux, clk_ref_freq) = { 898 let usb_pll_freq = match &config.xosc {
684 use {ClkRefCtrlAuxsrc as Aux, ClkRefCtrlSrc as Src}; 899 Some(config) => match &config.usb_pll {
685 let div = config.ref_clk.div as u32; 900 Some(usb_pll_config) => {
686 assert!(div >= 1 && div <= 4); 901 // Reset USB PLL
687 match config.ref_clk.src { 902 let mut peris = reset::Peripherals(0);
688 RefClkSrc::Xosc => (Src::XOSC_CLKSRC, Aux::CLKSRC_PLL_USB, xosc_freq / div), 903 peris.set_pll_usb(true);
689 RefClkSrc::Rosc => (Src::ROSC_CLKSRC_PH, Aux::CLKSRC_PLL_USB, rosc_freq / div), 904 reset::reset(peris);
690 RefClkSrc::PllUsb => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq / div), 905 reset::unreset_wait(peris);
691 // RefClkSrc::Gpin0 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN0, gpin0_freq / div), 906
692 // RefClkSrc::Gpin1 => (Src::CLKSRC_CLK_REF_AUX, Aux::CLKSRC_GPIN1, gpin1_freq / div), 907 // Configure the USB PLL - this should give us 48MHz
693 } 908 let usb_pll_freq = configure_pll(pac::PLL_USB, xosc_freq, *usb_pll_config);
909 CLOCKS.pll_usb.store(usb_pll_freq, Ordering::Relaxed);
910 usb_pll_freq
911 }
912 None => 0,
913 },
914 None => 0,
694 }; 915 };
695 assert!(clk_ref_freq != 0); 916
696 CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed); 917 // Configure REF clock to use XOSC
697 c.clk_ref_ctrl().write(|w| { 918 c.clk_ref_ctrl().write(|w| {
698 w.set_src(ref_src); 919 w.set_src(ClkRefCtrlSrc::XOSC_CLKSRC);
699 w.set_auxsrc(ref_aux);
700 }); 920 });
701 #[cfg(feature = "rp2040")] 921 #[cfg(feature = "rp2040")]
702 while c.clk_ref_selected().read() != (1 << ref_src as u32) {} 922 while c.clk_ref_selected().read() != (1 << ClkRefCtrlSrc::XOSC_CLKSRC as u32) {}
703 #[cfg(feature = "_rp235x")] 923 #[cfg(feature = "_rp235x")]
704 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ref_src as u32) {} 924 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1 << ClkRefCtrlSrc::XOSC_CLKSRC as u32) {}
705 c.clk_ref_div().write(|w| { 925
706 w.set_int(config.ref_clk.div); 926 // First switch the system clock to a stable source (USB PLL at 48MHz)
927 // This follows the Pico SDK's approach to ensure stability during reconfiguration
928 c.clk_sys_ctrl().write(|w| {
929 w.set_auxsrc(ClkSysCtrlAuxsrc::CLKSRC_PLL_USB);
930 w.set_src(ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX);
707 }); 931 });
708 932
933 #[cfg(feature = "rp2040")]
934 while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX as u32) {}
935 #[cfg(feature = "_rp235x")]
936 while c.clk_sys_selected().read()
937 != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX as u32)
938 {}
939
940 // Short delay after switching to USB PLL to ensure stability
941 cortex_m::asm::delay(100);
942
943 // NOW CONFIGURE THE SYSTEM PLL (safely, since we're running from the USB PLL)
944 let pll_sys_freq = match &config.xosc {
945 Some(config) => match &config.sys_pll {
946 Some(sys_pll_config) => {
947 // Reset SYS PLL
948 let mut peris = reset::Peripherals(0);
949 peris.set_pll_sys(true);
950 reset::reset(peris);
951 reset::unreset_wait(peris);
952
953 // Configure the SYS PLL
954 let pll_sys_freq = configure_pll(pac::PLL_SYS, xosc_freq, *sys_pll_config);
955
956 // Ensure PLL is locked and stable
957 cortex_m::asm::delay(100);
958
959 CLOCKS.pll_sys.store(pll_sys_freq, Ordering::Relaxed);
960 pll_sys_freq
961 }
962 None => 0,
963 },
964 None => 0,
965 };
966
709 // Configure tick generation using REF clock 967 // Configure tick generation using REF clock
968 let clk_ref_freq = xosc_freq; // REF clock is now XOSC
969 CLOCKS.reference.store(clk_ref_freq, Ordering::Relaxed);
710 #[cfg(feature = "rp2040")] 970 #[cfg(feature = "rp2040")]
711 pac::WATCHDOG.tick().write(|w| { 971 pac::WATCHDOG.tick().write(|w| {
712 w.set_cycles((clk_ref_freq / 1_000_000) as u16); 972 w.set_cycles((clk_ref_freq / 1_000_000) as u16);
@@ -724,34 +984,14 @@ pub(crate) unsafe fn init(config: ClockConfig) {
724 pac::TICKS.watchdog_ctrl().write(|w| w.set_enable(true)); 984 pac::TICKS.watchdog_ctrl().write(|w| w.set_enable(true));
725 } 985 }
726 986
727 // Set Core Voltage (RP2040 only) BEFORE switching SYS clock to high speed PLL 987 // NOW SWITCH THE SYSTEM CLOCK TO THE CONFIGURED SOURCE
728 #[cfg(feature = "rp2040")] 988 // The SYS PLL is now stable and we can safely switch to it
729 if let Some(voltage) = config.voltage_scale {
730 let vreg = pac::VREG_AND_CHIP_RESET;
731 let current_vsel = vreg.vreg().read().vsel();
732 let target_vsel = voltage as u8;
733
734 if target_vsel != current_vsel {
735 // Set voltage and recommended BOD level
736 vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod()));
737 vreg.vreg().write(|w| w.set_vsel(target_vsel));
738
739 // Wait 10us for regulator to settle. Delay calculation uses REF clock
740 // as it's guaranteed to be stable here, before SYS potentially switches.
741 // 10 us = 1/100_000 s. cycles = freq * time.
742 let delay_cycles = clk_ref_freq / 100_000;
743 // delay(N) waits N+1 cycles.
744 cortex_m::asm::delay(delay_cycles.saturating_sub(1));
745 }
746 }
747
748 // Configure SYS clock source and divider
749 let (sys_src, sys_aux, clk_sys_freq) = { 989 let (sys_src, sys_aux, clk_sys_freq) = {
750 use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src}; 990 use {ClkSysCtrlAuxsrc as Aux, ClkSysCtrlSrc as Src};
751 let (src, aux, freq) = match config.sys_clk.src { 991 let (src, aux, freq) = match config.sys_clk.src {
752 SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq), 992 SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq),
753 SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq), 993 SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq),
754 SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq), 994 SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, usb_pll_freq),
755 SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), 995 SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq),
756 SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), 996 SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq),
757 // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), 997 // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq),
@@ -762,28 +1002,48 @@ pub(crate) unsafe fn init(config: ClockConfig) {
762 }; 1002 };
763 assert!(clk_sys_freq != 0); 1003 assert!(clk_sys_freq != 0);
764 CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed); 1004 CLOCKS.sys.store(clk_sys_freq, Ordering::Relaxed);
765 if sys_src != ClkSysCtrlSrc::CLK_REF { 1005
766 c.clk_sys_ctrl().write(|w| w.set_src(ClkSysCtrlSrc::CLK_REF)); 1006 // Set the divider before changing the source if it's increasing
767 #[cfg(feature = "rp2040")] 1007 if config.sys_clk.div_int > 1 || config.sys_clk.div_frac > 0 {
768 while c.clk_sys_selected().read() != (1 << ClkSysCtrlSrc::CLK_REF as u32) {} 1008 c.clk_sys_div().write(|w| {
769 #[cfg(feature = "_rp235x")] 1009 w.set_int(config.sys_clk.div_int);
770 while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << ClkSysCtrlSrc::CLK_REF as u32) {} 1010 w.set_frac(config.sys_clk.div_frac);
1011 });
1012 }
1013
1014 // Configure aux source first if needed
1015 if sys_src == ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX {
1016 c.clk_sys_ctrl().modify(|w| {
1017 w.set_auxsrc(sys_aux);
1018 });
771 } 1019 }
1020
1021 // Now set the source
772 c.clk_sys_ctrl().write(|w| { 1022 c.clk_sys_ctrl().write(|w| {
773 w.set_auxsrc(sys_aux); 1023 if sys_src == ClkSysCtrlSrc::CLKSRC_CLK_SYS_AUX {
1024 w.set_auxsrc(sys_aux);
1025 }
774 w.set_src(sys_src); 1026 w.set_src(sys_src);
775 }); 1027 });
776 1028
1029 // Wait for the clock to be selected
777 #[cfg(feature = "rp2040")] 1030 #[cfg(feature = "rp2040")]
778 while c.clk_sys_selected().read() != (1 << sys_src as u32) {} 1031 while c.clk_sys_selected().read() != (1 << sys_src as u32) {}
779 #[cfg(feature = "_rp235x")] 1032 #[cfg(feature = "_rp235x")]
780 while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << sys_src as u32) {} 1033 while c.clk_sys_selected().read() != pac::clocks::regs::ClkSysSelected(1 << sys_src as u32) {}
781 1034
782 c.clk_sys_div().write(|w| { 1035 // Short delay after final clock switch to ensure stability
783 w.set_int(config.sys_clk.div_int); 1036 cortex_m::asm::delay(100);
784 w.set_frac(config.sys_clk.div_frac);
785 });
786 1037
1038 // Set the divider after changing the source if it's decreasing
1039 if config.sys_clk.div_int == 1 && config.sys_clk.div_frac == 0 {
1040 c.clk_sys_div().write(|w| {
1041 w.set_int(config.sys_clk.div_int);
1042 w.set_frac(config.sys_clk.div_frac);
1043 });
1044 }
1045
1046 // CONFIGURE PERIPHERAL CLOCK
787 let mut peris = reset::ALL_PERIPHERALS; 1047 let mut peris = reset::ALL_PERIPHERALS;
788 1048
789 if let Some(src) = config.peri_clk_src { 1049 if let Some(src) = config.peri_clk_src {
@@ -794,7 +1054,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
794 let peri_freq = match src { 1054 let peri_freq = match src {
795 PeriClkSrc::Sys => clk_sys_freq, 1055 PeriClkSrc::Sys => clk_sys_freq,
796 PeriClkSrc::PllSys => pll_sys_freq, 1056 PeriClkSrc::PllSys => pll_sys_freq,
797 PeriClkSrc::PllUsb => pll_usb_freq, 1057 PeriClkSrc::PllUsb => usb_pll_freq,
798 PeriClkSrc::Rosc => rosc_freq, 1058 PeriClkSrc::Rosc => rosc_freq,
799 PeriClkSrc::Xosc => xosc_freq, 1059 PeriClkSrc::Xosc => xosc_freq,
800 // PeriClkSrc::Gpin0 => gpin0_freq, 1060 // PeriClkSrc::Gpin0 => gpin0_freq,
@@ -810,6 +1070,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
810 CLOCKS.peri.store(0, Ordering::Relaxed); 1070 CLOCKS.peri.store(0, Ordering::Relaxed);
811 } 1071 }
812 1072
1073 // CONFIGURE USB CLOCK
813 if let Some(conf) = config.usb_clk { 1074 if let Some(conf) = config.usb_clk {
814 c.clk_usb_div().write(|w| w.set_int(conf.div)); 1075 c.clk_usb_div().write(|w| w.set_int(conf.div));
815 c.clk_usb_ctrl().write(|w| { 1076 c.clk_usb_ctrl().write(|w| {
@@ -818,7 +1079,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
818 w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _)); 1079 w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _));
819 }); 1080 });
820 let usb_freq = match conf.src { 1081 let usb_freq = match conf.src {
821 UsbClkSrc::PllUsb => pll_usb_freq, 1082 UsbClkSrc::PllUsb => usb_pll_freq,
822 UsbClkSrc::PllSys => pll_sys_freq, 1083 UsbClkSrc::PllSys => pll_sys_freq,
823 UsbClkSrc::Rosc => rosc_freq, 1084 UsbClkSrc::Rosc => rosc_freq,
824 UsbClkSrc::Xosc => xosc_freq, 1085 UsbClkSrc::Xosc => xosc_freq,
@@ -833,6 +1094,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
833 CLOCKS.usb.store(0, Ordering::Relaxed); 1094 CLOCKS.usb.store(0, Ordering::Relaxed);
834 } 1095 }
835 1096
1097 // CONFIGURE ADC CLOCK
836 if let Some(conf) = config.adc_clk { 1098 if let Some(conf) = config.adc_clk {
837 c.clk_adc_div().write(|w| w.set_int(conf.div)); 1099 c.clk_adc_div().write(|w| w.set_int(conf.div));
838 c.clk_adc_ctrl().write(|w| { 1100 c.clk_adc_ctrl().write(|w| {
@@ -841,7 +1103,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
841 w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _)); 1103 w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _));
842 }); 1104 });
843 let adc_in_freq = match conf.src { 1105 let adc_in_freq = match conf.src {
844 AdcClkSrc::PllUsb => pll_usb_freq, 1106 AdcClkSrc::PllUsb => usb_pll_freq,
845 AdcClkSrc::PllSys => pll_sys_freq, 1107 AdcClkSrc::PllSys => pll_sys_freq,
846 AdcClkSrc::Rosc => rosc_freq, 1108 AdcClkSrc::Rosc => rosc_freq,
847 AdcClkSrc::Xosc => xosc_freq, 1109 AdcClkSrc::Xosc => xosc_freq,
@@ -856,7 +1118,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
856 CLOCKS.adc.store(0, Ordering::Relaxed); 1118 CLOCKS.adc.store(0, Ordering::Relaxed);
857 } 1119 }
858 1120
859 // rp2040 specific clocks 1121 // CONFIGURE RTC CLOCK
860 #[cfg(feature = "rp2040")] 1122 #[cfg(feature = "rp2040")]
861 if let Some(conf) = config.rtc_clk { 1123 if let Some(conf) = config.rtc_clk {
862 c.clk_rtc_div().write(|w| { 1124 c.clk_rtc_div().write(|w| {
@@ -869,7 +1131,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
869 w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _)); 1131 w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _));
870 }); 1132 });
871 let rtc_in_freq = match conf.src { 1133 let rtc_in_freq = match conf.src {
872 RtcClkSrc::PllUsb => pll_usb_freq, 1134 RtcClkSrc::PllUsb => usb_pll_freq,
873 RtcClkSrc::PllSys => pll_sys_freq, 1135 RtcClkSrc::PllSys => pll_sys_freq,
874 RtcClkSrc::Rosc => rosc_freq, 1136 RtcClkSrc::Rosc => rosc_freq,
875 RtcClkSrc::Xosc => xosc_freq, 1137 RtcClkSrc::Xosc => xosc_freq,
@@ -999,43 +1261,101 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
999 1261
1000#[inline(always)] 1262#[inline(always)]
1001fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { 1263fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 {
1264 // Calculate reference frequency
1002 let ref_freq = input_freq / config.refdiv as u32; 1265 let ref_freq = input_freq / config.refdiv as u32;
1003 assert!(config.fbdiv >= 16 && config.fbdiv <= 320); 1266
1004 assert!(config.post_div1 >= 1 && config.post_div1 <= 7); 1267 // Validate PLL parameters
1005 assert!(config.post_div2 >= 1 && config.post_div2 <= 7); 1268 assert!(
1006 assert!(config.refdiv >= 1 && config.refdiv <= 63); 1269 config.fbdiv >= 16 && config.fbdiv <= 320,
1007 assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000); 1270 "fbdiv must be between 16 and 320"
1271 );
1272 assert!(
1273 config.post_div1 >= 1 && config.post_div1 <= 7,
1274 "post_div1 must be between 1 and 7"
1275 );
1276 assert!(
1277 config.post_div2 >= 1 && config.post_div2 <= 7,
1278 "post_div2 must be between 1 and 7"
1279 );
1280 assert!(config.post_div2 <= config.post_div1, "post_div2 must be <= post_div1");
1281 assert!(
1282 config.refdiv >= 1 && config.refdiv <= 63,
1283 "refdiv must be between 1 and 63"
1284 );
1285 assert!(
1286 ref_freq >= 5_000_000 && ref_freq <= 800_000_000,
1287 "ref_freq must be between 5MHz and 800MHz"
1288 );
1289
1290 // Calculate VCO frequency
1008 let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32); 1291 let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32);
1009 assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); 1292 assert!(
1293 vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000,
1294 "VCO frequency must be between 750MHz and 1800MHz"
1295 );
1296
1297 // We follow the SDK's approach to PLL configuration which is:
1298 // 1. Power down PLL
1299 // 2. Configure the reference divider
1300 // 3. Configure the feedback divider
1301 // 4. Power up PLL and VCO
1302 // 5. Wait for PLL to lock
1303 // 6. Configure post-dividers
1304 // 7. Enable post-divider output
1305
1306 // 1. Power down PLL before configuration
1307 p.pwr().write(|w| {
1308 w.set_pd(true); // Power down the PLL
1309 w.set_vcopd(true); // Power down the VCO
1310 w.set_postdivpd(true); // Power down the post divider
1311 w.set_dsmpd(true); // Disable fractional mode
1312 *w
1313 });
1314
1315 // Short delay after powering down
1316 cortex_m::asm::delay(10);
1010 1317
1011 // Load VCO-related dividers before starting VCO 1318 // 2. Configure reference divider first
1012 p.cs().write(|w| w.set_refdiv(config.refdiv as _)); 1319 p.cs().write(|w| w.set_refdiv(config.refdiv as _));
1320
1321 // 3. Configure feedback divider
1013 p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv)); 1322 p.fbdiv_int().write(|w| w.set_fbdiv_int(config.fbdiv));
1014 1323
1015 // Turn on PLL 1324 // 4. Power up PLL and VCO, but keep post divider powered down during initial lock
1016 let pwr = p.pwr().write(|w| { 1325 p.pwr().write(|w| {
1017 w.set_dsmpd(true); // "nothing is achieved by setting this low" 1326 w.set_pd(false); // Power up the PLL
1018 w.set_pd(false); 1327 w.set_vcopd(false); // Power up the VCO
1019 w.set_vcopd(false); 1328 w.set_postdivpd(true); // Keep post divider powered down during initial lock
1020 w.set_postdivpd(true); 1329 w.set_dsmpd(true); // Disable fractional mode (simpler configuration)
1021 *w 1330 *w
1022 }); 1331 });
1023 1332
1024 // Wait for PLL to lock 1333 // 5. Wait for PLL to lock with a timeout
1025 while !p.cs().read().lock() {} 1334 let mut timeout = 1_000_000; // Reasonable timeout value
1335 while !p.cs().read().lock() {
1336 timeout -= 1;
1337 if timeout == 0 {
1338 // PLL failed to lock, return 0 to indicate failure
1339 return 0;
1340 }
1341 }
1026 1342
1027 // Set post-dividers 1343 // 6. Configure post dividers after PLL is locked
1028 p.prim().write(|w| { 1344 p.prim().write(|w| {
1029 w.set_postdiv1(config.post_div1); 1345 w.set_postdiv1(config.post_div1);
1030 w.set_postdiv2(config.post_div2); 1346 w.set_postdiv2(config.post_div2);
1031 }); 1347 });
1032 1348
1033 // Turn on post divider 1349 // 7. Enable the post divider output
1034 p.pwr().write(|w| { 1350 p.pwr().modify(|w| {
1035 *w = pwr; 1351 w.set_postdivpd(false); // Power up post divider
1036 w.set_postdivpd(false); 1352 *w
1037 }); 1353 });
1038 1354
1355 // Final delay to ensure everything is stable
1356 cortex_m::asm::delay(100);
1357
1358 // Calculate and return actual output frequency
1039 vco_freq / ((config.post_div1 * config.post_div2) as u32) 1359 vco_freq / ((config.post_div1 * config.post_div2) as u32)
1040} 1360}
1041 1361
diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs
index 429fff1ac..db6c8f448 100644
--- a/examples/rp/src/bin/overclock.rs
+++ b/examples/rp/src/bin/overclock.rs
@@ -3,22 +3,18 @@
3 3
4use defmt::*; 4use defmt::*;
5use embassy_executor::Spawner; 5use embassy_executor::Spawner;
6use embassy_rp::clocks::{clk_sys_freq, ClockConfig}; 6use embassy_rp::clocks::{clk_sys_freq, ClockConfig, VoltageScale};
7use embassy_rp::config::Config; 7use embassy_rp::config::Config;
8use embassy_rp::gpio::{Level, Output}; 8use embassy_rp::gpio::{Level, Output};
9use embassy_time::{Duration, Instant, Timer}; 9use embassy_time::{Duration, Instant, Timer};
10use {defmt_rtt as _, panic_probe as _}; 10use {defmt_rtt as _, panic_probe as _};
11 11
12const COUNT_TO: i32 = 1_000_000; 12const COUNT_TO: i64 = 10_000_000;
13 13
14#[embassy_executor::main] 14#[embassy_executor::main]
15async fn main(_spawner: Spawner) -> ! { 15async fn main(_spawner: Spawner) -> ! {
16 // Set up for clock frequency of 200 MHz 16 // Set up for clock frequency of 200 MHz
17 // We will need a clock config in the HAL config that supports this frequency 17 let config = Config::new(ClockConfig::with_speed_mhz(200));
18 // The RP2040 can run at 200 MHz with a 12 MHz crystal
19 let config = Config::new(ClockConfig::crystal_freq(12_000_000, 200_000_000));
20
21 // Initialize the peripherals
22 let p = embassy_rp::init(config); 18 let p = embassy_rp::init(config);
23 19
24 // Show CPU frequency for verification 20 // Show CPU frequency for verification
@@ -37,20 +33,19 @@ async fn main(_spawner: Spawner) -> ! {
37 33
38 let start = Instant::now(); 34 let start = Instant::now();
39 35
40 // Count to COUNT_TO
41 // This is a busy loop that will take some time to complete 36 // This is a busy loop that will take some time to complete
42 while counter < COUNT_TO { 37 while counter < COUNT_TO {
43 counter += 1; 38 counter += 1;
44 } 39 }
45 40
46 let elapsed = start - Instant::now(); 41 let elapsed = Instant::now() - start;
47 42
48 // Report the elapsed time 43 // Report the elapsed time
49 led.set_low(); 44 led.set_low();
50 info!( 45 info!(
51 "At {}Mhz: Elapsed time to count to {}: {}ms", 46 "At {}Mhz: Elapsed time to count to {}: {}ms",
52 sys_freq / 1_000_000, 47 sys_freq / 1_000_000,
53 COUNT_TO, 48 counter,
54 elapsed.as_millis() 49 elapsed.as_millis()
55 ); 50 );
56 51
@@ -58,3 +53,14 @@ async fn main(_spawner: Spawner) -> ! {
58 Timer::after(Duration::from_secs(2)).await; 53 Timer::after(Duration::from_secs(2)).await;
59 } 54 }
60} 55}
56
57// let config = Config::new(ClockConfig::with_speed_mhz_test_voltage(125, Some(VoltageScale::V1_10)));
58// let config = Config::default();
59// let config = Config::new(ClockConfig::with_speed_mhz_test_voltage_extended_delay(
60// 200, // Standard 125MHz clock
61// Some(VoltageScale::V1_15), // 1.15V voltage
62// Some(1000), // 1000μs (1ms) stabilization delay - significantly longer than default
63// ));
64// Initialize the peripherals
65
66// let p = embassy_rp::init(Default::default()); //testing the bog standard