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>
5 """Clock Abstraction Modules"""
8 from migen
.genlib
.io
import DifferentialInput
9 from migen
.genlib
.resetsync
import AsyncResetSynchronizer
11 from litex
.soc
.interconnect
.csr
import *
17 # Xilinx / Generic ---------------------------------------------------------------------------------
19 class XilinxClocking(Module
, AutoCSR
):
20 clkfbout_mult_frange
= (2, 64+1)
21 clkout_divide_range
= (1, 128+1)
23 def __init__(self
, vco_margin
=0):
24 self
.vco_margin
= vco_margin
26 self
.locked
= Signal()
27 self
.clkin_freq
= None
34 def register_clkin(self
, clkin
, freq
):
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
)
42 self
.clkin_freq
= freq
44 def create_clkout(self
, cd
, freq
, phase
=0, buf
="bufg", margin
=1e-2, with_reset
=True):
45 assert self
.nclkouts
< self
.nclkouts_max
47 self
.clkouts
[self
.nclkouts
] = (clkout
, freq
, phase
, margin
)
50 self
.specials
+= AsyncResetSynchronizer(cd
, ~self
.locked | self
.reset
)
52 self
.comb
+= cd
.clk
.eq(clkout
)
55 self
.comb
+= cd
.clk
.eq(clkout_buf
)
57 self
.specials
+= Instance("BUFG", i_I
=clkout
, o_O
=clkout_buf
)
59 self
.specials
+= Instance("BUFR", i_I
=clkout
, o_O
=clkout_buf
)
63 def compute_config(self
):
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
)):
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()):
75 for d
in range(*self
.clkout_divide_range
):
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
88 config
["vco"] = vco_freq
89 config
["clkfbout_mult"] = clkfbout_mult
91 raise ValueError("No PLL config found")
94 self
.drp_reset
= 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)
106 i_DCLK
=ClockSignal(),
107 i_DWE
=self
.drp_write
.re
,
108 i_DEN
=self
.drp_read
.re | self
.drp_write
.re
,
110 i_DADDR
=self
.drp_adr
.storage
,
111 i_DI
=self
.drp_dat_w
.storage
,
112 o_DO
=self
.drp_dat_r
.status
115 If(self
.drp_read
.re | self
.drp_write
.re
,
116 self
.drp_drdy
.status
.eq(0)
118 self
.drp_drdy
.status
.eq(1)
122 def do_finalize(self
):
123 assert hasattr(self
, "clkin")
125 # Xilinx / Spartan6 --------------------------------------------------------------------------------
127 class S6PLL(XilinxClocking
):
129 clkin_freq_range
= (19e6
, 540e6
)
131 def __init__(self
, speedgrade
=-1):
132 XilinxClocking
.__init
__(self
)
133 self
.divclk_divide_range
= (1, 52 + 1)
134 self
.vco_freq_range
= {
140 def do_finalize(self
):
141 XilinxClocking
.do_finalize(self
)
142 config
= self
.compute_config()
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
),
151 p_CLKFBOUT_MULT
=config
["clkfbout_mult"],
153 p_DIVCLK_DIVIDE
=config
["divclk_divide"],
159 o_LOCKED
=self
.locked
,
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
)
169 class S6DCM(XilinxClocking
):
170 """ single output with f_out = f_in * {2 .. 256} / {1 .. 256} """
172 clkfbout_mult_frange
= (2, 256 + 1)
173 clkout_divide_range
= (1, 256 + 1)
175 def __init__(self
, speedgrade
=-1):
176 XilinxClocking
.__init
__(self
)
177 self
.divclk_divide_range
= (1, 1) # FIXME
178 self
.clkin_freq_range
= {
184 self
.vco_freq_range
= {
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]
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
),
203 o_LOCKED
=self
.locked
,
205 self
.specials
+= Instance("DCM_CLKGEN", **self
.params
)
207 # Xilinx / 7-Series --------------------------------------------------------------------------------
209 class S7PLL(XilinxClocking
):
211 clkin_freq_range
= (19e6
, 800e6
)
213 def __init__(self
, speedgrade
=-1):
214 XilinxClocking
.__init
__(self
)
215 self
.divclk_divide_range
= (1, 56+1)
216 self
.vco_freq_range
= {
222 def do_finalize(self
):
223 XilinxClocking
.do_finalize(self
)
224 config
= self
.compute_config()
227 p_STARTUP_WAIT
="FALSE", o_LOCKED
=self
.locked
,
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
,
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
)
241 class S7MMCM(XilinxClocking
):
244 def __init__(self
, speedgrade
=-1):
245 XilinxClocking
.__init
__(self
)
246 self
.divclk_divide_range
= (1, 106+1)
247 self
.clkin_freq_range
= {
253 self
.vco_freq_range
= {
259 def do_finalize(self
):
260 XilinxClocking
.do_finalize(self
)
261 config
= self
.compute_config()
264 p_BANDWIDTH
="OPTIMIZED", o_LOCKED
=self
.locked
,
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
,
271 for n
, (clk
, f
, p
, m
) in sorted(self
.clkouts
.items()):
273 self
.params
["p_CLKOUT{}_DIVIDE_F".format(n
)] = config
["clkout{}_divide".format(n
)]
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
)
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
)
287 If(reset_counter
!= 0,
288 reset_counter
.eq(reset_counter
- 1)
292 self
.specials
+= Instance("IDELAYCTRL", i_REFCLK
=cd
.clk
, i_RST
=ic_reset
)
294 # Xilinx / Ultrascale ------------------------------------------------------------------------------
297 # - use Ultrascale primitives instead of 7-Series' ones. (Vivado recognize and convert them).
299 class USPLL(XilinxClocking
):
302 def __init__(self
, speedgrade
=-1):
303 XilinxClocking
.__init
__(self
)
304 self
.divclk_divide_range
= (1, 56+1)
305 self
.clkin_freq_range
= {
310 self
.vco_freq_range
= {
316 def do_finalize(self
):
317 XilinxClocking
.do_finalize(self
)
318 config
= self
.compute_config()
321 p_STARTUP_WAIT
="FALSE", o_LOCKED
=self
.locked
,
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
,
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
)
335 class USMMCM(XilinxClocking
):
338 def __init__(self
, speedgrade
=-1):
339 XilinxClocking
.__init
__(self
)
340 self
.divclk_divide_range
= (1, 106+1)
341 self
.clkin_freq_range
= {
346 self
.vco_freq_range
= {
352 def do_finalize(self
):
353 XilinxClocking
.do_finalize(self
)
354 config
= self
.compute_config()
357 p_BANDWIDTH
="OPTIMIZED", o_LOCKED
=self
.locked
,
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
,
364 for n
, (clk
, f
, p
, m
) in sorted(self
.clkouts
.items()):
366 self
.params
["p_CLKOUT{}_DIVIDE_F".format(n
)] = config
["clkout{}_divide".format(n
)]
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
)
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
)
380 If(reset_counter
!= 0,
381 reset_counter
.eq(reset_counter
- 1)
385 self
.specials
+= Instance("IDELAYCTRL",
386 p_SIM_DEVICE
="ULTRASCALE",
390 # Lattice / iCE40 ----------------------------------------------------------------------------------
393 # - add phase support.
394 # - add support for GENCLK_HALF to be able to generate clock down to 8MHz.
396 class iCE40PLL(Module
):
399 divf_range
= (0, 128)
401 clki_freq_range
= ( 10e6
, 133e9
)
402 clko_freq_range
= ( 16e6
, 275e9
)
403 vco_freq_range
= (533e6
, 1066e6
)
406 self
.reset
= Signal()
407 self
.locked
= Signal()
408 self
.clkin_freq
= None
409 self
.vcxo_freq
= None
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
)
424 self
.clkin_freq
= freq
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
432 self
.clkouts
[self
.nclkouts
] = (clkout
, freq
, 0, margin
)
434 self
.comb
+= cd
.clk
.eq(clkout
)
436 def compute_config(self
):
438 for divr
in range(*self
.divr_range
):
439 for divf
in range(*self
.divf_range
):
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()):
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
457 config
["vco"] = vco_freq
458 config
["divr"] = divr
459 config
["divf"] = divf
461 raise ValueError("No PLL config found")
463 def do_finalize(self
):
464 config
= self
.compute_config()
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)
472 p_FEEDBACK_PATH
="SIMPLE",
473 p_FILTER_RANGE
=filter_range
,
474 i_RESETB
=~self
.reset
,
475 i_REFERENCECLK
=self
.clkin
,
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
)
485 # Lattice / ECP5 -----------------------------------------------------------------------------------
488 # - add proper phase support.
490 class ECP5PLL(Module
):
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
)
499 self
.reset
= Signal()
500 self
.locked
= Signal()
501 self
.clkin_freq
= None
502 self
.vcxo_freq
= None
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
)
517 self
.clkin_freq
= freq
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
525 self
.clkouts
[self
.nclkouts
] = (clkout
, freq
, phase
, margin
)
527 self
.comb
+= cd
.clk
.eq(clkout
)
529 def compute_config(self
):
531 config
["clki_div"] = 1
532 for clkfb_div
in range(*self
.clkfb_div_range
):
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()):
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
552 config
["vco"] = vco_freq
553 config
["clkfb_div"] = clkfb_div
555 raise ValueError("No PLL config found")
557 def do_finalize(self
):
558 config
= self
.compute_config()
562 ("ICP_CURRENT", "6"),
563 ("LPF_RESISTOR", "16"),
564 ("MFG_ENABLE_FILTEROPAMP", "1"),
565 ("MFG_GMCREF_SEL", "2")],
571 p_FEEDBK_PATH
="INT_OS3", # CLKOS3 reserved for
572 p_CLKOS3_ENABLE
="ENABLED", # feedback with div=1.
575 p_CLKFB_DIV
=config
["clkfb_div"],
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
)