cores/clock/s6pll: add phase support
[litex.git] / litex / soc / cores / clock.py
1 # This file is Copyright (c) 2018-2019 Florent Kermarrec <florent@enjoy-digital.fr>
2 # This file is Copyright (c) 2019 Michael Betz <michibetz@gmail.com>
3 # License: BSD
4
5 """Clock Abstraction Modules"""
6
7 from migen import *
8 from migen.genlib.io import DifferentialInput
9 from migen.genlib.resetsync import AsyncResetSynchronizer
10
11 from litex.soc.interconnect.csr import *
12
13
14 def period_ns(freq):
15 return 1e9/freq
16
17 # Xilinx / Generic ---------------------------------------------------------------------------------
18
19 class XilinxClocking(Module, AutoCSR):
20 clkfbout_mult_frange = (2, 64+1)
21 clkout_divide_range = (1, 128+1)
22
23 def __init__(self, vco_margin=0):
24 self.vco_margin = vco_margin
25 self.reset = Signal()
26 self.locked = Signal()
27 self.clkin_freq = None
28 self.vcxo_freq = None
29 self.nclkouts = 0
30 self.clkouts = {}
31 self.config = {}
32 self.params = {}
33
34 def register_clkin(self, clkin, freq):
35 self.clkin = Signal()
36 if isinstance(clkin, (Signal, ClockSignal)):
37 self.comb += self.clkin.eq(clkin)
38 elif isinstance(clkin, Record):
39 self.specials += DifferentialInput(clkin.p, clkin.n, self.clkin)
40 else:
41 raise ValueError
42 self.clkin_freq = freq
43
44 def create_clkout(self, cd, freq, phase=0, buf="bufg", margin=1e-2, with_reset=True):
45 assert self.nclkouts < self.nclkouts_max
46 clkout = Signal()
47 self.clkouts[self.nclkouts] = (clkout, freq, phase, margin)
48 self.nclkouts += 1
49 if with_reset:
50 self.specials += AsyncResetSynchronizer(cd, ~self.locked | self.reset)
51 if buf is None:
52 self.comb += cd.clk.eq(clkout)
53 else:
54 clkout_buf = Signal()
55 self.comb += cd.clk.eq(clkout_buf)
56 if buf == "bufg":
57 self.specials += Instance("BUFG", i_I=clkout, o_O=clkout_buf)
58 elif buf == "bufr":
59 self.specials += Instance("BUFR", i_I=clkout, o_O=clkout_buf)
60 else:
61 raise ValueError
62
63 def compute_config(self):
64 config = {}
65 for divclk_divide in range(*self.divclk_divide_range):
66 config["divclk_divide"] = divclk_divide
67 for clkfbout_mult in reversed(range(*self.clkfbout_mult_frange)):
68 all_valid = True
69 vco_freq = self.clkin_freq*clkfbout_mult/divclk_divide
70 (vco_freq_min, vco_freq_max) = self.vco_freq_range
71 if (vco_freq >= vco_freq_min*(1 + self.vco_margin) and
72 vco_freq <= vco_freq_max*(1 - self.vco_margin)):
73 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
74 valid = False
75 for d in range(*self.clkout_divide_range):
76 clk_freq = vco_freq/d
77 if abs(clk_freq - f) < f*m:
78 config["clkout{}_freq".format(n)] = clk_freq
79 config["clkout{}_divide".format(n)] = d
80 config["clkout{}_phase".format(n)] = p
81 valid = True
82 break
83 if not valid:
84 all_valid = False
85 else:
86 all_valid = False
87 if all_valid:
88 config["vco"] = vco_freq
89 config["clkfbout_mult"] = clkfbout_mult
90 return config
91 raise ValueError("No PLL config found")
92
93 def expose_drp(self):
94 self.drp_reset = CSR()
95 self.drp_read = CSR()
96 self.drp_write = CSR()
97 self.drp_drdy = CSRStatus()
98 self.drp_adr = CSRStorage(7)
99 self.drp_dat_w = CSRStorage(16)
100 self.drp_dat_r = CSRStatus(16)
101
102 # # #
103
104 drp_drdy = Signal()
105 self.params.update(
106 i_DCLK=ClockSignal(),
107 i_DWE=self.drp_write.re,
108 i_DEN=self.drp_read.re | self.drp_write.re,
109 o_DRDY=drp_drdy,
110 i_DADDR=self.drp_adr.storage,
111 i_DI=self.drp_dat_w.storage,
112 o_DO=self.drp_dat_r.status
113 )
114 self.sync += [
115 If(self.drp_read.re | self.drp_write.re,
116 self.drp_drdy.status.eq(0)
117 ).Elif(drp_drdy,
118 self.drp_drdy.status.eq(1)
119 )
120 ]
121
122 def do_finalize(self):
123 assert hasattr(self, "clkin")
124
125 # Xilinx / Spartan6 --------------------------------------------------------------------------------
126
127 class S6PLL(XilinxClocking):
128 nclkouts_max = 6
129 clkin_freq_range = (19e6, 540e6)
130
131 def __init__(self, speedgrade=-1):
132 XilinxClocking.__init__(self)
133 self.divclk_divide_range = (1, 52 + 1)
134 self.vco_freq_range = {
135 -1: (400e6, 1000e6),
136 -2: (400e6, 1000e6),
137 -3: (400e6, 1080e6),
138 }[speedgrade]
139
140 def do_finalize(self):
141 XilinxClocking.do_finalize(self)
142 config = self.compute_config()
143 pll_fb = Signal()
144 self.params.update(
145 p_SIM_DEVICE="SPARTAN6",
146 p_BANDWIDTH="OPTIMIZED",
147 p_COMPENSATION="INTERNAL",
148 p_REF_JITTER=.01, p_CLK_FEEDBACK="CLKFBOUT",
149 p_CLKIN1_PERIOD=period_ns(self.clkin_freq),
150 p_CLKIN2_PERIOD=0.,
151 p_CLKFBOUT_MULT=config["clkfbout_mult"],
152 p_CLKFBOUT_PHASE=0.,
153 p_DIVCLK_DIVIDE=config["divclk_divide"],
154 i_CLKINSEL=1,
155 i_RST=self.reset,
156 i_CLKIN1=self.clkin,
157 i_CLKFBIN=pll_fb,
158 o_CLKFBOUT=pll_fb,
159 o_LOCKED=self.locked,
160 )
161 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
162 self.params["p_CLKOUT{}_DIVIDE".format(n)] = config["clkout{}_divide".format(n)]
163 self.params["p_CLKOUT{}_PHASE".format(n)] = float(config["clkout{}_phase".format(n)])
164 self.params["p_CLKOUT{}_DUTY_CYCLE".format(n)] = 0.5
165 self.params["o_CLKOUT{}".format(n)] = clk
166 self.specials += Instance("PLL_ADV", **self.params)
167
168
169 class S6DCM(XilinxClocking):
170 """ single output with f_out = f_in * {2 .. 256} / {1 .. 256} """
171 nclkouts_max = 1
172 clkfbout_mult_frange = (2, 256 + 1)
173 clkout_divide_range = (1, 256 + 1)
174
175 def __init__(self, speedgrade=-1):
176 XilinxClocking.__init__(self)
177 self.divclk_divide_range = (1, 1) # FIXME
178 self.clkin_freq_range = {
179 -1: (0.5e6, 200e6),
180 -2: (0.5e6, 333e6),
181 -3: (0.5e6, 375e6),
182 }[speedgrade]
183
184 self.vco_freq_range = {
185 -1: (5e6, 1e16),
186 -2: (5e6, 1e16),
187 -3: (5e6, 1e16),
188 }[speedgrade]
189
190 def do_finalize(self):
191 XilinxClocking.do_finalize(self)
192 config = self.compute_config()
193 clk, f, p, m = sorted(self.clkouts.items())[0][1]
194 self.params.update(
195 p_CLKFX_MULTIPLY=config["clkfbout_mult"],
196 p_CLKFX_DIVIDE=config["clkout0_divide"] * config["divclk_divide"],
197 p_SPREAD_SPECTRUM="NONE",
198 p_CLKIN_PERIOD=period_ns(self.clkin_freq),
199 i_CLKIN=self.clkin,
200 i_RST=self.reset,
201 i_FREEZEDCM=0,
202 o_CLKFX=clk,
203 o_LOCKED=self.locked,
204 )
205 self.specials += Instance("DCM_CLKGEN", **self.params)
206
207 # Xilinx / 7-Series --------------------------------------------------------------------------------
208
209 class S7PLL(XilinxClocking):
210 nclkouts_max = 6
211 clkin_freq_range = (19e6, 800e6)
212
213 def __init__(self, speedgrade=-1):
214 XilinxClocking.__init__(self)
215 self.divclk_divide_range = (1, 56+1)
216 self.vco_freq_range = {
217 -1: (800e6, 2133e6),
218 -2: (800e6, 1866e6),
219 -3: (800e6, 1600e6),
220 }[speedgrade]
221
222 def do_finalize(self):
223 XilinxClocking.do_finalize(self)
224 config = self.compute_config()
225 pll_fb = Signal()
226 self.params.update(
227 p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked,
228
229 # VCO
230 p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=period_ns(self.clkin_freq),
231 p_CLKFBOUT_MULT=config["clkfbout_mult"], p_DIVCLK_DIVIDE=config["divclk_divide"],
232 i_CLKIN1=self.clkin, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb,
233 )
234 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
235 self.params["p_CLKOUT{}_DIVIDE".format(n)] = config["clkout{}_divide".format(n)]
236 self.params["p_CLKOUT{}_PHASE".format(n)] = config["clkout{}_phase".format(n)]
237 self.params["o_CLKOUT{}".format(n)] = clk
238 self.specials += Instance("PLLE2_ADV", **self.params)
239
240
241 class S7MMCM(XilinxClocking):
242 nclkouts_max = 7
243
244 def __init__(self, speedgrade=-1):
245 XilinxClocking.__init__(self)
246 self.divclk_divide_range = (1, 106+1)
247 self.clkin_freq_range = {
248 -1: (10e6, 800e6),
249 -2: (10e6, 933e6),
250 -3: (10e6, 1066e6),
251 }[speedgrade]
252
253 self.vco_freq_range = {
254 -1: (600e6, 1200e6),
255 -2: (600e6, 1440e6),
256 -3: (600e6, 1600e6),
257 }[speedgrade]
258
259 def do_finalize(self):
260 XilinxClocking.do_finalize(self)
261 config = self.compute_config()
262 mmcm_fb = Signal()
263 self.params.update(
264 p_BANDWIDTH="OPTIMIZED", o_LOCKED=self.locked,
265
266 # VCO
267 p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=period_ns(self.clkin_freq),
268 p_CLKFBOUT_MULT_F=config["clkfbout_mult"], p_DIVCLK_DIVIDE=config["divclk_divide"],
269 i_CLKIN1=self.clkin, i_CLKFBIN=mmcm_fb, o_CLKFBOUT=mmcm_fb,
270 )
271 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
272 if n == 0:
273 self.params["p_CLKOUT{}_DIVIDE_F".format(n)] = config["clkout{}_divide".format(n)]
274 else:
275 self.params["p_CLKOUT{}_DIVIDE".format(n)] = config["clkout{}_divide".format(n)]
276 self.params["p_CLKOUT{}_PHASE".format(n)] = config["clkout{}_phase".format(n)]
277 self.params["o_CLKOUT{}".format(n)] = clk
278 self.specials += Instance("MMCME2_ADV", **self.params)
279
280
281 class S7IDELAYCTRL(Module):
282 def __init__(self, cd):
283 reset_counter = Signal(4, reset=15)
284 ic_reset = Signal(reset=1)
285 sync = getattr(self.sync, cd.name)
286 sync += \
287 If(reset_counter != 0,
288 reset_counter.eq(reset_counter - 1)
289 ).Else(
290 ic_reset.eq(0)
291 )
292 self.specials += Instance("IDELAYCTRL", i_REFCLK=cd.clk, i_RST=ic_reset)
293
294 # Xilinx / Ultrascale ------------------------------------------------------------------------------
295
296 # TODO:
297 # - use Ultrascale primitives instead of 7-Series' ones. (Vivado recognize and convert them).
298
299 class USPLL(XilinxClocking):
300 nclkouts_max = 6
301
302 def __init__(self, speedgrade=-1):
303 XilinxClocking.__init__(self)
304 self.divclk_divide_range = (1, 56+1)
305 self.clkin_freq_range = {
306 -1: (70e6, 800e6),
307 -2: (70e6, 933e6),
308 -3: (70e6, 1066e6),
309 }[speedgrade]
310 self.vco_freq_range = {
311 -1: (600e6, 1200e6),
312 -2: (600e6, 1335e6),
313 -3: (600e6, 1335e6),
314 }[speedgrade]
315
316 def do_finalize(self):
317 XilinxClocking.do_finalize(self)
318 config = self.compute_config()
319 pll_fb = Signal()
320 self.params.update(
321 p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked,
322
323 # VCO
324 p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=period_ns(self.clkin_freq),
325 p_CLKFBOUT_MULT=config["clkfbout_mult"], p_DIVCLK_DIVIDE=config["divclk_divide"],
326 i_CLKIN1=self.clkin, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb,
327 )
328 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
329 self.params["p_CLKOUT{}_DIVIDE".format(n)] = config["clkout{}_divide".format(n)]
330 self.params["p_CLKOUT{}_PHASE".format(n)] = config["clkout{}_phase".format(n)]
331 self.params["o_CLKOUT{}".format(n)] = clk
332 self.specials += Instance("PLLE2_ADV", **self.params)
333
334
335 class USMMCM(XilinxClocking):
336 nclkouts_max = 7
337
338 def __init__(self, speedgrade=-1):
339 XilinxClocking.__init__(self)
340 self.divclk_divide_range = (1, 106+1)
341 self.clkin_freq_range = {
342 -1: (10e6, 800e6),
343 -2: (10e6, 933e6),
344 -3: (10e6, 1066e6),
345 }[speedgrade]
346 self.vco_freq_range = {
347 -1: (600e6, 1200e6),
348 -2: (600e6, 1440e6),
349 -3: (600e6, 1600e6),
350 }[speedgrade]
351
352 def do_finalize(self):
353 XilinxClocking.do_finalize(self)
354 config = self.compute_config()
355 mmcm_fb = Signal()
356 self.params.update(
357 p_BANDWIDTH="OPTIMIZED", o_LOCKED=self.locked,
358
359 # VCO
360 p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=period_ns(self.clkin_freq),
361 p_CLKFBOUT_MULT_F=config["clkfbout_mult"], p_DIVCLK_DIVIDE=config["divclk_divide"],
362 i_CLKIN1=self.clkin, i_CLKFBIN=mmcm_fb, o_CLKFBOUT=mmcm_fb,
363 )
364 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
365 if n == 0:
366 self.params["p_CLKOUT{}_DIVIDE_F".format(n)] = config["clkout{}_divide".format(n)]
367 else:
368 self.params["p_CLKOUT{}_DIVIDE".format(n)] = config["clkout{}_divide".format(n)]
369 self.params["p_CLKOUT{}_PHASE".format(n)] = config["clkout{}_phase".format(n)]
370 self.params["o_CLKOUT{}".format(n)] = clk
371 self.specials += Instance("MMCME2_ADV", **self.params)
372
373
374 class USIDELAYCTRL(Module):
375 def __init__(self, cd):
376 reset_counter = Signal(6, reset=63)
377 ic_reset = Signal(reset=1)
378 sync = getattr(self.sync, cd.name)
379 sync += \
380 If(reset_counter != 0,
381 reset_counter.eq(reset_counter - 1)
382 ).Else(
383 ic_reset.eq(0)
384 )
385 self.specials += Instance("IDELAYCTRL",
386 p_SIM_DEVICE="ULTRASCALE",
387 i_REFCLK=cd.clk,
388 i_RST=ic_reset)
389
390 # Lattice / iCE40 ----------------------------------------------------------------------------------
391
392 # TODO:
393 # - add phase support.
394 # - add support for GENCLK_HALF to be able to generate clock down to 8MHz.
395
396 class iCE40PLL(Module):
397 nclkouts_max = 1
398 divr_range = (0, 16)
399 divf_range = (0, 128)
400 divq_range = (0, 7)
401 clki_freq_range = ( 10e6, 133e9)
402 clko_freq_range = ( 16e6, 275e9)
403 vco_freq_range = (533e6, 1066e6)
404
405 def __init__(self):
406 self.reset = Signal()
407 self.locked = Signal()
408 self.clkin_freq = None
409 self.vcxo_freq = None
410 self.nclkouts = 0
411 self.clkouts = {}
412 self.config = {}
413 self.params = {}
414
415 def register_clkin(self, clkin, freq):
416 (clki_freq_min, clki_freq_max) = self.clki_freq_range
417 assert freq >= clki_freq_min
418 assert freq <= clki_freq_max
419 self.clkin = Signal()
420 if isinstance(clkin, (Signal, ClockSignal)):
421 self.comb += self.clkin.eq(clkin)
422 else:
423 raise ValueError
424 self.clkin_freq = freq
425
426 def create_clkout(self, cd, freq, margin=1e-2):
427 (clko_freq_min, clko_freq_max) = self.clko_freq_range
428 assert freq >= clko_freq_min
429 assert freq <= clko_freq_max
430 assert self.nclkouts < self.nclkouts_max
431 clkout = Signal()
432 self.clkouts[self.nclkouts] = (clkout, freq, 0, margin)
433 self.nclkouts += 1
434 self.comb += cd.clk.eq(clkout)
435
436 def compute_config(self):
437 config = {}
438 for divr in range(*self.divr_range):
439 for divf in range(*self.divf_range):
440 all_valid = True
441 vco_freq = self.clkin_freq/(divr + 1)*(divf + 1)
442 (vco_freq_min, vco_freq_max) = self.vco_freq_range
443 if vco_freq >= vco_freq_min and vco_freq <= vco_freq_max:
444 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
445 valid = False
446 for divq in range(*self.divq_range):
447 clk_freq = vco_freq/(2**divq)
448 if abs(clk_freq - f) < f*m:
449 config["divq"] = divq
450 valid = True
451 break
452 if not valid:
453 all_valid = False
454 else:
455 all_valid = False
456 if all_valid:
457 config["vco"] = vco_freq
458 config["divr"] = divr
459 config["divf"] = divf
460 return config
461 raise ValueError("No PLL config found")
462
463 def do_finalize(self):
464 config = self.compute_config()
465 clkfb = Signal()
466 for f, v in [(17e6, 1), (26e6, 2), (44e6, 3), (66e6, 4), (101e6, 5), (133e6, 6)]:
467 pfd_freq = self.clkin_freq/(config["divr"] + 1)
468 if pfd_freq < f:
469 filter_range = v
470 break
471 self.params.update(
472 p_FEEDBACK_PATH="SIMPLE",
473 p_FILTER_RANGE=filter_range,
474 i_RESETB=~self.reset,
475 i_REFERENCECLK=self.clkin,
476 o_LOCK=self.locked,
477 )
478 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
479 self.params["p_DIVR"] = config["divr"]
480 self.params["p_DIVF"] = config["divf"]
481 self.params["p_DIVQ"] = config["divq"]
482 self.params["o_PLLOUTGLOBAL"] = clk
483 self.specials += Instance("SB_PLL40_CORE", **self.params)
484
485 # Lattice / ECP5 -----------------------------------------------------------------------------------
486
487 # TODO:
488 # - add proper phase support.
489
490 class ECP5PLL(Module):
491 nclkouts_max = 3
492 clkfb_div_range = (1, 128+1)
493 clko_div_range = (1, 128+1)
494 clki_freq_range = ( 8e6, 400e6)
495 clko_freq_range = (3.125e6, 400e6)
496 vco_freq_range = ( 550e6, 1250e6)
497
498 def __init__(self):
499 self.reset = Signal()
500 self.locked = Signal()
501 self.clkin_freq = None
502 self.vcxo_freq = None
503 self.nclkouts = 0
504 self.clkouts = {}
505 self.config = {}
506 self.params = {}
507
508 def register_clkin(self, clkin, freq):
509 (clki_freq_min, clki_freq_max) = self.clki_freq_range
510 assert freq >= clki_freq_min
511 assert freq <= clki_freq_max
512 self.clkin = Signal()
513 if isinstance(clkin, (Signal, ClockSignal)):
514 self.comb += self.clkin.eq(clkin)
515 else:
516 raise ValueError
517 self.clkin_freq = freq
518
519 def create_clkout(self, cd, freq, phase=0, margin=1e-2):
520 (clko_freq_min, clko_freq_max) = self.clko_freq_range
521 assert freq >= clko_freq_min
522 assert freq <= clko_freq_max
523 assert self.nclkouts < self.nclkouts_max
524 clkout = Signal()
525 self.clkouts[self.nclkouts] = (clkout, freq, phase, margin)
526 self.nclkouts += 1
527 self.comb += cd.clk.eq(clkout)
528
529 def compute_config(self):
530 config = {}
531 config["clki_div"] = 1
532 for clkfb_div in range(*self.clkfb_div_range):
533 all_valid = True
534 vco_freq = self.clkin_freq*clkfb_div*1 # clkos3_div=1
535 (vco_freq_min, vco_freq_max) = self.vco_freq_range
536 if vco_freq >= vco_freq_min and vco_freq <= vco_freq_max:
537 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
538 valid = False
539 for d in range(*self.clko_div_range):
540 clk_freq = vco_freq/d
541 if abs(clk_freq - f) < f*m:
542 config["clko{}_freq".format(n)] = clk_freq
543 config["clko{}_div".format(n)] = d
544 config["clko{}_phase".format(n)] = p
545 valid = True
546 break
547 if not valid:
548 all_valid = False
549 else:
550 all_valid = False
551 if all_valid:
552 config["vco"] = vco_freq
553 config["clkfb_div"] = clkfb_div
554 return config
555 raise ValueError("No PLL config found")
556
557 def do_finalize(self):
558 config = self.compute_config()
559 clkfb = Signal()
560 self.params.update(
561 attr=[
562 ("ICP_CURRENT", "6"),
563 ("LPF_RESISTOR", "16"),
564 ("MFG_ENABLE_FILTEROPAMP", "1"),
565 ("MFG_GMCREF_SEL", "2")],
566 i_RST=self.reset,
567
568 i_CLKI=self.clkin,
569 o_LOCK=self.locked,
570
571 p_FEEDBK_PATH="INT_OS3", # CLKOS3 reserved for
572 p_CLKOS3_ENABLE="ENABLED", # feedback with div=1.
573 p_CLKOS3_DIV=1,
574
575 p_CLKFB_DIV=config["clkfb_div"],
576 p_CLKI_DIV=1,
577 )
578 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
579 n_to_l = {0: "P", 1: "S", 2: "S2"}
580 self.params["p_CLKO{}_ENABLE".format(n_to_l[n])] = "ENABLED"
581 self.params["p_CLKO{}_DIV".format(n_to_l[n])] = config["clko{}_div".format(n)]
582 self.params["p_CLKO{}_FPHASE".format(n_to_l[n])] = 0
583 self.params["p_CLKO{}_CPHASE".format(n_to_l[n])] = p
584 self.params["o_CLKO{}".format(n_to_l[n])] = clk
585 self.specials += Instance("EHXPLLL", **self.params)