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
12 from nmigen
import (Elaboratable
, Module
, Signal
, ClockDomain
, Instance
,
13 ClockSignal
, ResetSignal
)
14 from nmigen
.lib
.cdc
import ResetSynchronizer
20 # Helpers -----------------------------------------------------------------
22 def clkdiv_range(start
, stop
, step
=1):
28 yield int(current
) if math
.floor(current
) == current
else current
32 # Xilinx / Generic -----------------------------------------------------
34 class XilinxClocking(Elaboratable
):
35 clkfbout_mult_frange
= (2, 64+1)
36 clkout_divide_range
= (1, 128+1)
38 def __init__(self
, clkin
, vco_margin
=0):
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
51 def set_clkin_freq(self
, freq
):
52 self
.clkin_freq
= freq
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
58 self
.clkouts
[self
.nclkouts
] = (clkout
, freq
, phase
, margin
, cd
,
61 def elaborate(self
, platform
):
62 assert self
.clkin_freq
is not None
65 for n
, clkcfg
in self
.clkouts
.items():
66 (clkout
, freq
, phase
, margin
, cd
, buf
, with_reset
, ce
) = clkcfg
69 m
.submodules
+= ResetSynchronizer(arst
, domain
=cd
.name
)
70 comb
+= arst
.eq(~self
.locked | self
.reset | ~self
.pod_done
)
72 comb
+= cd
.clk
.eq(clkout
)
75 comb
+= cd
.clk
.eq(clkout_buf
)
77 m
.submodules
+= Instance("BUFG",
81 m
.submodules
+= Instance("BUFR",
86 raise ValueError("BUFGCE requires user to provide "
87 "a clock enable ce Signal")
88 m
.submodules
+= Instance("BUFGCE",
93 m
.submodules
+= Instance("BUFIO",
97 raise ValueError("Unsupported clock buffer: %s" % buf
)
98 print(cd
.name
, freq
, margin
, self
.nclkouts
)
102 def compute_config(self
):
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
)):
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
118 d_ranges
= [self
.clkout_divide_range
]
119 r
= getattr(self
, "clkout%d_divide_range" % n
, None)
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
138 config
["vco"] = vco_freq
139 config
["clkfbout_mult"] = clkfbout_mult
142 raise ValueError("No PLL config found")
145 # Xilinx / 7-Series --------------------------------------------------------
147 class S7PLL(XilinxClocking
):
149 clkin_freq_range
= (19e6
, 800e6
)
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
= {
162 def elaborate(self
, platform
):
163 m
= super().elaborate(platform
)
164 config
= self
.compute_config()
167 p_STARTUP_WAIT
="FALSE", o_LOCKED
=self
.locked
, i_RST
=self
.reset
,
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
,
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
)
185 # CRG ----------------------------------------------------------------
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)
192 def elaborate(self
, platform
):
196 reset
= platform
.request(platform
.default_rst
).i
197 m
.d
.comb
+= self
.reset
.eq(reset
)
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
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")
216 #m.domains += sync4x_dqs
217 #m.domains += cd_clk200
219 m
.domains
+= dramsync
222 m
.submodules
.pll
= pll
= S7PLL(clk100
, speedgrade
=-1)
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)
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
)
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)
241 # temporarily set dram sync clock exactly equal to main sync
242 m
.d
.comb
+= ClockSignal("dramsync").eq(ClockSignal("sync"))