check ulx3s, add CRG support for ulx3s
[ls2.git] / src / 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
13 from nmigen import (Elaboratable, Module, Signal, ClockDomain, Instance,
14 ClockSignal, ResetSignal, Const)
15
16 __ALL__ = ["ECPIX5CRG"]
17
18 class PLL(Elaboratable):
19 nclkouts_max = 3
20 clki_div_range = (1, 128+1)
21 clkfb_div_range = (1, 128+1)
22 clko_div_range = (1, 128+1)
23 clki_freq_range = ( 8e6, 400e6)
24 clko_freq_range = (3.125e6, 400e6)
25 vco_freq_range = ( 400e6, 800e6)
26
27 def __init__(self, clkin,
28 clksel=Signal(shape=2, reset=2),
29 reset=Signal(reset_less=True),
30 locked=Signal()):
31 self.clkin = clkin
32 self.clkin_freq = None
33 self.clksel = clksel
34 self.locked = locked
35 self.reset = reset
36 self.nclkouts = 0
37 self.clkouts = {}
38 self.config = {}
39 self.params = {}
40
41 def ports(self):
42 return [
43 self.clkin,
44 self.clksel,
45 self.lock,
46 ] + list(self.clkouts.values())
47
48 def set_clkin_freq(self, freq):
49 (clki_freq_min, clki_freq_max) = self.clki_freq_range
50 assert freq >= clki_freq_min
51 assert freq <= clki_freq_max
52 self.clkin_freq = freq
53
54 def create_clkout(self, cd, freq, phase=0, margin=1e-2):
55 (clko_freq_min, clko_freq_max) = self.clko_freq_range
56 assert freq >= clko_freq_min
57 assert freq <= clko_freq_max
58 assert self.nclkouts < self.nclkouts_max
59 self.clkouts[self.nclkouts] = (cd, freq, phase, margin)
60 #create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts)
61 print("clock domain", cd.domain, freq, margin, self.nclkouts)
62 self.nclkouts += 1
63
64 def compute_config(self):
65 config = {}
66 for clki_div in range(*self.clki_div_range):
67 config["clki_div"] = clki_div
68 for clkfb_div in range(*self.clkfb_div_range):
69 all_valid = True
70 vco_freq = self.clkin_freq/clki_div*clkfb_div*1 # clkos3_div=1
71 (vco_freq_min, vco_freq_max) = self.vco_freq_range
72 if vco_freq >= vco_freq_min and vco_freq <= vco_freq_max:
73 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
74 valid = False
75 for d in range(*self.clko_div_range):
76 clk_freq = vco_freq/d
77 if abs(clk_freq - f) <= f*m:
78 config["clko{}_freq".format(n)] = clk_freq
79 config["clko{}_div".format(n)] = d
80 config["clko{}_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["clkfb_div"] = clkfb_div
90 #compute_config_log(self.logger, config)
91 print ("PLL config", config)
92 return config
93 raise ValueError("No PLL config found")
94
95 def elaborate(self, platform):
96 config = self.compute_config()
97 clkfb = Signal()
98 self.params.update(
99 # attributes
100 a_FREQUENCY_PIN_CLKI = str(self.clkin_freq/1e6),
101 a_ICP_CURRENT = "6",
102 a_LPF_RESISTOR = "16",
103 a_MFG_ENABLE_FILTEROPAMP = "1",
104 a_MFG_GMCREF_SEL = "2",
105 # parameters
106 p_FEEDBK_PATH = "INT_OS3", # CLKOS3 rsvd for feedback with div=1.
107 p_CLKOS3_ENABLE = "ENABLED",
108 p_CLKOS3_DIV = 1,
109 p_CLKFB_DIV = config["clkfb_div"],
110 p_CLKI_DIV = config["clki_div"],
111 # reset, input clock, lock-achieved output
112 i_RST = self.reset,
113 i_CLKI = self.clkin,
114 o_LOCK = self.locked,
115 )
116 # for each clock-out, set additional parameters
117 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
118 n_to_l = {0: "P", 1: "S", 2: "S2"}
119 div = config["clko{}_div".format(n)]
120 cphase = int(p*(div + 1)/360 + div)
121 self.params["p_CLKO{}_ENABLE".format(n_to_l[n])] = "ENABLED"
122 self.params["p_CLKO{}_DIV".format(n_to_l[n])] = div
123 self.params["p_CLKO{}_FPHASE".format(n_to_l[n])] = 0
124 self.params["p_CLKO{}_CPHASE".format(n_to_l[n])] = cphase
125 self.params["o_CLKO{}".format(n_to_l[n])] = clk
126
127 m = Module()
128 print ("params", self.params)
129 pll = Instance("EHXPLLL", **self.params)
130 m.submodules.pll = pll
131 return m
132
133 pll = Instance("EHXPLLL",
134 p_OUTDIVIDER_MUXA='DIVA',
135 p_OUTDIVIDER_MUXB='DIVB',
136 p_CLKOP_ENABLE='ENABLED',
137 p_CLKOS_ENABLE='ENABLED',
138 p_CLKOS2_ENABLE='DISABLED',
139 p_CLKOS3_ENABLE='DISABLED',
140 p_CLKOP_DIV=self.CLKOP_DIV,
141 p_CLKOS_DIV=self.CLKOS_DIV,
142 p_CLKFB_DIV=self.CLKFB_DIV,
143 p_CLKI_DIV=self.CLKI_DIV,
144 p_FEEDBK_PATH='INT_OP',
145 p_CLKOP_TRIM_POL="FALLING",
146 p_CLKOP_TRIM_DELAY=0,
147 p_CLKOS_TRIM_POL="FALLING",
148 p_CLKOS_TRIM_DELAY=0,
149 i_CLKI=self.clkin,
150 i_RST=0,
151 i_STDBY=0,
152 i_PHASESEL0=0,
153 i_PHASESEL1=0,
154 i_PHASEDIR=0,
155 i_PHASESTEP=0,
156 i_PHASELOADREG=0,
157 i_PLLWAKESYNC=0,
158 i_ENCLKOP=1,
159 i_ENCLKOS=1,
160 i_ENCLKOS2=0,
161 i_ENCLKOS3=0,
162 o_CLKOP=self.clkout1,
163 o_CLKOS=self.clkout2,
164 o_CLKOS2=self.clkout3,
165 o_CLKOS3=self.clkout4,
166 o_LOCK=self.lock,
167 )
168
169
170 class ECPIX5CRG(Elaboratable):
171 def __init__(self, sys_clk_freq=100e6):
172 self.sys_clk_freq = sys_clk_freq
173
174 def elaborate(self, platform):
175 m = Module()
176
177 # Get 100Mhz from oscillator
178 extclk = platform.request(platform.default_clk)
179 cd_rawclk = ClockDomain("rawclk", local=True, reset_less=True)
180 m.d.comb += cd_rawclk.clk.eq(extclk)
181 m.domains += cd_rawclk
182
183 # Reset
184 if platform.default_rst is not None:
185 reset = platform.request(platform.default_rst).i
186 else:
187 reset = Const(0) # whoops
188
189 gsr0 = Signal()
190 gsr1 = Signal()
191
192 m.submodules += [
193 Instance("FD1S3AX", p_GSR="DISABLED",
194 i_CK=ClockSignal("rawclk"),
195 i_D=~reset,
196 o_Q=gsr0),
197 Instance("FD1S3AX", p_GSR="DISABLED",
198 i_CK=ClockSignal("rawclk"),
199 i_D=gsr0,
200 o_Q=gsr1),
201 Instance("SGSR", i_CLK=ClockSignal("rawclk"),
202 i_GSR=gsr1),
203 ]
204
205 # PLL
206 m.submodules.pll = pll = PLL(ClockSignal("rawclk"), reset=~reset)
207
208 # Power-on delay (655us)
209 podcnt = Signal(18, reset=-1)
210 pod_done = Signal()
211 with m.If((podcnt != 0) & pll.locked):
212 m.d.rawclk += podcnt.eq(podcnt-1)
213 m.d.rawclk += pod_done.eq(podcnt == 0)
214
215 # Generating sync2x (200Mhz) and init (25Mhz) from extclk
216 cd_sync2x = ClockDomain("sync2x", local=False)
217 cd_sync2x_unbuf = ClockDomain("sync2x_unbuf",
218 local=False, reset_less=True)
219 cd_init = ClockDomain("init", local=False)
220 cd_sync = ClockDomain("sync", local=False)
221 cd_dramsync = ClockDomain("dramsync", local=False)
222
223 # create PLL clocks
224 pll.set_clkin_freq(platform.default_clk_frequency)
225 pll.create_clkout(ClockSignal("sync2x_unbuf"), 2*self.sys_clk_freq)
226 pll.create_clkout(ClockSignal("init"), 25e6)
227 m.submodules += Instance("ECLKSYNCB",
228 i_ECLKI = ClockSignal("sync2x_unbuf"),
229 i_STOP = 0,
230 o_ECLKO = ClockSignal("sync2x"))
231 m.domains += cd_sync2x_unbuf
232 m.domains += cd_sync2x
233 m.domains += cd_init
234 m.domains += cd_sync
235 m.domains += cd_dramsync
236 reset_ok = Signal(reset_less=True)
237 m.d.comb += reset_ok.eq(~pll.locked|~pod_done)
238 m.d.comb += ResetSignal("init").eq(reset_ok)
239 m.d.comb += ResetSignal("sync").eq(reset_ok)
240 m.d.comb += ResetSignal("dramsync").eq(reset_ok)
241
242 # # Generating sync (100Mhz) from sync2x
243
244 m.submodules += Instance("CLKDIVF",
245 p_DIV="2.0",
246 i_ALIGNWD=0,
247 i_CLKI=ClockSignal("sync2x"),
248 i_RST=0,
249 o_CDIVX=ClockSignal("sync"))
250
251 # temporarily set dram sync clock exactly equal to main sync
252 m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync"))
253
254 return m
255