aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2025-10-29 07:49:52 +0000
committerGitHub <[email protected]>2025-10-29 07:49:52 +0000
commita0d7c087a2bd4a370d24097e022a812f7119dd78 (patch)
tree2b6ba801e2077e9cab6c8ce7ca037604f4bbf257
parent0b780871a31c7cb933bcbe11963c693001e84946 (diff)
parenta863aadc643d84707815e5c0f4564f2195809fec (diff)
Merge pull request #4792 from usedhondacivic/cyw43-pio-clock-divider
cyw43-pio: core clock speed based pio program selection
-rw-r--r--cyw43-pio/CHANGELOG.md2
-rw-r--r--cyw43-pio/src/lib.rs108
2 files changed, 93 insertions, 17 deletions
diff --git a/cyw43-pio/CHANGELOG.md b/cyw43-pio/CHANGELOG.md
index ec38989e3..c2d18919c 100644
--- a/cyw43-pio/CHANGELOG.md
+++ b/cyw43-pio/CHANGELOG.md
@@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8<!-- next-header --> 8<!-- next-header -->
9## Unreleased - ReleaseDate 9## Unreleased - ReleaseDate
10 10
11- Select pio program based on core clock speed #4792
12
11## 0.8.0 - 2025-08-28 13## 0.8.0 - 2025-08-28
12 14
13- Bump cyw43 version 15- Bump cyw43 version
diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs
index 41ac6816d..c8715e662 100644
--- a/cyw43-pio/src/lib.rs
+++ b/cyw43-pio/src/lib.rs
@@ -7,6 +7,7 @@ use core::slice;
7 7
8use cyw43::SpiBusCyw43; 8use cyw43::SpiBusCyw43;
9use embassy_rp::Peri; 9use embassy_rp::Peri;
10use embassy_rp::clocks::clk_sys_freq;
10use embassy_rp::dma::Channel; 11use embassy_rp::dma::Channel;
11use embassy_rp::gpio::{Drive, Level, Output, Pull, SlewRate}; 12use embassy_rp::gpio::{Drive, Level, Output, Pull, SlewRate};
12use embassy_rp::pio::program::pio_asm; 13use embassy_rp::pio::program::pio_asm;
@@ -23,23 +24,24 @@ pub struct PioSpi<'d, PIO: Instance, const SM: usize, DMA: Channel> {
23 wrap_target: u8, 24 wrap_target: u8,
24} 25}
25 26
26/// The default clock divider that works for Pico 1 and 2 W. As well as the RM2 on rp2040 devices. 27/// Clock divider used for most applications
27/// same speed as pico-sdk, 62.5Mhz 28/// With default core clock configuration:
28/// This is actually the fastest we can go without overclocking. 29/// RP2350: 150Mhz / 2 = 75Mhz pio clock -> 37.5Mhz GSPI clock
29/// According to data sheet, the theoretical maximum is 100Mhz Pio => 50Mhz SPI Freq. 30/// RP2040: 133Mhz / 2 = 66.5Mhz pio clock -> 33.25Mhz GSPI clock
30/// However, the PIO uses a fractional divider, which works by introducing jitter when
31/// the divider is not an integer. It does some clocks at 125mhz and others at 62.5mhz
32/// so that it averages out to the desired frequency of 100mhz. The 125mhz clock cycles
33/// violate the maximum from the data sheet.
34pub const DEFAULT_CLOCK_DIVIDER: FixedU32<U8> = FixedU32::from_bits(0x0200); 31pub const DEFAULT_CLOCK_DIVIDER: FixedU32<U8> = FixedU32::from_bits(0x0200);
35 32
36/// The overclock clock divider for the Pico 1 W. Does not work on any known RM2 devices. 33/// Clock divider used to overclock the cyw43
37/// 125mhz Pio => 62.5Mhz SPI Freq. 25% higher than theoretical maximum according to 34/// With default core clock configuration:
38/// data sheet, but seems to work fine. 35/// RP2350: 150Mhz / 1 = 150Mhz pio clock -> 75Mhz GSPI clock (50% greater that manufacturer
36/// recommended 50Mhz)
37/// RP2040: 133Mhz / 1 = 133Mhz pio clock -> 66.5Mhz GSPI clock (33% greater that manufacturer
38/// recommended 50Mhz)
39pub const OVERCLOCK_CLOCK_DIVIDER: FixedU32<U8> = FixedU32::from_bits(0x0100); 39pub const OVERCLOCK_CLOCK_DIVIDER: FixedU32<U8> = FixedU32::from_bits(0x0100);
40 40
41/// The clock divider for the RM2 module. Found to be needed for the Pimoroni Pico Plus 2 W, 41/// Clock divider used with the RM2
42/// Pico Plus 2 Non w with the RM2 breakout module, and the Pico 2 with the RM2 breakout module. 42/// With default core clock configuration:
43/// RP2350: 150Mhz / 3 = 50Mhz pio clock -> 25Mhz GSPI clock
44/// RP2040: 133Mhz / 3 = 44.33Mhz pio clock -> 22.16Mhz GSPI clock
43pub const RM2_CLOCK_DIVIDER: FixedU32<U8> = FixedU32::from_bits(0x0300); 45pub const RM2_CLOCK_DIVIDER: FixedU32<U8> = FixedU32::from_bits(0x0300);
44 46
45impl<'d, PIO, const SM: usize, DMA> PioSpi<'d, PIO, SM, DMA> 47impl<'d, PIO, const SM: usize, DMA> PioSpi<'d, PIO, SM, DMA>
@@ -58,7 +60,40 @@ where
58 clk: Peri<'d, impl PioPin>, 60 clk: Peri<'d, impl PioPin>,
59 dma: Peri<'d, DMA>, 61 dma: Peri<'d, DMA>,
60 ) -> Self { 62 ) -> Self {
61 let loaded_program = if clock_divider < DEFAULT_CLOCK_DIVIDER { 63 let effective_pio_frequency = (clk_sys_freq() as f32 / clock_divider.to_num::<f32>()) as u32;
64
65 #[cfg(feature = "defmt")]
66 defmt::trace!("Effective pio frequency: {}Hz", effective_pio_frequency);
67
68 // Non-integer pio clock dividers are achieved by introducing clock jitter resulting in a
69 // combination of long and short cycles. The long and short cycles average to achieve the
70 // requested clock speed.
71 // This can be a problem for peripherals that expect a consistent clock / have a clock
72 // speed upper bound that is violated by the short cycles. The cyw43 seems to handle the
73 // jitter well, but we emit a warning to recommend an integer divider anyway.
74 if clock_divider.frac() != FixedU32::<U8>::ZERO {
75 #[cfg(feature = "defmt")]
76 defmt::trace!(
77 "Configured clock divider is not a whole number. Some clock cycles may violate the maximum recommended GSPI speed. Use at your own risk."
78 );
79 }
80
81 // Different pio programs must be used for different pio clock speeds.
82 // The programs used below are based on the pico SDK: https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_cyw43_driver/cyw43_bus_pio_spi.pio
83 // The clock speed cutoff for each program has been determined experimentally:
84 // > 100Mhz -> Overclock program
85 // [75Mhz, 100Mhz] -> High speed program
86 // [0, 75Mhz) -> Low speed program
87 let loaded_program = if effective_pio_frequency > 100_000_000 {
88 // Any frequency > 100Mhz is overclocking the chip (manufacturer recommends max 50Mhz GSPI
89 // clock)
90 // Example:
91 // * RP2040 @ 133Mhz (stock) with OVERCLOCK_CLOCK_DIVIDER (133MHz)
92 #[cfg(feature = "defmt")]
93 defmt::trace!(
94 "Configured clock divider results in a GSPI frequency greater than the manufacturer recommendation (50Mhz). Use at your own risk."
95 );
96
62 let overclock_program = pio_asm!( 97 let overclock_program = pio_asm!(
63 ".side_set 1" 98 ".side_set 1"
64 99
@@ -69,7 +104,7 @@ where
69 "jmp x-- lp side 1" 104 "jmp x-- lp side 1"
70 // switch directions 105 // switch directions
71 "set pindirs, 0 side 0" 106 "set pindirs, 0 side 0"
72 "nop side 1" // necessary for clkdiv=1. 107 "nop side 1"
73 "nop side 0" 108 "nop side 0"
74 // read in y-1 bits 109 // read in y-1 bits
75 "lp2:" 110 "lp2:"
@@ -83,8 +118,47 @@ where
83 ".wrap" 118 ".wrap"
84 ); 119 );
85 common.load_program(&overclock_program.program) 120 common.load_program(&overclock_program.program)
121 } else if effective_pio_frequency >= 75_000_000 {
122 // Experimentally determined cutoff.
123 // Notably includes the stock RP2350 configured with clk_div of 2 (150Mhz base clock / 2 = 75Mhz)
124 // but does not include stock RP2040 configured with clk_div of 2 (133Mhz base clock / 2 = 66.5Mhz)
125 // Example:
126 // * RP2350 @ 150Mhz (stock) with DEFAULT_CLOCK_DIVIDER (75Mhz)
127 // * RP2XXX @ 200Mhz with DEFAULT_CLOCK_DIVIDER (100Mhz)
128 #[cfg(feature = "defmt")]
129 defmt::trace!("Using high speed pio program.");
130 let high_speed_program = pio_asm!(
131 ".side_set 1"
132
133 ".wrap_target"
134 // write out x-1 bits
135 "lp:"
136 "out pins, 1 side 0"
137 "jmp x-- lp side 1"
138 // switch directions
139 "set pindirs, 0 side 0"
140 "nop side 1"
141 // read in y-1 bits
142 "lp2:"
143 "in pins, 1 side 0"
144 "jmp y-- lp2 side 1"
145
146 // wait for event and irq host
147 "wait 1 pin 0 side 0"
148 "irq 0 side 0"
149
150 ".wrap"
151 );
152 common.load_program(&high_speed_program.program)
86 } else { 153 } else {
87 let default_program = pio_asm!( 154 // Low speed
155 // Examples:
156 // * RP2040 @ 133Mhz (stock) with DEFAULT_CLOCK_DIVIDER (66.5Mhz)
157 // * RP2040 @ 133Mhz (stock) with RM2_CLOCK_DIVIDER (44.3Mhz)
158 // * RP2350 @ 150Mhz (stock) with RM2_CLOCK_DIVIDER (50Mhz)
159 #[cfg(feature = "defmt")]
160 defmt::trace!("Using low speed pio program.");
161 let low_speed_program = pio_asm!(
88 ".side_set 1" 162 ".side_set 1"
89 163
90 ".wrap_target" 164 ".wrap_target"
@@ -106,7 +180,7 @@ where
106 180
107 ".wrap" 181 ".wrap"
108 ); 182 );
109 common.load_program(&default_program.program) 183 common.load_program(&low_speed_program.program)
110 }; 184 };
111 185
112 let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio); 186 let mut pin_io: embassy_rp::pio::Pin<PIO> = common.make_pio_pin(dio);