4 // parametric ECP5 PLL generator in systemverilog
5 // actual frequency can be equal or higher than requested
6 // to see actual frequencies
7 // trellis log/stdout : search for "MHz", "Derived", "frequency"
8 // to see actual phase shifts
9 // diamond log/*.mrp : search for "Phase", "Desired"
13 parameter integer in_hz = 25000000,
14 parameter integer out0_hz = 25000000,
15 parameter integer out0_deg = 0, // keep 0
16 parameter integer out0_tol_hz= 0, // tolerance: if freq differs more, then error
17 parameter integer out1_hz = 0,
18 parameter integer out1_deg = 0,
19 parameter integer out1_tol_hz= 0,
20 parameter integer out2_hz = 0,
21 parameter integer out2_deg = 0,
22 parameter integer out2_tol_hz= 0,
23 parameter integer out3_hz = 0,
24 parameter integer out3_deg = 0,
25 parameter integer out3_tol_hz= 0,
26 parameter integer reset_en = 0,
27 parameter integer standby_en = 0,
28 parameter integer dynamic_en = 0
36 input phasedir, phasestep, phaseloadreg,
40 localparam PFD_MIN = 3125000;
41 localparam PFD_MAX = 400000000;
42 localparam VCO_MIN = 400000000;
43 localparam VCO_MAX = 800000000;
44 localparam VCO_OPTIMAL = (VCO_MIN+VCO_MAX)/2;
46 function integer abs(input integer x);
50 function integer F_ecp5pll(input integer x);
51 integer input_div, input_div_min, input_div_max;
52 integer output_div, output_div_min, output_div_max;
53 integer feedback_div, feedback_div_min, feedback_div_max;
55 integer error, error_prev;
57 integer div1, div2, div3;
59 integer params_refclk_div;
60 integer params_feedback_div;
61 integer params_output_div;
64 error_prev = 999999999;
65 input_div_min = in_hz/PFD_MAX;
68 input_div_max = in_hz/PFD_MIN;
69 if(input_div_max > 128)
71 for(input_div = input_div_min; input_div <= input_div_max; input_div=input_div+1)
73 if(out0_hz / 1000000 * input_div < 2000)
74 feedback_div = out0_hz * input_div / in_hz;
76 feedback_div = out0_hz / in_hz * input_div;
77 feedback_div_min = feedback_div;
78 feedback_div_max = feedback_div+1;
79 if(feedback_div_min < 1)
81 if(feedback_div_max > 80)
82 feedback_div_max = 80;
83 for(feedback_div = feedback_div_min; feedback_div <= feedback_div_max; feedback_div = feedback_div+1)
85 output_div_min = (VCO_MIN/feedback_div) / (in_hz/input_div);
86 if(output_div_min < 1)
88 output_div_max = (VCO_MAX/feedback_div) / (in_hz/input_div);
89 if(output_div_max > 128)
91 fout = in_hz * feedback_div / input_div;
92 for(output_div = output_div_min; output_div <= output_div_max; output_div=output_div+1)
94 fvco = fout * output_div;
95 error = abs(fout-out0_hz)
96 + (out1_hz > 0 ? abs(fvco/(fvco >= out1_hz ? fvco/out1_hz : 1)-out1_hz) : 0)
97 + (out2_hz > 0 ? abs(fvco/(fvco >= out2_hz ? fvco/out2_hz : 1)-out2_hz) : 0)
98 + (out3_hz > 0 ? abs(fvco/(fvco >= out3_hz ? fvco/out3_hz : 1)-out3_hz) : 0);
99 if( error < error_prev
100 || (error == error_prev && abs(fvco-VCO_OPTIMAL) < abs(params_fvco-VCO_OPTIMAL)) )
103 params_refclk_div = input_div;
104 params_feedback_div = feedback_div;
105 params_output_div = output_div;
111 // FIXME in the future when yosys supports struct
113 F_ecp5pll = params_refclk_div;
115 F_ecp5pll = params_feedback_div;
117 F_ecp5pll = params_output_div;
120 function integer F_primary_phase(input integer output_div, deg);
121 integer phase_compensation;
122 integer phase_count_x8;
124 phase_compensation = (output_div+1)/2*8-8+output_div/2*8; // output_div/2*8 = 180 deg shift
125 phase_count_x8 = phase_compensation + 8*output_div*deg/360;
126 if(phase_count_x8 > 1023)
127 phase_count_x8 = phase_count_x8 % (output_div*8); // wraparound 360 deg
128 F_primary_phase = phase_count_x8;
131 // FIXME it is inefficient to call F_ecp5pll multiple times
132 localparam params_refclk_div = F_ecp5pll(0);
133 localparam params_feedback_div = F_ecp5pll(1);
134 localparam params_output_div = F_ecp5pll(2);
135 localparam params_fout = in_hz * params_feedback_div / params_refclk_div;
136 localparam params_fvco = params_fout * params_output_div;
138 localparam params_primary_phase_x8 = F_ecp5pll(3);
139 localparam params_primary_cphase = F_primary_phase(params_output_div, out0_deg) / 8;
140 localparam params_primary_fphase = F_primary_phase(params_output_div, out0_deg) % 8;
142 function integer F_secondary_divisor(input integer sfreq);
143 F_secondary_divisor = 1;
145 if(params_fvco >= sfreq)
146 F_secondary_divisor = params_fvco/sfreq;
149 function integer F_secondary_phase(input integer sfreq, sphase);
151 integer phase_compensation, phase_count_x8;
157 if(params_fvco >= sfreq)
158 div = params_fvco/sfreq;
159 freq = params_fvco/div;
160 phase_compensation = div*8-8;
161 phase_count_x8 = phase_compensation + 8*div*sphase/360;
162 if(phase_count_x8 > 1023)
163 phase_count_x8 = phase_count_x8 % (div*8); // wraparound 360 deg
166 F_secondary_phase = phase_count_x8;
169 localparam params_secondary1_div = F_secondary_divisor(out1_hz);
170 localparam params_secondary1_cphase = F_secondary_phase (out1_hz, out1_deg) / 8;
171 localparam params_secondary1_fphase = F_secondary_phase (out1_hz, out1_deg) % 8;
172 localparam params_secondary2_div = F_secondary_divisor(out2_hz);
173 localparam params_secondary2_cphase = F_secondary_phase (out2_hz, out2_deg) / 8;
174 localparam params_secondary2_fphase = F_secondary_phase (out2_hz, out2_deg) % 8;
175 localparam params_secondary3_div = F_secondary_divisor(out3_hz);
176 localparam params_secondary3_cphase = F_secondary_phase (out3_hz, out3_deg) / 8;
177 localparam params_secondary3_fphase = F_secondary_phase (out3_hz, out3_deg) % 8;
179 // check if generated frequencies are out of range
180 localparam error_out0_hz = abs(out0_hz - params_fout) > out0_tol_hz;
181 localparam error_out1_hz = out1_hz > 0 ? abs(out1_hz - params_fvco / params_secondary1_div) > out1_tol_hz : 0;
182 localparam error_out2_hz = out2_hz > 0 ? abs(out2_hz - params_fvco / params_secondary2_div) > out2_tol_hz : 0;
183 localparam error_out3_hz = out3_hz > 0 ? abs(out3_hz - params_fvco / params_secondary3_div) > out3_tol_hz : 0;
184 // diamond: won't compile this, comment it out. Workaround follows using division by zero
186 if(error_out0_hz) $error("out0_hz tolerance exceeds out0_tol_hz");
187 if(error_out1_hz) $error("out1_hz tolerance exceeds out1_tol_hz");
188 if(error_out2_hz) $error("out2_hz tolerance exceeds out2_tol_hz");
189 if(error_out3_hz) $error("out3_hz tolerance exceeds out3_tol_hz");
191 // diamond: trigger error with division by zero, doesn't accept $error()
192 localparam trig_out0_hz = error_out0_hz ? 1/0 : 0;
193 localparam trig_out1_hz = error_out1_hz ? 1/0 : 0;
194 localparam trig_out2_hz = error_out2_hz ? 1/0 : 0;
195 localparam trig_out3_hz = error_out3_hz ? 1/0 : 0;
197 wire [1:0] PHASESEL_HW = phasesel-1;
198 wire CLKOP; // internal
200 // TODO: frequencies in MHz if passed as "attributes"
201 // will appear in diamond *.mrp file like "Output Clock(P) Frequency (MHz):"
202 // but I don't know how to pass string parameters for this:
203 // (* FREQUENCY_PIN_CLKI="025.000000" *)
204 // (* FREQUENCY_PIN_CLKOP="023.345678" *)
205 // (* FREQUENCY_PIN_CLKOS="034.234567" *)
206 // (* FREQUENCY_PIN_CLKOS2="111.345678" *)
207 // (* FREQUENCY_PIN_CLKOS3="123.456789" *)
208 (* ICP_CURRENT="12" *) (* LPF_RESISTOR="8" *) (* MFG_ENABLE_FILTEROPAMP="1" *) (* MFG_GMCREF_SEL="2" *)
211 .CLKI_DIV (params_refclk_div),
212 .CLKFB_DIV (params_feedback_div),
213 .FEEDBK_PATH ("CLKOP"),
215 .OUTDIVIDER_MUXA("DIVA"),
216 .CLKOP_ENABLE ("ENABLED"),
217 .CLKOP_DIV (params_output_div),
218 .CLKOP_CPHASE (params_primary_cphase),
219 .CLKOP_FPHASE (params_primary_fphase),
221 .OUTDIVIDER_MUXB("DIVB"),
222 .CLKOS_ENABLE (out1_hz > 0 ? "ENABLED" : "DISABLED"),
223 .CLKOS_DIV (params_secondary1_div),
224 .CLKOS_CPHASE (params_secondary1_cphase),
225 .CLKOS_FPHASE (params_secondary1_fphase),
227 .OUTDIVIDER_MUXC("DIVC"),
228 .CLKOS2_ENABLE(out2_hz > 0 ? "ENABLED" : "DISABLED"),
229 .CLKOS2_DIV (params_secondary2_div),
230 .CLKOS2_CPHASE(params_secondary2_cphase),
231 .CLKOS2_FPHASE(params_secondary2_fphase),
233 .OUTDIVIDER_MUXD("DIVD"),
234 .CLKOS3_ENABLE(out3_hz > 0 ? "ENABLED" : "DISABLED"),
235 .CLKOS3_DIV (params_secondary3_div),
236 .CLKOS3_CPHASE(params_secondary3_cphase),
237 .CLKOS3_FPHASE(params_secondary3_fphase),
239 .INTFB_WAKE ("DISABLED"),
240 .STDBY_ENABLE (standby_en ? "ENABLED" : "DISABLED"),
241 .PLLRST_ENA ( reset_en ? "ENABLED" : "DISABLED"),
242 .DPHASE_SOURCE(dynamic_en ? "ENABLED" : "DISABLED"),
256 .PHASESEL1(PHASESEL_HW[1]),
257 .PHASESEL0(PHASESEL_HW[0]),
259 .PHASESTEP(phasestep),
260 .PHASELOADREG(phaseloadreg),
268 assign clk_o[0] = CLKOP;