Don't reset the core / peripherals on DRAM controller reset request
[gram.git] / gram / simulation / 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)
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, dram_clk_freq=None):
172 self.sys_clk_freq = sys_clk_freq
173 self.dram_clk_freq = dram_clk_freq
174
175 def elaborate(self, platform):
176 m = Module()
177
178 # Get 100Mhz from oscillator
179 clk100 = platform.request("clk100")
180 cd_rawclk = ClockDomain("rawclk", local=True, reset_less=True)
181 m.d.comb += cd_rawclk.clk.eq(clk100)
182 m.domains += cd_rawclk
183
184 # Reset
185 reset = platform.request(platform.default_rst).i
186 gsr0 = Signal()
187 gsr1 = Signal()
188
189 m.submodules += [
190 Instance("FD1S3AX", p_GSR="DISABLED",
191 i_CK=ClockSignal("rawclk"),
192 i_D=reset,
193 o_Q=gsr0),
194 Instance("FD1S3AX", p_GSR="DISABLED",
195 i_CK=ClockSignal("rawclk"),
196 i_D=gsr0,
197 o_Q=gsr1),
198 Instance("SGSR", i_CLK=ClockSignal("rawclk"),
199 i_GSR=gsr1),
200 ]
201
202 # Power-on delay (655us)
203 podcnt = Signal(3, reset=-1)
204 pod_done = Signal()
205 with m.If(podcnt != 0):
206 m.d.rawclk += podcnt.eq(podcnt-1)
207 m.d.rawclk += pod_done.eq(podcnt == 0)
208
209 # Generating sync2x (200Mhz) and init (25Mhz) from clk100
210 cd_sync2x = ClockDomain("sync2x", local=False)
211 cd_sync2x_unbuf = ClockDomain("sync2x_unbuf",
212 local=False, reset_less=True)
213 cd_init = ClockDomain("init", local=False)
214 cd_sync = ClockDomain("sync", local=False)
215 # generate dram (and 2xdram if requested)
216 cd_dramsync = ClockDomain("dramsync", local=False)
217 if self.dram_clk_freq is not None:
218 cd_dramsync2x = ClockDomain("dramsync2x", local=False)
219 cd_dramsync2x_unbuf = ClockDomain("dramsync2x_unbuf",
220 local=False, reset_less=True)
221 m.submodules.pll = pll = PLL(ClockSignal("rawclk"), reset=~reset)
222 pll.set_clkin_freq(100e6)
223 pll.create_clkout(ClockSignal("sync2x_unbuf"), 2*self.sys_clk_freq)
224 pll.create_clkout(ClockSignal("init"), 25e6)
225 m.submodules += Instance("ECLKSYNCB",
226 i_ECLKI = ClockSignal("sync2x_unbuf"),
227 i_STOP = 0,
228 o_ECLKO = ClockSignal("sync2x"))
229
230 # if dram is a separate frequency request it. set up a 2nd 2x unbuf
231 if self.dram_clk_freq is not None:
232 pll.create_clkout(ClockSignal("dramsync2x_unbuf"),
233 2*self.dram_clk_freq)
234 m.submodules += Instance("ECLKSYNCB",
235 i_ECLKI = ClockSignal("dramsync2x_unbuf"),
236 i_STOP = 0,
237 o_ECLKO = ClockSignal("dramsync2x"))
238 m.domains += cd_dramsync2x_unbuf
239 m.domains += cd_dramsync2x
240
241 m.domains += cd_sync2x_unbuf
242 m.domains += cd_sync2x
243 m.domains += cd_init
244 m.domains += cd_sync
245 m.domains += cd_dramsync
246 reset_ok = Signal(reset_less=True)
247 m.d.comb += reset_ok.eq(~pll.locked|~pod_done)
248 m.d.comb += ResetSignal("init").eq(reset_ok)
249 m.d.comb += ResetSignal("sync").eq(reset_ok)
250 m.d.comb += ResetSignal("dramsync").eq(reset_ok)
251
252 # # Generating sync (100Mhz) from sync2x
253
254 m.submodules += Instance("CLKDIVF",
255 p_DIV="2.0",
256 i_ALIGNWD=0,
257 i_CLKI=ClockSignal("sync2x"),
258 i_RST=0,
259 o_CDIVX=ClockSignal("sync"))
260
261 # Generating dramsync (100Mhz) from dramsync2x
262 if self.dram_clk_freq is not None:
263 m.submodules += Instance("CLKDIVF",
264 p_DIV="2.0",
265 i_ALIGNWD=0,
266 i_CLKI=ClockSignal("dramsync2x"),
267 i_RST=0,
268 o_CDIVX=ClockSignal("dramsync"))
269
270 # if no separate dram set dram sync clock exactly equal to main sync
271 if self.dram_clk_freq is None:
272 m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync"))
273
274 return m