1pub const FREQ_OSC_MHZ: u32 = 48;
2const FD_MAX: u32 = 256;
3
4pub fn divide_by_fd(fd: u32, in_freq_hz: u32) -> u32 {
7 let in_freq_khz = in_freq_hz / 1_000;
9 let out_freq_khz = (in_freq_khz * (fd + 1)) / FD_MAX;
10 out_freq_khz * 1_000
12}
13
14#[allow(dead_code)]
19pub fn clk_to_fd(fd_in_mhz: u32, desired_mhz: u32) -> Option<(u32, i32)> {
20 let platonic_fd: u32 = (desired_mhz * 256) / fd_in_mhz;
21 if platonic_fd > 0 {
22 let actual_fd = platonic_fd - 1;
23 let actual_clk = divide_by_fd(actual_fd, fd_in_mhz * 1_000_000);
24 Some((actual_fd, desired_mhz as i32 - actual_clk as i32))
25 } else {
26 None
27 }
28}
29
30#[derive(num_derive::FromPrimitive, num_derive::ToPrimitive, Debug)]
31pub enum PowerOp {
32 Wfi,
33 Invalid,
34}
35
36const FDW: u32 = 8;
37const MODULUS: u64 = 1 << FDW; #[derive(Debug, Clone, Copy)]
40pub struct ClockDividerParams {
41 pub fd0: u8,
42 pub fd2: u8,
43 pub actual_freq_hz: u32,
44 pub error_ppm: u32,
45}
46
47#[inline]
49pub fn div_round(a: u64, b: u64) -> u64 { (a + b / 2) / b }
50
51pub fn find_optimal_divider(base_freq_hz: u32, target_freq_hz: u32) -> Option<ClockDividerParams> {
56 let base = base_freq_hz as u64;
57 let target = target_freq_hz as u64;
58
59 if target == 0 || base == 0 {
60 return None;
61 }
62
63 if target > base || base > target * 65536 {
66 return None;
67 }
68
69 let mut best: Option<ClockDividerParams> = None;
70
71 for fd0 in 0u32..=255 {
72 let fd0_plus_1 = (fd0 as u64) + 1;
73
74 let fd2_plus_1 = div_round(target * fd0_plus_1 * MODULUS, base);
77
78 if fd2_plus_1 < 1 || fd2_plus_1 > 256 {
79 continue;
80 }
81
82 let fd2 = (fd2_plus_1 - 1) as u8;
83
84 let divisor = fd0_plus_1 * MODULUS;
86 let actual_freq_hz = div_round(base * fd2_plus_1, divisor) as u32;
87
88 let actual_scaled = base * fd2_plus_1;
91 let target_scaled = target * divisor;
92 let diff = if actual_scaled >= target_scaled {
93 actual_scaled - target_scaled
94 } else {
95 target_scaled - actual_scaled
96 };
97 let error_ppm = div_round(diff * 1_000_000, target_scaled) as u32;
98
99 let is_better = match &best {
100 None => true,
101 Some(b) => error_ppm < b.error_ppm || (error_ppm == b.error_ppm && fd0 < b.fd0 as u32),
102 };
103
104 if is_better {
105 best = Some(ClockDividerParams { fd0: fd0 as u8, fd2, actual_freq_hz, error_ppm });
106 }
107 }
108
109 best
110}
111
112const FREF_HZ: u64 = 48_000_000;
113const VCO_MIN_HZ: u64 = 1_000_000_000;
114const VCO_MAX_HZ: u64 = 3_000_000_000;
115const FRAC_BITS: u32 = 24;
116const FRAC_SCALE: u64 = 1 << FRAC_BITS; #[derive(Debug, Clone, Copy)]
119pub struct PllParams {
120 pub m: u8, pub n: u16, pub frac: u32, pub q0: u8, pub q1: u8, pub vco_freq_hz: u32,
126 pub actual_freq_hz: u32,
127 pub error_ppm: u32,
128}
129
130#[inline]
132fn is_valid_n(n: u16) -> bool { n >= 8 && n <= 4095 && n != 11 }
133
134fn calc_vco_hz(m: u8, n: u16, frac: u32) -> Option<u64> {
137 let n_plus_f_scaled = (n as u64) * FRAC_SCALE + (frac as u64);
139 let numerator = FREF_HZ.checked_mul(n_plus_f_scaled)?;
140 Some(numerator / ((m as u64) * FRAC_SCALE))
141}
142
143fn is_vco_valid(m: u8, n: u16, frac: u32) -> bool {
145 calc_vco_hz(m, n, frac).map(|vco| vco >= VCO_MIN_HZ && vco <= VCO_MAX_HZ).unwrap_or(false)
146}
147
148const COMMON_CLOCKS: [(u32, PllParams); 7] = [
149 (
150 700_000_000,
151 PllParams {
152 m: 4,
153 n: 175,
154 frac: 0,
155 q0: 1,
156 q1: 3,
157 vco_freq_hz: 2100000000,
158 actual_freq_hz: 700000000,
159 error_ppm: 0,
160 },
161 ),
162 (
163 696_000_000,
164 PllParams {
165 m: 1,
166 n: 29,
167 frac: 0,
168 q0: 1,
169 q1: 2,
170 vco_freq_hz: 1392000000,
171 actual_freq_hz: 696000000,
172 error_ppm: 5714,
173 },
174 ),
175 (
176 400_000_000,
177 PllParams {
178 m: 1,
179 n: 25,
180 frac: 0,
181 q0: 1,
182 q1: 3,
183 vco_freq_hz: 1200000000,
184 actual_freq_hz: 400000000,
185 error_ppm: 0,
186 },
187 ),
188 (
189 350_000_000,
190 PllParams {
191 m: 4,
192 n: 175,
193 frac: 0,
194 q0: 1,
195 q1: 6,
196 vco_freq_hz: 2100000000,
197 actual_freq_hz: 350000000,
198 error_ppm: 0,
199 },
200 ),
201 (
202 200_000_000,
203 PllParams {
204 m: 1,
205 n: 25,
206 frac: 0,
207 q0: 1,
208 q1: 6,
209 vco_freq_hz: 1200000000,
210 actual_freq_hz: 200000000,
211 error_ppm: 0,
212 },
213 ),
214 (
215 100_000_000,
216 PllParams {
217 m: 1,
218 n: 25,
219 frac: 0,
220 q0: 2,
221 q1: 6,
222 vco_freq_hz: 1200000000,
223 actual_freq_hz: 100000000,
224 error_ppm: 0,
225 },
226 ),
227 (
229 800_000_000,
230 PllParams {
231 m: 3,
232 n: 100,
233 frac: 0,
234 q0: 1,
235 q1: 2,
236 vco_freq_hz: 1600000000,
237 actual_freq_hz: 800000000,
238 error_ppm: 0,
239 },
240 ),
241];
242pub fn find_pll_params(target_freq_hz: u32, allow_frac: bool) -> Option<PllParams> {
248 let target = target_freq_hz as u64;
249
250 if let Some(memoized) = COMMON_CLOCKS.iter().find(|(freq, _param)| *freq == target_freq_hz) {
253 return Some(memoized.1);
254 }
255
256 if target == 0 {
257 return None;
258 }
259
260 let mut best: Option<PllParams> = None;
261
262 for m in 1u8..=4 {
268 for q0 in 1u8..=8 {
269 for q1 in 1u8..=8 {
270 let total_div = (m as u64) * (q0 as u64) * (q1 as u64);
271
272 let n_plus_f_scaled = div_round(target * total_div * FRAC_SCALE, FREF_HZ);
279
280 let n_base = (n_plus_f_scaled / FRAC_SCALE) as u16;
281 let frac_remainder = (n_plus_f_scaled % FRAC_SCALE) as u32;
282
283 let candidates: &[(u16, u32)] = if allow_frac {
285 &[
286 (n_base, 0), (n_base + 1, 0), (n_base, frac_remainder), ]
290 } else {
291 &[(n_base, 0), (n_base + 1, 0)]
292 };
293
294 for &(n, frac) in candidates {
295 if !is_valid_n(n) {
296 continue;
297 }
298
299 if !is_vco_valid(m, n, frac) {
300 continue;
301 }
302
303 let vco_hz = calc_vco_hz(m, n, frac).unwrap();
305 let actual_hz = vco_hz / (q0 as u64) / (q1 as u64);
307 if actual_hz > u32::MAX as u64 {
311 continue;
312 }
313
314 let actual_freq_hz = actual_hz as u32;
315 let vco_freq_hz = vco_hz as u32;
316
317 let diff = actual_freq_hz.abs_diff(target_freq_hz) as u64;
319 let error_ppm = (diff * 1_000_000 / target) as u32;
320
321 let candidate = PllParams { m, n, frac, q0, q1, vco_freq_hz, actual_freq_hz, error_ppm };
322
323 let dominated = best.as_ref().is_some_and(|b| {
325 if error_ppm != b.error_ppm {
326 error_ppm > b.error_ppm
327 } else {
328 if frac > 0 && b.frac == 0 {
330 true
331 } else if frac == 0 && b.frac > 0 {
332 false
333 } else {
334 (q0 as u16) * (q1 as u16) >= (b.q0 as u16) * (b.q1 as u16)
336 }
337 }
338 });
339
340 if !dominated {
341 best = Some(candidate);
342 }
343 }
344 }
345 }
346 }
347
348 best
349}