mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-28 04:40:39 +00:00
Add ClockError enum and update system_freq to return Result for error handling
This commit is contained in:
parent
133500167c
commit
79e452922a
@ -82,6 +82,18 @@ use crate::{pac, reset, Peri};
|
|||||||
// be very useful until we have runtime clock reconfiguration. once this
|
// be very useful until we have runtime clock reconfiguration. once this
|
||||||
// happens we can resurrect the commented-out gpin bits.
|
// happens we can resurrect the commented-out gpin bits.
|
||||||
|
|
||||||
|
/// Clock error types.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum ClockError {
|
||||||
|
/// PLL failed to lock within the timeout period.
|
||||||
|
PllLockTimedOut,
|
||||||
|
/// Could not find valid PLL parameters for system clock.
|
||||||
|
InvalidPllParameters,
|
||||||
|
/// Reading the core voltage failed due to an unexpected value in the register.
|
||||||
|
UnexpectedCoreVoltageRead,
|
||||||
|
}
|
||||||
|
|
||||||
struct Clocks {
|
struct Clocks {
|
||||||
xosc: AtomicU32,
|
xosc: AtomicU32,
|
||||||
sys: AtomicU32,
|
sys: AtomicU32,
|
||||||
@ -144,8 +156,9 @@ pub enum PeriClkSrc {
|
|||||||
/// **Note**: For RP235x the maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit
|
/// **Note**: For RP235x the maximum voltage is 1.30V, unless unlocked by setting unless the voltage limit
|
||||||
/// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this
|
/// is disabled using the disable_voltage_limit field in the vreg_ctrl register. For lack of practical use at this
|
||||||
/// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now.
|
/// point in time, this is not implemented here. So the maximum voltage in this enum is 1.30V for now.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum CoreVoltage {
|
pub enum CoreVoltage {
|
||||||
// RP2040 voltage levels
|
// RP2040 voltage levels
|
||||||
#[cfg(feature = "rp2040")]
|
#[cfg(feature = "rp2040")]
|
||||||
@ -468,7 +481,7 @@ impl ClockConfig {
|
|||||||
/// # Returns
|
/// # Returns
|
||||||
///
|
///
|
||||||
/// A ClockConfig configured to achieve the requested system frequency using the
|
/// A ClockConfig configured to achieve the requested system frequency using the
|
||||||
/// the usual 12Mhz crystal, or panic if no valid parameters can be found.
|
/// the usual 12Mhz crystal, or an error if no valid parameters can be found.
|
||||||
///
|
///
|
||||||
/// # Note on core voltage:
|
/// # Note on core voltage:
|
||||||
///
|
///
|
||||||
@ -482,7 +495,11 @@ impl ClockConfig {
|
|||||||
/// **For RP235x**:
|
/// **For RP235x**:
|
||||||
/// At this point in time there is no official manufacturer endorsement for running the chip on other core voltages and/or other clock speeds than the defaults.
|
/// At this point in time there is no official manufacturer endorsement for running the chip on other core voltages and/or other clock speeds than the defaults.
|
||||||
/// Using this function is experimental and may not work as expected or even damage the chip.
|
/// Using this function is experimental and may not work as expected or even damage the chip.
|
||||||
pub fn system_freq(hz: u32) -> Self {
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A Result containing either the configured ClockConfig or a ClockError.
|
||||||
|
pub fn system_freq(hz: u32) -> Result<Self, ClockError> {
|
||||||
// Start with the standard configuration from crystal()
|
// Start with the standard configuration from crystal()
|
||||||
const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000;
|
const DEFAULT_CRYSTAL_HZ: u32 = 12_000_000;
|
||||||
let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ);
|
let mut config = Self::crystal(DEFAULT_CRYSTAL_HZ);
|
||||||
@ -491,16 +508,15 @@ impl ClockConfig {
|
|||||||
// (which is what crystal() configures by default)
|
// (which is what crystal() configures by default)
|
||||||
#[cfg(feature = "rp2040")]
|
#[cfg(feature = "rp2040")]
|
||||||
if hz == 125_000_000 {
|
if hz == 125_000_000 {
|
||||||
return config;
|
return Ok(config);
|
||||||
}
|
}
|
||||||
#[cfg(feature = "_rp235x")]
|
#[cfg(feature = "_rp235x")]
|
||||||
if hz == 150_000_000 {
|
if hz == 150_000_000 {
|
||||||
return config;
|
return Ok(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find optimal PLL parameters for the requested frequency
|
// Find optimal PLL parameters for the requested frequency
|
||||||
let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz)
|
let sys_pll_params = find_pll_params(DEFAULT_CRYSTAL_HZ, hz).ok_or(ClockError::InvalidPllParameters)?;
|
||||||
.unwrap_or_else(|| panic!("Could not find valid PLL parameters for system clock"));
|
|
||||||
|
|
||||||
// Replace the sys_pll configuration with our custom parameters
|
// Replace the sys_pll configuration with our custom parameters
|
||||||
if let Some(xosc) = &mut config.xosc {
|
if let Some(xosc) = &mut config.xosc {
|
||||||
@ -525,7 +541,7 @@ impl ClockConfig {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
config
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Configure with manual PLL settings for full control over system clock
|
/// Configure with manual PLL settings for full control over system clock
|
||||||
@ -620,6 +636,7 @@ impl ClockConfig {
|
|||||||
#[repr(u16)]
|
#[repr(u16)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum RoscRange {
|
pub enum RoscRange {
|
||||||
/// Low range.
|
/// Low range.
|
||||||
Low = pac::rosc::vals::FreqRange::LOW.0,
|
Low = pac::rosc::vals::FreqRange::LOW.0,
|
||||||
@ -726,6 +743,7 @@ pub struct RefClkConfig {
|
|||||||
/// Reference clock source.
|
/// Reference clock source.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum RefClkSrc {
|
pub enum RefClkSrc {
|
||||||
/// XOSC.
|
/// XOSC.
|
||||||
Xosc,
|
Xosc,
|
||||||
@ -741,6 +759,7 @@ pub enum RefClkSrc {
|
|||||||
/// SYS clock source.
|
/// SYS clock source.
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum SysClkSrc {
|
pub enum SysClkSrc {
|
||||||
/// REF.
|
/// REF.
|
||||||
Ref,
|
Ref,
|
||||||
@ -779,6 +798,7 @@ pub struct SysClkConfig {
|
|||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum UsbClkSrc {
|
pub enum UsbClkSrc {
|
||||||
/// PLL USB.
|
/// PLL USB.
|
||||||
PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
PllUsb = ClkUsbCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
||||||
@ -807,6 +827,7 @@ pub struct UsbClkConfig {
|
|||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub enum AdcClkSrc {
|
pub enum AdcClkSrc {
|
||||||
/// PLL USB.
|
/// PLL USB.
|
||||||
PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
PllUsb = ClkAdcCtrlAuxsrc::CLKSRC_PLL_USB as _,
|
||||||
@ -835,6 +856,7 @@ pub struct AdcClkConfig {
|
|||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
#[cfg(feature = "rp2040")]
|
#[cfg(feature = "rp2040")]
|
||||||
pub enum RtcClkSrc {
|
pub enum RtcClkSrc {
|
||||||
/// PLL USB.
|
/// PLL USB.
|
||||||
@ -1084,14 +1106,20 @@ pub(crate) unsafe fn init(config: ClockConfig) {
|
|||||||
let pll_sys_freq = match config.sys_pll {
|
let pll_sys_freq = match config.sys_pll {
|
||||||
Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) {
|
Some(sys_pll_config) => match configure_pll(pac::PLL_SYS, config.hz, sys_pll_config) {
|
||||||
Ok(freq) => freq,
|
Ok(freq) => freq,
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
Err(e) => panic!("Failed to configure PLL_SYS: {}", e),
|
Err(e) => panic!("Failed to configure PLL_SYS: {}", e),
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
Err(_e) => panic!("Failed to configure PLL_SYS"),
|
||||||
},
|
},
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
let pll_usb_freq = match config.usb_pll {
|
let pll_usb_freq = match config.usb_pll {
|
||||||
Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) {
|
Some(usb_pll_config) => match configure_pll(pac::PLL_USB, config.hz, usb_pll_config) {
|
||||||
Ok(freq) => freq,
|
Ok(freq) => freq,
|
||||||
|
#[cfg(feature = "defmt")]
|
||||||
Err(e) => panic!("Failed to configure PLL_USB: {}", e),
|
Err(e) => panic!("Failed to configure PLL_USB: {}", e),
|
||||||
|
#[cfg(not(feature = "defmt"))]
|
||||||
|
Err(_e) => panic!("Failed to configure PLL_USB"),
|
||||||
},
|
},
|
||||||
None => 0,
|
None => 0,
|
||||||
};
|
};
|
||||||
@ -1401,7 +1429,7 @@ pub fn clk_rtc_freq() -> u16 {
|
|||||||
///
|
///
|
||||||
/// Returns the current core voltage or an error if the voltage register
|
/// Returns the current core voltage or an error if the voltage register
|
||||||
/// contains an unknown value.
|
/// contains an unknown value.
|
||||||
pub fn core_voltage() -> Result<CoreVoltage, &'static str> {
|
pub fn core_voltage() -> Result<CoreVoltage, ClockError> {
|
||||||
#[cfg(feature = "rp2040")]
|
#[cfg(feature = "rp2040")]
|
||||||
{
|
{
|
||||||
let vreg = pac::VREG_AND_CHIP_RESET;
|
let vreg = pac::VREG_AND_CHIP_RESET;
|
||||||
@ -1418,7 +1446,7 @@ pub fn core_voltage() -> Result<CoreVoltage, &'static str> {
|
|||||||
0b1101 => Ok(CoreVoltage::V1_20),
|
0b1101 => Ok(CoreVoltage::V1_20),
|
||||||
0b1110 => Ok(CoreVoltage::V1_25),
|
0b1110 => Ok(CoreVoltage::V1_25),
|
||||||
0b1111 => Ok(CoreVoltage::V1_30),
|
0b1111 => Ok(CoreVoltage::V1_30),
|
||||||
_ => Err("Unexpected value in register"),
|
_ => Err(ClockError::UnexpectedCoreVoltageRead),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1443,7 +1471,7 @@ pub fn core_voltage() -> Result<CoreVoltage, &'static str> {
|
|||||||
0b01101 => Ok(CoreVoltage::V1_20),
|
0b01101 => Ok(CoreVoltage::V1_20),
|
||||||
0b01110 => Ok(CoreVoltage::V1_25),
|
0b01110 => Ok(CoreVoltage::V1_25),
|
||||||
0b01111 => Ok(CoreVoltage::V1_30),
|
0b01111 => Ok(CoreVoltage::V1_30),
|
||||||
_ => Err("Unexpected value in register"),
|
_ => Err(ClockError::UnexpectedCoreVoltageRead),
|
||||||
// see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point
|
// see CoreVoltage: we do not support setting Voltages higher than 1.30V at this point
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1461,7 +1489,7 @@ fn start_xosc(crystal_hz: u32, delay_multiplier: u32) {
|
|||||||
|
|
||||||
/// PLL (Phase-Locked Loop) configuration
|
/// PLL (Phase-Locked Loop) configuration
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, &'static str> {
|
fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result<u32, ClockError> {
|
||||||
// Calculate reference frequency
|
// Calculate reference frequency
|
||||||
let ref_freq = input_freq / config.refdiv as u32;
|
let ref_freq = input_freq / config.refdiv as u32;
|
||||||
|
|
||||||
@ -1532,7 +1560,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> Result
|
|||||||
timeout -= 1;
|
timeout -= 1;
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
// PLL failed to lock, return 0 to indicate failure
|
// PLL failed to lock, return 0 to indicate failure
|
||||||
return Err("PLL failed to lock");
|
return Err(ClockError::PllLockTimedOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2103,21 +2131,21 @@ mod tests {
|
|||||||
{
|
{
|
||||||
// Test automatic voltage scaling based on frequency
|
// Test automatic voltage scaling based on frequency
|
||||||
// Under 133 MHz should use default voltage (V1_10)
|
// Under 133 MHz should use default voltage (V1_10)
|
||||||
let config = ClockConfig::system_freq(125_000_000);
|
let config = ClockConfig::system_freq(125_000_000).unwrap();
|
||||||
assert_eq!(config.core_voltage, CoreVoltage::V1_10);
|
assert_eq!(config.core_voltage, CoreVoltage::V1_10);
|
||||||
|
|
||||||
// 133-200 MHz should use V1_15
|
// 133-200 MHz should use V1_15
|
||||||
let config = ClockConfig::system_freq(150_000_000);
|
let config = ClockConfig::system_freq(150_000_000).unwrap();
|
||||||
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
|
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
|
||||||
let config = ClockConfig::system_freq(200_000_000);
|
let config = ClockConfig::system_freq(200_000_000).unwrap();
|
||||||
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
|
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
|
||||||
|
|
||||||
// Above 200 MHz should use V1_25
|
// Above 200 MHz should use V1_15
|
||||||
let config = ClockConfig::system_freq(250_000_000);
|
let config = ClockConfig::system_freq(250_000_000).unwrap();
|
||||||
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
|
assert_eq!(config.core_voltage, CoreVoltage::V1_15);
|
||||||
|
|
||||||
// Below 125 MHz should use V1_10
|
// Below 125 MHz should use V1_10
|
||||||
let config = ClockConfig::system_freq(100_000_000);
|
let config = ClockConfig::system_freq(100_000_000).unwrap();
|
||||||
assert_eq!(config.core_voltage, CoreVoltage::V1_10);
|
assert_eq!(config.core_voltage, CoreVoltage::V1_10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ const COUNT_TO: i64 = 10_000_000;
|
|||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) -> ! {
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
// Set up for clock frequency of 200 MHz, setting all necessary defaults.
|
// Set up for clock frequency of 200 MHz, setting all necessary defaults.
|
||||||
let config = Config::new(ClockConfig::system_freq(200_000_000));
|
let config = Config::new(ClockConfig::system_freq(200_000_000).unwrap());
|
||||||
|
|
||||||
// Initialize the peripherals
|
// Initialize the peripherals
|
||||||
let p = embassy_rp::init(config);
|
let p = embassy_rp::init(config);
|
||||||
|
@ -23,7 +23,7 @@ const COUNT_TO: i64 = 10_000_000;
|
|||||||
#[embassy_executor::main]
|
#[embassy_executor::main]
|
||||||
async fn main(_spawner: Spawner) -> ! {
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
// Set up for clock frequency of 200 MHz, setting all necessary defaults.
|
// Set up for clock frequency of 200 MHz, setting all necessary defaults.
|
||||||
let mut config = Config::new(ClockConfig::system_freq(200_000_000));
|
let mut config = Config::new(ClockConfig::system_freq(200_000_000).unwrap());
|
||||||
|
|
||||||
// since for the rp235x there is no official support for higher clock frequencies, `system_freq()` will not set a voltage for us.
|
// since for the rp235x there is no official support for higher clock frequencies, `system_freq()` will not set a voltage for us.
|
||||||
// We need to guess the core voltage, that is needed for the higher clock frequency. Going with a small increase from the default 1.1V here, based on
|
// We need to guess the core voltage, that is needed for the higher clock frequency. Going with a small increase from the default 1.1V here, based on
|
||||||
|
@ -20,7 +20,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let mut config = Config::default();
|
let mut config = Config::default();
|
||||||
|
|
||||||
// Initialize with 200MHz clock configuration
|
// Initialize with 200MHz clock configuration
|
||||||
config.clocks = ClockConfig::system_freq(200_000_000);
|
config.clocks = ClockConfig::system_freq(200_000_000).unwrap();
|
||||||
|
|
||||||
// if we are rp235x, we need to manually set the core voltage. rp2040 should do this automatically
|
// if we are rp235x, we need to manually set the core voltage. rp2040 should do this automatically
|
||||||
#[cfg(feature = "rp235xb")]
|
#[cfg(feature = "rp235xb")]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user