aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-rp/src/clocks.rs268
-rw-r--r--examples/rp/src/bin/overclock.rs24
-rw-r--r--examples/rp/src/bin/overclock_manual.rs81
3 files changed, 227 insertions, 146 deletions
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 1f5c27df1..86c172879 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -147,56 +147,30 @@ pub enum PeriClkSrc {
147/// Core voltage scaling options for RP2040. 147/// Core voltage scaling options for RP2040.
148/// 148///
149/// The RP2040 voltage regulator can be configured for different output voltages. 149/// The RP2040 voltage regulator can be configured for different output voltages.
150/// Higher voltages allow for higher clock frequencies but increase power consumption. 150/// Higher voltages allow for higher clock frequencies but increase power consumption and heat.
151///
152/// # Typical Use Cases
153///
154/// - `V0_85` to `V1_05`: Power saving for lower frequencies (below 100MHz)
155/// - `V1_10`: Default voltage, safe for standard 125MHz operation
156/// - `V1_15`: Required for frequencies above 133MHz (e.g., 200MHz overclocking)
157/// - `V1_20`: For more extreme overclocking (200MHz+)
158/// - `V1_25` and `V1_30`: Highest voltage settings, use with caution
159///
160/// # Overclocking Notes
161///
162/// When overclocking:
163/// - Frequencies up to 133MHz are typically stable at default voltage (`V1_10`)
164/// - Frequencies from 133MHz to 200MHz generally require `V1_15`
165/// - Frequencies above 200MHz typically require `V1_20` or higher
166///
167/// # Power Consumption
168///
169/// Higher voltages increase power consumption and heat generation. In battery-powered
170/// applications, consider using lower voltages when maximum performance is not required.
171///
172/// # Safety
173///
174/// Increased voltage can reduce the lifespan of the chip if used for extended periods,
175/// especially at `V1_25` and `V1_30`. These higher voltages should be used with
176/// consideration of thermal management.
177#[cfg(feature = "rp2040")] 151#[cfg(feature = "rp2040")]
178#[derive(Clone, Copy, Debug, PartialEq, Eq)] 152#[derive(Clone, Copy, Debug, PartialEq, Eq)]
179#[repr(u8)] 153#[repr(u8)]
180pub enum VoltageScale { 154pub enum VoltageScale {
181 /// 0.85V - Lowest power consumption, suitable for low frequencies 155 /// 0.85V
182 V0_85 = 0b0110, 156 V0_85 = 0b0110,
183 /// 0.90V - Low power consumption 157 /// 0.90V
184 V0_90 = 0b0111, 158 V0_90 = 0b0111,
185 /// 0.95V - Low power consumption 159 /// 0.95V
186 V0_95 = 0b1000, 160 V0_95 = 0b1000,
187 /// 1.00V - Medium power consumption 161 /// 1.00V
188 V1_00 = 0b1001, 162 V1_00 = 0b1001,
189 /// 1.05V - Medium power consumption 163 /// 1.05V
190 V1_05 = 0b1010, 164 V1_05 = 0b1010,
191 /// 1.10V (Default) - Standard voltage for 125MHz operation 165 /// 1.10V
192 V1_10 = 0b1011, 166 V1_10 = 0b1011,
193 /// 1.15V - Required for frequencies above 133MHz 167 /// 1.15V
194 V1_15 = 0b1100, 168 V1_15 = 0b1100,
195 /// 1.20V - For higher overclocking (200MHz+) 169 /// 1.20V
196 V1_20 = 0b1101, 170 V1_20 = 0b1101,
197 /// 1.25V - High voltage, use with caution 171 /// 1.25V
198 V1_25 = 0b1110, 172 V1_25 = 0b1110,
199 /// 1.30V - Maximum voltage, use with extreme caution 173 /// 1.30V
200 V1_30 = 0b1111, 174 V1_30 = 0b1111,
201} 175}
202 176
@@ -204,10 +178,8 @@ pub enum VoltageScale {
204impl VoltageScale { 178impl VoltageScale {
205 /// Get the recommended Brown-Out Detection (BOD) setting for this voltage. 179 /// Get the recommended Brown-Out Detection (BOD) setting for this voltage.
206 /// Sets the BOD threshold to approximately 90% of the core voltage. 180 /// Sets the BOD threshold to approximately 90% of the core voltage.
207 /// See RP2040 Datasheet, Table 190, BOD Register
208 fn recommended_bod(self) -> u8 { 181 fn recommended_bod(self) -> u8 {
209 match self { 182 match self {
210 // ~90% of voltage setting based on Table 190 values
211 VoltageScale::V0_85 => 0b0111, // 0.774V (~91% of 0.85V) 183 VoltageScale::V0_85 => 0b0111, // 0.774V (~91% of 0.85V)
212 VoltageScale::V0_90 => 0b1000, // 0.817V (~91% of 0.90V) 184 VoltageScale::V0_90 => 0b1000, // 0.817V (~91% of 0.90V)
213 VoltageScale::V0_95 => 0b1001, // 0.860V (~91% of 0.95V) 185 VoltageScale::V0_95 => 0b1001, // 0.860V (~91% of 0.95V)
@@ -246,7 +218,7 @@ pub struct ClockConfig {
246 #[cfg(feature = "rp2040")] 218 #[cfg(feature = "rp2040")]
247 pub voltage_scale: Option<VoltageScale>, 219 pub voltage_scale: Option<VoltageScale>,
248 /// Voltage stabilization delay in microseconds. 220 /// Voltage stabilization delay in microseconds.
249 /// If not set, appropriate defaults will be used based on voltage level. 221 /// If not set, defaults will be used based on voltage level.
250 #[cfg(feature = "rp2040")] 222 #[cfg(feature = "rp2040")]
251 pub voltage_stabilization_delay_us: Option<u32>, 223 pub voltage_stabilization_delay_us: Option<u32>,
252 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, 224 // gpin0: Option<(u32, Gpin<'static, AnyPin>)>,
@@ -409,7 +381,7 @@ impl ClockConfig {
409 381
410 /// Configure the system clock to a specific frequency in MHz. 382 /// Configure the system clock to a specific frequency in MHz.
411 /// 383 ///
412 /// This is a more user-friendly way to configure the system clock, similar to 384 /// This is a user-friendly way to configure the system clock, similar to
413 /// the Pico SDK's approach. It automatically handles voltage scaling based on the 385 /// the Pico SDK's approach. It automatically handles voltage scaling based on the
414 /// requested frequency and uses the standard 12MHz crystal found on most RP2040 boards. 386 /// requested frequency and uses the standard 12MHz crystal found on most RP2040 boards.
415 /// 387 ///
@@ -420,9 +392,6 @@ impl ClockConfig {
420 /// # Example 392 /// # Example
421 /// 393 ///
422 /// ``` 394 /// ```
423 /// // Configure for standard 125MHz clock
424 /// let config = ClockConfig::at_sys_frequency_mhz(125);
425 ///
426 /// // Overclock to 200MHz 395 /// // Overclock to 200MHz
427 /// let config = ClockConfig::at_sys_frequency_mhz(200); 396 /// let config = ClockConfig::at_sys_frequency_mhz(200);
428 /// ``` 397 /// ```
@@ -551,6 +520,98 @@ impl ClockConfig {
551 voltage_stabilization_delay_us: None, 520 voltage_stabilization_delay_us: None,
552 } 521 }
553 } 522 }
523
524 /// Configure with manual PLL settings for full control over system clock
525 ///
526 /// This method provides a simple way to configure the system with custom PLL parameters
527 /// without needing to understand the full nested configuration structure.
528 ///
529 /// # Arguments
530 ///
531 /// * `xosc_hz` - The frequency of the external crystal in Hz
532 /// * `pll_config` - The PLL configuration parameters to achieve desired frequency
533 /// * `voltage_scale` - Optional voltage scaling for overclocking (required for >133MHz)
534 ///
535 /// # Returns
536 ///
537 /// A ClockConfig configured with the specified PLL parameters
538 ///
539 /// # Example
540 ///
541 /// ```rust,ignore
542 /// // Configure for 200MHz operation
543 /// let config = Config::default();
544 /// config.clocks = ClockConfig::manual_pll(
545 /// 12_000_000,
546 /// PllConfig {
547 /// refdiv: 1, // Reference divider (12 MHz / 1 = 12 MHz)
548 /// fbdiv: 100, // Feedback divider (12 MHz * 100 = 1200 MHz VCO)
549 /// post_div1: 3, // First post divider (1200 MHz / 3 = 400 MHz)
550 /// post_div2: 2, // Second post divider (400 MHz / 2 = 200 MHz)
551 /// },
552 /// Some(VoltageScale::V1_15)
553 /// );
554 /// ```
555 #[cfg(feature = "rp2040")]
556 pub fn manual_pll(xosc_hz: u32, pll_config: PllConfig, voltage_scale: Option<VoltageScale>) -> Self {
557 // Calculate the actual output frequency for documentation
558 // let ref_freq = xosc_hz / pll_config.refdiv as u32;
559 // let vco_freq = ref_freq * pll_config.fbdiv as u32;
560 // let sys_freq = vco_freq / ((pll_config.post_div1 * pll_config.post_div2) as u32);
561
562 // Validate PLL parameters
563 assert!(pll_config.is_valid(xosc_hz), "Invalid PLL parameters");
564
565 let mut config = Self::default();
566
567 config.xosc = Some(XoscConfig {
568 hz: xosc_hz,
569 sys_pll: Some(pll_config),
570 usb_pll: Some(PllConfig {
571 refdiv: 1,
572 fbdiv: 120,
573 post_div1: 6,
574 post_div2: 5,
575 }),
576 delay_multiplier: 128,
577 });
578
579 config.ref_clk = RefClkConfig {
580 src: RefClkSrc::Xosc,
581 div: 1,
582 };
583
584 config.sys_clk = SysClkConfig {
585 src: SysClkSrc::PllSys,
586 div_int: 1,
587 div_frac: 0,
588 };
589
590 config.voltage_scale = voltage_scale;
591 config.peri_clk_src = Some(PeriClkSrc::Sys);
592
593 // Set reasonable defaults for other clocks
594 config.usb_clk = Some(UsbClkConfig {
595 src: UsbClkSrc::PllUsb,
596 div: 1,
597 phase: 0,
598 });
599
600 config.adc_clk = Some(AdcClkConfig {
601 src: AdcClkSrc::PllUsb,
602 div: 1,
603 phase: 0,
604 });
605
606 config.rtc_clk = Some(RtcClkConfig {
607 src: RtcClkSrc::PllUsb,
608 div_int: 1024,
609 div_frac: 0,
610 phase: 0,
611 });
612
613 config
614 }
554} 615}
555 616
556/// ROSC freq range. 617/// ROSC freq range.
@@ -596,30 +657,6 @@ pub struct XoscConfig {
596} 657}
597 658
598/// PLL configuration. 659/// PLL configuration.
599///
600/// This struct defines the parameters used to configure the Phase-Locked Loop (PLL)
601/// in the RP2040. The parameters follow the definitions from the RP2040 datasheet,
602/// section 2.18.3.
603///
604/// # Parameters
605///
606/// * `refdiv` - Reference divider (1-63)
607/// * `fbdiv` - VCO feedback divider (16-320)
608/// * `post_div1` - First post divider (1-7)
609/// * `post_div2` - Second post divider (1-7) - must be less than or equal to post_div1
610///
611/// # Constraints
612///
613/// * VCO frequency (input_hz / refdiv * fbdiv) must be between 750MHz and 1800MHz
614/// * post_div2 must be less than or equal to post_div1
615///
616/// # Calculation
617///
618/// The output frequency of the PLL is calculated as:
619///
620/// `output_hz = (input_hz / refdiv * fbdiv) / (post_div1 * post_div2)`
621///
622/// Where input_hz is typically the crystal frequency (e.g., 12MHz).
623#[derive(Clone, Copy, Debug)] 660#[derive(Clone, Copy, Debug)]
624pub struct PllConfig { 661pub struct PllConfig {
625 /// Reference divisor. 662 /// Reference divisor.
@@ -836,35 +873,17 @@ pub struct RtcClkConfig {
836/// * `Some(PllConfig)` if valid parameters were found 873/// * `Some(PllConfig)` if valid parameters were found
837/// * `None` if no valid parameters could be found for the requested combination 874/// * `None` if no valid parameters could be found for the requested combination
838/// 875///
839/// # Algorithm
840///
841/// 1. Set reference divider to 1 (fixed for simplicity)
842/// 2. Try different feedback divisors (fbdiv) starting from highest to lowest
843/// 3. For each fbdiv value, check if the resulting VCO frequency is valid (750-1800MHz)
844/// 4. Find post-divider combinations that give the exact requested frequency
845/// 5. If no exact match, return the closest approximation
846///
847/// # Example 876/// # Example
848/// 877///
849/// ``` 878/// ```
850/// // Find parameters for 133MHz system clock from 12MHz crystal 879/// // Find parameters for 133MHz system clock from 12MHz crystal
851/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); 880/// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap();
852/// ``` 881/// ```
853///
854/// Similar to the Pico SDK's parameter selection approach, prioritizing stability.
855/// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL
856#[cfg(feature = "rp2040")] 882#[cfg(feature = "rp2040")]
857fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> { 883fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
858 // Fixed reference divider for system PLL 884 // Fixed reference divider for system PLL
859 const PLL_SYS_REFDIV: u8 = 1; 885 const PLL_SYS_REFDIV: u8 = 1;
860 886
861 // Constraints from datasheet:
862 // REFDIV: 1..=63
863 // FBDIV: 16..=320
864 // POSTDIV1: 1..=7
865 // POSTDIV2: 1..=7 (must be <= POSTDIV1)
866 // VCO frequency (input_hz / refdiv * fbdiv): 750MHz ..= 1800MHz
867
868 // Calculate reference frequency 887 // Calculate reference frequency
869 let reference_freq = input_hz / PLL_SYS_REFDIV as u32; 888 let reference_freq = input_hz / PLL_SYS_REFDIV as u32;
870 889
@@ -969,8 +988,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
969 #[cfg(feature = "_rp235x")] 988 #[cfg(feature = "_rp235x")]
970 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {} 989 while c.clk_ref_selected().read() != pac::clocks::regs::ClkRefSelected(1) {}
971 990
972 // Set Core Voltage (RP2040 only) BEFORE doing anything with PLLs 991 // Set Core Voltage (RP2040 only), if we have config for it and we're not using the default
973 // This is critical for overclocking - must be done before PLL setup
974 #[cfg(feature = "rp2040")] 992 #[cfg(feature = "rp2040")]
975 if let Some(voltage) = config.voltage_scale { 993 if let Some(voltage) = config.voltage_scale {
976 let vreg = pac::VREG_AND_CHIP_RESET; 994 let vreg = pac::VREG_AND_CHIP_RESET;
@@ -978,17 +996,15 @@ pub(crate) unsafe fn init(config: ClockConfig) {
978 let target_vsel = voltage as u8; 996 let target_vsel = voltage as u8;
979 997
980 if target_vsel != current_vsel { 998 if target_vsel != current_vsel {
981 // IMPORTANT: Use modify() instead of write() to preserve the HIZ and EN bits 999 // Use modify() instead of write() to preserve the HIZ and EN bits - otherwise we will disable the regulator when changing voltage
982 // This is critical - otherwise we might disable the regulator when changing voltage
983 vreg.vreg().modify(|w| w.set_vsel(target_vsel)); 1000 vreg.vreg().modify(|w| w.set_vsel(target_vsel));
984 1001
985 // For higher voltage settings (overclocking), we need a longer stabilization time 1002 // Wait for the voltage to stabilize. Use the provided delay or default based on voltage
986 // Default to 1000 µs (1ms) like the SDK, but allow user override
987 let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| { 1003 let settling_time_us = config.voltage_stabilization_delay_us.unwrap_or_else(|| {
988 match voltage { 1004 match voltage {
989 VoltageScale::V1_15 => 1000, // 1ms for 1.15V (matches SDK default) 1005 VoltageScale::V1_15 => 1000, // 1ms for 1.15V
990 VoltageScale::V1_20 | VoltageScale::V1_25 | VoltageScale::V1_30 => 2000, // 2ms for higher voltages 1006 VoltageScale::V1_20 | VoltageScale::V1_25 | VoltageScale::V1_30 => 2000, // 2ms for higher voltages
991 _ => 500, // 500 µs for standard voltages 1007 _ => 0, // no delay for all others
992 } 1008 }
993 }); 1009 });
994 1010
@@ -1001,7 +1017,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1001 // Wait for voltage to stabilize 1017 // Wait for voltage to stabilize
1002 cortex_m::asm::delay(delay_cycles); 1018 cortex_m::asm::delay(delay_cycles);
1003 1019
1004 // Only NOW set the BOD level after voltage has stabilized 1020 // Only now set the BOD level after voltage has stabilized
1005 vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod())); 1021 vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod()));
1006 } 1022 }
1007 } 1023 }
@@ -1026,9 +1042,9 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1026 // SETUP TEMPORARY STABLE CLOCKS FIRST 1042 // SETUP TEMPORARY STABLE CLOCKS FIRST
1027 // Configure USB PLL for our stable temporary clock 1043 // Configure USB PLL for our stable temporary clock
1028 // This follows the SDK's approach of using USB PLL as a stable intermediate clock 1044 // This follows the SDK's approach of using USB PLL as a stable intermediate clock
1029 let usb_pll_freq = match &config.xosc { 1045 let pll_usb_freq = match &config.xosc {
1030 Some(config) => match &config.usb_pll { 1046 Some(config) => match &config.usb_pll {
1031 Some(usb_pll_config) => { 1047 Some(pll_usb_config) => {
1032 // Reset USB PLL 1048 // Reset USB PLL
1033 let mut peris = reset::Peripherals(0); 1049 let mut peris = reset::Peripherals(0);
1034 peris.set_pll_usb(true); 1050 peris.set_pll_usb(true);
@@ -1036,7 +1052,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1036 reset::unreset_wait(peris); 1052 reset::unreset_wait(peris);
1037 1053
1038 // Configure the USB PLL - this should give us 48MHz 1054 // Configure the USB PLL - this should give us 48MHz
1039 let usb_pll_freq = configure_pll(pac::PLL_USB, xosc_freq, *usb_pll_config); 1055 let usb_pll_freq = configure_pll(pac::PLL_USB, xosc_freq, *pll_usb_config);
1040 CLOCKS.pll_usb.store(usb_pll_freq, Ordering::Relaxed); 1056 CLOCKS.pll_usb.store(usb_pll_freq, Ordering::Relaxed);
1041 usb_pll_freq 1057 usb_pll_freq
1042 } 1058 }
@@ -1122,7 +1138,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1122 let (src, aux, freq) = match config.sys_clk.src { 1138 let (src, aux, freq) = match config.sys_clk.src {
1123 SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq), 1139 SysClkSrc::Ref => (Src::CLK_REF, Aux::CLKSRC_PLL_SYS, clk_ref_freq),
1124 SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq), 1140 SysClkSrc::PllSys => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_SYS, pll_sys_freq),
1125 SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, usb_pll_freq), 1141 SysClkSrc::PllUsb => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_PLL_USB, pll_usb_freq),
1126 SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq), 1142 SysClkSrc::Rosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::ROSC_CLKSRC, rosc_freq),
1127 SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq), 1143 SysClkSrc::Xosc => (Src::CLKSRC_CLK_SYS_AUX, Aux::XOSC_CLKSRC, xosc_freq),
1128 // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq), 1144 // SysClkSrc::Gpin0 => (Src::CLKSRC_CLK_SYS_AUX, Aux::CLKSRC_GPIN0, gpin0_freq),
@@ -1185,7 +1201,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1185 let peri_freq = match src { 1201 let peri_freq = match src {
1186 PeriClkSrc::Sys => clk_sys_freq, 1202 PeriClkSrc::Sys => clk_sys_freq,
1187 PeriClkSrc::PllSys => pll_sys_freq, 1203 PeriClkSrc::PllSys => pll_sys_freq,
1188 PeriClkSrc::PllUsb => usb_pll_freq, 1204 PeriClkSrc::PllUsb => pll_usb_freq,
1189 PeriClkSrc::Rosc => rosc_freq, 1205 PeriClkSrc::Rosc => rosc_freq,
1190 PeriClkSrc::Xosc => xosc_freq, 1206 PeriClkSrc::Xosc => xosc_freq,
1191 // PeriClkSrc::Gpin0 => gpin0_freq, 1207 // PeriClkSrc::Gpin0 => gpin0_freq,
@@ -1210,7 +1226,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1210 w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _)); 1226 w.set_auxsrc(ClkUsbCtrlAuxsrc::from_bits(conf.src as _));
1211 }); 1227 });
1212 let usb_freq = match conf.src { 1228 let usb_freq = match conf.src {
1213 UsbClkSrc::PllUsb => usb_pll_freq, 1229 UsbClkSrc::PllUsb => pll_usb_freq,
1214 UsbClkSrc::PllSys => pll_sys_freq, 1230 UsbClkSrc::PllSys => pll_sys_freq,
1215 UsbClkSrc::Rosc => rosc_freq, 1231 UsbClkSrc::Rosc => rosc_freq,
1216 UsbClkSrc::Xosc => xosc_freq, 1232 UsbClkSrc::Xosc => xosc_freq,
@@ -1234,7 +1250,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1234 w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _)); 1250 w.set_auxsrc(ClkAdcCtrlAuxsrc::from_bits(conf.src as _));
1235 }); 1251 });
1236 let adc_in_freq = match conf.src { 1252 let adc_in_freq = match conf.src {
1237 AdcClkSrc::PllUsb => usb_pll_freq, 1253 AdcClkSrc::PllUsb => pll_usb_freq,
1238 AdcClkSrc::PllSys => pll_sys_freq, 1254 AdcClkSrc::PllSys => pll_sys_freq,
1239 AdcClkSrc::Rosc => rosc_freq, 1255 AdcClkSrc::Rosc => rosc_freq,
1240 AdcClkSrc::Xosc => xosc_freq, 1256 AdcClkSrc::Xosc => xosc_freq,
@@ -1262,7 +1278,7 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1262 w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _)); 1278 w.set_auxsrc(ClkRtcCtrlAuxsrc::from_bits(conf.src as _));
1263 }); 1279 });
1264 let rtc_in_freq = match conf.src { 1280 let rtc_in_freq = match conf.src {
1265 RtcClkSrc::PllUsb => usb_pll_freq, 1281 RtcClkSrc::PllUsb => pll_usb_freq,
1266 RtcClkSrc::PllSys => pll_sys_freq, 1282 RtcClkSrc::PllSys => pll_sys_freq,
1267 RtcClkSrc::Rosc => rosc_freq, 1283 RtcClkSrc::Rosc => rosc_freq,
1268 RtcClkSrc::Xosc => xosc_freq, 1284 RtcClkSrc::Xosc => xosc_freq,
@@ -1390,40 +1406,36 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
1390 while !pac::XOSC.status().read().stable() {} 1406 while !pac::XOSC.status().read().stable() {}
1391} 1407}
1392 1408
1409/// PLL (Phase-Locked Loop) configuration
1393#[inline(always)] 1410#[inline(always)]
1394fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { 1411fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 {
1395 // Calculate reference frequency 1412 // Calculate reference frequency
1396 let ref_freq = input_freq / config.refdiv as u32; 1413 let ref_freq = input_freq / config.refdiv as u32;
1397 1414
1398 // Validate PLL parameters 1415 // Validate PLL parameters
1399 assert!( 1416 // Feedback divider (FBDIV) must be between 16 and 320
1400 config.fbdiv >= 16 && config.fbdiv <= 320, 1417 assert!(config.fbdiv >= 16 && config.fbdiv <= 320);
1401 "fbdiv must be between 16 and 320" 1418
1402 ); 1419 // Post divider 1 (POSTDIV1) must be between 1 and 7
1403 assert!( 1420 assert!(config.post_div1 >= 1 && config.post_div1 <= 7);
1404 config.post_div1 >= 1 && config.post_div1 <= 7, 1421
1405 "post_div1 must be between 1 and 7" 1422 // Post divider 2 (POSTDIV2) must be between 1 and 7
1406 ); 1423 assert!(config.post_div2 >= 1 && config.post_div2 <= 7);
1407 assert!( 1424
1408 config.post_div2 >= 1 && config.post_div2 <= 7, 1425 // Post divider 2 (POSTDIV2) must be less than or equal to post divider 1 (POSTDIV1)
1409 "post_div2 must be between 1 and 7" 1426 assert!(config.post_div2 <= config.post_div1);
1410 ); 1427
1411 assert!(config.post_div2 <= config.post_div1, "post_div2 must be <= post_div1"); 1428 // Reference divider (REFDIV) must be between 1 and 63
1412 assert!( 1429 assert!(config.refdiv >= 1 && config.refdiv <= 63);
1413 config.refdiv >= 1 && config.refdiv <= 63, 1430
1414 "refdiv must be between 1 and 63" 1431 // Reference frequency (REF_FREQ) must be between 5MHz and 800MHz
1415 ); 1432 assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000);
1416 assert!(
1417 ref_freq >= 5_000_000 && ref_freq <= 800_000_000,
1418 "ref_freq must be between 5MHz and 800MHz"
1419 );
1420 1433
1421 // Calculate VCO frequency 1434 // Calculate VCO frequency
1422 let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32); 1435 let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32);
1423 assert!( 1436
1424 vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000, 1437 // VCO (Voltage Controlled Oscillator) frequency must be between 750MHz and 1800MHz
1425 "VCO frequency must be between 750MHz and 1800MHz" 1438 assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000);
1426 );
1427 1439
1428 // We follow the SDK's approach to PLL configuration which is: 1440 // We follow the SDK's approach to PLL configuration which is:
1429 // 1. Power down PLL 1441 // 1. Power down PLL
diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs
index 9027f1516..e3ac77340 100644
--- a/examples/rp/src/bin/overclock.rs
+++ b/examples/rp/src/bin/overclock.rs
@@ -1,9 +1,13 @@
1//! # Overclocking the RP2040 to 200 MHz
2//!
3//! This example demonstrates how to configure the RP2040 to run at 200 MHz using a higher level API.
4
1#![no_std] 5#![no_std]
2#![no_main] 6#![no_main]
3 7
4use defmt::*; 8use defmt::*;
5use embassy_executor::Spawner; 9use embassy_executor::Spawner;
6use embassy_rp::clocks::{clk_sys_freq, ClockConfig, VoltageScale}; 10use embassy_rp::clocks::{clk_sys_freq, ClockConfig};
7use embassy_rp::config::Config; 11use embassy_rp::config::Config;
8use embassy_rp::gpio::{Level, Output}; 12use embassy_rp::gpio::{Level, Output};
9use embassy_time::{Duration, Instant, Timer}; 13use embassy_time::{Duration, Instant, Timer};
@@ -15,15 +19,10 @@ const COUNT_TO: i64 = 10_000_000;
15async fn main(_spawner: Spawner) -> ! { 19async fn main(_spawner: Spawner) -> ! {
16 // Set up for clock frequency of 200 MHz 20 // Set up for clock frequency of 200 MHz
17 // This will set all the necessary defaults including slightly raised voltage 21 // This will set all the necessary defaults including slightly raised voltage
18 // See embassy_rp::clocks::ClockConfig for more options, including full manual control
19 let config = Config::new(ClockConfig::at_sys_frequency_mhz(200)); 22 let config = Config::new(ClockConfig::at_sys_frequency_mhz(200));
20 23
21 // Show the voltage scale and brownout-detection for verification 24 // Show the voltage scale for verification
22 info!("System core voltage: {}", Debug2Format(&config.clocks.voltage_scale)); 25 info!("System core voltage: {}", Debug2Format(&config.clocks.voltage_scale));
23 // info!(
24 // "Brownout detection: {}",
25 // Debug2Format(&config.clocks.brownout_detection)
26 // );
27 26
28 // Initialize the peripherals 27 // Initialize the peripherals
29 let p = embassy_rp::init(config); 28 let p = embassy_rp::init(config);
@@ -64,14 +63,3 @@ async fn main(_spawner: Spawner) -> ! {
64 Timer::after(Duration::from_secs(2)).await; 63 Timer::after(Duration::from_secs(2)).await;
65 } 64 }
66} 65}
67
68// let config = Config::new(ClockConfig::with_speed_mhz_test_voltage(125, Some(VoltageScale::V1_10)));
69// let config = Config::default();
70// let config = Config::new(ClockConfig::with_speed_mhz_test_voltage_extended_delay(
71// 200, // Standard 125MHz clock
72// Some(VoltageScale::V1_15), // 1.15V voltage
73// Some(1000), // 1000μs (1ms) stabilization delay - significantly longer than default
74// ));
75// Initialize the peripherals
76
77// let p = embassy_rp::init(Default::default()); //testing the bog standard
diff --git a/examples/rp/src/bin/overclock_manual.rs b/examples/rp/src/bin/overclock_manual.rs
new file mode 100644
index 000000000..ad6abf0e7
--- /dev/null
+++ b/examples/rp/src/bin/overclock_manual.rs
@@ -0,0 +1,81 @@
1//! # Overclocking the RP2040 to 200 MHz manually
2//!
3//! This example demonstrates how to manually configure the RP2040 to run at 200 MHz.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_rp::clocks;
11use embassy_rp::clocks::{ClockConfig, PllConfig, VoltageScale};
12use embassy_rp::config::Config;
13use embassy_rp::gpio::{Level, Output};
14use embassy_time::{Duration, Instant, Timer};
15use {defmt_rtt as _, panic_probe as _};
16
17const COUNT_TO: i64 = 10_000_000;
18
19/// Configure the RP2040 for 200 MHz operation by manually specifying
20/// all the required parameters instead of using higher-level APIs.
21fn configure_manual_overclock() -> Config {
22 // Set the PLL configuration manually, starting from default values
23 let mut config = Config::default();
24
25 // Set the system clock to 200 MHz using a PLL with a reference frequency of 12 MHz
26 config.clocks = ClockConfig::manual_pll(
27 12_000_000,
28 PllConfig {
29 refdiv: 1,
30 fbdiv: 100,
31 post_div1: 3,
32 post_div2: 2,
33 },
34 // For 200 MHz, we need a voltage scale of 1.15V
35 Some(VoltageScale::V1_15),
36 );
37
38 config
39}
40
41#[embassy_executor::main]
42async fn main(_spawner: Spawner) -> ! {
43 // Initialize with our manual overclock configuration
44 let p = embassy_rp::init(configure_manual_overclock());
45
46 // Verify the actual system clock frequency
47 let sys_freq = clocks::clk_sys_freq();
48 info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
49
50 // LED to indicate the system is running
51 let mut led = Output::new(p.PIN_25, Level::Low);
52
53 loop {
54 // Reset the counter at the start of measurement period
55 let mut counter = 0;
56
57 // Turn LED on while counting
58 led.set_high();
59
60 let start = Instant::now();
61
62 // This is a busy loop that will take some time to complete
63 while counter < COUNT_TO {
64 counter += 1;
65 }
66
67 let elapsed = Instant::now() - start;
68
69 // Report the elapsed time
70 led.set_low();
71 info!(
72 "At {}Mhz: Elapsed time to count to {}: {}ms",
73 sys_freq / 1_000_000,
74 counter,
75 elapsed.as_millis()
76 );
77
78 // Wait 2 seconds before starting the next measurement
79 Timer::after(Duration::from_secs(2)).await;
80 }
81}