1 # Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
2 # Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Copyright (c) 2018-2020 Florent Kermarrec <florent@enjoy-digital.fr>
4 # Copyright (c) 2019 Michael Betz <michibetz@gmail.com>
6 # Based on code from LambaConcept, from the gram example which is BSD-2-License
7 # https://github.com/jeanthom/gram/tree/master/examples
9 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
10 # under EU Grants 871528 and 957073, under the LGPLv3+ License
12 from nmigen
import (Elaboratable
, Module
, Signal
, ClockDomain
, Instance
,
13 ClockSignal
, ResetSignal
)
14 from nmigen
.lib
.cdc
import ResetSynchronizer
20 # Helpers -----------------------------------------------------------------
22 def clkdiv_range(start
, stop
, step
=1):
28 yield int(current
) if math
.floor(current
) == current
else current
32 # Xilinx / Generic -----------------------------------------------------
34 class XilinxClocking(Elaboratable
):
35 clkfbout_mult_frange
= (2, 64+1)
36 clkout_divide_range
= (1, 128+1)
38 def __init__(self
, clkin
, vco_margin
=0):
40 self
.vco_margin
= vco_margin
42 self
.locked
= Signal()
43 self
.clkin_freq
= None
50 def set_clkin_freq(self
, freq
):
51 self
.clkin_freq
= freq
53 def create_clkout(self
, cd
, freq
, phase
=0, buf
="bufg",
54 margin
=1e-2, with_reset
=True, ce
=None):
55 assert self
.nclkouts
< self
.nclkouts_max
57 self
.clkouts
[self
.nclkouts
] = (clkout
, freq
, phase
, margin
, cd
,
60 def elaborate(self
, platform
):
61 assert self
.clkin_freq
is not None
64 for n
, clkcfg
in self
.clkouts
.items():
65 (clkout
, freq
, phase
, margin
, cd
, buf
, with_reset
, ce
) = clkcfg
68 m
.submodules
+= ResetSynchronizer(arst
, domain
=cd
.name
)
69 comb
+= arst
.eq(~self
.locked | self
.reset
)
71 comb
+= cd
.clk
.eq(clkout
)
74 comb
+= cd
.clk
.eq(clkout_buf
)
76 m
.submodules
+= Instance("BUFG",
80 m
.submodules
+= Instance("BUFR",
85 raise ValueError("BUFGCE requires user to provide "
86 "a clock enable ce Signal")
87 m
.submodules
+= Instance("BUFGCE",
92 m
.submodules
+= Instance("BUFIO",
96 raise ValueError("Unsupported clock buffer: %s" % buf
)
97 print(cd
.name
, freq
, margin
, self
.nclkouts
)
101 def compute_config(self
):
103 print ("compute_config", self
.divclk_divide_range
)
104 print ("mult", self
.clkfbout_mult_frange
)
105 print ("divrange", self
.clkout_divide_range
)
106 for divclk_divide
in range(*self
.divclk_divide_range
):
107 config
["divclk_divide"] = divclk_divide
108 for clkfbout_mult
in reversed(range(*self
.clkfbout_mult_frange
)):
110 vco_freq
= self
.clkin_freq
*clkfbout_mult
/divclk_divide
111 (vco_freq_min
, vco_freq_max
) = self
.vco_freq_range
112 if (vco_freq
>= vco_freq_min
*(1 + self
.vco_margin
) and
113 vco_freq
<= vco_freq_max
*(1 - self
.vco_margin
)):
114 for n
, clkcfg
in sorted(self
.clkouts
.items()):
115 (clkout
, f
, p
, m
, _
, _
, _
, _
) = clkcfg
117 d_ranges
= [self
.clkout_divide_range
]
118 r
= getattr(self
, "clkout%d_divide_range" % n
, None)
121 for d_range
in d_ranges
:
122 for d
in clkdiv_range(*d_range
):
123 clk_freq
= vco_freq
/d
124 if abs(clk_freq
- f
) <= f
*m
:
125 config
["clkout%d_freq" % n
] = clk_freq
126 config
["clkout%d_divide" % n
] = d
127 config
["clkout%d_phase" % n
] = p
137 config
["vco"] = vco_freq
138 config
["clkfbout_mult"] = clkfbout_mult
141 raise ValueError("No PLL config found")
144 # Xilinx / 7-Series --------------------------------------------------------
146 class S7PLL(XilinxClocking
):
148 clkin_freq_range
= (19e6
, 800e6
)
150 def __init__(self
, clkin
, speedgrade
=-1):
151 super().__init
__(clkin
)
152 #self.logger = logging.getLogger("S7PLL")
153 print ("Creating S7PLL, speedgrade %d" % speedgrade
)
154 self
.divclk_divide_range
= (1, 56+1)
155 self
.vco_freq_range
= {
161 def elaborate(self
, platform
):
162 m
= super().elaborate(platform
)
163 config
= self
.compute_config()
166 p_STARTUP_WAIT
="FALSE", o_LOCKED
=self
.locked
, i_RST
=self
.reset
,
169 p_REF_JITTER1
=0.01, p_CLKIN1_PERIOD
=1e9
/self
.clkin_freq
,
170 p_CLKFBOUT_MULT
=config
["clkfbout_mult"],
171 p_DIVCLK_DIVIDE
=config
["divclk_divide"],
172 i_CLKIN1
=self
.clkin
, i_CLKFBIN
=pll_fb
, o_CLKFBOUT
=pll_fb
,
174 for n
, (clk
, f
, p
, _
, _
, _
, _
, _
) in sorted(self
.clkouts
.items()):
175 div
= config
["clkout{}_divide".format(n
)]
176 phs
= config
["clkout{}_phase".format(n
)]
177 self
.params
["p_CLKOUT{}_DIVIDE".format(n
)] = div
178 self
.params
["p_CLKOUT{}_PHASE".format(n
)] = phs
179 self
.params
["o_CLKOUT{}".format(n
)] = clk
180 m
.submodules
+= Instance("PLLE2_ADV", **self
.params
)
184 class ECP5PLL(Elaboratable
):
186 clki_div_range
= (1, 128+1)
187 clkfb_div_range
= (1, 128+1)
188 clko_div_range
= (1, 128+1)
189 clki_freq_range
= ( 8e6
, 400e6
)
190 clko_freq_range
= (3.125e6
, 400e6
)
191 vco_freq_range
= ( 400e6
, 800e6
)
193 def __init__(self
, clkin
,
194 clksel
=Signal(shape
=2, reset
=2),
195 reset
=Signal(reset_less
=True),
198 self
.clkin_freq
= None
212 ] + list(self
.clkouts
.values())
214 def set_clkin_freq(self
, freq
):
215 (clki_freq_min
, clki_freq_max
) = self
.clki_freq_range
216 assert freq
>= clki_freq_min
217 assert freq
<= clki_freq_max
218 self
.clkin_freq
= freq
220 def create_clkout(self
, cd
, freq
, phase
=0, margin
=1e-2):
221 (clko_freq_min
, clko_freq_max
) = self
.clko_freq_range
222 assert freq
>= clko_freq_min
223 assert freq
<= clko_freq_max
224 assert self
.nclkouts
< self
.nclkouts_max
225 self
.clkouts
[self
.nclkouts
] = (cd
, freq
, phase
, margin
)
226 #create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts)
227 print("clock domain", cd
.domain
, freq
, margin
, self
.nclkouts
)
230 def compute_config(self
):
232 for clki_div
in range(*self
.clki_div_range
):
233 config
["clki_div"] = clki_div
234 for clkfb_div
in range(*self
.clkfb_div_range
):
236 vco_freq
= self
.clkin_freq
/clki_div
*clkfb_div
*1 # clkos3_div=1
237 (vco_freq_min
, vco_freq_max
) = self
.vco_freq_range
238 if vco_freq
>= vco_freq_min
and vco_freq
<= vco_freq_max
:
239 for n
, (clk
, f
, p
, m
) in sorted(self
.clkouts
.items()):
241 for d
in range(*self
.clko_div_range
):
242 clk_freq
= vco_freq
/d
243 if abs(clk_freq
- f
) <= f
*m
:
244 config
["clko{}_freq".format(n
)] = clk_freq
245 config
["clko{}_div".format(n
)] = d
246 config
["clko{}_phase".format(n
)] = p
254 config
["vco"] = vco_freq
255 config
["clkfb_div"] = clkfb_div
256 #compute_config_log(self.logger, config)
257 print ("PLL config", config
)
259 raise ValueError("No PLL config found")
261 def elaborate(self
, platform
):
262 config
= self
.compute_config()
266 a_FREQUENCY_PIN_CLKI
= str(self
.clkin_freq
/1e6
),
268 a_LPF_RESISTOR
= "16",
269 a_MFG_ENABLE_FILTEROPAMP
= "1",
270 a_MFG_GMCREF_SEL
= "2",
272 p_FEEDBK_PATH
= "INT_OS3", # CLKOS3 rsvd for feedback with div=1.
273 p_CLKOS3_ENABLE
= "ENABLED",
275 p_CLKFB_DIV
= config
["clkfb_div"],
276 p_CLKI_DIV
= config
["clki_div"],
277 # reset, input clock, lock-achieved output
280 o_LOCK
= self
.locked
,
282 # for each clock-out, set additional parameters
283 for n
, (clk
, f
, p
, m
) in sorted(self
.clkouts
.items()):
284 n_to_l
= {0: "P", 1: "S", 2: "S2"}
285 div
= config
["clko{}_div".format(n
)]
286 cphase
= int(p
*(div
+ 1)/360 + div
)
287 self
.params
["p_CLKO{}_ENABLE".format(n_to_l
[n
])] = "ENABLED"
288 self
.params
["p_CLKO{}_DIV".format(n_to_l
[n
])] = div
289 self
.params
["p_CLKO{}_FPHASE".format(n_to_l
[n
])] = 0
290 self
.params
["p_CLKO{}_CPHASE".format(n_to_l
[n
])] = cphase
291 self
.params
["o_CLKO{}".format(n_to_l
[n
])] = clk
294 print ("params", self
.params
)
295 pll
= Instance("EHXPLLL", **self
.params
)
296 m
.submodules
.pll
= pll
299 pll
= Instance("EHXPLLL",
300 p_OUTDIVIDER_MUXA
='DIVA',
301 p_OUTDIVIDER_MUXB
='DIVB',
302 p_CLKOP_ENABLE
='ENABLED',
303 p_CLKOS_ENABLE
='ENABLED',
304 p_CLKOS2_ENABLE
='DISABLED',
305 p_CLKOS3_ENABLE
='DISABLED',
306 p_CLKOP_DIV
=self
.CLKOP_DIV
,
307 p_CLKOS_DIV
=self
.CLKOS_DIV
,
308 p_CLKFB_DIV
=self
.CLKFB_DIV
,
309 p_CLKI_DIV
=self
.CLKI_DIV
,
310 p_FEEDBK_PATH
='INT_OP',
311 p_CLKOP_TRIM_POL
="FALLING",
312 p_CLKOP_TRIM_DELAY
=0,
313 p_CLKOS_TRIM_POL
="FALLING",
314 p_CLKOS_TRIM_DELAY
=0,
328 o_CLKOP
=self
.clkout1
,
329 o_CLKOS
=self
.clkout2
,
330 o_CLKOS2
=self
.clkout3
,
331 o_CLKOS3
=self
.clkout4
,
335 # CRG ----------------------------------------------------------------
337 class ArtyA7CRG(Elaboratable
):
338 def __init__(self
, sys_clk_freq
):
339 self
.sys_clk_freq
= sys_clk_freq
341 def elaborate(self
, platform
):
344 # Get 100Mhz from oscillator
345 clk100
= platform
.request("clk100")
346 cd_rawclk
= ClockDomain("rawclk", local
=True, reset_less
=True)
347 m
.d
.comb
+= cd_rawclk
.clk
.eq(clk100
)
348 m
.domains
+= cd_rawclk
350 sync
= ClockDomain("sync")
351 #sync2x = ClockDomain("sync2x", reset_less=True)
352 #sync4x = ClockDomain("sync4x", reset_less=True)
353 #sync4x_dqs = ClockDomain("sync4x_dqs", reset_less=True)
354 #cd_clk200 = ClockDomain("cd_clk200")
355 #cd_eth = ClockDomain("cd_eth")
356 dramsync
= ClockDomain("dramsync")
361 #m.domains += sync4x_dqs
362 #m.domains += cd_clk200
364 m
.domains
+= dramsync
366 m
.submodules
.pll
= pll
= S7PLL(clk100
, speedgrade
=-1)
367 reset
= platform
.request(platform
.default_rst
).i
368 m
.d
.comb
+= pll
.reset
.eq(reset
)
369 pll
.set_clkin_freq(100e6
)
371 pll
.create_clkout(sync
, self
.sys_clk_freq
)
373 #platform.add_period_constraint(clk100_buf, 1e9/100e6)
374 #platform.add_period_constraint(sync.clk, 1e9/sys_clk_freq)
375 #platform.add_false_path_constraints(clk100_buf, sync.clk)
377 # temporarily set dram sync clock exactly equal to main sync
378 m
.d
.comb
+= ClockSignal("dramsync").eq(ClockSignal("sync"))
382 class ECPIX5CRG(Elaboratable
):
383 def __init__(self
, sys_clk_freq
=100e6
):
384 self
.sys_clk_freq
= sys_clk_freq
386 def elaborate(self
, platform
):
389 # Get 100Mhz from oscillator
390 clk100
= platform
.request("clk100")
391 cd_rawclk
= ClockDomain("rawclk", local
=True, reset_less
=True)
392 m
.d
.comb
+= cd_rawclk
.clk
.eq(clk100
)
393 m
.domains
+= cd_rawclk
396 reset
= platform
.request(platform
.default_rst
).i
401 Instance("FD1S3AX", p_GSR
="DISABLED",
402 i_CK
=ClockSignal("rawclk"),
405 Instance("FD1S3AX", p_GSR
="DISABLED",
406 i_CK
=ClockSignal("rawclk"),
409 Instance("SGSR", i_CLK
=ClockSignal("rawclk"),
413 # Power-on delay (655us)
414 podcnt
= Signal(3, reset
=-1)
416 with m
.If(podcnt
!= 0):
417 m
.d
.rawclk
+= podcnt
.eq(podcnt
-1)
418 m
.d
.rawclk
+= pod_done
.eq(podcnt
== 0)
420 # Generating sync2x (200Mhz) and init (25Mhz) from clk100
421 cd_sync2x
= ClockDomain("sync2x", local
=False)
422 cd_sync2x_unbuf
= ClockDomain("sync2x_unbuf",
423 local
=False, reset_less
=True)
424 cd_init
= ClockDomain("init", local
=False)
425 cd_sync
= ClockDomain("sync", local
=False)
426 cd_dramsync
= ClockDomain("dramsync", local
=False)
427 m
.submodules
.pll
= pll
= ECP5PLL(ClockSignal("rawclk"), reset
=~reset
)
428 pll
.set_clkin_freq(100e6
)
429 pll
.create_clkout(ClockSignal("sync2x_unbuf"), 2*self
.sys_clk_freq
)
430 pll
.create_clkout(ClockSignal("init"), 25e6
)
431 m
.submodules
+= Instance("ECLKSYNCB",
432 i_ECLKI
= ClockSignal("sync2x_unbuf"),
434 o_ECLKO
= ClockSignal("sync2x"))
435 m
.domains
+= cd_sync2x_unbuf
436 m
.domains
+= cd_sync2x
439 m
.domains
+= cd_dramsync
440 reset_ok
= Signal(reset_less
=True)
441 m
.d
.comb
+= reset_ok
.eq(~pll
.locked|~pod_done
)
442 m
.d
.comb
+= ResetSignal("init").eq(reset_ok
)
443 m
.d
.comb
+= ResetSignal("sync").eq(reset_ok
)
444 m
.d
.comb
+= ResetSignal("dramsync").eq(reset_ok
)
446 # # Generating sync (100Mhz) from sync2x
448 m
.submodules
+= Instance("CLKDIVF",
451 i_CLKI
=ClockSignal("sync2x"),
453 o_CDIVX
=ClockSignal("sync"))
455 # temporarily set dram sync clock exactly equal to main sync
456 m
.d
.comb
+= ClockSignal("dramsync").eq(ClockSignal("sync"))