rust_hdl_fpga_support/lattice/ice40/
ice_pll.rs1use rust_hdl_core::prelude::*;
20
21#[derive(Clone, Default, Debug)]
22struct ICE40PLLSettings {
23 f_pllin: f64,
24 fout: f64,
25 divr: i32,
26 divf: i32,
27 divq: i32,
28 simple: bool,
29}
30
31impl ICE40PLLSettings {
32 fn filter_range(&self) -> usize {
33 let f_pfd = self.f_pllin / (self.divr as f64 + 1.);
34 let filter_range = if f_pfd < 17. {
35 1
36 } else if f_pfd < 26. {
37 2
38 } else if f_pfd < 44. {
39 3
40 } else if f_pfd < 66. {
41 4
42 } else if f_pfd < 101. {
43 5
44 } else {
45 6
46 };
47 filter_range
48 }
49}
50
51fn analyze(simple_feedback: bool, f_pllin: f64, f_pllout: f64) -> Option<ICE40PLLSettings> {
52 let mut found_something = false;
53 let mut best = ICE40PLLSettings::default();
54 best.simple = simple_feedback;
55
56 let divf_max = if simple_feedback { 127 } else { 63 };
57 if f_pllin < 10. || f_pllin > 133. {
62 panic!(
63 "Error: PLL input frequency {} MHz is outside range 10 MHz - 133 MHz!\n",
64 f_pllin
65 );
66 }
67
68 if f_pllout < 16. || f_pllout > 275. {
69 panic!(
70 "Error: PLL output frequency {} MHz is outside range 16 MHz - 275 MHz!\n",
71 f_pllout
72 );
73 }
74
75 for divr in 0..=15 {
76 let f_pfd = f_pllin / (divr as f64 + 1.);
77 if f_pfd < 10. || f_pfd > 133. {
78 continue;
79 }
80 for divf in 0..=divf_max {
81 if simple_feedback {
82 let f_vco = f_pfd * (divf as f64 + 1.);
83 if f_vco < 533. || f_vco > 1066. {
84 continue;
85 }
86 for divq in 1..=6 {
87 let fout = f_vco * f64::exp2(-divq as f64);
88 if f64::abs(fout - f_pllout) < f64::abs(best.fout - f_pllout)
89 || !found_something
90 {
91 best.fout = fout;
92 best.divr = divr;
93 best.divf = divf;
94 best.divq = divq;
95 found_something = true;
96 }
97 }
98 } else {
99 for divq in 1..=6 {
100 let f_vco = f_pfd * (divf as f64 + 1.) * f64::exp2(divq as f64);
101 if f_vco < 533. || f_vco > 1066. {
102 continue;
103 }
104 let fout = f_vco * f64::exp2(-divq as f64);
105 if f64::abs(fout - f_pllout) < f64::abs(best.fout - f_pllout)
106 || !found_something
107 {
108 best.fout = fout;
109 best.divr = divr;
110 best.divf = divf;
111 best.divq = divq;
112 found_something = true;
113 }
114 }
115 }
116 }
117 }
118 if found_something {
119 Some(best)
120 } else {
121 None
122 }
123}
124
125#[test]
126fn test_pll_gen() {
127 let x = analyze(true, 100., 33.33333);
128 println!("x: {:?}", x);
129 assert!(x.is_some());
130 let x = x.unwrap();
131 assert!((x.fout - 33.3333).abs() < 1e-3);
132}
133
134#[derive(LogicBlock)]
135pub struct ICE40PLLBlock<const FIN_FREQ: u64, const FOUT_FREQ: u64> {
136 pub clock_in: Signal<In, Clock>,
137 pub clock_out: Signal<Out, Clock>,
138 pub locked: Signal<Out, Bit>,
139 core: ICEPLL40Core,
140 _settings: ICE40PLLSettings,
141}
142
143impl<const FIN_FREQ: u64, const FOUT_FREQ: u64> Default for ICE40PLLBlock<FIN_FREQ, FOUT_FREQ> {
144 fn default() -> Self {
145 let freq_in_mhz = (FIN_FREQ as f64) / (1_000_000.0);
146 let freq_out_mhz = (FOUT_FREQ as f64) / (1_000_000.0);
147 Self {
148 clock_in: Signal::default(),
149 clock_out: Signal::new_with_default(Clock::default()),
150 locked: Signal::new_with_default(false),
151 core: ICEPLL40Core::new(),
152 _settings: analyze(true, freq_in_mhz, freq_out_mhz).unwrap(),
153 }
154 }
155}
156
157impl<const FIN_FREQ: u64, const FOUT_FREQ: u64> Logic for ICE40PLLBlock<FIN_FREQ, FOUT_FREQ> {
158 fn update(&mut self) {}
159
160 fn connect(&mut self) {
161 self.clock_out.connect();
162 self.locked.connect();
163 }
164
165 fn hdl(&self) -> Verilog {
166 Verilog::Custom(format!(
167 "\
168SB_PLL40_CORE #(
169 .FEEDBACK_PATH(\"{feedback}\"),
170 .DIVR({DIVR}),
171 .DIVF({DIVF}),
172 .DIVQ({DIVQ}),
173 .FILTER_RANGE({FILTER_RANGE})
174 ) uut (
175 .LOCK(locked),
176 .RESETB(1'b1),
177 .BYPASS(1'b0),
178 .REFERENCECLK(clock_in),
179 .PLLOUTCORE(clock_out));
180",
181 feedback = if self._settings.simple {
182 "SIMPLE"
183 } else {
184 "NON_SIMPLE"
185 },
186 DIVR = VerilogLiteral::from(self._settings.divr as u32),
187 DIVF = VerilogLiteral::from(self._settings.divf as u32),
188 DIVQ = VerilogLiteral::from(self._settings.divq as u32),
189 FILTER_RANGE = VerilogLiteral::from(self._settings.filter_range())
190 ))
191 }
192}
193
194#[derive(LogicBlock)]
195pub struct ICEPLL40Core {}
196
197impl ICEPLL40Core {
198 pub fn new() -> ICEPLL40Core {
199 Self {}
200 }
201}
202
203impl Logic for ICEPLL40Core {
204 fn update(&mut self) {}
205
206 fn hdl(&self) -> Verilog {
207 Verilog::Blackbox(BlackBox {
208 code: r#"
209(* blackbox *)
210module SB_PLL40_CORE (
211 input REFERENCECLK,
212 output PLLOUTCORE,
213 output PLLOUTGLOBAL,
214 input EXTFEEDBACK,
215 input [7:0] DYNAMICDELAY,
216 output LOCK,
217 input BYPASS,
218 input RESETB,
219 input LATCHINPUTVALUE,
220 output SDO,
221 input SDI,
222 input SCLK
223);
224parameter FEEDBACK_PATH = "SIMPLE";
225parameter DELAY_ADJUSTMENT_MODE_FEEDBACK = "FIXED";
226parameter DELAY_ADJUSTMENT_MODE_RELATIVE = "FIXED";
227parameter SHIFTREG_DIV_MODE = 1'b0;
228parameter FDA_FEEDBACK = 4'b0000;
229parameter FDA_RELATIVE = 4'b0000;
230parameter PLLOUT_SELECT = "GENCLK";
231parameter DIVR = 4'b0000;
232parameter DIVF = 7'b0000000;
233parameter DIVQ = 3'b000;
234parameter FILTER_RANGE = 3'b000;
235parameter ENABLE_ICEGATE = 1'b0;
236parameter TEST_MODE = 1'b0;
237parameter EXTERNAL_DIVIDE_FACTOR = 1;
238endmodule
239 "#
240 .into(),
241 name: "SB_PLL40_CORE".into(),
242 })
243 }
244}