diff options
| author | 1-rafael-1 <[email protected]> | 2025-04-29 22:49:05 +0200 |
|---|---|---|
| committer | 1-rafael-1 <[email protected]> | 2025-04-29 22:49:05 +0200 |
| commit | 77e8bc9b28d6988b2703029679f290b351fc54a0 (patch) | |
| tree | 87d6c6a693f6456f3be78232cbd592efeb2d9d80 | |
| parent | 3a6dc910ffc66d4a30b89f299432b383271a719f (diff) | |
refactoring to have higher and lower level api
| m--------- | embassy-rp/sdk examples | 0 | ||||
| -rw-r--r-- | embassy-rp/src/clocks.rs | 546 | ||||
| -rw-r--r-- | examples/rp/src/bin/overclock.rs | 13 |
3 files changed, 351 insertions, 208 deletions
diff --git a/embassy-rp/sdk examples b/embassy-rp/sdk examples new file mode 160000 | |||
| Subproject ee68c78d0afae2b69c03ae1a72bf5cc267a2d94 | |||
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index b74e90f5e..1f5c27df1 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs | |||
| @@ -1,4 +1,78 @@ | |||
| 1 | //! Clock configuration for the RP2040 | 1 | //! # Clock configuration for the RP2040 and RP235x microcontrollers. |
| 2 | //! | ||
| 3 | //! # Clock Configuration API | ||
| 4 | //! | ||
| 5 | //! This module provides both high-level convenience functions and low-level manual | ||
| 6 | //! configuration options for the RP2040 clock system. | ||
| 7 | //! | ||
| 8 | //! ## High-Level Convenience Functions | ||
| 9 | //! | ||
| 10 | //! For most users, these functions provide an easy way to configure clocks: | ||
| 11 | //! | ||
| 12 | //! - `ClockConfig::crystal(12_000_000)` - Default configuration with 12MHz crystal giving 125MHz system clock | ||
| 13 | //! - `ClockConfig::at_sys_frequency_mhz(200)` - Set system clock to a specific frequency with automatic voltage scaling | ||
| 14 | //! - `ClockConfig::with_external_crystal(16_000_000)` - Configure with a non-standard crystal frequency | ||
| 15 | //! | ||
| 16 | //! ## Manual Configuration | ||
| 17 | //! | ||
| 18 | //! For advanced users who need precise control: | ||
| 19 | //! | ||
| 20 | //! ```rust,ignore | ||
| 21 | //! // Start with default configuration and customize it | ||
| 22 | //! let mut config = ClockConfig::default(); | ||
| 23 | //! | ||
| 24 | //! // Set custom PLL parameters | ||
| 25 | //! config.xosc = Some(XoscConfig { | ||
| 26 | //! hz: 12_000_000, | ||
| 27 | //! sys_pll: Some(PllConfig { | ||
| 28 | //! refdiv: 1, | ||
| 29 | //! fbdiv: 200, | ||
| 30 | //! post_div1: 6, | ||
| 31 | //! post_div2: 2, | ||
| 32 | //! }), | ||
| 33 | //! // ... other fields | ||
| 34 | //! }); | ||
| 35 | //! | ||
| 36 | //! // Set voltage for overclocking | ||
| 37 | //! config.voltage_scale = Some(VoltageScale::V1_15); | ||
| 38 | //! ``` | ||
| 39 | //! | ||
| 40 | //! ## Voltage Scaling for Overclocking (RP2040 only) | ||
| 41 | //! | ||
| 42 | //! When overclocking beyond 133MHz, higher core voltages are needed: | ||
| 43 | //! | ||
| 44 | //! - Up to 133MHz: `VoltageScale::V1_10` (default) | ||
| 45 | //! - 133-200MHz: `VoltageScale::V1_15` | ||
| 46 | //! - Above 200MHz: `VoltageScale::V1_20` or higher | ||
| 47 | //! | ||
| 48 | //! The `at_sys_frequency_mhz()` function automatically sets appropriate voltages. | ||
| 49 | //! | ||
| 50 | //! ## Examples | ||
| 51 | //! | ||
| 52 | //! ### Standard 125MHz configuration | ||
| 53 | //! ```rust,ignore | ||
| 54 | //! let config = ClockConfig::crystal(12_000_000); | ||
| 55 | //! ``` | ||
| 56 | //! | ||
| 57 | //! Or using the default configuration: | ||
| 58 | //! ```rust,ignore | ||
| 59 | //! let config = ClockConfig::default(); | ||
| 60 | //! ``` | ||
| 61 | //! | ||
| 62 | //! ### Overclock to 200MHz | ||
| 63 | //! ```rust,ignore | ||
| 64 | //! let config = ClockConfig::at_sys_frequency_mhz(200); | ||
| 65 | //! ``` | ||
| 66 | //! | ||
| 67 | //! ### Manual configuration for advanced scenarios | ||
| 68 | //! ```rust,ignore | ||
| 69 | //! use embassy_rp::clocks::{ClockConfig, XoscConfig, PllConfig, VoltageScale}; | ||
| 70 | //! | ||
| 71 | //! // Start with defaults and customize | ||
| 72 | //! let mut config = ClockConfig::default(); | ||
| 73 | //! config.voltage_scale = Some(VoltageScale::V1_15); | ||
| 74 | //! // Set other parameters as needed... | ||
| 75 | //! ``` | ||
| 2 | 76 | ||
| 3 | #[cfg(feature = "rp2040")] | 77 | #[cfg(feature = "rp2040")] |
| 4 | use core::arch::asm; | 78 | use core::arch::asm; |
| @@ -18,6 +92,7 @@ use crate::{pac, reset, Peri}; | |||
| 18 | // gpin is not usually safe to use during the boot init() call, so it won't | 92 | // gpin is not usually safe to use during the boot init() call, so it won't |
| 19 | // be very useful until we have runtime clock reconfiguration. once this | 93 | // be very useful until we have runtime clock reconfiguration. once this |
| 20 | // happens we can resurrect the commented-out gpin bits. | 94 | // happens we can resurrect the commented-out gpin bits. |
| 95 | |||
| 21 | struct Clocks { | 96 | struct Clocks { |
| 22 | xosc: AtomicU32, | 97 | xosc: AtomicU32, |
| 23 | sys: AtomicU32, | 98 | sys: AtomicU32, |
| @@ -70,30 +145,58 @@ pub enum PeriClkSrc { | |||
| 70 | } | 145 | } |
| 71 | 146 | ||
| 72 | /// Core voltage scaling options for RP2040. | 147 | /// Core voltage scaling options for RP2040. |
| 73 | /// See RP2040 Datasheet, Table 189, VREG Register. | 148 | /// |
| 149 | /// The RP2040 voltage regulator can be configured for different output voltages. | ||
| 150 | /// Higher voltages allow for higher clock frequencies but increase power consumption. | ||
| 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. | ||
| 74 | #[cfg(feature = "rp2040")] | 177 | #[cfg(feature = "rp2040")] |
| 75 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 178 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| 76 | #[repr(u8)] | 179 | #[repr(u8)] |
| 77 | pub enum VoltageScale { | 180 | pub enum VoltageScale { |
| 78 | /// 0.85V | 181 | /// 0.85V - Lowest power consumption, suitable for low frequencies |
| 79 | V0_85 = 0b0110, | 182 | V0_85 = 0b0110, |
| 80 | /// 0.90V | 183 | /// 0.90V - Low power consumption |
| 81 | V0_90 = 0b0111, | 184 | V0_90 = 0b0111, |
| 82 | /// 0.95V | 185 | /// 0.95V - Low power consumption |
| 83 | V0_95 = 0b1000, | 186 | V0_95 = 0b1000, |
| 84 | /// 1.00V | 187 | /// 1.00V - Medium power consumption |
| 85 | V1_00 = 0b1001, | 188 | V1_00 = 0b1001, |
| 86 | /// 1.05V | 189 | /// 1.05V - Medium power consumption |
| 87 | V1_05 = 0b1010, | 190 | V1_05 = 0b1010, |
| 88 | /// 1.10V (Default) | 191 | /// 1.10V (Default) - Standard voltage for 125MHz operation |
| 89 | V1_10 = 0b1011, | 192 | V1_10 = 0b1011, |
| 90 | /// 1.15V | 193 | /// 1.15V - Required for frequencies above 133MHz |
| 91 | V1_15 = 0b1100, | 194 | V1_15 = 0b1100, |
| 92 | /// 1.20V | 195 | /// 1.20V - For higher overclocking (200MHz+) |
| 93 | V1_20 = 0b1101, | 196 | V1_20 = 0b1101, |
| 94 | /// 1.25V | 197 | /// 1.25V - High voltage, use with caution |
| 95 | V1_25 = 0b1110, | 198 | V1_25 = 0b1110, |
| 96 | /// 1.30V | 199 | /// 1.30V - Maximum voltage, use with extreme caution |
| 97 | V1_30 = 0b1111, | 200 | V1_30 = 0b1111, |
| 98 | } | 201 | } |
| 99 | 202 | ||
| @@ -143,122 +246,58 @@ pub struct ClockConfig { | |||
| 143 | #[cfg(feature = "rp2040")] | 246 | #[cfg(feature = "rp2040")] |
| 144 | pub voltage_scale: Option<VoltageScale>, | 247 | pub voltage_scale: Option<VoltageScale>, |
| 145 | /// Voltage stabilization delay in microseconds. | 248 | /// Voltage stabilization delay in microseconds. |
| 249 | /// If not set, appropriate defaults will be used based on voltage level. | ||
| 146 | #[cfg(feature = "rp2040")] | 250 | #[cfg(feature = "rp2040")] |
| 147 | pub voltage_stabilization_delay_us: Option<u32>, | 251 | pub voltage_stabilization_delay_us: Option<u32>, |
| 148 | // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, | 252 | // gpin0: Option<(u32, Gpin<'static, AnyPin>)>, |
| 149 | // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, | 253 | // gpin1: Option<(u32, Gpin<'static, AnyPin>)>, |
| 150 | } | 254 | } |
| 151 | 255 | ||
| 152 | impl ClockConfig { | 256 | impl Default for ClockConfig { |
| 153 | /// Clock configuration derived from external crystal. | 257 | /// Creates a minimal default configuration with safe values. |
| 154 | pub fn crystal(crystal_hz: u32) -> Self { | 258 | /// |
| 259 | /// This configuration uses the ring oscillator (ROSC) as the clock source | ||
| 260 | /// and sets minimal defaults that guarantee a working system. It's intended | ||
| 261 | /// as a starting point for manual configuration. | ||
| 262 | /// | ||
| 263 | /// Most users should use one of the more specific configuration functions: | ||
| 264 | /// - `ClockConfig::crystal()` - Standard configuration with external crystal | ||
| 265 | /// - `ClockConfig::rosc()` - Configuration using only the internal oscillator | ||
| 266 | /// - `ClockConfig::at_sys_frequency_mhz()` - Configuration for a specific system frequency | ||
| 267 | fn default() -> Self { | ||
| 155 | Self { | 268 | Self { |
| 156 | rosc: Some(RoscConfig { | 269 | rosc: None, |
| 157 | hz: 6_500_000, | 270 | xosc: None, |
| 158 | range: RoscRange::Medium, | ||
| 159 | drive_strength: [0; 8], | ||
| 160 | div: 16, | ||
| 161 | }), | ||
| 162 | xosc: Some(XoscConfig { | ||
| 163 | hz: crystal_hz, | ||
| 164 | sys_pll: Some(PllConfig { | ||
| 165 | refdiv: 1, | ||
| 166 | fbdiv: 125, | ||
| 167 | #[cfg(feature = "rp2040")] | ||
| 168 | post_div1: 6, | ||
| 169 | #[cfg(feature = "_rp235x")] | ||
| 170 | post_div1: 5, | ||
| 171 | post_div2: 2, | ||
| 172 | }), | ||
| 173 | usb_pll: Some(PllConfig { | ||
| 174 | refdiv: 1, | ||
| 175 | fbdiv: 120, | ||
| 176 | post_div1: 6, | ||
| 177 | post_div2: 5, | ||
| 178 | }), | ||
| 179 | delay_multiplier: 128, | ||
| 180 | }), | ||
| 181 | ref_clk: RefClkConfig { | 271 | ref_clk: RefClkConfig { |
| 182 | src: RefClkSrc::Xosc, | 272 | src: RefClkSrc::Rosc, |
| 183 | div: 1, | 273 | div: 1, |
| 184 | }, | 274 | }, |
| 185 | sys_clk: SysClkConfig { | 275 | sys_clk: SysClkConfig { |
| 186 | src: SysClkSrc::PllSys, | 276 | src: SysClkSrc::Rosc, |
| 187 | div_int: 1, | 277 | div_int: 1, |
| 188 | div_frac: 0, | 278 | div_frac: 0, |
| 189 | }, | 279 | }, |
| 190 | peri_clk_src: Some(PeriClkSrc::Sys), | 280 | peri_clk_src: None, |
| 191 | // CLK USB = PLL USB (48MHz) / 1 = 48MHz | 281 | usb_clk: None, |
| 192 | usb_clk: Some(UsbClkConfig { | 282 | adc_clk: None, |
| 193 | src: UsbClkSrc::PllUsb, | ||
| 194 | div: 1, | ||
| 195 | phase: 0, | ||
| 196 | }), | ||
| 197 | // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz | ||
| 198 | adc_clk: Some(AdcClkConfig { | ||
| 199 | src: AdcClkSrc::PllUsb, | ||
| 200 | div: 1, | ||
| 201 | phase: 0, | ||
| 202 | }), | ||
| 203 | // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz | ||
| 204 | #[cfg(feature = "rp2040")] | 283 | #[cfg(feature = "rp2040")] |
| 205 | rtc_clk: Some(RtcClkConfig { | 284 | rtc_clk: None, |
| 206 | src: RtcClkSrc::PllUsb, | ||
| 207 | div_int: 1024, | ||
| 208 | div_frac: 0, | ||
| 209 | phase: 0, | ||
| 210 | }), | ||
| 211 | #[cfg(feature = "rp2040")] | 285 | #[cfg(feature = "rp2040")] |
| 212 | voltage_scale: None, // Use hardware default (1.10V) | 286 | voltage_scale: None, |
| 213 | #[cfg(feature = "rp2040")] | 287 | #[cfg(feature = "rp2040")] |
| 214 | voltage_stabilization_delay_us: None, | 288 | voltage_stabilization_delay_us: None, |
| 215 | // gpin0: None, | 289 | // gpin0: None, |
| 216 | // gpin1: None, | 290 | // gpin1: None, |
| 217 | } | 291 | } |
| 218 | } | 292 | } |
| 293 | } | ||
| 219 | 294 | ||
| 220 | /// Clock configuration derived from external crystal, targeting a specific SYS clock frequency for RP2040. | 295 | impl ClockConfig { |
| 221 | /// | 296 | /// Clock configuration derived from external crystal. |
| 222 | /// This function calculates the required PLL settings and core voltage based on the target frequency. | ||
| 223 | /// | ||
| 224 | /// # Arguments | ||
| 225 | /// | ||
| 226 | /// * `crystal_hz`: The frequency of the external crystal (e.g., 12_000_000 for 12MHz). | ||
| 227 | /// * `sys_freq_hz`: The target system clock frequency. | ||
| 228 | /// | ||
| 229 | /// # Panics | ||
| 230 | /// | ||
| 231 | /// Panics if the requested frequency is impossible to achieve with the given crystal, | ||
| 232 | /// or if the required voltage for the frequency is not supported or known. | ||
| 233 | /// | ||
| 234 | /// # Safety Notes (RP2040) | ||
| 235 | /// | 297 | /// |
| 236 | /// * Frequencies > 133MHz require increased core voltage. | 298 | /// This uses default settings for most parameters, suitable for typical use cases. |
| 237 | /// * This function automatically selects `VoltageScale::V1_15` for frequencies > 133MHz and <= 200MHz. | 299 | /// For manual control of PLL parameters, use `new_manual()` or modify the struct fields directly. |
| 238 | /// * Frequencies > 200MHz might require `VoltageScale::V1_20` or higher and are considered overclocking beyond datasheet recommendations. | 300 | pub fn crystal(crystal_hz: u32) -> Self { |
| 239 | /// This function will select `VoltageScale::V1_20` for frequencies > 200MHz, use with caution. | ||
| 240 | /// * Ensure your hardware supports the selected voltage and frequency. | ||
| 241 | #[cfg(feature = "rp2040")] | ||
| 242 | pub fn crystal_freq(crystal_hz: u32, sys_freq_hz: u32) -> Self { | ||
| 243 | // Determine required voltage based on target frequency | ||
| 244 | let voltage_scale = match sys_freq_hz { | ||
| 245 | 0..=133_000_000 => VoltageScale::V1_10, // Default voltage is sufficient | ||
| 246 | 133_000_001..=200_000_000 => VoltageScale::V1_15, // Requires 1.15V | ||
| 247 | _ => VoltageScale::V1_20, // Frequencies > 200MHz require at least 1.20V (Overclocking) | ||
| 248 | }; | ||
| 249 | |||
| 250 | // Find suitable PLL parameters | ||
| 251 | let pll_params = match find_pll_params(crystal_hz, sys_freq_hz) { | ||
| 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 | }; | ||
| 261 | |||
| 262 | Self { | 301 | Self { |
| 263 | rosc: Some(RoscConfig { | 302 | rosc: Some(RoscConfig { |
| 264 | hz: 6_500_000, | 303 | hz: 6_500_000, |
| @@ -268,8 +307,15 @@ impl ClockConfig { | |||
| 268 | }), | 307 | }), |
| 269 | xosc: Some(XoscConfig { | 308 | xosc: Some(XoscConfig { |
| 270 | hz: crystal_hz, | 309 | hz: crystal_hz, |
| 271 | sys_pll: Some(pll_params), | 310 | sys_pll: Some(PllConfig { |
| 272 | // Keep USB PLL at 48MHz for compatibility | 311 | refdiv: 1, |
| 312 | fbdiv: 125, | ||
| 313 | #[cfg(feature = "rp2040")] | ||
| 314 | post_div1: 6, | ||
| 315 | #[cfg(feature = "_rp235x")] | ||
| 316 | post_div1: 5, | ||
| 317 | post_div2: 2, | ||
| 318 | }), | ||
| 273 | usb_pll: Some(PllConfig { | 319 | usb_pll: Some(PllConfig { |
| 274 | refdiv: 1, | 320 | refdiv: 1, |
| 275 | fbdiv: 120, | 321 | fbdiv: 120, |
| @@ -288,23 +334,28 @@ impl ClockConfig { | |||
| 288 | div_frac: 0, | 334 | div_frac: 0, |
| 289 | }, | 335 | }, |
| 290 | peri_clk_src: Some(PeriClkSrc::Sys), | 336 | peri_clk_src: Some(PeriClkSrc::Sys), |
| 337 | // CLK USB = PLL USB (48MHz) / 1 = 48MHz | ||
| 291 | usb_clk: Some(UsbClkConfig { | 338 | usb_clk: Some(UsbClkConfig { |
| 292 | src: UsbClkSrc::PllUsb, | 339 | src: UsbClkSrc::PllUsb, |
| 293 | div: 1, | 340 | div: 1, |
| 294 | phase: 0, | 341 | phase: 0, |
| 295 | }), | 342 | }), |
| 343 | // CLK ADC = PLL USB (48MHZ) / 1 = 48MHz | ||
| 296 | adc_clk: Some(AdcClkConfig { | 344 | adc_clk: Some(AdcClkConfig { |
| 297 | src: AdcClkSrc::PllUsb, | 345 | src: AdcClkSrc::PllUsb, |
| 298 | div: 1, | 346 | div: 1, |
| 299 | phase: 0, | 347 | phase: 0, |
| 300 | }), | 348 | }), |
| 349 | // CLK RTC = PLL USB (48MHz) / 1024 = 46875Hz | ||
| 350 | #[cfg(feature = "rp2040")] | ||
| 301 | rtc_clk: Some(RtcClkConfig { | 351 | rtc_clk: Some(RtcClkConfig { |
| 302 | src: RtcClkSrc::PllUsb, | 352 | src: RtcClkSrc::PllUsb, |
| 303 | div_int: 1024, | 353 | div_int: 1024, |
| 304 | div_frac: 0, | 354 | div_frac: 0, |
| 305 | phase: 0, | 355 | phase: 0, |
| 306 | }), | 356 | }), |
| 307 | voltage_scale: Some(voltage_scale), | 357 | #[cfg(feature = "rp2040")] |
| 358 | voltage_scale: None, // Use hardware default (1.10V) | ||
| 308 | #[cfg(feature = "rp2040")] | 359 | #[cfg(feature = "rp2040")] |
| 309 | voltage_stabilization_delay_us: None, | 360 | voltage_stabilization_delay_us: None, |
| 310 | // gpin0: None, | 361 | // gpin0: None, |
| @@ -366,22 +417,17 @@ impl ClockConfig { | |||
| 366 | /// | 417 | /// |
| 367 | /// * `sys_freq_mhz` - The target system clock frequency in MHz | 418 | /// * `sys_freq_mhz` - The target system clock frequency in MHz |
| 368 | /// | 419 | /// |
| 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 | 420 | /// # Example |
| 375 | /// | 421 | /// |
| 376 | /// ``` | 422 | /// ``` |
| 377 | /// // Configure for standard 125MHz clock | 423 | /// // Configure for standard 125MHz clock |
| 378 | /// let config = ClockConfig::with_speed_mhz(125); | 424 | /// let config = ClockConfig::at_sys_frequency_mhz(125); |
| 379 | /// | 425 | /// |
| 380 | /// // Overclock to 200MHz (requires higher voltage) | 426 | /// // Overclock to 200MHz |
| 381 | /// let config = ClockConfig::with_speed_mhz(200); | 427 | /// let config = ClockConfig::at_sys_frequency_mhz(200); |
| 382 | /// ``` | 428 | /// ``` |
| 383 | #[cfg(feature = "rp2040")] | 429 | #[cfg(feature = "rp2040")] |
| 384 | pub fn with_speed_mhz(sys_freq_mhz: u32) -> Self { | 430 | pub fn at_sys_frequency_mhz(sys_freq_mhz: u32) -> Self { |
| 385 | // For 125MHz, use exactly the same config as the default to avoid any differences | 431 | // For 125MHz, use exactly the same config as the default to avoid any differences |
| 386 | if sys_freq_mhz == 125 { | 432 | if sys_freq_mhz == 125 { |
| 387 | return Self::crystal(12_000_000); | 433 | return Self::crystal(12_000_000); |
| @@ -392,12 +438,7 @@ impl ClockConfig { | |||
| 392 | const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; | 438 | const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000; |
| 393 | 439 | ||
| 394 | let sys_freq_hz = sys_freq_mhz * 1_000_000; | 440 | let sys_freq_hz = sys_freq_mhz * 1_000_000; |
| 395 | let mut config = Self::crystal_freq(DEFAULT_CRYSTAL_HZ, sys_freq_hz); | 441 | let 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 | 442 | ||
| 402 | config | 443 | config |
| 403 | } | 444 | } |
| @@ -412,11 +453,6 @@ impl ClockConfig { | |||
| 412 | /// * `crystal_hz` - The frequency of the external crystal in Hz | 453 | /// * `crystal_hz` - The frequency of the external crystal in Hz |
| 413 | /// * `sys_freq_hz` - The target system clock frequency in Hz | 454 | /// * `sys_freq_hz` - The target system clock frequency in Hz |
| 414 | /// | 455 | /// |
| 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 | 456 | /// # Example |
| 421 | /// | 457 | /// |
| 422 | /// ``` | 458 | /// ``` |
| @@ -428,58 +464,93 @@ impl ClockConfig { | |||
| 428 | Self::crystal_freq(crystal_hz, sys_freq_hz) | 464 | Self::crystal_freq(crystal_hz, sys_freq_hz) |
| 429 | } | 465 | } |
| 430 | 466 | ||
| 431 | #[cfg(feature = "rp2040")] | 467 | /// Configure clocks derived from an external crystal with specific system frequency. |
| 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 | /// | 468 | /// |
| 448 | /// This function is useful for testing voltage stability issues where the default delay | 469 | /// This function calculates optimal PLL parameters to achieve the requested system |
| 449 | /// may not be sufficient for the voltage regulator to fully stabilize. | 470 | /// frequency from the given crystal frequency. It's used internally by higher-level |
| 471 | /// configuration functions. | ||
| 450 | /// | 472 | /// |
| 451 | /// # Arguments | 473 | /// # Arguments |
| 452 | /// | 474 | /// |
| 453 | /// * `sys_freq_mhz` - The target system clock frequency in MHz | 475 | /// * `crystal_hz` - The frequency of the external crystal in Hz |
| 454 | /// * `voltage` - The desired voltage scale setting | 476 | /// * `sys_freq_hz` - The desired system clock frequency in Hz |
| 455 | /// * `stabilization_delay_us` - Voltage stabilization delay in microseconds (default: 500μs) | 477 | /// |
| 478 | /// # Returns | ||
| 479 | /// | ||
| 480 | /// A ClockConfig configured to achieve the requested system frequency using the | ||
| 481 | /// specified crystal, or panic if no valid parameters can be found. | ||
| 456 | #[cfg(feature = "rp2040")] | 482 | #[cfg(feature = "rp2040")] |
| 457 | pub fn with_speed_mhz_test_voltage_extended_delay( | 483 | fn crystal_freq(crystal_hz: u32, sys_freq_hz: u32) -> Self { |
| 458 | sys_freq_mhz: u32, | 484 | // Find optimal PLL parameters for the requested frequency |
| 459 | voltage: Option<VoltageScale>, | 485 | let sys_pll_params = find_pll_params(crystal_hz, sys_freq_hz) |
| 460 | stabilization_delay_us: Option<u32>, | 486 | .unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock")); |
| 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 | 487 | ||
| 466 | // Override the voltage setting | 488 | // Set the voltage scale based on the target frequency |
| 467 | config.voltage_scale = voltage; | 489 | // Higher frequencies require higher voltage |
| 490 | let voltage_scale = match sys_freq_hz { | ||
| 491 | freq if freq > 200_000_000 => Some(VoltageScale::V1_20), | ||
| 492 | freq if freq > 133_000_000 => Some(VoltageScale::V1_15), | ||
| 493 | _ => None, // Use default voltage (V1_10) | ||
| 494 | }; | ||
| 468 | 495 | ||
| 469 | // Add a custom voltage stabilization delay | 496 | // For USB PLL, we always want 48MHz for USB |
| 470 | config.voltage_stabilization_delay_us = stabilization_delay_us; | 497 | let usb_pll_params = if crystal_hz == 12_000_000 { |
| 498 | // For standard 12MHz crystal, use the default parameters | ||
| 499 | PllConfig { | ||
| 500 | refdiv: 1, | ||
| 501 | fbdiv: 120, | ||
| 502 | post_div1: 6, | ||
| 503 | post_div2: 5, | ||
| 504 | } | ||
| 505 | } else { | ||
| 506 | // For other crystals, calculate parameters to get 48MHz | ||
| 507 | find_pll_params(crystal_hz, 48_000_000) | ||
| 508 | .unwrap_or_else(|| panic!("Could not find valid PLL parameters for USB clock")) | ||
| 509 | }; | ||
| 471 | 510 | ||
| 472 | config | 511 | Self { |
| 512 | rosc: Some(RoscConfig { | ||
| 513 | hz: 6_500_000, | ||
| 514 | range: RoscRange::Medium, | ||
| 515 | drive_strength: [0; 8], | ||
| 516 | div: 16, | ||
| 517 | }), | ||
| 518 | xosc: Some(XoscConfig { | ||
| 519 | hz: crystal_hz, | ||
| 520 | sys_pll: Some(sys_pll_params), | ||
| 521 | usb_pll: Some(usb_pll_params), | ||
| 522 | delay_multiplier: 128, | ||
| 523 | }), | ||
| 524 | ref_clk: RefClkConfig { | ||
| 525 | src: RefClkSrc::Xosc, | ||
| 526 | div: 1, | ||
| 527 | }, | ||
| 528 | sys_clk: SysClkConfig { | ||
| 529 | src: SysClkSrc::PllSys, | ||
| 530 | div_int: 1, | ||
| 531 | div_frac: 0, | ||
| 532 | }, | ||
| 533 | peri_clk_src: Some(PeriClkSrc::Sys), | ||
| 534 | usb_clk: Some(UsbClkConfig { | ||
| 535 | src: UsbClkSrc::PllUsb, | ||
| 536 | div: 1, | ||
| 537 | phase: 0, | ||
| 538 | }), | ||
| 539 | adc_clk: Some(AdcClkConfig { | ||
| 540 | src: AdcClkSrc::PllUsb, | ||
| 541 | div: 1, | ||
| 542 | phase: 0, | ||
| 543 | }), | ||
| 544 | rtc_clk: Some(RtcClkConfig { | ||
| 545 | src: RtcClkSrc::PllUsb, | ||
| 546 | div_int: 1024, | ||
| 547 | div_frac: 0, | ||
| 548 | phase: 0, | ||
| 549 | }), | ||
| 550 | voltage_scale, | ||
| 551 | voltage_stabilization_delay_us: None, | ||
| 552 | } | ||
| 473 | } | 553 | } |
| 474 | // pub fn bind_gpin<P: GpinPin>(&mut self, gpin: Gpin<'static, P>, hz: u32) { | ||
| 475 | // match P::NR { | ||
| 476 | // 0 => self.gpin0 = Some((hz, gpin.into())), | ||
| 477 | // 1 => self.gpin1 = Some((hz, gpin.into())), | ||
| 478 | // _ => unreachable!(), | ||
| 479 | // } | ||
| 480 | // // pin is now provisionally bound. if the config is applied it must be forgotten, | ||
| 481 | // // or Gpin::drop will deconfigure the clock input. | ||
| 482 | // } | ||
| 483 | } | 554 | } |
| 484 | 555 | ||
| 485 | /// ROSC freq range. | 556 | /// ROSC freq range. |
| @@ -525,6 +596,30 @@ pub struct XoscConfig { | |||
| 525 | } | 596 | } |
| 526 | 597 | ||
| 527 | /// PLL configuration. | 598 | /// 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). | ||
| 528 | #[derive(Clone, Copy, Debug)] | 623 | #[derive(Clone, Copy, Debug)] |
| 529 | pub struct PllConfig { | 624 | pub struct PllConfig { |
| 530 | /// Reference divisor. | 625 | /// Reference divisor. |
| @@ -537,6 +632,50 @@ pub struct PllConfig { | |||
| 537 | pub post_div2: u8, | 632 | pub post_div2: u8, |
| 538 | } | 633 | } |
| 539 | 634 | ||
| 635 | impl PllConfig { | ||
| 636 | /// Calculate the output frequency for this PLL configuration | ||
| 637 | /// given an input frequency. | ||
| 638 | pub fn output_frequency(&self, input_hz: u32) -> u32 { | ||
| 639 | let ref_freq = input_hz / self.refdiv as u32; | ||
| 640 | let vco_freq = ref_freq * self.fbdiv as u32; | ||
| 641 | vco_freq / ((self.post_div1 * self.post_div2) as u32) | ||
| 642 | } | ||
| 643 | |||
| 644 | /// Check if this PLL configuration is valid for the given input frequency. | ||
| 645 | pub fn is_valid(&self, input_hz: u32) -> bool { | ||
| 646 | // Check divisor constraints | ||
| 647 | if self.refdiv < 1 || self.refdiv > 63 { | ||
| 648 | return false; | ||
| 649 | } | ||
| 650 | if self.fbdiv < 16 || self.fbdiv > 320 { | ||
| 651 | return false; | ||
| 652 | } | ||
| 653 | if self.post_div1 < 1 || self.post_div1 > 7 { | ||
| 654 | return false; | ||
| 655 | } | ||
| 656 | if self.post_div2 < 1 || self.post_div2 > 7 { | ||
| 657 | return false; | ||
| 658 | } | ||
| 659 | if self.post_div2 > self.post_div1 { | ||
| 660 | return false; | ||
| 661 | } | ||
| 662 | |||
| 663 | // Calculate reference frequency | ||
| 664 | let ref_freq = input_hz / self.refdiv as u32; | ||
| 665 | |||
| 666 | // Check reference frequency range | ||
| 667 | if ref_freq < 5_000_000 || ref_freq > 800_000_000 { | ||
| 668 | return false; | ||
| 669 | } | ||
| 670 | |||
| 671 | // Calculate VCO frequency | ||
| 672 | let vco_freq = ref_freq * self.fbdiv as u32; | ||
| 673 | |||
| 674 | // Check VCO frequency range | ||
| 675 | vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000 | ||
| 676 | } | ||
| 677 | } | ||
| 678 | |||
| 540 | /// Reference clock config. | 679 | /// Reference clock config. |
| 541 | pub struct RefClkConfig { | 680 | pub struct RefClkConfig { |
| 542 | /// Reference clock source. | 681 | /// Reference clock source. |
| @@ -683,6 +822,35 @@ pub struct RtcClkConfig { | |||
| 683 | /// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency | 822 | /// Find valid PLL parameters (refdiv, fbdiv, post_div1, post_div2) for a target output frequency |
| 684 | /// based on the input frequency. | 823 | /// based on the input frequency. |
| 685 | /// | 824 | /// |
| 825 | /// This function searches for the best PLL configuration to achieve the requested target frequency | ||
| 826 | /// while staying within the VCO frequency range of 750MHz to 1800MHz. It prioritizes stability | ||
| 827 | /// over exact frequency matching by using larger divisors where possible. | ||
| 828 | /// | ||
| 829 | /// # Parameters | ||
| 830 | /// | ||
| 831 | /// * `input_hz`: The input frequency in Hz (typically the crystal frequency, e.g. 12MHz) | ||
| 832 | /// * `target_hz`: The desired output frequency in Hz (e.g. 125MHz for standard RP2040 operation) | ||
| 833 | /// | ||
| 834 | /// # Returns | ||
| 835 | /// | ||
| 836 | /// * `Some(PllConfig)` if valid parameters were found | ||
| 837 | /// * `None` if no valid parameters could be found for the requested combination | ||
| 838 | /// | ||
| 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 | ||
| 848 | /// | ||
| 849 | /// ``` | ||
| 850 | /// // Find parameters for 133MHz system clock from 12MHz crystal | ||
| 851 | /// let pll_params = find_pll_params(12_000_000, 133_000_000).unwrap(); | ||
| 852 | /// ``` | ||
| 853 | /// | ||
| 686 | /// Similar to the Pico SDK's parameter selection approach, prioritizing stability. | 854 | /// Similar to the Pico SDK's parameter selection approach, prioritizing stability. |
| 687 | /// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL | 855 | /// See RP2040 Datasheet section 2.16.3. Reference Clock (ref) and 2.18.3. PLL |
| 688 | #[cfg(feature = "rp2040")] | 856 | #[cfg(feature = "rp2040")] |
| @@ -766,43 +934,6 @@ fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> { | |||
| 766 | best_config | 934 | best_config |
| 767 | } | 935 | } |
| 768 | 936 | ||
| 769 | #[cfg(feature = "rp2040")] | ||
| 770 | pub 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} | ||
| 804 | } | ||
| 805 | |||
| 806 | /// safety: must be called exactly once at bootup | 937 | /// safety: must be called exactly once at bootup |
| 807 | pub(crate) unsafe fn init(config: ClockConfig) { | 938 | pub(crate) unsafe fn init(config: ClockConfig) { |
| 808 | // Reset everything except: | 939 | // Reset everything except: |
| @@ -1603,6 +1734,7 @@ impl rand_core::RngCore for RoscRng { | |||
| 1603 | dest.fill_with(Self::next_u8) | 1734 | dest.fill_with(Self::next_u8) |
| 1604 | } | 1735 | } |
| 1605 | } | 1736 | } |
| 1737 | |||
| 1606 | /// Enter the `DORMANT` sleep state. This will stop *all* internal clocks | 1738 | /// Enter the `DORMANT` sleep state. This will stop *all* internal clocks |
| 1607 | /// and can only be exited through resets, dormant-wake GPIO interrupts, | 1739 | /// and can only be exited through resets, dormant-wake GPIO interrupts, |
| 1608 | /// and RTC interrupts. If RTC is clocked from an internal clock source | 1740 | /// and RTC interrupts. If RTC is clocked from an internal clock source |
diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index db6c8f448..9027f1516 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs | |||
| @@ -14,7 +14,18 @@ const COUNT_TO: i64 = 10_000_000; | |||
| 14 | #[embassy_executor::main] | 14 | #[embassy_executor::main] |
| 15 | async fn main(_spawner: Spawner) -> ! { | 15 | async fn main(_spawner: Spawner) -> ! { |
| 16 | // Set up for clock frequency of 200 MHz | 16 | // Set up for clock frequency of 200 MHz |
| 17 | let config = Config::new(ClockConfig::with_speed_mhz(200)); | 17 | // 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)); | ||
| 20 | |||
| 21 | // Show the voltage scale and brownout-detection for verification | ||
| 22 | info!("System core voltage: {}", Debug2Format(&config.clocks.voltage_scale)); | ||
| 23 | // info!( | ||
| 24 | // "Brownout detection: {}", | ||
| 25 | // Debug2Format(&config.clocks.brownout_detection) | ||
| 26 | // ); | ||
| 27 | |||
| 28 | // Initialize the peripherals | ||
| 18 | let p = embassy_rp::init(config); | 29 | let p = embassy_rp::init(config); |
| 19 | 30 | ||
| 20 | // Show CPU frequency for verification | 31 | // Show CPU frequency for verification |
