fix Arty A7-100t PLL with quick demo
[ls2.git] / src / arty_crg.py
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>
5 #
6 # Based on code from LambaConcept, from the gram example which is BSD-2-License
7 # https://github.com/jeanthom/gram/tree/master/examples
8 #
9 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
10 # under EU Grants 871528 and 957073, under the LGPLv3+ License
11
12 from nmigen import (Elaboratable, Module, Signal, ClockDomain, Instance,
13 ClockSignal, ResetSignal)
14 from nmigen.lib.cdc import ResetSynchronizer
15 import math
16
17 __ALL__ = ["ArtyCRG"]
18
19
20 # Helpers -----------------------------------------------------------------
21
22 def clkdiv_range(start, stop, step=1):
23 start = float(start)
24 stop = float(stop)
25 step = float(step)
26 current = start
27 while current < stop:
28 yield int(current) if math.floor(current) == current else current
29 current += step
30
31
32 # Xilinx / Generic -----------------------------------------------------
33
34 class XilinxClocking(Elaboratable):
35 clkfbout_mult_frange = (2, 64+1)
36 clkout_divide_range = (1, 128+1)
37
38 def __init__(self, clkin, vco_margin=0):
39 self.clkin = clkin
40 self.vco_margin = vco_margin
41 self.reset = Signal()
42 self.locked = Signal()
43 self.clkin_freq = None
44 self.vcxo_freq = None
45 self.nclkouts = 0
46 self.clkouts = {}
47 self.config = {}
48 self.params = {}
49
50 def set_clkin_freq(self, freq):
51 self.clkin_freq = freq
52
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
56 clkout = Signal()
57 self.clkouts[self.nclkouts] = (clkout, freq, phase, margin, cd,
58 buf, with_reset, ce)
59
60 def elaborate(self, platform):
61 assert self.clkin_freq is not None
62 m = Module()
63 comb = m.d.comb
64 for n, clkcfg in self.clkouts.items():
65 (clkout, freq, phase, margin, cd, buf, with_reset, ce) = clkcfg
66 if with_reset:
67 arst = Signal()
68 m.submodules += ResetSynchronizer(arst, domain=cd.name)
69 comb += arst.eq(~self.locked | self.reset)
70 if buf is None:
71 comb += cd.clk.eq(clkout)
72 else:
73 clkout_buf = Signal()
74 comb += cd.clk.eq(clkout_buf)
75 if buf == "bufg":
76 m.submodules += Instance("BUFG",
77 i_I=clkout,
78 o_O=clkout_buf)
79 elif buf == "bufr":
80 m.submodules += Instance("BUFR",
81 i_I=clkout,
82 o_O=clkout_buf)
83 elif buf == "bufgce":
84 if ce is None:
85 raise ValueError("BUFGCE requires user to provide "
86 "a clock enable ce Signal")
87 m.submodules += Instance("BUFGCE",
88 i_I=clkout,
89 o_O=clkout_buf,
90 i_CE=ce)
91 elif buf == "bufio":
92 m.submodules += Instance("BUFIO",
93 i_I=clkout,
94 o_O=clkout_buf)
95 else:
96 raise ValueError("Unsupported clock buffer: %s" % buf)
97 print(cd.name, freq, margin, self.nclkouts)
98
99 return m
100
101 def compute_config(self):
102 config = {}
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)):
109 all_valid = True
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
116 valid = False
117 d_ranges = [self.clkout_divide_range]
118 r = getattr(self, "clkout%d_divide_range" % n, None)
119 if r is not None:
120 d_ranges += [r]
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
128 valid = True
129 break
130 if valid:
131 break
132 if not valid:
133 all_valid = False
134 else:
135 all_valid = False
136 if all_valid:
137 config["vco"] = vco_freq
138 config["clkfbout_mult"] = clkfbout_mult
139 print(config)
140 return config
141 raise ValueError("No PLL config found")
142
143
144 # Xilinx / 7-Series --------------------------------------------------------
145
146 class S7PLL(XilinxClocking):
147 nclkouts_max = 6
148 clkin_freq_range = (19e6, 800e6)
149
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 = {
156 -1: (800e6, 1600e6),
157 -2: (800e6, 1866e6),
158 -3: (800e6, 2133e6),
159 }[speedgrade]
160
161 def elaborate(self, platform):
162 m = super().elaborate(platform)
163 config = self.compute_config()
164 pll_fb = Signal()
165 self.params.update(
166 p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked, i_RST=self.reset,
167
168 # VCO
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,
173 )
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)
181 return m
182
183
184 class ECP5PLL(Elaboratable):
185 nclkouts_max = 3
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)
192
193 def __init__(self, clkin,
194 clksel=Signal(shape=2, reset=2),
195 reset=Signal(reset_less=True),
196 locked=Signal()):
197 self.clkin = clkin
198 self.clkin_freq = None
199 self.clksel = clksel
200 self.locked = locked
201 self.reset = reset
202 self.nclkouts = 0
203 self.clkouts = {}
204 self.config = {}
205 self.params = {}
206
207 def ports(self):
208 return [
209 self.clkin,
210 self.clksel,
211 self.lock,
212 ] + list(self.clkouts.values())
213
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
219
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)
228 self.nclkouts += 1
229
230 def compute_config(self):
231 config = {}
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):
235 all_valid = True
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()):
240 valid = False
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
247 valid = True
248 break
249 if not valid:
250 all_valid = False
251 else:
252 all_valid = False
253 if all_valid:
254 config["vco"] = vco_freq
255 config["clkfb_div"] = clkfb_div
256 #compute_config_log(self.logger, config)
257 print ("PLL config", config)
258 return config
259 raise ValueError("No PLL config found")
260
261 def elaborate(self, platform):
262 config = self.compute_config()
263 clkfb = Signal()
264 self.params.update(
265 # attributes
266 a_FREQUENCY_PIN_CLKI = str(self.clkin_freq/1e6),
267 a_ICP_CURRENT = "6",
268 a_LPF_RESISTOR = "16",
269 a_MFG_ENABLE_FILTEROPAMP = "1",
270 a_MFG_GMCREF_SEL = "2",
271 # parameters
272 p_FEEDBK_PATH = "INT_OS3", # CLKOS3 rsvd for feedback with div=1.
273 p_CLKOS3_ENABLE = "ENABLED",
274 p_CLKOS3_DIV = 1,
275 p_CLKFB_DIV = config["clkfb_div"],
276 p_CLKI_DIV = config["clki_div"],
277 # reset, input clock, lock-achieved output
278 i_RST = self.reset,
279 i_CLKI = self.clkin,
280 o_LOCK = self.locked,
281 )
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
292
293 m = Module()
294 print ("params", self.params)
295 pll = Instance("EHXPLLL", **self.params)
296 m.submodules.pll = pll
297 return m
298
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,
315 i_CLKI=self.clkin,
316 i_RST=0,
317 i_STDBY=0,
318 i_PHASESEL0=0,
319 i_PHASESEL1=0,
320 i_PHASEDIR=0,
321 i_PHASESTEP=0,
322 i_PHASELOADREG=0,
323 i_PLLWAKESYNC=0,
324 i_ENCLKOP=1,
325 i_ENCLKOS=1,
326 i_ENCLKOS2=0,
327 i_ENCLKOS3=0,
328 o_CLKOP=self.clkout1,
329 o_CLKOS=self.clkout2,
330 o_CLKOS2=self.clkout3,
331 o_CLKOS3=self.clkout4,
332 o_LOCK=self.lock,
333 )
334
335 # CRG ----------------------------------------------------------------
336
337 class ArtyA7CRG(Elaboratable):
338 def __init__(self, sys_clk_freq):
339 self.sys_clk_freq = sys_clk_freq
340
341 def elaborate(self, platform):
342 m = Module()
343
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
349
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")
357
358 m.domains += sync
359 #m.domains += sync2x
360 #m.domains += sync4x
361 #m.domains += sync4x_dqs
362 #m.domains += cd_clk200
363 #m.domains += cd_eth
364 m.domains += dramsync
365
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)
370
371 pll.create_clkout(sync, self.sys_clk_freq)
372
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)
376
377 # temporarily set dram sync clock exactly equal to main sync
378 m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync"))
379 return m
380
381
382 class ECPIX5CRG(Elaboratable):
383 def __init__(self, sys_clk_freq=100e6):
384 self.sys_clk_freq = sys_clk_freq
385
386 def elaborate(self, platform):
387 m = Module()
388
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
394
395 # Reset
396 reset = platform.request(platform.default_rst).i
397 gsr0 = Signal()
398 gsr1 = Signal()
399
400 m.submodules += [
401 Instance("FD1S3AX", p_GSR="DISABLED",
402 i_CK=ClockSignal("rawclk"),
403 i_D=~reset,
404 o_Q=gsr0),
405 Instance("FD1S3AX", p_GSR="DISABLED",
406 i_CK=ClockSignal("rawclk"),
407 i_D=gsr0,
408 o_Q=gsr1),
409 Instance("SGSR", i_CLK=ClockSignal("rawclk"),
410 i_GSR=gsr1),
411 ]
412
413 # Power-on delay (655us)
414 podcnt = Signal(3, reset=-1)
415 pod_done = Signal()
416 with m.If(podcnt != 0):
417 m.d.rawclk += podcnt.eq(podcnt-1)
418 m.d.rawclk += pod_done.eq(podcnt == 0)
419
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"),
433 i_STOP = 0,
434 o_ECLKO = ClockSignal("sync2x"))
435 m.domains += cd_sync2x_unbuf
436 m.domains += cd_sync2x
437 m.domains += cd_init
438 m.domains += cd_sync
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)
445
446 # # Generating sync (100Mhz) from sync2x
447
448 m.submodules += Instance("CLKDIVF",
449 p_DIV="2.0",
450 i_ALIGNWD=0,
451 i_CLKI=ClockSignal("sync2x"),
452 i_RST=0,
453 o_CDIVX=ClockSignal("sync"))
454
455 # temporarily set dram sync clock exactly equal to main sync
456 m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync"))
457
458 return m