From 14e196ab5d97ad347343154a81d70c950f3f5c76 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 9 Nov 2020 10:31:53 +0100 Subject: [PATCH] soc/cores/clock: create directory and split code in separate files to ease maintenance/adding new devices. clock.py was originally created/prototyped for 7-Series FPGAs, but has since been extended to almost all FPGA devices supported by LiteX making it large enough to justify the split. soc/cores/clock/__init__.py provides the retro-compatibily layer. --- litex/soc/cores/clock.py | 1142 ----------------- litex/soc/cores/clock/__init__.py | 16 + litex/soc/cores/clock/common.py | 58 + litex/soc/cores/clock/intel_common.py | 110 ++ litex/soc/cores/clock/intel_cyclone10.py | 38 + litex/soc/cores/clock/intel_cyclone4.py | 37 + litex/soc/cores/clock/intel_cyclone5.py | 45 + litex/soc/cores/clock/intel_max10.py | 30 + litex/soc/cores/clock/lattice_ecp5.py | 120 ++ litex/soc/cores/clock/lattice_ice40.py | 117 ++ .../{clock_nxpll.py => clock/lattice_nx.py} | 92 +- litex/soc/cores/clock/xilinx_common.py | 152 +++ litex/soc/cores/clock/xilinx_s6.py | 147 +++ litex/soc/cores/clock/xilinx_s7.py | 102 ++ litex/soc/cores/clock/xilinx_us.py | 214 +++ litex/soc/cores/clock/xilinx_usp.py | 127 ++ 16 files changed, 1401 insertions(+), 1146 deletions(-) delete mode 100644 litex/soc/cores/clock.py create mode 100644 litex/soc/cores/clock/__init__.py create mode 100644 litex/soc/cores/clock/common.py create mode 100644 litex/soc/cores/clock/intel_common.py create mode 100644 litex/soc/cores/clock/intel_cyclone10.py create mode 100644 litex/soc/cores/clock/intel_cyclone4.py create mode 100644 litex/soc/cores/clock/intel_cyclone5.py create mode 100644 litex/soc/cores/clock/intel_max10.py create mode 100644 litex/soc/cores/clock/lattice_ecp5.py create mode 100644 litex/soc/cores/clock/lattice_ice40.py rename litex/soc/cores/{clock_nxpll.py => clock/lattice_nx.py} (83%) create mode 100644 litex/soc/cores/clock/xilinx_common.py create mode 100644 litex/soc/cores/clock/xilinx_s6.py create mode 100644 litex/soc/cores/clock/xilinx_s7.py create mode 100644 litex/soc/cores/clock/xilinx_us.py create mode 100644 litex/soc/cores/clock/xilinx_usp.py diff --git a/litex/soc/cores/clock.py b/litex/soc/cores/clock.py deleted file mode 100644 index 17194fe6f..000000000 --- a/litex/soc/cores/clock.py +++ /dev/null @@ -1,1142 +0,0 @@ -# -# This file is part of LiteX. -# -# Copyright (c) 2018-2020 Florent Kermarrec -# Copyright (c) 2019 Michael Betz -# Copyright (c) 2020 David Corrigan -# SPDX-License-Identifier: BSD-2-Clause - -"""Clock Abstraction Modules""" - -import math -import logging - -from migen import * -from migen.genlib.resetsync import AsyncResetSynchronizer - -from litex.build.io import DifferentialInput - -from litex.soc.integration.soc import colorer -from litex.soc.interconnect.csr import * - -logging.basicConfig(level=logging.INFO) - -def period_ns(freq): - return 1e9/freq - -# Logging ------------------------------------------------------------------------------------------ - -def register_clkin_log(logger, clkin, freq): - logger.info("Registering {} {} of {}.".format( - colorer("Differential") if isinstance(clkin, Record) else colorer("Single Ended"), - colorer("ClkIn"), - colorer("{:3.2f}MHz".format(freq/1e6)) - )) - -def create_clkout_log(logger, name, freq, margin, nclkouts): - logger.info("Creating {} of {} {}.".format( - colorer("ClkOut{} {}".format(nclkouts, name)), - colorer("{:3.2f}MHz".format(freq/1e6)), - "(+-{:3.2f}ppm)".format(margin*1e6), - )) - -def compute_config_log(logger, config): - log = "Config:\n" - length = 0 - for name in config.keys(): - if len(name) > length: length = len(name) - for name, value in config.items(): - if "freq" in name or "vco" in name: - value = "{:3.2f}MHz".format(value/1e6) - if "phase" in name: - value = "{:3.2f}°".format(value) - log += "{}{}: {}\n".format(name, " "*(length-len(name)), value) - log = log[:-1] - logger.info(log) - -# Helpers ------------------------------------------------------------------------------------------ - -def clkdiv_range(start, stop, step=1): - start = float(start) - stop = float(stop) - step = float(step) - current = start - while current < stop: - yield int(current) if math.floor(current) == current else current - current += step - -# Xilinx / Generic --------------------------------------------------------------------------------- - -class XilinxClocking(Module, AutoCSR): - clkfbout_mult_frange = (2, 64+1) - clkout_divide_range = (1, 128+1) - - def __init__(self, vco_margin=0): - self.vco_margin = vco_margin - 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, ClockSignal)): - 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 - register_clkin_log(self.logger, clkin, freq) - - def create_clkout(self, cd, freq, phase=0, buf="bufg", margin=1e-2, with_reset=True, ce=None): - assert self.nclkouts < self.nclkouts_max - clkout = Signal() - self.clkouts[self.nclkouts] = (clkout, freq, phase, margin) - if with_reset: - self.specials += AsyncResetSynchronizer(cd, ~self.locked) - 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) - elif buf == "bufgce": - if ce is None: - raise ValueError("BUFGCE requires user to provide a clock enable ce Signal") - self.specials += Instance("BUFGCE", i_I=clkout, o_O=clkout_buf, i_CE=ce) - elif buf == "bufio": - self.specials += Instance("BUFIO", i_I=clkout, o_O=clkout_buf) - else: - raise ValueError("Unsupported clock buffer: {}".format(buf)) - create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts) - self.nclkouts += 1 - - def compute_config(self): - config = {} - for divclk_divide in range(*self.divclk_divide_range): - config["divclk_divide"] = divclk_divide - for clkfbout_mult in reversed(range(*self.clkfbout_mult_frange)): - all_valid = True - vco_freq = self.clkin_freq*clkfbout_mult/divclk_divide - (vco_freq_min, vco_freq_max) = self.vco_freq_range - if (vco_freq >= vco_freq_min*(1 + self.vco_margin) and - vco_freq <= vco_freq_max*(1 - self.vco_margin)): - for n, (clk, f, p, m) in sorted(self.clkouts.items()): - valid = False - d_ranges = [self.clkout_divide_range] - if getattr(self, "clkout{}_divide_range".format(n), None) is not None: - d_ranges += [getattr(self, "clkout{}_divide_range".format(n))] - for d_range in d_ranges: - for d in clkdiv_range(*d_range): - clk_freq = vco_freq/d - if abs(clk_freq - f) <= f*m: - config["clkout{}_freq".format(n)] = clk_freq - config["clkout{}_divide".format(n)] = d - config["clkout{}_phase".format(n)] = p - valid = True - break - if valid: - break - if not valid: - all_valid = False - else: - all_valid = False - if all_valid: - config["vco"] = vco_freq - config["clkfbout_mult"] = clkfbout_mult - compute_config_log(self.logger, config) - return config - raise ValueError("No PLL config found") - - def expose_drp(self): - self.drp_reset = CSR() - self.drp_locked = CSRStatus() - self.drp_read = CSR() - self.drp_write = CSR() - self.drp_drdy = CSRStatus() - self.drp_adr = CSRStorage(7, reset_less=True) - self.drp_dat_w = CSRStorage(16, reset_less=True) - self.drp_dat_r = CSRStatus(16) - - # # # - - den_pipe = Signal() - dwe_pipe = Signal() - - drp_drdy = Signal() - self.params.update( - i_DCLK = ClockSignal(), - i_DWE = dwe_pipe, - i_DEN = den_pipe, - 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 += [ - den_pipe.eq(self.drp_read.re | self.drp_write.re), - dwe_pipe.eq(self.drp_write.re), - If(self.drp_read.re | self.drp_write.re, - self.drp_drdy.status.eq(0) - ).Elif(drp_drdy, - self.drp_drdy.status.eq(1) - ) - ] - self.comb += self.drp_locked.status.eq(self.locked) - self.logger.info("Exposing DRP interface.") - - def add_reset_delay(self, cycles): - for i in range(cycles): - reset = Signal() - self.specials += Instance("FD", i_C=self.clkin, i_D=self.reset, o_Q=reset) - self.reset = reset - - def do_finalize(self): - assert hasattr(self, "clkin") - self.add_reset_delay(cycles=8) # Prevents interlock when reset driven from sys_clk. - -# Xilinx / Spartan6 -------------------------------------------------------------------------------- - -class S6PLL(XilinxClocking): - nclkouts_max = 6 - clkin_freq_range = (19e6, 540e6) - - def __init__(self, speedgrade=-1): - self.logger = logging.getLogger("S6PLL") - self.logger.info("Creating S6PLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - XilinxClocking.__init__(self) - self.divclk_divide_range = (1, 52 + 1) - self.vco_freq_range = { - -1: (400e6, 1000e6), - -2: (400e6, 1000e6), - -3: (400e6, 1080e6), - }[speedgrade] - - def do_finalize(self): - XilinxClocking.do_finalize(self) - config = self.compute_config() - pll_fb = Signal() - self.params.update( - p_SIM_DEVICE = "SPARTAN6", - p_BANDWIDTH = "OPTIMIZED", - p_COMPENSATION = "INTERNAL", - p_REF_JITTER = .01, p_CLK_FEEDBACK="CLKFBOUT", - p_CLKIN1_PERIOD = 1e9/self.clkin_freq, - p_CLKIN2_PERIOD = 0., - p_CLKFBOUT_MULT = config["clkfbout_mult"], - p_CLKFBOUT_PHASE = 0., - p_DIVCLK_DIVIDE = config["divclk_divide"], - i_CLKINSEL = 1, - i_RST = self.reset, - i_CLKIN1 = self.clkin, - i_CLKFBIN = pll_fb, - o_CLKFBOUT = pll_fb, - o_LOCKED = self.locked, - ) - 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)] = float(config["clkout{}_phase".format(n)]) - self.params["p_CLKOUT{}_DUTY_CYCLE".format(n)] = 0.5 - self.params["o_CLKOUT{}".format(n)] = clk - self.specials += Instance("PLL_ADV", **self.params) - - -class S6DCM(XilinxClocking): - """ single output with f_out = f_in * {2 .. 256} / {1 .. 256} """ - nclkouts_max = 1 - clkfbout_mult_frange = (2, 256 + 1) - clkout_divide_range = (1, 256 + 1) - - def __init__(self, speedgrade=-1): - self.logger = logging.getLogger("S6DCM") - self.logger.info("Creating S6DCM, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - XilinxClocking.__init__(self) - self.divclk_divide_range = (1, 2) # FIXME - self.clkin_freq_range = { - -1: (0.5e6, 200e6), - -2: (0.5e6, 333e6), - -3: (0.5e6, 375e6), - }[speedgrade] - - self.vco_freq_range = { - -1: (5e6, 1e16), - -2: (5e6, 1e16), - -3: (5e6, 1e16), - }[speedgrade] - - def do_finalize(self): - XilinxClocking.do_finalize(self) - config = self.compute_config() - clk, f, p, m = sorted(self.clkouts.items())[0][1] - self.params.update( - p_CLKFX_MULTIPLY = config["clkfbout_mult"], - p_CLKFX_DIVIDE = config["clkout0_divide"] * config["divclk_divide"], - p_SPREAD_SPECTRUM = "NONE", - p_CLKIN_PERIOD = 1e9/self.clkin_freq, - i_CLKIN = self.clkin, - i_RST = self.reset, - i_FREEZEDCM = 0, - o_CLKFX = clk, - o_LOCKED = self.locked, - ) - self.specials += Instance("DCM_CLKGEN", **self.params) - - def expose_drp(self): - self._cmd_data = CSRStorage(10) - self._send_cmd_data = CSR() - self._send_go = CSR() - self._status = CSRStatus(4) - - progdata = Signal() - progen = Signal() - progdone = Signal() - locked = Signal() - - self.params.update( - i_PROGCLK = ClockSignal(), - i_PROGDATA = progdata, - i_PROGEN = progen, - o_PROGDONE = progdone - ) - - remaining_bits = Signal(max=11) - transmitting = Signal() - self.comb += transmitting.eq(remaining_bits != 0) - sr = Signal(10) - self.sync += [ - If(self._send_cmd_data.re, - remaining_bits.eq(10), - sr.eq(self._cmd_data.storage) - ).Elif(transmitting, - remaining_bits.eq(remaining_bits - 1), - sr.eq(sr[1:]) - ) - ] - self.comb += [ - progdata.eq(transmitting & sr[0]), - progen.eq(transmitting | self._send_go.re) - ] - - # Enforce gap between commands - busy_counter = Signal(max=14) - busy = Signal() - self.comb += busy.eq(busy_counter != 0) - self.sync += If(self._send_cmd_data.re, - busy_counter.eq(13) - ).Elif(busy, - busy_counter.eq(busy_counter - 1) - ) - - self.comb += self._status.status.eq(Cat(busy, progdone, self.locked)) - - self.logger.info("Exposing DRP interface.") - -# Xilinx / 7-Series -------------------------------------------------------------------------------- - -class S7PLL(XilinxClocking): - nclkouts_max = 6 - clkin_freq_range = (19e6, 800e6) - - def __init__(self, speedgrade=-1): - self.logger = logging.getLogger("S7PLL") - self.logger.info("Creating S7PLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - XilinxClocking.__init__(self) - self.divclk_divide_range = (1, 56+1) - self.vco_freq_range = { - -1: (800e6, 1600e6), - -2: (800e6, 1866e6), - -3: (800e6, 2133e6), - }[speedgrade] - - def do_finalize(self): - XilinxClocking.do_finalize(self) - config = self.compute_config() - pll_fb = Signal() - self.params.update( - p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked, i_RST=self.reset, - - # VCO - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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 S7MMCM(XilinxClocking): - nclkouts_max = 7 - clkout0_divide_range = (1, (128 + 1/8), 1/8) # Fractional Divide available on CLKOUT0 - - def __init__(self, speedgrade=-1): - self.logger = logging.getLogger("S7MMCM") - self.logger.info("Creating S7MMCM, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - XilinxClocking.__init__(self) - self.divclk_divide_range = (1, 106+1) - 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): - XilinxClocking.do_finalize(self) - config = self.compute_config() - mmcm_fb = Signal() - self.params.update( - p_BANDWIDTH="OPTIMIZED", o_LOCKED=self.locked, i_RST=self.reset, - - # VCO - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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) - - -class S7IDELAYCTRL(Module): - def __init__(self, cd, reset_cycles=16): - reset_counter = Signal(log2_int(reset_cycles), reset=reset_cycles - 1) - 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) - -# Xilinx / Ultrascale ------------------------------------------------------------------------------ - -# TODO: -# - use Ultrascale primitives instead of 7-Series' ones. (Vivado recognize and convert them). - -class USPLL(XilinxClocking): - nclkouts_max = 6 - - def __init__(self, speedgrade=-1): - self.logger = logging.getLogger("USPLL") - self.logger.info("Creating USPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - XilinxClocking.__init__(self) - self.divclk_divide_range = (1, 56+1) - 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): - XilinxClocking.do_finalize(self) - config = self.compute_config() - pll_fb = Signal() - self.params.update( - p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked, i_RST=self.reset, - - # VCO - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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(XilinxClocking): - nclkouts_max = 7 - - def __init__(self, speedgrade=-1): - self.logger = logging.getLogger("USMMCM") - self.logger.info("Creating USMMCM, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - XilinxClocking.__init__(self) - self.divclk_divide_range = (1, 106+1) - 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): - XilinxClocking.do_finalize(self) - config = self.compute_config() - mmcm_fb = Signal() - self.params.update( - p_BANDWIDTH="OPTIMIZED", o_LOCKED=self.locked, i_RST=self.reset, - - # VCO - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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) - - -class USIDELAYCTRL(Module): - def __init__(self, cd_ref, cd_sys, reset_cycles=64, ready_cycles=64): - cd_sys.rst.reset = 1 - self.clock_domains.cd_ic = ClockDomain() - ic_reset_counter = Signal(max=reset_cycles, reset=reset_cycles-1) - ic_reset = Signal(reset=1) - cd_ref_sync = getattr(self.sync, cd_ref.name) - cd_ref_sync += [ - If(ic_reset_counter != 0, - ic_reset_counter.eq(ic_reset_counter - 1) - ).Else( - ic_reset.eq(0) - ) - ] - ic_ready_counter = Signal(max=ready_cycles, reset=ready_cycles-1) - ic_ready = Signal() - self.comb += self.cd_ic.clk.eq(cd_sys.clk) - self.sync.ic += [ - If(ic_ready, - If(ic_ready_counter != 0, - ic_ready_counter.eq(ic_ready_counter - 1) - ).Else( - cd_sys.rst.eq(0) - ) - ) - ] - self.specials += [ - Instance("IDELAYCTRL", - p_SIM_DEVICE = "ULTRASCALE", - i_REFCLK = cd_ref.clk, - i_RST = ic_reset, - o_RDY = ic_ready), - AsyncResetSynchronizer(self.cd_ic, ic_reset) - ] - - -# Xilinx / Ultrascale Plus ------------------------------------------------------------------------- - -# TODO: -# - use Ultrascale Plus primitives instead of 7-Series' ones. (Vivado recognize and convert them). - -class USPPLL(XilinxClocking): - nclkouts_max = 6 - - def __init__(self, speedgrade=-1): - self.logger = logging.getLogger("USPPLL") - self.logger.info("Creating USPPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - XilinxClocking.__init__(self) - self.divclk_divide_range = (1, 56+1) - self.clkin_freq_range = { - -1: (70e6, 800e6), - -2: (70e6, 933e6), - -3: (70e6, 1066e6), - }[speedgrade] - self.vco_freq_range = { - -1: (750e6, 1500e6), - -2: (750e6, 1500e6), - -3: (750e6, 1500e6), - }[speedgrade] - - def do_finalize(self): - XilinxClocking.do_finalize(self) - config = self.compute_config() - pll_fb = Signal() - self.params.update( - p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked, i_RST=self.reset, - - # VCO - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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 USPMMCM(XilinxClocking): - nclkouts_max = 7 - - def __init__(self, speedgrade=-1): - self.logger = logging.getLogger("USPMMCM") - self.logger.info("Creating USPMMCM, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - XilinxClocking.__init__(self) - self.divclk_divide_range = (1, 106+1) - self.clkin_freq_range = { - -1: (10e6, 800e6), - -2: (10e6, 933e6), - -3: (10e6, 1066e6), - }[speedgrade] - self.vco_freq_range = { - -1: (800e6, 1600e6), - -2: (800e6, 1600e6), - -3: (800e6, 1600e6), - }[speedgrade] - - def do_finalize(self): - XilinxClocking.do_finalize(self) - config = self.compute_config() - mmcm_fb = Signal() - self.params.update( - p_BANDWIDTH="OPTIMIZED", o_LOCKED=self.locked, i_RST=self.reset, - - # VCO - p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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) - - -class USPIDELAYCTRL(USIDELAYCTRL): pass - -# Lattice / iCE40 ---------------------------------------------------------------------------------- - -# TODO: -# - add phase support. -# - add support for GENCLK_HALF to be able to generate clock down to 8MHz. - -class iCE40PLL(Module): - nclkouts_max = 1 - divr_range = (0, 16) - divf_range = (0, 128) - divq_range = (0, 7) - clki_freq_range = ( 10e6, 133e9) - clko_freq_range = ( 16e6, 275e9) - vco_freq_range = (533e6, 1066e6) - - def __init__(self, primitive="SB_PLL40_CORE"): - assert primitive in ["SB_PLL40_CORE", "SB_PLL40_PAD"] - self.logger = logging.getLogger("iCE40PLL") - self.logger.info("Creating iCE40PLL, {} primitive.".format(colorer(primitive))) - self.primitive = primitive - 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): - (clki_freq_min, clki_freq_max) = self.clki_freq_range - assert freq >= clki_freq_min - assert freq <= clki_freq_max - self.clkin = Signal() - if isinstance(clkin, (Signal, ClockSignal)): - self.comb += self.clkin.eq(clkin) - else: - raise ValueError - self.clkin_freq = freq - register_clkin_log(self.logger, clkin, freq) - - def create_clkout(self, cd, freq, margin=1e-2, with_reset=True): - (clko_freq_min, clko_freq_max) = self.clko_freq_range - assert freq >= clko_freq_min - assert freq <= clko_freq_max - assert self.nclkouts < self.nclkouts_max - clkout = Signal() - self.clkouts[self.nclkouts] = (clkout, freq, 0, margin) - if with_reset: - self.specials += AsyncResetSynchronizer(cd, ~self.locked) - self.comb += cd.clk.eq(clkout) - create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts) - self.nclkouts += 1 - - def compute_config(self): - config = {} - for divr in range(*self.divr_range): - for divf in range(*self.divf_range): - all_valid = True - vco_freq = self.clkin_freq/(divr + 1)*(divf + 1) - (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 divq in range(*self.divq_range): - clk_freq = vco_freq/(2**divq) - if abs(clk_freq - f) <= f*m: - config["clkout_freq"] = clk_freq - config["divq"] = divq - valid = True - break - if not valid: - all_valid = False - else: - all_valid = False - if all_valid: - config["vco"] = vco_freq - config["divr"] = divr - config["divf"] = divf - compute_config_log(self.logger, config) - return config - raise ValueError("No PLL config found") - - def do_finalize(self): - config = self.compute_config() - clkfb = Signal() - for f, v in [(17e6, 1), (26e6, 2), (44e6, 3), (66e6, 4), (101e6, 5), (133e6, 6)]: - pfd_freq = self.clkin_freq/(config["divr"] + 1) - if pfd_freq < f: - filter_range = v - break - self.params.update( - p_FEEDBACK_PATH = "SIMPLE", - p_FILTER_RANGE = filter_range, - i_RESETB = ~self.reset, - o_LOCK = self.locked, - ) - if self.primitive == "SB_PLL40_CORE": - self.params.update(i_REFERENCECLK=self.clkin) - if self.primitive == "SB_PLL40_PAD": - self.params.update(i_PACKAGEPIN=self.clkin) - for n, (clk, f, p, m) in sorted(self.clkouts.items()): - self.params["p_DIVR"] = config["divr"] - self.params["p_DIVF"] = config["divf"] - self.params["p_DIVQ"] = config["divq"] - self.params["o_PLLOUTGLOBAL"] = clk - self.specials += Instance(self.primitive, **self.params) - -# Lattice / ECP5 ----------------------------------------------------------------------------------- - -class ECP5PLL(Module): - nclkouts_max = 3 - clki_div_range = (1, 128+1) - clkfb_div_range = (1, 128+1) - clko_div_range = (1, 128+1) - clki_freq_range = ( 8e6, 400e6) - clko_freq_range = (3.125e6, 400e6) - vco_freq_range = ( 400e6, 800e6) - - def __init__(self): - self.logger = logging.getLogger("ECP5PLL") - self.logger.info("Creating ECP5PLL.") - 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): - (clki_freq_min, clki_freq_max) = self.clki_freq_range - assert freq >= clki_freq_min - assert freq <= clki_freq_max - self.clkin = Signal() - if isinstance(clkin, (Signal, ClockSignal)): - self.comb += self.clkin.eq(clkin) - else: - raise ValueError - self.clkin_freq = freq - register_clkin_log(self.logger, clkin, freq) - - def create_clkout(self, cd, freq, phase=0, margin=1e-2, with_reset=True): - (clko_freq_min, clko_freq_max) = self.clko_freq_range - assert freq >= clko_freq_min - assert freq <= clko_freq_max - assert self.nclkouts < self.nclkouts_max - clkout = Signal() - self.clkouts[self.nclkouts] = (clkout, freq, phase, margin) - if with_reset: - self.specials += AsyncResetSynchronizer(cd, ~self.locked) - self.comb += cd.clk.eq(clkout) - create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts) - self.nclkouts += 1 - - def compute_config(self): - config = {} - for clki_div in range(*self.clki_div_range): - config["clki_div"] = clki_div - for clkfb_div in range(*self.clkfb_div_range): - all_valid = True - vco_freq = self.clkin_freq/clki_div*clkfb_div*1 # clkos3_div=1 - (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.clko_div_range): - clk_freq = vco_freq/d - if abs(clk_freq - f) <= f*m: - config["clko{}_freq".format(n)] = clk_freq - config["clko{}_div".format(n)] = d - config["clko{}_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["clkfb_div"] = clkfb_div - compute_config_log(self.logger, config) - return config - raise ValueError("No PLL config found") - - def do_finalize(self): - config = self.compute_config() - clkfb = Signal() - self.params.update( - attr=[ - ("FREQUENCY_PIN_CLKI", str(self.clkin_freq/1e6)), - ("ICP_CURRENT", "6"), - ("LPF_RESISTOR", "16"), - ("MFG_ENABLE_FILTEROPAMP", "1"), - ("MFG_GMCREF_SEL", "2")], - i_RST = self.reset, - i_CLKI = self.clkin, - o_LOCK = self.locked, - p_FEEDBK_PATH = "INT_OS3", # CLKOS3 reserved for feedback with div=1. - p_CLKOS3_ENABLE = "ENABLED", - p_CLKOS3_DIV = 1, - p_CLKOS3_FPHASE = 0, - p_CLKOS3_CPHASE = 23, - p_CLKFB_DIV = config["clkfb_div"], - p_CLKI_DIV = config["clki_div"], - ) - for n, (clk, f, p, m) in sorted(self.clkouts.items()): - n_to_l = {0: "P", 1: "S", 2: "S2"} - div = config["clko{}_div".format(n)] - cphase = int(p*(div + 1)/360 + div - 1) - self.params["p_CLKO{}_ENABLE".format(n_to_l[n])] = "ENABLED" - self.params["p_CLKO{}_DIV".format(n_to_l[n])] = div - self.params["p_CLKO{}_FPHASE".format(n_to_l[n])] = 0 - self.params["p_CLKO{}_CPHASE".format(n_to_l[n])] = cphase - self.params["o_CLKO{}".format(n_to_l[n])] = clk - self.specials += Instance("EHXPLLL", **self.params) - -# Lattice / NX ------------------------------------------------------------------------------------- -# NOTE This clock has +/- 15% accuracy -class NXOSCA(Module): - nclkouts_max = 2 - clk_hf_div_range = (0, 255) - clk_hf_freq_range = (1.76, 450e6) - clk_hf_freq = 450e6 - - def __init__(self): - self.logger = logging.getLogger("NXOSCA") - self.logger.info("Creating NXOSCA.") - - self.hf_clk_out = {} - self.hfsdc_clk_out = {} - self.lf_clk_out = None - self.params = {} - - def create_hf_clk(self, cd, freq, margin=.05): - """450 - 1.7 Mhz Clk""" - (clko_freq_min, clko_freq_max) = self.clk_hf_freq_range - assert freq >= clko_freq_min - assert freq <= clko_freq_max - clkout = Signal() - self.hf_clk_out = (clkout, freq, margin) - self.comb += cd.clk.eq(clkout) - create_clkout_log(self.logger, cd.name, freq, margin, -1) - - def create_hfsdc_clk(self, cd, freq, margin=.05): - """450 - 1.7 Mhz Clk. Can only be connected to the SEDC_CLK port of CONFIG_CLKRST_CORE""" - (clko_freq_min, clko_freq_max) = self.clk_hf_freq_range - assert freq >= clko_freq_min - assert freq <= clko_freq_max - clkout = Signal() - self.hfsdc_clk_out = (clkout, freq, margin) - self.comb += cd.clk.eq(clkout) - create_clkout_log(self.logger, cd.name, freq, margin, -1) - - def create_lf_clk(self, cd): - """128 kHz Clock""" - clkout = Signal() - self.lf_clk_out = (clkout) - self.comb += cd.clk.eq(clkout) - create_clkout_log(self.logger, cd.name, 128e3, 19e3, -1) - - def compute_divisor(self, freq, margin): - config = {} - - for divisor in range(*self.clk_hf_div_range): - clk_freq = self.clk_hf_freq/(divisor+1) - if abs(clk_freq - freq) <= freq*margin: - config["freq"] = clk_freq - config["div"] = str(divisor) - break - - if config: - compute_config_log(self.logger, config) - return config["div"] - - raise ValueError("Bad OSC freq.") - - def do_finalize(self): - if self.hf_clk_out: - divisor = self.compute_divisor(self.hf_clk_out[1], self.hf_clk_out[2]) - self.params["i_HFOUTEN"] = 0b1 - self.params["p_HF_CLK_DIV"] = divisor - self.params["o_HFCLKOUT"] = self.hf_clk_out[0] - self.params["p_HF_OSC_EN"] = "ENABLED" - - if self.hfsdc_clk_out: - divisor = self.compute_divisor(self.hfsdc_clk_out[1], self.hfsdc_clk_out[2]) - self.params["i_HFSDSCEN"] = 0b1 - self.params["p_HF_SED_SEC_DIV"] = divisor - self.params["o_HFSDCOUT"] = self.hfsdc_clk_out[0] - - if self.lf_clk_out is not None: - self.params["o_LFCLKOUT"] = self.lf_clk_out[0] - self.params["p_LF_OUTPUT_EN"] = "ENABLED" - - self.specials += Instance("OSCA", **self.params) - -# Intel / Generic --------------------------------------------------------------------------------- - -class IntelClocking(Module, AutoCSR): - def __init__(self, vco_margin=0): - self.vco_margin = vco_margin - 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, ClockSignal)): - 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 - register_clkin_log(self.logger, clkin, freq) - - def create_clkout(self, cd, freq, phase=0, margin=1e-2, with_reset=True): - assert self.nclkouts < self.nclkouts_max - clkout = Signal() - self.clkouts[self.nclkouts] = (clkout, freq, phase, margin) - if with_reset: - self.specials += AsyncResetSynchronizer(cd, ~self.locked) - self.comb += cd.clk.eq(clkout) - create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts) - self.nclkouts += 1 - - def compute_config(self): - config = {} - for n in range(*self.n_div_range): - config["n"] = n - for m in reversed(range(*self.m_div_range)): - all_valid = True - vco_freq = self.clkin_freq*m/n - (vco_freq_min, vco_freq_max) = self.vco_freq_range - if (vco_freq >= vco_freq_min*(1 + self.vco_margin) and - vco_freq <= vco_freq_max*(1 - self.vco_margin)): - for _n, (clk, f, p, _m) in sorted(self.clkouts.items()): - valid = False - for c in clkdiv_range(*self.c_div_range): - clk_freq = vco_freq/c - if abs(clk_freq - f) <= f*_m: - config["clk{}_freq".format(_n)] = clk_freq - config["clk{}_divide".format(_n)] = c - config["clk{}_phase".format(_n)] = p - valid = True - break - if valid: - break - if not valid: - all_valid = False - else: - all_valid = False - if all_valid: - config["vco"] = vco_freq - config["m"] = m - compute_config_log(self.logger, config) - return config - raise ValueError("No PLL config found") - - def do_finalize(self): - assert hasattr(self, "clkin") - config = self.compute_config() - clks = Signal(self.nclkouts) - self.params.update( - p_BANDWIDTH_TYPE = "AUTO", - p_COMPENSATE_CLOCK = "CLK0", - p_INCLK0_INPUT_FREQUENCY = int(1e12/self.clkin_freq), - p_OPERATION_MODE = "NORMAL", - i_INCLK = self.clkin, - o_CLK = clks, - i_ARESET = 0, - i_CLKENA = 2**self.nclkouts_max - 1, - i_EXTCLKENA = 0xf, - i_FBIN = 1, - i_PFDENA = 1, - i_PLLENA = 1, - o_LOCKED = self.locked, - ) - for n, (clk, f, p, m) in sorted(self.clkouts.items()): - clk_phase_ps = int((1e12/config["clk{}_freq".format(n)])*config["clk{}_phase".format(n)]/360) - self.params["p_CLK{}_DIVIDE_BY".format(n)] = config["clk{}_divide".format(n)] - self.params["p_CLK{}_DUTY_CYCLE".format(n)] = 50 - self.params["p_CLK{}_MULTIPLY_BY".format(n)] = config["m"] - self.params["p_CLK{}_PHASE_SHIFT".format(n)] = clk_phase_ps - self.comb += clk.eq(clks[n]) - self.specials += Instance("ALTPLL", **self.params) - -# Intel / CycloneIV ------------------------------------------------------------------------------- - -class CycloneIVPLL(IntelClocking): - nclkouts_max = 5 - n_div_range = (1, 512+1) - m_div_range = (1, 512+1) - c_div_range = (1, 512+1) - vco_freq_range = (600e6, 1300e6) - def __init__(self, speedgrade="-6"): - self.logger = logging.getLogger("CycloneIVPLL") - self.logger.info("Creating CycloneIVPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - IntelClocking.__init__(self) - self.clkin_freq_range = { - "-6" : (5e6, 472.5e6), - "-7" : (5e6, 472.5e6), - "-8" : (5e6, 472.5e6), - "-8L": (5e6, 362e6), - "-9L": (5e6, 256e6), - }[speedgrade] - self.clko_freq_range = { - "-6" : (0e6, 472.5e6), - "-7" : (0e6, 450e6), - "-8" : (0e6, 402.5e6), - "-8L": (0e6, 362e6), - "-9L": (0e6, 265e6), - }[speedgrade] - -# Intel / CycloneV -------------------------------------------------------------------------------- - -class CycloneVPLL(IntelClocking): - nclkouts_max = 5 - n_div_range = (1, 512+1) - m_div_range = (1, 512+1) - c_div_range = (1, 512+1) - clkin_pfd_freq_range = (5e6, 325e6) # FIXME: use - clkfin_pfd_freq_range = (50e6, 160e6) # FIXME: use - def __init__(self, speedgrade="-C6"): - self.logger = logging.getLogger("CycloneVPLL") - self.logger.info("Creating CycloneVPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - IntelClocking.__init__(self) - self.clkin_freq_range = { - "-C6" : (5e6, 670e6), - "-C7" : (5e6, 622e6), - "-I7" : (5e6, 622e6), - "-C8" : (5e6, 622e6), - "-A7" : (5e6, 500e6), - }[speedgrade] - self.vco_freq_range = { - "-C6" : (600e6, 1600e6), - "-C7" : (600e6, 1600e6), - "-I7" : (600e6, 1600e6), - "-C8" : (600e6, 1300e6), - "-A7" : (600e6, 1300e6), - }[speedgrade] - self.clko_freq_range = { - "-C6" : (0e6, 550e6), - "-C7" : (0e6, 550e6), - "-I7" : (0e6, 550e6), - "-C8" : (0e6, 460e6), - "-A7" : (0e6, 460e6), - }[speedgrade] - -# Intel / Cyclone10LP ------------------------------------------------------------------------------ - -class Cyclone10LPPLL(IntelClocking): - nclkouts_max = 5 - n_div_range = (1, 512+1) - m_div_range = (1, 512+1) - c_div_range = (1, 512+1) - clkin_pfd_freq_range = (5e6, 325e6) # FIXME: use - vco_freq_range = (600e6, 1300e6) - def __init__(self, speedgrade="-C6"): - self.logger = logging.getLogger("Cyclone10LPPLL") - self.logger.info("Creating Cyclone10LPPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - IntelClocking.__init__(self) - self.clkin_freq_range = { - "-C6" : (5e6, 472.5e6), - "-C8" : (5e6, 472.5e6), - "-I7" : (5e6, 472.5e6), - "-A7" : (5e6, 472.5e6), - "-I8" : (5e6, 362e6), - }[speedgrade] - self.clko_freq_range = { - "-C6" : (0e6, 472.5e6), - "-C8" : (0e6, 402.5e6), - "-I7" : (0e6, 450e6), - "-A7" : (0e6, 450e6), - "-I8" : (0e6, 362e6), - }[speedgrade] - -# Intel / Max10 ------------------------------------------------------------------------------------ - -class Max10PLL(IntelClocking): - nclkouts_max = 5 - n_div_range = (1, 512+1) - m_div_range = (1, 512+1) - c_div_range = (1, 512+1) - clkin_freq_range = (5e6, 472.5e6) - clkin_pfd_freq_range = (5e6, 325e6) # FIXME: use - vco_freq_range = (600e6, 1300e6) - def __init__(self, speedgrade="-6"): - self.logger = logging.getLogger("Max10PLL") - self.logger.info("Creating Max10PLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) - IntelClocking.__init__(self) - self.clko_freq_range = { - "-6" : (0e6, 472.5e6), - "-7" : (0e6, 450e6), - "-8" : (0e6, 402.5e6), - }[speedgrade] diff --git a/litex/soc/cores/clock/__init__.py b/litex/soc/cores/clock/__init__.py new file mode 100644 index 000000000..f8096c2b0 --- /dev/null +++ b/litex/soc/cores/clock/__init__.py @@ -0,0 +1,16 @@ +# Xilinx +from litex.soc.cores.clock.xilinx_s6 import S6PLL, S6DCM +from litex.soc.cores.clock.xilinx_s7 import S7PLL, S7MMCM, S7IDELAYCTRL +from litex.soc.cores.clock.xilinx_us import USPLL, USMMCM, USIDELAYCTRL +from litex.soc.cores.clock.xilinx_usp import USPPLL, USPMMCM, USPIDELAYCTRL + +# Intel +from litex.soc.cores.clock.intel_max10 import Max10PLL +from litex.soc.cores.clock.intel_cyclone4 import CycloneIVPLL +from litex.soc.cores.clock.intel_cyclone5 import CycloneVPLL +from litex.soc.cores.clock.intel_cyclone10 import Cyclone10LPPLL + +# Lattice +from litex.soc.cores.clock.lattice_ice40 import iCE40PLL +from litex.soc.cores.clock.lattice_ecp5 import ECP5PLL +from litex.soc.cores.clock.lattice_nx import NXOSCA, NXPLL diff --git a/litex/soc/cores/clock/common.py b/litex/soc/cores/clock/common.py new file mode 100644 index 000000000..1dda879bf --- /dev/null +++ b/litex/soc/cores/clock/common.py @@ -0,0 +1,58 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +import logging +import math + +from migen import Record + +from litex.soc.integration.soc import colorer + +logging.basicConfig(level=logging.INFO) + +# Logging ------------------------------------------------------------------------------------------ + +def register_clkin_log(logger, clkin, freq): + logger.info("Registering {} {} of {}.".format( + colorer("Differential") if isinstance(clkin, Record) else colorer("Single Ended"), + colorer("ClkIn"), + colorer("{:3.2f}MHz".format(freq/1e6)) + )) + +def create_clkout_log(logger, name, freq, margin, nclkouts): + logger.info("Creating {} of {} {}.".format( + colorer("ClkOut{} {}".format(nclkouts, name)), + colorer("{:3.2f}MHz".format(freq/1e6)), + "(+-{:3.2f}ppm)".format(margin*1e6), + )) + +def compute_config_log(logger, config): + log = "Config:\n" + length = 0 + for name in config.keys(): + if len(name) > length: length = len(name) + for name, value in config.items(): + if "freq" in name or "vco" in name: + value = "{:3.2f}MHz".format(value/1e6) + if "phase" in name: + value = "{:3.2f}°".format(value) + log += "{}{}: {}\n".format(name, " "*(length-len(name)), value) + log = log[:-1] + logger.info(log) + +# Helpers ------------------------------------------------------------------------------------------ + +def period_ns(freq): + return 1e9/freq + +def clkdiv_range(start, stop, step=1): + start = float(start) + stop = float(stop) + step = float(step) + current = start + while current < stop: + yield int(current) if math.floor(current) == current else current + current += step diff --git a/litex/soc/cores/clock/intel_common.py b/litex/soc/cores/clock/intel_common.py new file mode 100644 index 000000000..6a0be5382 --- /dev/null +++ b/litex/soc/cores/clock/intel_common.py @@ -0,0 +1,110 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex.build.io import DifferentialInput + +from litex.soc.interconnect.csr import * + +from litex.soc.cores.clock.common import * + +# Intel / Generic --------------------------------------------------------------------------------- + +class IntelClocking(Module, AutoCSR): + def __init__(self, vco_margin=0): + self.vco_margin = vco_margin + 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, ClockSignal)): + 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 + register_clkin_log(self.logger, clkin, freq) + + def create_clkout(self, cd, freq, phase=0, margin=1e-2, with_reset=True): + assert self.nclkouts < self.nclkouts_max + clkout = Signal() + self.clkouts[self.nclkouts] = (clkout, freq, phase, margin) + if with_reset: + self.specials += AsyncResetSynchronizer(cd, ~self.locked) + self.comb += cd.clk.eq(clkout) + create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts) + self.nclkouts += 1 + + def compute_config(self): + config = {} + for n in range(*self.n_div_range): + config["n"] = n + for m in reversed(range(*self.m_div_range)): + all_valid = True + vco_freq = self.clkin_freq*m/n + (vco_freq_min, vco_freq_max) = self.vco_freq_range + if (vco_freq >= vco_freq_min*(1 + self.vco_margin) and + vco_freq <= vco_freq_max*(1 - self.vco_margin)): + for _n, (clk, f, p, _m) in sorted(self.clkouts.items()): + valid = False + for c in clkdiv_range(*self.c_div_range): + clk_freq = vco_freq/c + if abs(clk_freq - f) <= f*_m: + config["clk{}_freq".format(_n)] = clk_freq + config["clk{}_divide".format(_n)] = c + config["clk{}_phase".format(_n)] = p + valid = True + break + if valid: + break + if not valid: + all_valid = False + else: + all_valid = False + if all_valid: + config["vco"] = vco_freq + config["m"] = m + compute_config_log(self.logger, config) + return config + raise ValueError("No PLL config found") + + def do_finalize(self): + assert hasattr(self, "clkin") + config = self.compute_config() + clks = Signal(self.nclkouts) + self.params.update( + p_BANDWIDTH_TYPE = "AUTO", + p_COMPENSATE_CLOCK = "CLK0", + p_INCLK0_INPUT_FREQUENCY = int(1e12/self.clkin_freq), + p_OPERATION_MODE = "NORMAL", + i_INCLK = self.clkin, + o_CLK = clks, + i_ARESET = 0, + i_CLKENA = 2**self.nclkouts_max - 1, + i_EXTCLKENA = 0xf, + i_FBIN = 1, + i_PFDENA = 1, + i_PLLENA = 1, + o_LOCKED = self.locked, + ) + for n, (clk, f, p, m) in sorted(self.clkouts.items()): + clk_phase_ps = int((1e12/config["clk{}_freq".format(n)])*config["clk{}_phase".format(n)]/360) + self.params["p_CLK{}_DIVIDE_BY".format(n)] = config["clk{}_divide".format(n)] + self.params["p_CLK{}_DUTY_CYCLE".format(n)] = 50 + self.params["p_CLK{}_MULTIPLY_BY".format(n)] = config["m"] + self.params["p_CLK{}_PHASE_SHIFT".format(n)] = clk_phase_ps + self.comb += clk.eq(clks[n]) + self.specials += Instance("ALTPLL", **self.params) diff --git a/litex/soc/cores/clock/intel_cyclone10.py b/litex/soc/cores/clock/intel_cyclone10.py new file mode 100644 index 000000000..71e461762 --- /dev/null +++ b/litex/soc/cores/clock/intel_cyclone10.py @@ -0,0 +1,38 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * + +from litex.soc.cores.clock.common import * +from litex.soc.cores.clock.intel_common import * + +# Intel / Cyclone10LP ------------------------------------------------------------------------------ + +class Cyclone10LPPLL(IntelClocking): + nclkouts_max = 5 + n_div_range = (1, 512+1) + m_div_range = (1, 512+1) + c_div_range = (1, 512+1) + clkin_pfd_freq_range = (5e6, 325e6) # FIXME: use + vco_freq_range = (600e6, 1300e6) + def __init__(self, speedgrade="-C6"): + self.logger = logging.getLogger("Cyclone10LPPLL") + self.logger.info("Creating Cyclone10LPPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + IntelClocking.__init__(self) + self.clkin_freq_range = { + "-C6" : (5e6, 472.5e6), + "-C8" : (5e6, 472.5e6), + "-I7" : (5e6, 472.5e6), + "-A7" : (5e6, 472.5e6), + "-I8" : (5e6, 362e6), + }[speedgrade] + self.clko_freq_range = { + "-C6" : (0e6, 472.5e6), + "-C8" : (0e6, 402.5e6), + "-I7" : (0e6, 450e6), + "-A7" : (0e6, 450e6), + "-I8" : (0e6, 362e6), + }[speedgrade] diff --git a/litex/soc/cores/clock/intel_cyclone4.py b/litex/soc/cores/clock/intel_cyclone4.py new file mode 100644 index 000000000..d208a7376 --- /dev/null +++ b/litex/soc/cores/clock/intel_cyclone4.py @@ -0,0 +1,37 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * + +from litex.soc.cores.clock.common import * +from litex.soc.cores.clock.intel_common import * + +# Intel / CycloneIV ------------------------------------------------------------------------------- + +class CycloneIVPLL(IntelClocking): + nclkouts_max = 5 + n_div_range = (1, 512+1) + m_div_range = (1, 512+1) + c_div_range = (1, 512+1) + vco_freq_range = (600e6, 1300e6) + def __init__(self, speedgrade="-6"): + self.logger = logging.getLogger("CycloneIVPLL") + self.logger.info("Creating CycloneIVPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + IntelClocking.__init__(self) + self.clkin_freq_range = { + "-6" : (5e6, 472.5e6), + "-7" : (5e6, 472.5e6), + "-8" : (5e6, 472.5e6), + "-8L": (5e6, 362e6), + "-9L": (5e6, 256e6), + }[speedgrade] + self.clko_freq_range = { + "-6" : (0e6, 472.5e6), + "-7" : (0e6, 450e6), + "-8" : (0e6, 402.5e6), + "-8L": (0e6, 362e6), + "-9L": (0e6, 265e6), + }[speedgrade] diff --git a/litex/soc/cores/clock/intel_cyclone5.py b/litex/soc/cores/clock/intel_cyclone5.py new file mode 100644 index 000000000..c8e9572f2 --- /dev/null +++ b/litex/soc/cores/clock/intel_cyclone5.py @@ -0,0 +1,45 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * + +from litex.soc.cores.clock.common import * +from litex.soc.cores.clock.intel_common import * + +# Intel / CycloneV -------------------------------------------------------------------------------- + +class CycloneVPLL(IntelClocking): + nclkouts_max = 5 + n_div_range = (1, 512+1) + m_div_range = (1, 512+1) + c_div_range = (1, 512+1) + clkin_pfd_freq_range = (5e6, 325e6) # FIXME: use + clkfin_pfd_freq_range = (50e6, 160e6) # FIXME: use + def __init__(self, speedgrade="-C6"): + self.logger = logging.getLogger("CycloneVPLL") + self.logger.info("Creating CycloneVPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + IntelClocking.__init__(self) + self.clkin_freq_range = { + "-C6" : (5e6, 670e6), + "-C7" : (5e6, 622e6), + "-I7" : (5e6, 622e6), + "-C8" : (5e6, 622e6), + "-A7" : (5e6, 500e6), + }[speedgrade] + self.vco_freq_range = { + "-C6" : (600e6, 1600e6), + "-C7" : (600e6, 1600e6), + "-I7" : (600e6, 1600e6), + "-C8" : (600e6, 1300e6), + "-A7" : (600e6, 1300e6), + }[speedgrade] + self.clko_freq_range = { + "-C6" : (0e6, 550e6), + "-C7" : (0e6, 550e6), + "-I7" : (0e6, 550e6), + "-C8" : (0e6, 460e6), + "-A7" : (0e6, 460e6), + }[speedgrade] diff --git a/litex/soc/cores/clock/intel_max10.py b/litex/soc/cores/clock/intel_max10.py new file mode 100644 index 000000000..ab759a2f8 --- /dev/null +++ b/litex/soc/cores/clock/intel_max10.py @@ -0,0 +1,30 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * + +from litex.soc.cores.clock.common import * +from litex.soc.cores.clock.intel_common import * + +# Intel / Max10 ------------------------------------------------------------------------------------ + +class Max10PLL(IntelClocking): + nclkouts_max = 5 + n_div_range = (1, 512+1) + m_div_range = (1, 512+1) + c_div_range = (1, 512+1) + clkin_freq_range = (5e6, 472.5e6) + clkin_pfd_freq_range = (5e6, 325e6) # FIXME: use + vco_freq_range = (600e6, 1300e6) + def __init__(self, speedgrade="-6"): + self.logger = logging.getLogger("Max10PLL") + self.logger.info("Creating Max10PLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + IntelClocking.__init__(self) + self.clko_freq_range = { + "-6" : (0e6, 472.5e6), + "-7" : (0e6, 450e6), + "-8" : (0e6, 402.5e6), + }[speedgrade] diff --git a/litex/soc/cores/clock/lattice_ecp5.py b/litex/soc/cores/clock/lattice_ecp5.py new file mode 100644 index 000000000..b143fdbf9 --- /dev/null +++ b/litex/soc/cores/clock/lattice_ecp5.py @@ -0,0 +1,120 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex.soc.cores.clock.common import * + +# Lattice / ECP5 ----------------------------------------------------------------------------------- + +class ECP5PLL(Module): + nclkouts_max = 3 + clki_div_range = (1, 128+1) + clkfb_div_range = (1, 128+1) + clko_div_range = (1, 128+1) + clki_freq_range = ( 8e6, 400e6) + clko_freq_range = (3.125e6, 400e6) + vco_freq_range = ( 400e6, 800e6) + + def __init__(self): + self.logger = logging.getLogger("ECP5PLL") + self.logger.info("Creating ECP5PLL.") + 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): + (clki_freq_min, clki_freq_max) = self.clki_freq_range + assert freq >= clki_freq_min + assert freq <= clki_freq_max + self.clkin = Signal() + if isinstance(clkin, (Signal, ClockSignal)): + self.comb += self.clkin.eq(clkin) + else: + raise ValueError + self.clkin_freq = freq + register_clkin_log(self.logger, clkin, freq) + + def create_clkout(self, cd, freq, phase=0, margin=1e-2, with_reset=True): + (clko_freq_min, clko_freq_max) = self.clko_freq_range + assert freq >= clko_freq_min + assert freq <= clko_freq_max + assert self.nclkouts < self.nclkouts_max + clkout = Signal() + self.clkouts[self.nclkouts] = (clkout, freq, phase, margin) + if with_reset: + self.specials += AsyncResetSynchronizer(cd, ~self.locked) + self.comb += cd.clk.eq(clkout) + create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts) + self.nclkouts += 1 + + def compute_config(self): + config = {} + for clki_div in range(*self.clki_div_range): + config["clki_div"] = clki_div + for clkfb_div in range(*self.clkfb_div_range): + all_valid = True + vco_freq = self.clkin_freq/clki_div*clkfb_div*1 # clkos3_div=1 + (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.clko_div_range): + clk_freq = vco_freq/d + if abs(clk_freq - f) <= f*m: + config["clko{}_freq".format(n)] = clk_freq + config["clko{}_div".format(n)] = d + config["clko{}_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["clkfb_div"] = clkfb_div + compute_config_log(self.logger, config) + return config + raise ValueError("No PLL config found") + + def do_finalize(self): + config = self.compute_config() + clkfb = Signal() + self.params.update( + attr=[ + ("FREQUENCY_PIN_CLKI", str(self.clkin_freq/1e6)), + ("ICP_CURRENT", "6"), + ("LPF_RESISTOR", "16"), + ("MFG_ENABLE_FILTEROPAMP", "1"), + ("MFG_GMCREF_SEL", "2")], + i_RST = self.reset, + i_CLKI = self.clkin, + o_LOCK = self.locked, + p_FEEDBK_PATH = "INT_OS3", # CLKOS3 reserved for feedback with div=1. + p_CLKOS3_ENABLE = "ENABLED", + p_CLKOS3_DIV = 1, + p_CLKOS3_FPHASE = 0, + p_CLKOS3_CPHASE = 23, + p_CLKFB_DIV = config["clkfb_div"], + p_CLKI_DIV = config["clki_div"], + ) + for n, (clk, f, p, m) in sorted(self.clkouts.items()): + n_to_l = {0: "P", 1: "S", 2: "S2"} + div = config["clko{}_div".format(n)] + cphase = int(p*(div + 1)/360 + div - 1) + self.params["p_CLKO{}_ENABLE".format(n_to_l[n])] = "ENABLED" + self.params["p_CLKO{}_DIV".format(n_to_l[n])] = div + self.params["p_CLKO{}_FPHASE".format(n_to_l[n])] = 0 + self.params["p_CLKO{}_CPHASE".format(n_to_l[n])] = cphase + self.params["o_CLKO{}".format(n_to_l[n])] = clk + self.specials += Instance("EHXPLLL", **self.params) diff --git a/litex/soc/cores/clock/lattice_ice40.py b/litex/soc/cores/clock/lattice_ice40.py new file mode 100644 index 000000000..ea2616e3e --- /dev/null +++ b/litex/soc/cores/clock/lattice_ice40.py @@ -0,0 +1,117 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * + +from litex.soc.cores.clock.common import * + +# Lattice / iCE40 ---------------------------------------------------------------------------------- + +# TODO: +# - add phase support. +# - add support for GENCLK_HALF to be able to generate clock down to 8MHz. + +class iCE40PLL(Module): + nclkouts_max = 1 + divr_range = (0, 16) + divf_range = (0, 128) + divq_range = (0, 7) + clki_freq_range = ( 10e6, 133e9) + clko_freq_range = ( 16e6, 275e9) + vco_freq_range = (533e6, 1066e6) + + def __init__(self, primitive="SB_PLL40_CORE"): + assert primitive in ["SB_PLL40_CORE", "SB_PLL40_PAD"] + self.logger = logging.getLogger("iCE40PLL") + self.logger.info("Creating iCE40PLL, {} primitive.".format(colorer(primitive))) + self.primitive = primitive + 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): + (clki_freq_min, clki_freq_max) = self.clki_freq_range + assert freq >= clki_freq_min + assert freq <= clki_freq_max + self.clkin = Signal() + if isinstance(clkin, (Signal, ClockSignal)): + self.comb += self.clkin.eq(clkin) + else: + raise ValueError + self.clkin_freq = freq + register_clkin_log(self.logger, clkin, freq) + + def create_clkout(self, cd, freq, margin=1e-2, with_reset=True): + (clko_freq_min, clko_freq_max) = self.clko_freq_range + assert freq >= clko_freq_min + assert freq <= clko_freq_max + assert self.nclkouts < self.nclkouts_max + clkout = Signal() + self.clkouts[self.nclkouts] = (clkout, freq, 0, margin) + if with_reset: + self.specials += AsyncResetSynchronizer(cd, ~self.locked) + self.comb += cd.clk.eq(clkout) + create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts) + self.nclkouts += 1 + + def compute_config(self): + config = {} + for divr in range(*self.divr_range): + for divf in range(*self.divf_range): + all_valid = True + vco_freq = self.clkin_freq/(divr + 1)*(divf + 1) + (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 divq in range(*self.divq_range): + clk_freq = vco_freq/(2**divq) + if abs(clk_freq - f) <= f*m: + config["clkout_freq"] = clk_freq + config["divq"] = divq + valid = True + break + if not valid: + all_valid = False + else: + all_valid = False + if all_valid: + config["vco"] = vco_freq + config["divr"] = divr + config["divf"] = divf + compute_config_log(self.logger, config) + return config + raise ValueError("No PLL config found") + + def do_finalize(self): + config = self.compute_config() + clkfb = Signal() + for f, v in [(17e6, 1), (26e6, 2), (44e6, 3), (66e6, 4), (101e6, 5), (133e6, 6)]: + pfd_freq = self.clkin_freq/(config["divr"] + 1) + if pfd_freq < f: + filter_range = v + break + self.params.update( + p_FEEDBACK_PATH = "SIMPLE", + p_FILTER_RANGE = filter_range, + i_RESETB = ~self.reset, + o_LOCK = self.locked, + ) + if self.primitive == "SB_PLL40_CORE": + self.params.update(i_REFERENCECLK=self.clkin) + if self.primitive == "SB_PLL40_PAD": + self.params.update(i_PACKAGEPIN=self.clkin) + for n, (clk, f, p, m) in sorted(self.clkouts.items()): + self.params["p_DIVR"] = config["divr"] + self.params["p_DIVF"] = config["divf"] + self.params["p_DIVQ"] = config["divq"] + self.params["o_PLLOUTGLOBAL"] = clk + self.specials += Instance(self.primitive, **self.params) diff --git a/litex/soc/cores/clock_nxpll.py b/litex/soc/cores/clock/lattice_nx.py similarity index 83% rename from litex/soc/cores/clock_nxpll.py rename to litex/soc/cores/clock/lattice_nx.py index 35b715c9e..0460688e7 100644 --- a/litex/soc/cores/clock_nxpll.py +++ b/litex/soc/cores/clock/lattice_nx.py @@ -13,7 +13,7 @@ from cmath import phase from migen import * -from litex.soc.cores.clock import register_clkin_log, create_clkout_log, compute_config_log +from litex.soc.cores.clock.common import * logging.basicConfig(level=logging.INFO) @@ -22,6 +22,90 @@ nx_pll_param_permutation = namedtuple("nx_pll_param_permutation",[ "C1","C2","C3","C4","C5","C6", "IPP_CTRL","BW_CTL_BIAS","IPP_SEL","CSET","CRIPPLE","V2I_PP_RES","IPI_CMP"]) + +# Lattice / NX OSCA -------------------------------------------------------------------------------- +# NOTE This clock has +/- 15% accuracy + +class NXOSCA(Module): + nclkouts_max = 2 + clk_hf_div_range = (0, 255) + clk_hf_freq_range = (1.76, 450e6) + clk_hf_freq = 450e6 + + def __init__(self): + self.logger = logging.getLogger("NXOSCA") + self.logger.info("Creating NXOSCA.") + + self.hf_clk_out = {} + self.hfsdc_clk_out = {} + self.lf_clk_out = None + self.params = {} + + def create_hf_clk(self, cd, freq, margin=.05): + """450 - 1.7 Mhz Clk""" + (clko_freq_min, clko_freq_max) = self.clk_hf_freq_range + assert freq >= clko_freq_min + assert freq <= clko_freq_max + clkout = Signal() + self.hf_clk_out = (clkout, freq, margin) + self.comb += cd.clk.eq(clkout) + create_clkout_log(self.logger, cd.name, freq, margin, -1) + + def create_hfsdc_clk(self, cd, freq, margin=.05): + """450 - 1.7 Mhz Clk. Can only be connected to the SEDC_CLK port of CONFIG_CLKRST_CORE""" + (clko_freq_min, clko_freq_max) = self.clk_hf_freq_range + assert freq >= clko_freq_min + assert freq <= clko_freq_max + clkout = Signal() + self.hfsdc_clk_out = (clkout, freq, margin) + self.comb += cd.clk.eq(clkout) + create_clkout_log(self.logger, cd.name, freq, margin, -1) + + def create_lf_clk(self, cd): + """128 kHz Clock""" + clkout = Signal() + self.lf_clk_out = (clkout) + self.comb += cd.clk.eq(clkout) + create_clkout_log(self.logger, cd.name, 128e3, 19e3, -1) + + def compute_divisor(self, freq, margin): + config = {} + + for divisor in range(*self.clk_hf_div_range): + clk_freq = self.clk_hf_freq/(divisor+1) + if abs(clk_freq - freq) <= freq*margin: + config["freq"] = clk_freq + config["div"] = str(divisor) + break + + if config: + compute_config_log(self.logger, config) + return config["div"] + + raise ValueError("Bad OSC freq.") + + def do_finalize(self): + if self.hf_clk_out: + divisor = self.compute_divisor(self.hf_clk_out[1], self.hf_clk_out[2]) + self.params["i_HFOUTEN"] = 0b1 + self.params["p_HF_CLK_DIV"] = divisor + self.params["o_HFCLKOUT"] = self.hf_clk_out[0] + self.params["p_HF_OSC_EN"] = "ENABLED" + + if self.hfsdc_clk_out: + divisor = self.compute_divisor(self.hfsdc_clk_out[1], self.hfsdc_clk_out[2]) + self.params["i_HFSDSCEN"] = 0b1 + self.params["p_HF_SED_SEC_DIV"] = divisor + self.params["o_HFSDCOUT"] = self.hfsdc_clk_out[0] + + if self.lf_clk_out is not None: + self.params["o_LFCLKOUT"] = self.lf_clk_out[0] + self.params["p_LF_OUTPUT_EN"] = "ENABLED" + + self.specials += Instance("OSCA", **self.params) + +# Lattice / NX PLL --------------------------------------------------------------------------------- + class NXPLL(Module): nclkouts_max = 5 clki_div_range = ( 1, 128+1) @@ -187,7 +271,7 @@ class NXPLL(Module): # Later revs of the Lattice calculator BW_FACTOR is set to 10, may need to change it def calc_optimal_params(self, fref, fbkdiv, M = 1, BW_FACTOR = 5): - print("Calculating Analog Paramters for a reference freqeuncy of " + str(fref*1e-6) + + print("Calculating Analog Paramters for a reference freqeuncy of " + str(fref*1e-6) + " Mhz, feedback div " + str(fbkdiv) + ", and input div " + str(M) + "." ) @@ -196,7 +280,7 @@ class NXPLL(Module): for params in self.transfer_func_coefficients: closed_loop_peak = self.closed_loop_peak(fbkdiv, params) - if (closed_loop_peak["peak"] < 0.8 or + if (closed_loop_peak["peak"] < 0.8 or closed_loop_peak["peak"] > 1.35): continue @@ -360,7 +444,7 @@ class NXPLL(Module): if this_result > peak_value: peak_value = this_result peak_f = f - + if this_result < last_value and step_divs < 5: f = f/(step**2) step = (step - 1) * .5 + 1 diff --git a/litex/soc/cores/clock/xilinx_common.py b/litex/soc/cores/clock/xilinx_common.py new file mode 100644 index 000000000..4f7a62221 --- /dev/null +++ b/litex/soc/cores/clock/xilinx_common.py @@ -0,0 +1,152 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex.build.io import DifferentialInput + +from litex.soc.interconnect.csr import * + +from litex.soc.cores.clock.common import * + +# Xilinx / Generic --------------------------------------------------------------------------------- + +class XilinxClocking(Module, AutoCSR): + clkfbout_mult_frange = (2, 64+1) + clkout_divide_range = (1, 128+1) + + def __init__(self, vco_margin=0): + self.vco_margin = vco_margin + 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, ClockSignal)): + 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 + register_clkin_log(self.logger, clkin, freq) + + def create_clkout(self, cd, freq, phase=0, buf="bufg", margin=1e-2, with_reset=True, ce=None): + assert self.nclkouts < self.nclkouts_max + clkout = Signal() + self.clkouts[self.nclkouts] = (clkout, freq, phase, margin) + if with_reset: + self.specials += AsyncResetSynchronizer(cd, ~self.locked) + 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) + elif buf == "bufgce": + if ce is None: + raise ValueError("BUFGCE requires user to provide a clock enable ce Signal") + self.specials += Instance("BUFGCE", i_I=clkout, o_O=clkout_buf, i_CE=ce) + elif buf == "bufio": + self.specials += Instance("BUFIO", i_I=clkout, o_O=clkout_buf) + else: + raise ValueError("Unsupported clock buffer: {}".format(buf)) + create_clkout_log(self.logger, cd.name, freq, margin, self.nclkouts) + self.nclkouts += 1 + + def compute_config(self): + config = {} + for divclk_divide in range(*self.divclk_divide_range): + config["divclk_divide"] = divclk_divide + for clkfbout_mult in reversed(range(*self.clkfbout_mult_frange)): + all_valid = True + vco_freq = self.clkin_freq*clkfbout_mult/divclk_divide + (vco_freq_min, vco_freq_max) = self.vco_freq_range + if (vco_freq >= vco_freq_min*(1 + self.vco_margin) and + vco_freq <= vco_freq_max*(1 - self.vco_margin)): + for n, (clk, f, p, m) in sorted(self.clkouts.items()): + valid = False + d_ranges = [self.clkout_divide_range] + if getattr(self, "clkout{}_divide_range".format(n), None) is not None: + d_ranges += [getattr(self, "clkout{}_divide_range".format(n))] + for d_range in d_ranges: + for d in clkdiv_range(*d_range): + clk_freq = vco_freq/d + if abs(clk_freq - f) <= f*m: + config["clkout{}_freq".format(n)] = clk_freq + config["clkout{}_divide".format(n)] = d + config["clkout{}_phase".format(n)] = p + valid = True + break + if valid: + break + if not valid: + all_valid = False + else: + all_valid = False + if all_valid: + config["vco"] = vco_freq + config["clkfbout_mult"] = clkfbout_mult + compute_config_log(self.logger, config) + return config + raise ValueError("No PLL config found") + + def expose_drp(self): + self.drp_reset = CSR() + self.drp_locked = CSRStatus() + self.drp_read = CSR() + self.drp_write = CSR() + self.drp_drdy = CSRStatus() + self.drp_adr = CSRStorage(7, reset_less=True) + self.drp_dat_w = CSRStorage(16, reset_less=True) + self.drp_dat_r = CSRStatus(16) + + # # # + + den_pipe = Signal() + dwe_pipe = Signal() + + drp_drdy = Signal() + self.params.update( + i_DCLK = ClockSignal(), + i_DWE = dwe_pipe, + i_DEN = den_pipe, + 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 += [ + den_pipe.eq(self.drp_read.re | self.drp_write.re), + dwe_pipe.eq(self.drp_write.re), + If(self.drp_read.re | self.drp_write.re, + self.drp_drdy.status.eq(0) + ).Elif(drp_drdy, + self.drp_drdy.status.eq(1) + ) + ] + self.comb += self.drp_locked.status.eq(self.locked) + self.logger.info("Exposing DRP interface.") + + def add_reset_delay(self, cycles): + for i in range(cycles): + reset = Signal() + self.specials += Instance("FD", i_C=self.clkin, i_D=self.reset, o_Q=reset) + self.reset = reset + + def do_finalize(self): + assert hasattr(self, "clkin") + self.add_reset_delay(cycles=8) # Prevents interlock when reset driven from sys_clk. diff --git a/litex/soc/cores/clock/xilinx_s6.py b/litex/soc/cores/clock/xilinx_s6.py new file mode 100644 index 000000000..396546cb4 --- /dev/null +++ b/litex/soc/cores/clock/xilinx_s6.py @@ -0,0 +1,147 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2019 Michael Betz +# Copyright (c) 2019-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * + +from litex.soc.cores.clock.common import * +from litex.soc.cores.clock.xilinx_common import * + +# Xilinx / Spartan6 -------------------------------------------------------------------------------- + +class S6PLL(XilinxClocking): + nclkouts_max = 6 + clkin_freq_range = (19e6, 540e6) + + def __init__(self, speedgrade=-1): + self.logger = logging.getLogger("S6PLL") + self.logger.info("Creating S6PLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + XilinxClocking.__init__(self) + self.divclk_divide_range = (1, 52 + 1) + self.vco_freq_range = { + -1: (400e6, 1000e6), + -2: (400e6, 1000e6), + -3: (400e6, 1080e6), + }[speedgrade] + + def do_finalize(self): + XilinxClocking.do_finalize(self) + config = self.compute_config() + pll_fb = Signal() + self.params.update( + p_SIM_DEVICE = "SPARTAN6", + p_BANDWIDTH = "OPTIMIZED", + p_COMPENSATION = "INTERNAL", + p_REF_JITTER = .01, p_CLK_FEEDBACK="CLKFBOUT", + p_CLKIN1_PERIOD = 1e9/self.clkin_freq, + p_CLKIN2_PERIOD = 0., + p_CLKFBOUT_MULT = config["clkfbout_mult"], + p_CLKFBOUT_PHASE = 0., + p_DIVCLK_DIVIDE = config["divclk_divide"], + i_CLKINSEL = 1, + i_RST = self.reset, + i_CLKIN1 = self.clkin, + i_CLKFBIN = pll_fb, + o_CLKFBOUT = pll_fb, + o_LOCKED = self.locked, + ) + 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)] = float(config["clkout{}_phase".format(n)]) + self.params["p_CLKOUT{}_DUTY_CYCLE".format(n)] = 0.5 + self.params["o_CLKOUT{}".format(n)] = clk + self.specials += Instance("PLL_ADV", **self.params) + + +class S6DCM(XilinxClocking): + """ single output with f_out = f_in * {2 .. 256} / {1 .. 256} """ + nclkouts_max = 1 + clkfbout_mult_frange = (2, 256 + 1) + clkout_divide_range = (1, 256 + 1) + + def __init__(self, speedgrade=-1): + self.logger = logging.getLogger("S6DCM") + self.logger.info("Creating S6DCM, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + XilinxClocking.__init__(self) + self.divclk_divide_range = (1, 2) # FIXME + self.clkin_freq_range = { + -1: (0.5e6, 200e6), + -2: (0.5e6, 333e6), + -3: (0.5e6, 375e6), + }[speedgrade] + + self.vco_freq_range = { + -1: (5e6, 1e16), + -2: (5e6, 1e16), + -3: (5e6, 1e16), + }[speedgrade] + + def do_finalize(self): + XilinxClocking.do_finalize(self) + config = self.compute_config() + clk, f, p, m = sorted(self.clkouts.items())[0][1] + self.params.update( + p_CLKFX_MULTIPLY = config["clkfbout_mult"], + p_CLKFX_DIVIDE = config["clkout0_divide"] * config["divclk_divide"], + p_SPREAD_SPECTRUM = "NONE", + p_CLKIN_PERIOD = 1e9/self.clkin_freq, + i_CLKIN = self.clkin, + i_RST = self.reset, + i_FREEZEDCM = 0, + o_CLKFX = clk, + o_LOCKED = self.locked, + ) + self.specials += Instance("DCM_CLKGEN", **self.params) + + def expose_drp(self): + self._cmd_data = CSRStorage(10) + self._send_cmd_data = CSR() + self._send_go = CSR() + self._status = CSRStatus(4) + + progdata = Signal() + progen = Signal() + progdone = Signal() + locked = Signal() + + self.params.update( + i_PROGCLK = ClockSignal(), + i_PROGDATA = progdata, + i_PROGEN = progen, + o_PROGDONE = progdone + ) + + remaining_bits = Signal(max=11) + transmitting = Signal() + self.comb += transmitting.eq(remaining_bits != 0) + sr = Signal(10) + self.sync += [ + If(self._send_cmd_data.re, + remaining_bits.eq(10), + sr.eq(self._cmd_data.storage) + ).Elif(transmitting, + remaining_bits.eq(remaining_bits - 1), + sr.eq(sr[1:]) + ) + ] + self.comb += [ + progdata.eq(transmitting & sr[0]), + progen.eq(transmitting | self._send_go.re) + ] + + # Enforce gap between commands + busy_counter = Signal(max=14) + busy = Signal() + self.comb += busy.eq(busy_counter != 0) + self.sync += If(self._send_cmd_data.re, + busy_counter.eq(13) + ).Elif(busy, + busy_counter.eq(busy_counter - 1) + ) + + self.comb += self._status.status.eq(Cat(busy, progdone, self.locked)) + + self.logger.info("Exposing DRP interface.") diff --git a/litex/soc/cores/clock/xilinx_s7.py b/litex/soc/cores/clock/xilinx_s7.py new file mode 100644 index 000000000..28263c85f --- /dev/null +++ b/litex/soc/cores/clock/xilinx_s7.py @@ -0,0 +1,102 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * + +from litex.soc.cores.clock.common import * +from litex.soc.cores.clock.xilinx_common import * + +# Xilinx / 7-Series -------------------------------------------------------------------------------- + +class S7PLL(XilinxClocking): + nclkouts_max = 6 + clkin_freq_range = (19e6, 800e6) + + def __init__(self, speedgrade=-1): + self.logger = logging.getLogger("S7PLL") + self.logger.info("Creating S7PLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + XilinxClocking.__init__(self) + self.divclk_divide_range = (1, 56+1) + self.vco_freq_range = { + -1: (800e6, 1600e6), + -2: (800e6, 1866e6), + -3: (800e6, 2133e6), + }[speedgrade] + + def do_finalize(self): + XilinxClocking.do_finalize(self) + config = self.compute_config() + pll_fb = Signal() + self.params.update( + p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked, i_RST=self.reset, + + # VCO + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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 S7MMCM(XilinxClocking): + nclkouts_max = 7 + clkout0_divide_range = (1, (128 + 1/8), 1/8) # Fractional Divide available on CLKOUT0 + + def __init__(self, speedgrade=-1): + self.logger = logging.getLogger("S7MMCM") + self.logger.info("Creating S7MMCM, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + XilinxClocking.__init__(self) + self.divclk_divide_range = (1, 106+1) + 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): + XilinxClocking.do_finalize(self) + config = self.compute_config() + mmcm_fb = Signal() + self.params.update( + p_BANDWIDTH="OPTIMIZED", o_LOCKED=self.locked, i_RST=self.reset, + + # VCO + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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) + + +class S7IDELAYCTRL(Module): + def __init__(self, cd, reset_cycles=16): + reset_counter = Signal(log2_int(reset_cycles), reset=reset_cycles - 1) + 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) diff --git a/litex/soc/cores/clock/xilinx_us.py b/litex/soc/cores/clock/xilinx_us.py new file mode 100644 index 000000000..2d4a93936 --- /dev/null +++ b/litex/soc/cores/clock/xilinx_us.py @@ -0,0 +1,214 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from litex.soc.cores.clock.common import * +from litex.soc.cores.clock.xilinx_common import * + +# Xilinx / Ultrascale ------------------------------------------------------------------------------ + +# TODO: +# - use Ultrascale primitives instead of 7-Series' ones. (Vivado recognize and convert them). + +class USPLL(XilinxClocking): + nclkouts_max = 6 + + def __init__(self, speedgrade=-1): + self.logger = logging.getLogger("USPLL") + self.logger.info("Creating USPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + XilinxClocking.__init__(self) + self.divclk_divide_range = (1, 56+1) + 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): + XilinxClocking.do_finalize(self) + config = self.compute_config() + pll_fb = Signal() + self.params.update( + p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked, i_RST=self.reset, + + # VCO + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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(XilinxClocking): + nclkouts_max = 7 + + def __init__(self, speedgrade=-1): + self.logger = logging.getLogger("USMMCM") + self.logger.info("Creating USMMCM, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + XilinxClocking.__init__(self) + self.divclk_divide_range = (1, 106+1) + 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): + XilinxClocking.do_finalize(self) + config = self.compute_config() + mmcm_fb = Signal() + self.params.update( + p_BANDWIDTH="OPTIMIZED", o_LOCKED=self.locked, i_RST=self.reset, + + # VCO + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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) + + +class USIDELAYCTRL(Module): + def __init__(self, cd_ref, cd_sys, reset_cycles=64, ready_cycles=64): + cd_sys.rst.reset = 1 + self.clock_domains.cd_ic = ClockDomain() + ic_reset_counter = Signal(max=reset_cycles, reset=reset_cycles-1) + ic_reset = Signal(reset=1) + cd_ref_sync = getattr(self.sync, cd_ref.name) + cd_ref_sync += [ + If(ic_reset_counter != 0, + ic_reset_counter.eq(ic_reset_counter - 1) + ).Else( + ic_reset.eq(0) + ) + ] + ic_ready_counter = Signal(max=ready_cycles, reset=ready_cycles-1) + ic_ready = Signal() + self.comb += self.cd_ic.clk.eq(cd_sys.clk) + self.sync.ic += [ + If(ic_ready, + If(ic_ready_counter != 0, + ic_ready_counter.eq(ic_ready_counter - 1) + ).Else( + cd_sys.rst.eq(0) + ) + ) + ] + self.specials += [ + Instance("IDELAYCTRL", + p_SIM_DEVICE = "ULTRASCALE", + i_REFCLK = cd_ref.clk, + i_RST = ic_reset, + o_RDY = ic_ready), + AsyncResetSynchronizer(self.cd_ic, ic_reset) + ] + + +# Xilinx / Ultrascale Plus ------------------------------------------------------------------------- + +# TODO: +# - use Ultrascale Plus primitives instead of 7-Series' ones. (Vivado recognize and convert them). + +class USPPLL(XilinxClocking): + nclkouts_max = 6 + + def __init__(self, speedgrade=-1): + self.logger = logging.getLogger("USPPLL") + self.logger.info("Creating USPPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + XilinxClocking.__init__(self) + self.divclk_divide_range = (1, 56+1) + self.clkin_freq_range = { + -1: (70e6, 800e6), + -2: (70e6, 933e6), + -3: (70e6, 1066e6), + }[speedgrade] + self.vco_freq_range = { + -1: (750e6, 1500e6), + -2: (750e6, 1500e6), + -3: (750e6, 1500e6), + }[speedgrade] + + def do_finalize(self): + XilinxClocking.do_finalize(self) + config = self.compute_config() + pll_fb = Signal() + self.params.update( + p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked, i_RST=self.reset, + + # VCO + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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 USPMMCM(XilinxClocking): + nclkouts_max = 7 + + def __init__(self, speedgrade=-1): + self.logger = logging.getLogger("USPMMCM") + self.logger.info("Creating USPMMCM, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + XilinxClocking.__init__(self) + self.divclk_divide_range = (1, 106+1) + self.clkin_freq_range = { + -1: (10e6, 800e6), + -2: (10e6, 933e6), + -3: (10e6, 1066e6), + }[speedgrade] + self.vco_freq_range = { + -1: (800e6, 1600e6), + -2: (800e6, 1600e6), + -3: (800e6, 1600e6), + }[speedgrade] + + def do_finalize(self): + XilinxClocking.do_finalize(self) + config = self.compute_config() + mmcm_fb = Signal() + self.params.update( + p_BANDWIDTH="OPTIMIZED", o_LOCKED=self.locked, i_RST=self.reset, + + # VCO + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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) + + +class USPIDELAYCTRL(USIDELAYCTRL): pass diff --git a/litex/soc/cores/clock/xilinx_usp.py b/litex/soc/cores/clock/xilinx_usp.py new file mode 100644 index 000000000..de4afb49b --- /dev/null +++ b/litex/soc/cores/clock/xilinx_usp.py @@ -0,0 +1,127 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2018-2020 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from litex.soc.cores.clock.common import * +from litex.soc.cores.clock.xilinx_common import * + +# Xilinx / Ultrascale Plus ------------------------------------------------------------------------- + +# TODO: +# - use Ultrascale Plus primitives instead of 7-Series' ones. (Vivado recognize and convert them). + +class USPPLL(XilinxClocking): + nclkouts_max = 6 + + def __init__(self, speedgrade=-1): + self.logger = logging.getLogger("USPPLL") + self.logger.info("Creating USPPLL, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + XilinxClocking.__init__(self) + self.divclk_divide_range = (1, 56+1) + self.clkin_freq_range = { + -1: (70e6, 800e6), + -2: (70e6, 933e6), + -3: (70e6, 1066e6), + }[speedgrade] + self.vco_freq_range = { + -1: (750e6, 1500e6), + -2: (750e6, 1500e6), + -3: (750e6, 1500e6), + }[speedgrade] + + def do_finalize(self): + XilinxClocking.do_finalize(self) + config = self.compute_config() + pll_fb = Signal() + self.params.update( + p_STARTUP_WAIT="FALSE", o_LOCKED=self.locked, i_RST=self.reset, + + # VCO + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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 USPMMCM(XilinxClocking): + nclkouts_max = 7 + + def __init__(self, speedgrade=-1): + self.logger = logging.getLogger("USPMMCM") + self.logger.info("Creating USPMMCM, {}.".format(colorer("speedgrade {}".format(speedgrade)))) + XilinxClocking.__init__(self) + self.divclk_divide_range = (1, 106+1) + self.clkin_freq_range = { + -1: (10e6, 800e6), + -2: (10e6, 933e6), + -3: (10e6, 1066e6), + }[speedgrade] + self.vco_freq_range = { + -1: (800e6, 1600e6), + -2: (800e6, 1600e6), + -3: (800e6, 1600e6), + }[speedgrade] + + def do_finalize(self): + XilinxClocking.do_finalize(self) + config = self.compute_config() + mmcm_fb = Signal() + self.params.update( + p_BANDWIDTH="OPTIMIZED", o_LOCKED=self.locked, i_RST=self.reset, + + # VCO + p_REF_JITTER1=0.01, p_CLKIN1_PERIOD=1e9/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) + + +class USPIDELAYCTRL(Module): + def __init__(self, cd_ref, cd_sys, reset_cycles=64, ready_cycles=64): + cd_sys.rst.reset = 1 + self.clock_domains.cd_ic = ClockDomain() + ic_reset_counter = Signal(max=reset_cycles, reset=reset_cycles-1) + ic_reset = Signal(reset=1) + cd_ref_sync = getattr(self.sync, cd_ref.name) + cd_ref_sync += [ + If(ic_reset_counter != 0, + ic_reset_counter.eq(ic_reset_counter - 1) + ).Else( + ic_reset.eq(0) + ) + ] + ic_ready_counter = Signal(max=ready_cycles, reset=ready_cycles-1) + ic_ready = Signal() + self.comb += self.cd_ic.clk.eq(cd_sys.clk) + self.sync.ic += [ + If(ic_ready, + If(ic_ready_counter != 0, + ic_ready_counter.eq(ic_ready_counter - 1) + ).Else( + cd_sys.rst.eq(0) + ) + ) + ] + self.specials += [ + Instance("IDELAYCTRL", + p_SIM_DEVICE = "ULTRASCALE", + i_REFCLK = cd_ref.clk, + i_RST = ic_reset, + o_RDY = ic_ready), + AsyncResetSynchronizer(self.cd_ic, ic_reset) + ]