diff options
| author | 1-rafael-1 <[email protected]> | 2025-05-01 00:11:56 +0200 |
|---|---|---|
| committer | 1-rafael-1 <[email protected]> | 2025-05-01 00:11:56 +0200 |
| commit | 22b5f73811a7cc0dbca920e02b5d001d252d344c (patch) | |
| tree | d3fd2d801ffddc74a43f04c7892c953356647904 | |
| parent | d44b94523576d3f8c1f586811f600eb3ba223606 (diff) | |
add manual overclock example, finalize API, cleanup
| -rw-r--r-- | embassy-rp/src/clocks.rs | 268 | ||||
| -rw-r--r-- | examples/rp/src/bin/overclock.rs | 24 | ||||
| -rw-r--r-- | examples/rp/src/bin/overclock_manual.rs | 81 |
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)] |
| 180 | pub enum VoltageScale { | 154 | pub 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 { | |||
| 204 | impl VoltageScale { | 178 | impl 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)] |
| 624 | pub struct PllConfig { | 661 | pub 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")] |
| 857 | fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> { | 883 | fn 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)] |
| 1394 | fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { | 1411 | fn 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 | ||
| 4 | use defmt::*; | 8 | use defmt::*; |
| 5 | use embassy_executor::Spawner; | 9 | use embassy_executor::Spawner; |
| 6 | use embassy_rp::clocks::{clk_sys_freq, ClockConfig, VoltageScale}; | 10 | use embassy_rp::clocks::{clk_sys_freq, ClockConfig}; |
| 7 | use embassy_rp::config::Config; | 11 | use embassy_rp::config::Config; |
| 8 | use embassy_rp::gpio::{Level, Output}; | 12 | use embassy_rp::gpio::{Level, Output}; |
| 9 | use embassy_time::{Duration, Instant, Timer}; | 13 | use embassy_time::{Duration, Instant, Timer}; |
| @@ -15,15 +19,10 @@ const COUNT_TO: i64 = 10_000_000; | |||
| 15 | async fn main(_spawner: Spawner) -> ! { | 19 | async 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 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_rp::clocks; | ||
| 11 | use embassy_rp::clocks::{ClockConfig, PllConfig, VoltageScale}; | ||
| 12 | use embassy_rp::config::Config; | ||
| 13 | use embassy_rp::gpio::{Level, Output}; | ||
| 14 | use embassy_time::{Duration, Instant, Timer}; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | const 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. | ||
| 21 | fn 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] | ||
| 42 | async 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 | } | ||
