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>
6 # Based on code from LambaConcept, from the gram example which is BSD-2-License
7 # https://github.com/jeanthom/gram/tree/master/examples
9 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
10 # under EU Grants 871528 and 957073, under the LGPLv3+ License
13 from nmigen
import (Elaboratable
, Module
, Signal
, ClockDomain
, Instance
,
14 ClockSignal
, ResetSignal
)
16 __ALL__
= ["ECPIX5CRG"]
18 class PLL(Elaboratable
):
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
)
27 def __init__(self
, clkin
,
28 clksel
=Signal(shape
=2, reset
=2),
29 reset
=Signal(reset_less
=True),
32 self
.clkin_freq
= None
46 ] + list(self
.clkouts
.values())
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
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
)
64 def compute_config(self
):
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
):
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()):
75 for d
in range(*self
.clko_div_range
):
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
88 config
["vco"] = vco_freq
89 config
["clkfb_div"] = clkfb_div
90 #compute_config_log(self.logger, config)
91 print ("PLL config", config
)
93 raise ValueError("No PLL config found")
95 def elaborate(self
, platform
):
96 config
= self
.compute_config()
100 a_FREQUENCY_PIN_CLKI
= str(self
.clkin_freq
/1e6
),
102 a_LPF_RESISTOR
= "16",
103 a_MFG_ENABLE_FILTEROPAMP
= "1",
104 a_MFG_GMCREF_SEL
= "2",
106 p_FEEDBK_PATH
= "INT_OS3", # CLKOS3 rsvd for feedback with div=1.
107 p_CLKOS3_ENABLE
= "ENABLED",
109 p_CLKFB_DIV
= config
["clkfb_div"],
110 p_CLKI_DIV
= config
["clki_div"],
111 # reset, input clock, lock-achieved output
114 o_LOCK
= self
.locked
,
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
128 print ("params", self
.params
)
129 pll
= Instance("EHXPLLL", **self
.params
)
130 m
.submodules
.pll
= pll
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,
162 o_CLKOP
=self
.clkout1
,
163 o_CLKOS
=self
.clkout2
,
164 o_CLKOS2
=self
.clkout3
,
165 o_CLKOS3
=self
.clkout4
,
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
175 def elaborate(self
, platform
):
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
185 reset
= platform
.request(platform
.default_rst
).i
190 Instance("FD1S3AX", p_GSR
="DISABLED",
191 i_CK
=ClockSignal("rawclk"),
194 Instance("FD1S3AX", p_GSR
="DISABLED",
195 i_CK
=ClockSignal("rawclk"),
198 Instance("SGSR", i_CLK
=ClockSignal("rawclk"),
202 # Power-on delay (655us)
203 podcnt
= Signal(3, reset
=-1)
205 with m
.If(podcnt
!= 0):
206 m
.d
.rawclk
+= podcnt
.eq(podcnt
-1)
207 m
.d
.rawclk
+= pod_done
.eq(podcnt
== 0)
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"),
228 o_ECLKO
= ClockSignal("sync2x"))
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"),
237 o_ECLKO
= ClockSignal("dramsync2x"))
238 m
.domains
+= cd_dramsync2x_unbuf
239 m
.domains
+= cd_dramsync2x
241 m
.domains
+= cd_sync2x_unbuf
242 m
.domains
+= cd_sync2x
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
)
252 # # Generating sync (100Mhz) from sync2x
254 m
.submodules
+= Instance("CLKDIVF",
257 i_CLKI
=ClockSignal("sync2x"),
259 o_CDIVX
=ClockSignal("sync"))
261 # Generating dramsync (100Mhz) from dramsync2x
262 if self
.dram_clk_freq
is not None:
263 m
.submodules
+= Instance("CLKDIVF",
266 i_CLKI
=ClockSignal("dramsync2x"),
268 o_CDIVX
=ClockSignal("dramsync"))
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"))