Merge pull request #4150 from 1-rafael-1/rp2040-overclocking
RP: rp2040 overclocking
This commit is contained in:
commit
11364077a7
File diff suppressed because it is too large
Load Diff
25
embassy-rp/src/pio_programs/clock_divider.rs
Normal file
25
embassy-rp/src/pio_programs/clock_divider.rs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//! Helper functions for calculating PIO clock dividers
|
||||||
|
|
||||||
|
use fixed::traits::ToFixed;
|
||||||
|
use fixed::types::extra::U8;
|
||||||
|
|
||||||
|
use crate::clocks::clk_sys_freq;
|
||||||
|
|
||||||
|
/// Calculate a PIO clock divider value based on the desired target frequency.
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
///
|
||||||
|
/// * `target_hz` - The desired PIO clock frequency in Hz
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
///
|
||||||
|
/// A fixed-point divider value suitable for use in a PIO state machine configuration
|
||||||
|
#[inline]
|
||||||
|
pub fn calculate_pio_clock_divider(target_hz: u32) -> fixed::FixedU32<U8> {
|
||||||
|
// Requires a non-zero frequency
|
||||||
|
assert!(target_hz > 0, "PIO clock frequency cannot be zero");
|
||||||
|
|
||||||
|
// Calculate the divider
|
||||||
|
let divider = (clk_sys_freq() + target_hz / 2) / target_hz;
|
||||||
|
divider.to_fixed()
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@ use crate::pio::{
|
|||||||
Common, Config, Direction, FifoJoin, Instance, Irq, LoadedProgram, PioPin, ShiftConfig, ShiftDirection,
|
Common, Config, Direction, FifoJoin, Instance, Irq, LoadedProgram, PioPin, ShiftConfig, ShiftDirection,
|
||||||
StateMachine,
|
StateMachine,
|
||||||
};
|
};
|
||||||
|
use crate::pio_programs::clock_divider::calculate_pio_clock_divider;
|
||||||
use crate::Peri;
|
use crate::Peri;
|
||||||
|
|
||||||
/// This struct represents a HD44780 program that takes command words (<wait:24> <command:4> <0:4>)
|
/// This struct represents a HD44780 program that takes command words (<wait:24> <command:4> <0:4>)
|
||||||
@ -134,7 +135,10 @@ impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> {
|
|||||||
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&word_prg.prg, &[&e]);
|
cfg.use_program(&word_prg.prg, &[&e]);
|
||||||
cfg.clock_divider = 125u8.into();
|
|
||||||
|
// Target 1 MHz PIO clock (each cycle is 1µs)
|
||||||
|
cfg.clock_divider = calculate_pio_clock_divider(1_000_000);
|
||||||
|
|
||||||
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
||||||
cfg.shift_out = ShiftConfig {
|
cfg.shift_out = ShiftConfig {
|
||||||
auto_fill: true,
|
auto_fill: true,
|
||||||
@ -160,7 +164,10 @@ impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> {
|
|||||||
|
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.use_program(&seq_prg.prg, &[&e]);
|
cfg.use_program(&seq_prg.prg, &[&e]);
|
||||||
cfg.clock_divider = 8u8.into(); // ~64ns/insn
|
|
||||||
|
// Target ~15.6 MHz PIO clock (~64ns/insn)
|
||||||
|
cfg.clock_divider = calculate_pio_clock_divider(15_600_000);
|
||||||
|
|
||||||
cfg.set_jmp_pin(&db7);
|
cfg.set_jmp_pin(&db7);
|
||||||
cfg.set_set_pins(&[&rs, &rw]);
|
cfg.set_set_pins(&[&rs, &rw]);
|
||||||
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
//! Pre-built pio programs for common interfaces
|
//! Pre-built pio programs for common interfaces
|
||||||
|
|
||||||
|
pub mod clock_divider;
|
||||||
pub mod hd44780;
|
pub mod hd44780;
|
||||||
pub mod i2s;
|
pub mod i2s;
|
||||||
pub mod onewire;
|
pub mod onewire;
|
||||||
|
|||||||
@ -1,11 +1,10 @@
|
|||||||
//! PIO backed quadrature encoder
|
//! PIO backed quadrature encoder
|
||||||
|
|
||||||
use fixed::traits::ToFixed;
|
|
||||||
|
|
||||||
use crate::gpio::Pull;
|
use crate::gpio::Pull;
|
||||||
use crate::pio::{
|
use crate::pio::{
|
||||||
Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine,
|
Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine,
|
||||||
};
|
};
|
||||||
|
use crate::pio_programs::clock_divider::calculate_pio_clock_divider;
|
||||||
use crate::Peri;
|
use crate::Peri;
|
||||||
|
|
||||||
/// This struct represents an Encoder program loaded into pio instruction memory.
|
/// This struct represents an Encoder program loaded into pio instruction memory.
|
||||||
@ -48,7 +47,10 @@ impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> {
|
|||||||
cfg.set_in_pins(&[&pin_a, &pin_b]);
|
cfg.set_in_pins(&[&pin_a, &pin_b]);
|
||||||
cfg.fifo_join = FifoJoin::RxOnly;
|
cfg.fifo_join = FifoJoin::RxOnly;
|
||||||
cfg.shift_in.direction = ShiftDirection::Left;
|
cfg.shift_in.direction = ShiftDirection::Left;
|
||||||
cfg.clock_divider = 10_000.to_fixed();
|
|
||||||
|
// Target 12.5 KHz PIO clock
|
||||||
|
cfg.clock_divider = calculate_pio_clock_divider(12_500);
|
||||||
|
|
||||||
cfg.use_program(&program.prg, &[]);
|
cfg.use_program(&program.prg, &[]);
|
||||||
sm.set_config(&cfg);
|
sm.set_config(&cfg);
|
||||||
sm.set_enable(true);
|
sm.set_enable(true);
|
||||||
|
|||||||
@ -2,11 +2,8 @@
|
|||||||
|
|
||||||
use core::mem::{self, MaybeUninit};
|
use core::mem::{self, MaybeUninit};
|
||||||
|
|
||||||
use fixed::traits::ToFixed;
|
|
||||||
use fixed::types::extra::U8;
|
|
||||||
use fixed::FixedU32;
|
|
||||||
|
|
||||||
use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine};
|
use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine};
|
||||||
|
use crate::pio_programs::clock_divider::calculate_pio_clock_divider;
|
||||||
use crate::Peri;
|
use crate::Peri;
|
||||||
|
|
||||||
/// This struct represents a Stepper driver program loaded into pio instruction memory.
|
/// This struct represents a Stepper driver program loaded into pio instruction memory.
|
||||||
@ -64,7 +61,9 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
|
|||||||
sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
|
sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]);
|
||||||
let mut cfg = Config::default();
|
let mut cfg = Config::default();
|
||||||
cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
|
cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]);
|
||||||
cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed();
|
|
||||||
|
cfg.clock_divider = calculate_pio_clock_divider(100 * 136);
|
||||||
|
|
||||||
cfg.use_program(&program.prg, &[]);
|
cfg.use_program(&program.prg, &[]);
|
||||||
sm.set_config(&cfg);
|
sm.set_config(&cfg);
|
||||||
sm.set_enable(true);
|
sm.set_enable(true);
|
||||||
@ -73,9 +72,11 @@ impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> {
|
|||||||
|
|
||||||
/// Set pulse frequency
|
/// Set pulse frequency
|
||||||
pub fn set_frequency(&mut self, freq: u32) {
|
pub fn set_frequency(&mut self, freq: u32) {
|
||||||
let clock_divider: FixedU32<U8> = (125_000_000 / (freq * 136)).to_fixed();
|
let clock_divider = calculate_pio_clock_divider(freq * 136);
|
||||||
assert!(clock_divider <= 65536, "clkdiv must be <= 65536");
|
let divider_f32 = clock_divider.to_num::<f32>();
|
||||||
assert!(clock_divider >= 1, "clkdiv must be >= 1");
|
assert!(divider_f32 <= 65536.0, "clkdiv must be <= 65536");
|
||||||
|
assert!(divider_f32 >= 1.0, "clkdiv must be >= 1");
|
||||||
|
|
||||||
self.sm.set_clock_divider(clock_divider);
|
self.sm.set_clock_divider(clock_divider);
|
||||||
self.sm.clkdiv_restart();
|
self.sm.clkdiv_restart();
|
||||||
}
|
}
|
||||||
|
|||||||
64
examples/rp/src/bin/overclock.rs
Normal file
64
examples/rp/src/bin/overclock.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
//! # Overclocking the RP2040 to 200 MHz
|
||||||
|
//!
|
||||||
|
//! This example demonstrates how to configure the RP2040 to run at 200 MHz.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::clocks::{clk_sys_freq, ClockConfig};
|
||||||
|
use embassy_rp::config::Config;
|
||||||
|
use embassy_rp::gpio::{Level, Output};
|
||||||
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
const COUNT_TO: i64 = 10_000_000;
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
// Set up for clock frequency of 200 MHz, setting all necessary defaults.
|
||||||
|
let config = Config::new(ClockConfig::system_freq(200_000_000));
|
||||||
|
|
||||||
|
// Show the voltage scale for verification
|
||||||
|
info!("System core voltage: {}", Debug2Format(&config.clocks.core_voltage));
|
||||||
|
|
||||||
|
// Initialize the peripherals
|
||||||
|
let p = embassy_rp::init(config);
|
||||||
|
|
||||||
|
// Show CPU frequency for verification
|
||||||
|
let sys_freq = clk_sys_freq();
|
||||||
|
info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
|
||||||
|
|
||||||
|
// LED to indicate the system is running
|
||||||
|
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Reset the counter at the start of measurement period
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
// Turn LED on while counting
|
||||||
|
led.set_high();
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
// This is a busy loop that will take some time to complete
|
||||||
|
while counter < COUNT_TO {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let elapsed = Instant::now() - start;
|
||||||
|
|
||||||
|
// Report the elapsed time
|
||||||
|
led.set_low();
|
||||||
|
info!(
|
||||||
|
"At {}Mhz: Elapsed time to count to {}: {}ms",
|
||||||
|
sys_freq / 1_000_000,
|
||||||
|
counter,
|
||||||
|
elapsed.as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wait 2 seconds before starting the next measurement
|
||||||
|
Timer::after(Duration::from_secs(2)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
79
examples/rp/src/bin/overclock_manual.rs
Normal file
79
examples/rp/src/bin/overclock_manual.rs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
//! # Overclocking the RP2040 to 200 MHz manually
|
||||||
|
//!
|
||||||
|
//! This example demonstrates how to manually configure the RP2040 to run at 200 MHz.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use defmt::*;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::clocks;
|
||||||
|
use embassy_rp::clocks::{ClockConfig, CoreVoltage, PllConfig};
|
||||||
|
use embassy_rp::config::Config;
|
||||||
|
use embassy_rp::gpio::{Level, Output};
|
||||||
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
const COUNT_TO: i64 = 10_000_000;
|
||||||
|
|
||||||
|
/// Configure the RP2040 for 200 MHz operation by manually specifying the PLL settings.
|
||||||
|
fn configure_manual_overclock() -> Config {
|
||||||
|
// Set the PLL configuration manually, starting from default values
|
||||||
|
let mut config = Config::default();
|
||||||
|
|
||||||
|
// Set the system clock to 200 MHz
|
||||||
|
config.clocks = ClockConfig::manual_pll(
|
||||||
|
12_000_000, // Crystal frequency, 12 MHz is common. If using custom, set to your value.
|
||||||
|
PllConfig {
|
||||||
|
refdiv: 1, // Reference divider
|
||||||
|
fbdiv: 100, // Feedback divider
|
||||||
|
post_div1: 3, // Post divider 1
|
||||||
|
post_div2: 2, // Post divider 2
|
||||||
|
},
|
||||||
|
CoreVoltage::V1_15, // Core voltage, should be set to V1_15 for 200 MHz
|
||||||
|
);
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) -> ! {
|
||||||
|
// Initialize with our manual overclock configuration
|
||||||
|
let p = embassy_rp::init(configure_manual_overclock());
|
||||||
|
|
||||||
|
// Verify the actual system clock frequency
|
||||||
|
let sys_freq = clocks::clk_sys_freq();
|
||||||
|
info!("System clock frequency: {} MHz", sys_freq / 1_000_000);
|
||||||
|
|
||||||
|
// LED to indicate the system is running
|
||||||
|
let mut led = Output::new(p.PIN_25, Level::Low);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Reset the counter at the start of measurement period
|
||||||
|
let mut counter = 0;
|
||||||
|
|
||||||
|
// Turn LED on while counting
|
||||||
|
led.set_high();
|
||||||
|
|
||||||
|
let start = Instant::now();
|
||||||
|
|
||||||
|
// This is a busy loop that will take some time to complete
|
||||||
|
while counter < COUNT_TO {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let elapsed = Instant::now() - start;
|
||||||
|
|
||||||
|
// Report the elapsed time
|
||||||
|
led.set_low();
|
||||||
|
info!(
|
||||||
|
"At {}Mhz: Elapsed time to count to {}: {}ms",
|
||||||
|
sys_freq / 1_000_000,
|
||||||
|
counter,
|
||||||
|
elapsed.as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Wait 2 seconds before starting the next measurement
|
||||||
|
Timer::after(Duration::from_secs(2)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
70
tests/rp/src/bin/overclock.rs
Normal file
70
tests/rp/src/bin/overclock.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
#[cfg(feature = "rp2040")]
|
||||||
|
teleprobe_meta::target!(b"rpi-pico");
|
||||||
|
#[cfg(feature = "rp235xb")]
|
||||||
|
teleprobe_meta::target!(b"pimoroni-pico-plus-2");
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
#[cfg(feature = "rp2040")]
|
||||||
|
use defmt::{assert, assert_eq};
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::clocks;
|
||||||
|
#[cfg(feature = "rp2040")]
|
||||||
|
use embassy_rp::clocks::ClockConfig;
|
||||||
|
#[cfg(feature = "rp2040")]
|
||||||
|
use embassy_rp::clocks::CoreVoltage;
|
||||||
|
use embassy_rp::config::Config;
|
||||||
|
use embassy_time::Instant;
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
const COUNT_TO: i64 = 10_000_000;
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
#[cfg(feature = "rp2040")]
|
||||||
|
let mut config = Config::default();
|
||||||
|
#[cfg(not(feature = "rp2040"))]
|
||||||
|
let config = Config::default();
|
||||||
|
|
||||||
|
// Initialize with 200MHz clock configuration for RP2040, other chips will use default clock
|
||||||
|
#[cfg(feature = "rp2040")]
|
||||||
|
{
|
||||||
|
config.clocks = ClockConfig::system_freq(200_000_000);
|
||||||
|
let voltage = config.clocks.core_voltage;
|
||||||
|
assert!(matches!(voltage, CoreVoltage::V1_15), "Expected voltage scale V1_15");
|
||||||
|
}
|
||||||
|
|
||||||
|
let _p = embassy_rp::init(config);
|
||||||
|
|
||||||
|
// Test the system speed
|
||||||
|
let (time_elapsed, clk_sys_freq) = {
|
||||||
|
let mut counter = 0;
|
||||||
|
let start = Instant::now();
|
||||||
|
while counter < COUNT_TO {
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
let elapsed = Instant::now() - start;
|
||||||
|
|
||||||
|
(elapsed.as_millis(), clocks::clk_sys_freq())
|
||||||
|
};
|
||||||
|
|
||||||
|
// Report the elapsed time, so that the compiler doesn't optimize it away for chips other than RP2040
|
||||||
|
info!(
|
||||||
|
"At {}Mhz: Elapsed time to count to {}: {}ms",
|
||||||
|
clk_sys_freq / 1_000_000,
|
||||||
|
COUNT_TO,
|
||||||
|
time_elapsed
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "rp2040")]
|
||||||
|
{
|
||||||
|
// we should be at 200MHz
|
||||||
|
assert_eq!(clk_sys_freq, 200_000_000, "System clock frequency is not 200MHz");
|
||||||
|
// At 200MHz, the time to count to 10_000_000 should be at 600ms, testing with 1% margin
|
||||||
|
assert!(time_elapsed <= 606, "Elapsed time is too long");
|
||||||
|
}
|
||||||
|
|
||||||
|
cortex_m::asm::bkpt();
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user