From 2581a00380fe426adb148a97a2ae2ae01f248656 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 8 Jan 2019 13:19:49 +0100 Subject: [PATCH] soc/cores/clock: add Xilinx Ultrascale PLL/MMCM --- litex/soc/cores/clock.py | 186 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 184 insertions(+), 2 deletions(-) diff --git a/litex/soc/cores/clock.py b/litex/soc/cores/clock.py index 313e25838..a007bf74d 100644 --- a/litex/soc/cores/clock.py +++ b/litex/soc/cores/clock.py @@ -15,7 +15,7 @@ from litex.soc.interconnect.csr import * def period_ns(freq): return 1e9/freq -# Xilinx +# Xilinx / 7-Series class S7Clocking(Module, AutoCSR): clkfbout_mult_frange = (2, 64+1) @@ -203,7 +203,189 @@ class S7IDELAYCTRL(Module): ) self.specials += Instance("IDELAYCTRL", i_REFCLK=cd.clk, i_RST=ic_reset) -# Lattice +# Xilinx / Ultrascale + +# TODO: +# - use Ultrascale primitives instead of 7-Series' ones. (Vivado recognize and convert them). + +class USClocking(Module, AutoCSR): + 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 = {} + self.params = {} + + 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, buf="bufg", margin=1e-2, with_reset=True): + assert self.nclkouts < self.nclkouts_max + clkout = Signal() + self.clkouts[self.nclkouts] = (clkout, freq, phase, margin) + self.nclkouts += 1 + if with_reset: + self.specials += AsyncResetSynchronizer(cd, ~self.locked | self.reset) + if buf is None: + self.comb += cd.clk.eq(clkout) + else: + clkout_buf = Signal() + self.comb += cd.clk.eq(clkout_buf) + if buf == "bufg": + self.specials += Instance("BUFG", i_I=clkout, o_O=clkout_buf) + elif buf == "bufr": + self.specials += Instance("BUFR", i_I=clkout, o_O=clkout_buf) + else: + raise ValueError + + 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, m) in sorted(self.clkouts.items()): + valid = False + for d in range(*self.clkout_divide_range): + clk_freq = vco_freq/d + if abs(clk_freq - f) < f*m: + 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 expose_drp(self): + self.drp_reset = CSR() + self.drp_read = CSR() + self.drp_write = CSR() + self.drp_drdy = CSRStatus() + self.drp_adr = CSRStorage(7) + self.drp_dat_w = CSRStorage(16) + self.drp_dat_r = CSRStatus(16) + + # # # + + drp_drdy = Signal() + self.params.update( + i_DCLK=ClockSignal(), + i_DWE=self.drp_write.re, + i_DEN=self.drp_read.re | self.drp_write.re, + o_DRDY=drp_drdy, + i_DADDR=self.drp_adr.storage, + i_DI=self.drp_dat_w.storage, + o_DO=self.drp_dat_r.status + ) + self.sync += [ + If(self.drp_read.re | self.drp_write.re, + self.drp_drdy.status.eq(0) + ).Elif(drp_drdy, + self.drp_drdy.status.eq(1) + ) + ] + + def do_finalize(self): + assert hasattr(self, "clkin") + + +class USPLL(USClocking): + nclkouts_max = 6 + + def __init__(self, speedgrade=-1): + USClocking.__init__(self) + self.clkin_freq_range = { + -1: (70e6, 800e6), + -2: (70e6, 933e6), + -3: (70e6, 1066e6), + }[speedgrade] + + self.vco_freq_range = { + -1: (600e6, 1200e6), + -2: (600e6, 1335e6), + -3: (600e6, 1335e6), + }[speedgrade] + + def do_finalize(self): + USClocking.do_finalize(self) + config = self.compute_config() + pll_fb = Signal() + self.params.update( + 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, m) in sorted(self.clkouts.items()): + self.params["p_CLKOUT{}_DIVIDE".format(n)] = config["clkout{}_divide".format(n)] + self.params["p_CLKOUT{}_PHASE".format(n)] = config["clkout{}_phase".format(n)] + self.params["o_CLKOUT{}".format(n)] = clk + self.specials += Instance("PLLE2_ADV", **self.params) + + +class USMMCM(USClocking): + nclkouts_max = 7 + + def __init__(self, speedgrade=-1): + USClocking.__init__(self) + self.clkin_freq_range = { + -1: (10e6, 800e6), + -2: (10e6, 933e6), + -3: (10e6, 1066e6), + }[speedgrade] + + self.vco_freq_range = { + -1: (600e6, 1200e6), + -2: (600e6, 1440e6), + -3: (600e6, 1600e6), + }[speedgrade] + + def do_finalize(self): + USClocking.do_finalize(self) + config = self.compute_config() + mmcm_fb = Signal() + self.params.update( + p_BANDWIDTH="OPTIMIZED", o_LOCKED=self.locked, + + # VCO + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=period_ns(self.clkin_freq), + p_CLKFBOUT_MULT_F=config["clkfbout_mult"], p_DIVCLK_DIVIDE=config["divclk_divide"], + i_CLKIN1=self.clkin, i_CLKFBIN=mmcm_fb, o_CLKFBOUT=mmcm_fb, + ) + for n, (clk, f, p, m) in sorted(self.clkouts.items()): + if n == 0: + self.params["p_CLKOUT{}_DIVIDE_F".format(n)] = config["clkout{}_divide".format(n)] + else: + self.params["p_CLKOUT{}_DIVIDE".format(n)] = config["clkout{}_divide".format(n)] + self.params["p_CLKOUT{}_PHASE".format(n)] = config["clkout{}_phase".format(n)] + self.params["o_CLKOUT{}".format(n)] = clk + self.specials += Instance("MMCME2_ADV", **self.params) + +# Lattice / ECP5 # TODO: # - add proper phase support.