--- /dev/null
+"""
+Clock Abstraction Modules
+
+
+Made in Paris-CDG while waiting a delayed Air-France KLM flight...
+"""
+
+from migen import *
+from migen.genlib.io import DifferentialInput
+from migen.genlib.resetsync import AsyncResetSynchronizer
+
+
+# TODO:
+# - add S7PLL support for all family/speedgrades (currently Artix7 -3 speedgrade)
+# - add S7MMCM support (should be very similar to S7PLL)
+
+
+def period_ns(freq):
+ return 1e9/freq
+
+
+class S7PLL(Module):
+ nclkouts_max = 6
+ clkin_freq_range = (10e6, 800e6)
+ vco_freq_range = (600e6, 1600e6)
+ clkfbout_mult_frange = (2, 64+1)
+ clkout_divide_range = (1, 128+1)
+
+ def __init__(self):
+ self.reset = Signal()
+ self.locked = Signal()
+ self.clkin_freq = None
+ self.vcxo_freq = None
+ self.nclkouts = 0
+ self.clkouts = {}
+ self.config = {}
+
+ def register_clkin(self, clkin, freq):
+ self.clkin = Signal()
+ if isinstance(clkin, Signal):
+ self.comb += self.clkin.eq(clkin)
+ elif isinstance(clkin, Record):
+ self.specials += DifferentialInput(clkin.p, clkin.n, self.clkin)
+ else:
+ raise ValueError
+ self.clkin_freq = freq
+
+ def create_clkout(self, cd, freq, phase=0):
+ assert self.nclkouts < self.nclkouts_max
+ clkout = Signal()
+ clkout_bufg = Signal()
+ self.specials += AsyncResetSynchronizer(cd, ~self.locked | self.reset),
+ self.specials += Instance("BUFG", i_I=clkout, o_O=clkout_bufg)
+ self.comb += cd.clk.eq(clkout_bufg)
+ self.clkouts[self.nclkouts] = (clkout, freq, phase)
+ self.nclkouts += 1
+ return clkout_bufg
+
+ def compute_config(self):
+ config = {}
+ config["divclk_divide"] = 1
+ for clkfbout_mult in range(*self.clkfbout_mult_frange):
+ all_valid = True
+ vco_freq = self.clkin_freq*clkfbout_mult
+ (vco_freq_min, vco_freq_max) = self.vco_freq_range
+ if vco_freq >= vco_freq_min and vco_freq <= vco_freq_max:
+ for n, (clk, f, p) in sorted(self.clkouts.items()):
+ valid = False
+ for d in range(*self.clkout_divide_range):
+ clk_freq = vco_freq/d
+ if clk_freq == f:
+ config["clkout{}_divide".format(n)] = d
+ config["clkout{}_phase".format(n)] = p
+ valid = True
+ break
+ if not valid:
+ all_valid = False
+ else:
+ all_valid = False
+ if all_valid:
+ config["vco"] = vco_freq
+ config["clkfbout_mult"] = clkfbout_mult
+ return config
+ raise ValueError("No PLL config found")
+
+ def add_idelayctrl(self, cd):
+ reset_counter = Signal(4, reset=15)
+ ic_reset = Signal(reset=1)
+ sync = getattr(self.sync, cd.name)
+ sync += \
+ If(reset_counter != 0,
+ reset_counter.eq(reset_counter - 1)
+ ).Else(
+ ic_reset.eq(0)
+ )
+ self.specials += Instance("IDELAYCTRL", i_REFCLK=cd.clk, i_RST=ic_reset)
+
+ def do_finalize(self):
+ assert hasattr(self, "clkin")
+ config = self.compute_config()
+ pll_fb = Signal()
+ pll_params = dict(
+ p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked,
+
+ # VCO
+ p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=period_ns(self.clkin_freq),
+ p_CLKFBOUT_MULT=config["clkfbout_mult"], p_DIVCLK_DIVIDE=config["divclk_divide"],
+ i_CLKIN1=self.clkin, i_CLKFBIN=pll_fb, o_CLKFBOUT=pll_fb,
+ )
+ for n, (clk, f, p) in sorted(self.clkouts.items()):
+ pll_params["p_CLKOUT{}_DIVIDE".format(n)] = config["clkout{}_divide".format(n)]
+ pll_params["p_CLKOUT{}_PHASE".format(n)] = config["clkout{}_phase".format(n)]
+ pll_params["o_CLKOUT{}".format(n)] = clk
+ self.specials += Instance("PLLE2_BASE", **pll_params)