remove unneeded model variable
[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(reset_less=True)
42 self.locked = Signal(reset_less=True)
43 self.pod_done = Signal(reset_less=True) # provide this @ rawclk
44 self.clkin_freq = None
45 self.vcxo_freq = None
46 self.nclkouts = 0
47 self.clkouts = {}
48 self.config = {}
49 self.params = {}
50
51 def set_clkin_freq(self, freq):
52 self.clkin_freq = freq
53
54 def create_clkout(self, cd, freq, phase=0, buf="bufg",
55 margin=1e-2, with_reset=True, ce=None):
56 assert self.nclkouts < self.nclkouts_max
57 clkout = Signal()
58 self.clkouts[self.nclkouts] = (clkout, freq, phase, margin, cd,
59 buf, with_reset, ce)
60
61 def elaborate(self, platform):
62 assert self.clkin_freq is not None
63 m = Module()
64 comb = m.d.comb
65 for n, clkcfg in self.clkouts.items():
66 (clkout, freq, phase, margin, cd, buf, with_reset, ce) = clkcfg
67 if with_reset:
68 arst = Signal()
69 m.submodules += ResetSynchronizer(arst, domain=cd.name)
70 comb += arst.eq(~self.locked | self.reset | ~self.pod_done)
71 if buf is None:
72 comb += cd.clk.eq(clkout)
73 else:
74 clkout_buf = Signal()
75 comb += cd.clk.eq(clkout_buf)
76 if buf == "bufg":
77 m.submodules += Instance("BUFG",
78 i_I=clkout,
79 o_O=clkout_buf)
80 elif buf == "bufr":
81 m.submodules += Instance("BUFR",
82 i_I=clkout,
83 o_O=clkout_buf)
84 elif buf == "bufgce":
85 if ce is None:
86 raise ValueError("BUFGCE requires user to provide "
87 "a clock enable ce Signal")
88 m.submodules += Instance("BUFGCE",
89 i_I=clkout,
90 o_O=clkout_buf,
91 i_CE=ce)
92 elif buf == "bufio":
93 m.submodules += Instance("BUFIO",
94 i_I=clkout,
95 o_O=clkout_buf)
96 else:
97 raise ValueError("Unsupported clock buffer: %s" % buf)
98 print(cd.name, freq, margin, self.nclkouts)
99
100 return m
101
102 def compute_config(self):
103 config = {}
104 print ("compute_config", self.divclk_divide_range)
105 print ("mult", self.clkfbout_mult_frange)
106 print ("divrange", self.clkout_divide_range)
107 for divclk_divide in range(*self.divclk_divide_range):
108 config["divclk_divide"] = divclk_divide
109 for clkfbout_mult in reversed(range(*self.clkfbout_mult_frange)):
110 all_valid = True
111 vco_freq = self.clkin_freq*clkfbout_mult/divclk_divide
112 (vco_freq_min, vco_freq_max) = self.vco_freq_range
113 if (vco_freq >= vco_freq_min*(1 + self.vco_margin) and
114 vco_freq <= vco_freq_max*(1 - self.vco_margin)):
115 for n, clkcfg in sorted(self.clkouts.items()):
116 (clkout, f, p, m, _, _, _, _) = clkcfg
117 valid = False
118 d_ranges = [self.clkout_divide_range]
119 r = getattr(self, "clkout%d_divide_range" % n, None)
120 if r is not None:
121 d_ranges += [r]
122 for d_range in d_ranges:
123 for d in clkdiv_range(*d_range):
124 clk_freq = vco_freq/d
125 if abs(clk_freq - f) <= f*m:
126 config["clkout%d_freq" % n] = clk_freq
127 config["clkout%d_divide" % n] = d
128 config["clkout%d_phase" % n] = p
129 valid = True
130 break
131 if valid:
132 break
133 if not valid:
134 all_valid = False
135 else:
136 all_valid = False
137 if all_valid:
138 config["vco"] = vco_freq
139 config["clkfbout_mult"] = clkfbout_mult
140 print(config)
141 return config
142 raise ValueError("No PLL config found")
143
144
145 # Xilinx / 7-Series --------------------------------------------------------
146
147 class S7PLL(XilinxClocking):
148 nclkouts_max = 6
149 clkin_freq_range = (19e6, 800e6)
150
151 def __init__(self, clkin, speedgrade=-1):
152 super().__init__(clkin)
153 #self.logger = logging.getLogger("S7PLL")
154 print ("Creating S7PLL, speedgrade %d" % speedgrade)
155 self.divclk_divide_range = (1, 56+1)
156 self.vco_freq_range = {
157 -1: (800e6, 1600e6),
158 -2: (800e6, 1866e6),
159 -3: (800e6, 2133e6),
160 }[speedgrade]
161
162 def elaborate(self, platform):
163 m = super().elaborate(platform)
164 config = self.compute_config()
165 pll_fb = Signal()
166 self.params.update(
167 p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked, i_RST=self.reset,
168
169 # VCO
170 p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/self.clkin_freq,
171 p_CLKFBOUT_MULT=config["clkfbout_mult"],
172 p_DIVCLK_DIVIDE=config["divclk_divide"],
173 i_CLKIN1=self.clkin, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb,
174 )
175 for n, (clk, f, p, _, _, _, _, _) in sorted(self.clkouts.items()):
176 div = config["clkout{}_divide".format(n)]
177 phs = config["clkout{}_phase".format(n)]
178 self.params["p_CLKOUT{}_DIVIDE".format(n)] = div
179 self.params["p_CLKOUT{}_PHASE".format(n)] = phs
180 self.params["o_CLKOUT{}".format(n)] = clk
181 m.submodules += Instance("PLLE2_ADV", **self.params)
182 return m
183
184
185 # CRG ----------------------------------------------------------------
186
187 class ArtyA7CRG(Elaboratable):
188 def __init__(self, sys_clk_freq):
189 self.sys_clk_freq = sys_clk_freq
190 self.reset = Signal(reset_less=True)
191
192 def elaborate(self, platform):
193 m = Module()
194
195 # reset
196 reset = platform.request(platform.default_rst).i
197 m.d.comb += self.reset.eq(reset)
198
199 # Get 100Mhz from oscillator
200 clk100 = platform.request("clk100")
201 cd_rawclk = ClockDomain("rawclk", local=True, reset_less=True)
202 m.d.comb += cd_rawclk.clk.eq(clk100)
203 m.domains += cd_rawclk
204
205 sync = ClockDomain("sync")
206 #sync2x = ClockDomain("sync2x", reset_less=True)
207 #sync4x = ClockDomain("sync4x", reset_less=True)
208 #sync4x_dqs = ClockDomain("sync4x_dqs", reset_less=True)
209 #cd_clk200 = ClockDomain("cd_clk200")
210 #cd_eth = ClockDomain("cd_eth")
211 dramsync = ClockDomain("dramsync")
212
213 m.domains += sync
214 #m.domains += sync2x
215 #m.domains += sync4x
216 #m.domains += sync4x_dqs
217 #m.domains += cd_clk200
218 #m.domains += cd_eth
219 m.domains += dramsync
220
221 # PLL module
222 m.submodules.pll = pll = S7PLL(clk100, speedgrade=-1)
223
224 # Power-on delay (a LOT)
225 podcnt = Signal(18, reset=-1)
226 pod_done = Signal(reset_less=True)
227 with m.If((podcnt != 0) & pll.locked):
228 m.d.rawclk += podcnt.eq(podcnt-1)
229 m.d.rawclk += pod_done.eq(podcnt == 0)
230
231 # PLL setup
232 m.d.comb += pll.reset.eq(reset)
233 m.d.comb += pll.pod_done.eq(pod_done)
234 pll.set_clkin_freq(100e6)
235 pll.create_clkout(sync, self.sys_clk_freq)
236
237 #platform.add_period_constraint(clk100_buf, 1e9/100e6)
238 #platform.add_period_constraint(sync.clk, 1e9/sys_clk_freq)
239 #platform.add_false_path_constraints(clk100_buf, sync.clk)
240
241 # temporarily set dram sync clock exactly equal to main sync
242 m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync"))
243
244 return m
245
246