aboutsummaryrefslogtreecommitdiff
path: root/embassy-rp/src
diff options
context:
space:
mode:
author1-rafael-1 <[email protected]>2025-05-01 17:07:48 +0200
committer1-rafael-1 <[email protected]>2025-05-01 17:07:48 +0200
commitc01776a3d779caf5f43fda03e5506f569f1bf9ac (patch)
tree66704a232c270c976d4165bbe5f7d3676c1f1720 /embassy-rp/src
parent22b5f73811a7cc0dbca920e02b5d001d252d344c (diff)
- two more doc examples test ignored
- added tests for the new calculations and fixed an overflow issue these tests surfaced. - Activate brownout detection.
Diffstat (limited to 'embassy-rp/src')
-rw-r--r--embassy-rp/src/clocks.rs222
1 files changed, 214 insertions, 8 deletions
diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs
index 86c172879..005564b8b 100644
--- a/embassy-rp/src/clocks.rs
+++ b/embassy-rp/src/clocks.rs
@@ -391,7 +391,7 @@ impl ClockConfig {
391 /// 391 ///
392 /// # Example 392 /// # Example
393 /// 393 ///
394 /// ``` 394 /// ```rust,ignore
395 /// // Overclock to 200MHz 395 /// // Overclock to 200MHz
396 /// let config = ClockConfig::at_sys_frequency_mhz(200); 396 /// let config = ClockConfig::at_sys_frequency_mhz(200);
397 /// ``` 397 /// ```
@@ -424,7 +424,7 @@ impl ClockConfig {
424 /// 424 ///
425 /// # Example 425 /// # Example
426 /// 426 ///
427 /// ``` 427 /// ```rust,ignore
428 /// // Use a non-standard 16MHz crystal to achieve 250MHz 428 /// // Use a non-standard 16MHz crystal to achieve 250MHz
429 /// let config = ClockConfig::with_custom_crystal(16_000_000, 250_000_000); 429 /// let config = ClockConfig::with_custom_crystal(16_000_000, 250_000_000);
430 /// ``` 430 /// ```
@@ -875,7 +875,7 @@ pub struct RtcClkConfig {
875/// 875///
876/// # Example 876/// # Example
877/// 877///
878/// ``` 878/// ```rust,ignore
879/// // Find parameters for 133MHz system clock from 12MHz crystal 879/// // Find parameters for 133MHz system clock from 12MHz crystal
880/// 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();
881/// ``` 881/// ```
@@ -885,7 +885,7 @@ fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
885 const PLL_SYS_REFDIV: u8 = 1; 885 const PLL_SYS_REFDIV: u8 = 1;
886 886
887 // Calculate reference frequency 887 // Calculate reference frequency
888 let reference_freq = input_hz / PLL_SYS_REFDIV as u32; 888 let reference_freq = input_hz as u64 / PLL_SYS_REFDIV as u64;
889 889
890 // Start from highest fbdiv for better stability (like SDK does) 890 // Start from highest fbdiv for better stability (like SDK does)
891 for fbdiv in (16..=320).rev() { 891 for fbdiv in (16..=320).rev() {
@@ -900,10 +900,10 @@ fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
900 // (more conservative/stable approach) 900 // (more conservative/stable approach)
901 for post_div1 in (1..=7).rev() { 901 for post_div1 in (1..=7).rev() {
902 for post_div2 in (1..=post_div1).rev() { 902 for post_div2 in (1..=post_div1).rev() {
903 let out_freq = vco_freq / (post_div1 * post_div2) as u32; 903 let out_freq = vco_freq / (post_div1 * post_div2);
904 904
905 // Check if we get the exact target frequency without remainder 905 // Check if we get the exact target frequency without remainder
906 if out_freq == target_hz && (vco_freq % (post_div1 * post_div2) as u32 == 0) { 906 if out_freq == target_hz as u64 && (vco_freq % (post_div1 * post_div2) == 0) {
907 return Some(PllConfig { 907 return Some(PllConfig {
908 refdiv: PLL_SYS_REFDIV, 908 refdiv: PLL_SYS_REFDIV,
909 fbdiv: fbdiv as u16, 909 fbdiv: fbdiv as u16,
@@ -928,7 +928,7 @@ fn find_pll_params(input_hz: u32, target_hz: u32) -> Option<PllConfig> {
928 928
929 for post_div1 in (1..=7).rev() { 929 for post_div1 in (1..=7).rev() {
930 for post_div2 in (1..=post_div1).rev() { 930 for post_div2 in (1..=post_div1).rev() {
931 let out_freq = vco_freq / (post_div1 * post_div2) as u32; 931 let out_freq = (vco_freq / (post_div1 * post_div2) as u64) as u32;
932 let diff = if out_freq > target_hz { 932 let diff = if out_freq > target_hz {
933 out_freq - target_hz 933 out_freq - target_hz
934 } else { 934 } else {
@@ -1018,7 +1018,10 @@ pub(crate) unsafe fn init(config: ClockConfig) {
1018 cortex_m::asm::delay(delay_cycles); 1018 cortex_m::asm::delay(delay_cycles);
1019 1019
1020 // Only now set the BOD level after voltage has stabilized 1020 // Only now set the BOD level after voltage has stabilized
1021 vreg.bod().write(|w| w.set_vsel(voltage.recommended_bod())); 1021 vreg.bod().write(|w| {
1022 w.set_vsel(voltage.recommended_bod());
1023 w.set_en(true); // Enable brownout detection
1024 });
1022 } 1025 }
1023 } 1026 }
1024 1027
@@ -1875,3 +1878,206 @@ pub fn dormant_sleep() {
1875 } 1878 }
1876 } 1879 }
1877} 1880}
1881
1882#[cfg(test)]
1883mod tests {
1884 use super::*;
1885
1886 #[cfg(feature = "rp2040")]
1887 #[test]
1888 fn test_voltage_scale_bod_values() {
1889 // Test that each voltage level maps to the correct BOD threshold (approx. 90% of VDD)
1890 // This verifies our BOD settings match our documentation
1891 {
1892 assert_eq!(VoltageScale::V0_85.recommended_bod(), 0b0111); // ~0.774V (91% of 0.85V)
1893 assert_eq!(VoltageScale::V0_90.recommended_bod(), 0b1000); // ~0.817V (91% of 0.90V)
1894 assert_eq!(VoltageScale::V0_95.recommended_bod(), 0b1001); // ~0.860V (91% of 0.95V)
1895 assert_eq!(VoltageScale::V1_00.recommended_bod(), 0b1010); // ~0.903V (90% of 1.00V)
1896 assert_eq!(VoltageScale::V1_05.recommended_bod(), 0b1011); // ~0.946V (90% of 1.05V)
1897 assert_eq!(VoltageScale::V1_10.recommended_bod(), 0b1100); // ~0.989V (90% of 1.10V)
1898 assert_eq!(VoltageScale::V1_15.recommended_bod(), 0b1101); // ~1.032V (90% of 1.15V)
1899 assert_eq!(VoltageScale::V1_20.recommended_bod(), 0b1110); // ~1.075V (90% of 1.20V)
1900 assert_eq!(VoltageScale::V1_25.recommended_bod(), 0b1111); // ~1.118V (89% of 1.25V)
1901 assert_eq!(VoltageScale::V1_30.recommended_bod(), 0b1111); // ~1.118V (86% of 1.30V) - using max available
1902 }
1903 }
1904
1905 #[cfg(feature = "rp2040")]
1906 #[test]
1907 fn test_find_pll_params() {
1908 #[cfg(feature = "rp2040")]
1909 {
1910 // Test standard 125 MHz configuration with 12 MHz crystal
1911 let params = find_pll_params(12_000_000, 125_000_000).unwrap();
1912 assert_eq!(params.refdiv, 1);
1913 assert_eq!(params.fbdiv, 125);
1914
1915 // Test USB PLL configuration for 48MHz
1916 // The algorithm may find different valid parameters than the SDK defaults
1917 // We'll check that it generates a valid configuration that produces 48MHz
1918 let params = find_pll_params(12_000_000, 48_000_000).unwrap();
1919 assert_eq!(params.refdiv, 1);
1920
1921 // Calculate the actual output frequency
1922 let ref_freq = 12_000_000 / params.refdiv as u32;
1923 let vco_freq = ref_freq as u64 * params.fbdiv as u64;
1924 let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;
1925
1926 // Verify the output frequency is correct
1927 assert_eq!(output_freq, 48_000_000);
1928
1929 // Verify VCO frequency is in valid range
1930 assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000);
1931
1932 // Test overclocked configuration for 200 MHz
1933 let params = find_pll_params(12_000_000, 200_000_000).unwrap();
1934 assert_eq!(params.refdiv, 1);
1935 let vco_freq = 12_000_000 as u64 * params.fbdiv as u64;
1936 let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;
1937 assert_eq!(output_freq, 200_000_000);
1938 assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); // VCO in valid range
1939
1940 // Test non-standard crystal with 16 MHz
1941 let params = find_pll_params(16_000_000, 125_000_000).unwrap();
1942 let vco_freq = (16_000_000 / params.refdiv as u32) as u64 * params.fbdiv as u64;
1943 let output_freq = (vco_freq / ((params.post_div1 * params.post_div2) as u64)) as u32;
1944
1945 // With a 16 MHz crystal, we might not get exactly 125 MHz
1946 // Check that it's close enough (within 0.2% margin)
1947 let freq_diff = if output_freq > 125_000_000 {
1948 output_freq - 125_000_000
1949 } else {
1950 125_000_000 - output_freq
1951 };
1952 let error_percentage = (freq_diff as f64 / 125_000_000.0) * 100.0;
1953 assert!(
1954 error_percentage < 0.2,
1955 "Output frequency {} is not close enough to target 125 MHz. Error: {:.2}%",
1956 output_freq,
1957 error_percentage
1958 );
1959
1960 assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000);
1961 }
1962 }
1963
1964 #[cfg(feature = "rp2040")]
1965 #[test]
1966 fn test_pll_config_validation() {
1967 // Test PLL configuration validation logic
1968 let valid_config = PllConfig {
1969 refdiv: 1,
1970 fbdiv: 125,
1971 post_div1: 6,
1972 post_div2: 2,
1973 };
1974
1975 // Valid configuration should pass validation
1976 assert!(valid_config.is_valid(12_000_000));
1977
1978 // Test fbdiv constraints
1979 let mut invalid_config = valid_config;
1980 invalid_config.fbdiv = 15; // Below minimum of 16
1981 assert!(!invalid_config.is_valid(12_000_000));
1982
1983 invalid_config.fbdiv = 321; // Above maximum of 320
1984 assert!(!invalid_config.is_valid(12_000_000));
1985
1986 // Test post_div constraints
1987 invalid_config = valid_config;
1988 invalid_config.post_div1 = 0; // Below minimum of 1
1989 assert!(!invalid_config.is_valid(12_000_000));
1990
1991 invalid_config = valid_config;
1992 invalid_config.post_div1 = 8; // Above maximum of 7
1993 assert!(!invalid_config.is_valid(12_000_000));
1994
1995 // Test post_div2 must be <= post_div1
1996 invalid_config = valid_config;
1997 invalid_config.post_div2 = 7;
1998 invalid_config.post_div1 = 3;
1999 assert!(!invalid_config.is_valid(12_000_000));
2000
2001 // Test reference frequency constraints
2002 invalid_config = valid_config;
2003 assert!(!invalid_config.is_valid(4_000_000)); // Below minimum of 5 MHz
2004 assert!(!invalid_config.is_valid(900_000_000)); // Above maximum of 800 MHz
2005
2006 // Test VCO frequency constraints
2007 invalid_config = valid_config;
2008 invalid_config.fbdiv = 16;
2009 assert!(!invalid_config.is_valid(12_000_000)); // VCO too low: 12MHz * 16 = 192MHz
2010
2011 // Test VCO frequency constraints - too high
2012 invalid_config = valid_config;
2013 invalid_config.fbdiv = 200;
2014 invalid_config.refdiv = 1;
2015 // This should be INVALID: 12MHz * 200 = 2400MHz exceeds max VCO of 1800MHz
2016 assert!(!invalid_config.is_valid(12_000_000));
2017
2018 // Test a valid high VCO configuration
2019 invalid_config.fbdiv = 150; // 12MHz * 150 = 1800MHz, exactly at the limit
2020 assert!(invalid_config.is_valid(12_000_000));
2021 }
2022
2023 #[cfg(feature = "rp2040")]
2024 #[test]
2025 fn test_manual_pll_helper() {
2026 {
2027 // Test the new manual_pll helper method
2028 let config = ClockConfig::manual_pll(
2029 12_000_000,
2030 PllConfig {
2031 refdiv: 1,
2032 fbdiv: 100,
2033 post_div1: 3,
2034 post_div2: 2,
2035 },
2036 Some(VoltageScale::V1_15),
2037 );
2038
2039 // Check voltage scale was set correctly
2040 assert_eq!(config.voltage_scale, Some(VoltageScale::V1_15));
2041
2042 // Check PLL config was set correctly
2043 assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().refdiv, 1);
2044 assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().fbdiv, 100);
2045 assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().post_div1, 3);
2046 assert_eq!(config.xosc.as_ref().unwrap().sys_pll.as_ref().unwrap().post_div2, 2);
2047
2048 // Check we get the expected frequency
2049 assert_eq!(
2050 config
2051 .xosc
2052 .as_ref()
2053 .unwrap()
2054 .sys_pll
2055 .as_ref()
2056 .unwrap()
2057 .output_frequency(12_000_000),
2058 200_000_000
2059 );
2060 }
2061 }
2062
2063 #[cfg(feature = "rp2040")]
2064 #[test]
2065 fn test_auto_voltage_scaling() {
2066 {
2067 // Test automatic voltage scaling based on frequency
2068 // Under 133 MHz should use default voltage (None)
2069 let config = ClockConfig::at_sys_frequency_mhz(125);
2070 assert_eq!(config.voltage_scale, None);
2071
2072 // 133-200 MHz should use V1_15
2073 let config = ClockConfig::at_sys_frequency_mhz(150);
2074 assert_eq!(config.voltage_scale, Some(VoltageScale::V1_15));
2075 let config = ClockConfig::at_sys_frequency_mhz(200);
2076 assert_eq!(config.voltage_scale, Some(VoltageScale::V1_15));
2077
2078 // Above 200 MHz should use V1_20
2079 let config = ClockConfig::at_sys_frequency_mhz(250);
2080 assert_eq!(config.voltage_scale, Some(VoltageScale::V1_20));
2081 }
2082 }
2083}