add DRAM class to DDR3Soc
[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)
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 self.nclkouts += 1
62
63 def compute_config(self):
64 config = {}
65 for clki_div in range(*self.clki_div_range):
66 config["clki_div"] = clki_div
67 for clkfb_div in range(*self.clkfb_div_range):
68 all_valid = True
69 vco_freq = self.clkin_freq/clki_div*clkfb_div*1 # clkos3_div=1
70 (vco_freq_min, vco_freq_max) = self.vco_freq_range
71 if vco_freq >= vco_freq_min and vco_freq <= vco_freq_max:
72 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
73 valid = False
74 for d in range(*self.clko_div_range):
75 clk_freq = vco_freq/d
76 if abs(clk_freq - f) <= f*m:
77 config["clko{}_freq".format(n)] = clk_freq
78 config["clko{}_div".format(n)] = d
79 config["clko{}_phase".format(n)] = p
80 valid = True
81 break
82 if not valid:
83 all_valid = False
84 else:
85 all_valid = False
86 if all_valid:
87 config["vco"] = vco_freq
88 config["clkfb_div"] = clkfb_div
89 #compute_config_log(self.logger, config)
90 return config
91 raise ValueError("No PLL config found")
92
93 def elaborate(self, platform):
94 config = self.compute_config()
95 clkfb = Signal()
96 self.params.update(
97 # attributes
98 a_FREQUENCY_PIN_CLKI = str(self.clkin_freq/1e6),
99 a_ICP_CURRENT = "6",
100 a_LPF_RESISTOR = "16",
101 a_MFG_ENABLE_FILTEROPAMP = "1",
102 a_MFG_GMCREF_SEL = "2",
103 # parameters
104 p_FEEDBK_PATH = "INT_OS3", # CLKOS3 rsvd for feedback with div=1.
105 p_CLKOS3_ENABLE = "ENABLED",
106 p_CLKOS3_DIV = 1,
107 p_CLKFB_DIV = config["clkfb_div"],
108 p_CLKI_DIV = config["clki_div"],
109 # reset, input clock, lock-achieved output
110 i_RST = self.reset,
111 i_CLKI = self.clkin,
112 o_LOCK = self.locked,
113 )
114 # for each clock-out, set additional parameters
115 for n, (clk, f, p, m) in sorted(self.clkouts.items()):
116 n_to_l = {0: "P", 1: "S", 2: "S2"}
117 div = config["clko{}_div".format(n)]
118 cphase = int(p*(div + 1)/360 + div)
119 self.params["p_CLKO{}_ENABLE".format(n_to_l[n])] = "ENABLED"
120 self.params["p_CLKO{}_DIV".format(n_to_l[n])] = div
121 self.params["p_CLKO{}_FPHASE".format(n_to_l[n])] = 0
122 self.params["p_CLKO{}_CPHASE".format(n_to_l[n])] = cphase
123 self.params["o_CLKO{}".format(n_to_l[n])] = clk
124
125 m = Module()
126 pll = Instance("EHXPLLL", **self.params)
127 m.submodules.pll = pll
128 return m
129
130 pll = Instance("EHXPLLL",
131 p_OUTDIVIDER_MUXA='DIVA',
132 p_OUTDIVIDER_MUXB='DIVB',
133 p_CLKOP_ENABLE='ENABLED',
134 p_CLKOS_ENABLE='ENABLED',
135 p_CLKOS2_ENABLE='DISABLED',
136 p_CLKOS3_ENABLE='DISABLED',
137 p_CLKOP_DIV=self.CLKOP_DIV,
138 p_CLKOS_DIV=self.CLKOS_DIV,
139 p_CLKFB_DIV=self.CLKFB_DIV,
140 p_CLKI_DIV=self.CLKI_DIV,
141 p_FEEDBK_PATH='INT_OP',
142 p_CLKOP_TRIM_POL="FALLING",
143 p_CLKOP_TRIM_DELAY=0,
144 p_CLKOS_TRIM_POL="FALLING",
145 p_CLKOS_TRIM_DELAY=0,
146 i_CLKI=self.clkin,
147 i_RST=0,
148 i_STDBY=0,
149 i_PHASESEL0=0,
150 i_PHASESEL1=0,
151 i_PHASEDIR=0,
152 i_PHASESTEP=0,
153 i_PHASELOADREG=0,
154 i_PLLWAKESYNC=0,
155 i_ENCLKOP=1,
156 i_ENCLKOS=1,
157 i_ENCLKOS2=0,
158 i_ENCLKOS3=0,
159 o_CLKOP=self.clkout1,
160 o_CLKOS=self.clkout2,
161 o_CLKOS2=self.clkout3,
162 o_CLKOS3=self.clkout4,
163 o_LOCK=self.lock,
164 )
165
166
167 class ECPIX5CRG(Elaboratable):
168 def __init__(self, sys_clk_freq=100e6):
169 self.sys_clk_freq = sys_clk_freq
170
171 def elaborate(self, platform):
172 m = Module()
173
174 # Get 100Mhz from oscillator
175 clk100 = platform.request("clk100")
176 cd_rawclk = ClockDomain("rawclk", local=True, reset_less=True)
177 m.d.comb += cd_rawclk.clk.eq(clk100)
178 m.domains += cd_rawclk
179
180 # Reset
181 reset = platform.request(platform.default_rst).i
182 gsr0 = Signal()
183 gsr1 = Signal()
184
185 m.submodules += [
186 Instance("FD1S3AX", p_GSR="DISABLED",
187 i_CK=ClockSignal("rawclk"),
188 i_D=~reset,
189 o_Q=gsr0),
190 Instance("FD1S3AX", p_GSR="DISABLED",
191 i_CK=ClockSignal("rawclk"),
192 i_D=gsr0,
193 o_Q=gsr1),
194 Instance("SGSR", i_CLK=ClockSignal("rawclk"),
195 i_GSR=gsr1),
196 ]
197
198 # Power-on delay (655us)
199 podcnt = Signal(16, reset=2**16-1)
200 pod_done = Signal()
201 with m.If(podcnt != 0):
202 m.d.rawclk += podcnt.eq(podcnt-1)
203 m.d.rawclk += pod_done.eq(podcnt == 0)
204
205 # Generating sync2x (200Mhz) and init (25Mhz) from clk100
206 cd_sync2x = ClockDomain("sync2x", local=False)
207 cd_sync2x_unbuf = ClockDomain("sync2x_unbuf",
208 local=False, reset_less=True)
209 cd_init = ClockDomain("init", local=False)
210 cd_sync = ClockDomain("sync", local=False)
211 cd_dramsync = ClockDomain("dramsync", local=False)
212 m.submodules.pll = pll = PLL(ClockSignal("rawclk"), reset=reset)
213 pll.set_clkin_freq(100e6)
214 pll.create_clkout(ClockSignal("sync2x_unbuf"), 2*self.sys_clk_freq)
215 pll.create_clkout(ClockSignal("init"), 25e6)
216 m.submodules += Instance("ECLKSYNCB",
217 i_ECLKI = ClockSignal("sync2x_unbuf"),
218 i_STOP = 0,
219 o_ECLKO = ClockSignal("sync2x"))
220 m.domains += cd_sync2x_unbuf
221 m.domains += cd_sync2x
222 m.domains += cd_init
223 m.domains += cd_sync
224 m.domains += cd_dramsync
225 reset_ok = Signal(reset_less=True)
226 m.d.comb += reset_ok.eq(~pll.locked|~pod_done)
227 m.d.comb += ResetSignal("init").eq(reset_ok)
228 m.d.comb += ResetSignal("sync").eq(reset_ok)
229 m.d.comb += ResetSignal("dramsync").eq(reset_ok)
230
231 # # Generating sync (100Mhz) from sync2x
232
233 m.submodules += Instance("CLKDIVF",
234 p_DIV="2.0",
235 i_ALIGNWD=0,
236 i_CLKI=ClockSignal("sync2x"),
237 i_RST=0,
238 o_CDIVX=ClockSignal("sync"))
239 m.d.comb += ClockSignal("dramsync").eq(ClockSignal("sync"))
240
241 return m