From 4fe085cc1c9f97a02934f5ab9c6bc8e075049dc4 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Mon, 20 Sep 2021 08:39:25 +0200 Subject: [PATCH] cores/clock: Add initial GW1NSR's PLL support. --- litex/soc/cores/clock/gowin_gw1nsr.py | 131 ++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 litex/soc/cores/clock/gowin_gw1nsr.py diff --git a/litex/soc/cores/clock/gowin_gw1nsr.py b/litex/soc/cores/clock/gowin_gw1nsr.py new file mode 100644 index 000000000..948604d94 --- /dev/null +++ b/litex/soc/cores/clock/gowin_gw1nsr.py @@ -0,0 +1,131 @@ +# +# This file is part of LiteX. +# +# Copyright (c) 2021 Florent Kermarrec +# SPDX-License-Identifier: BSD-2-Clause + +from migen import * +from migen.genlib.resetsync import AsyncResetSynchronizer + +from litex.soc.cores.clock.common import * + +class Open(Signal): pass + +# GoWin / GW1NSRPLL -------------------------------------------------------------------------------- + +class GW1NSRPLL(Module): + nclkouts_max = 1 + pfd_freq_range = ( 3e6, 500e6) + vco_freq_range = (400e6, 1000e6) + def __init__(self, device, vco_margin=0): + self.logger = logging.getLogger("GW1NPLL") + self.logger.info("Creating GW1NPLL.".format()) + self.device = device + 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) + 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: + # FIXME: Should use PLL's lock but does not seem stable. + self.specials += AsyncResetSynchronizer(cd, self.reset) + 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 idiv in range(1, 64): + config["idiv"] = idiv + pfd_freq = self.clkin_freq/idiv + pfd_freq_min, pfd_freq_max = self.pfd_freq_range + if (pfd_freq < pfd_freq_min) or (pfd_freq > pfd_freq_max): + continue + for fdiv in range(1, 64): + out_freq = self.clkin_freq*fdiv/idiv + for odiv in [2, 4, 8, 16, 32, 48, 64, 80, 96, 112, 128]: + config["odiv"] = odiv + vco_freq = out_freq*odiv + (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()): + if abs(out_freq - f) <= f*_m: + config["clk{}_freq".format(_n)] = out_freq + config["vco"] = vco_freq + config["fdiv"] = fdiv + compute_config_log(self.logger, config) + return config + raise ValueError("No PLL config found") + + def do_finalize(self): + assert hasattr(self, "clkin") + assert len(self.clkouts) == 1 + config = self.compute_config() + # Based on UG286-1.3E Note. + self.params.update( + # Parameters. + p_DEVICE = self.device, # FPGA Device. + p_FCLKIN = str(self.clkin_freq/1e6), # Clk Input frequency (MHz). + p_DYN_IDIV_SEL = "false", # Disable dynamic IDIV. + p_IDIV_SEL = config["idiv"]-1, # Static IDIV value (1-64). + p_DYN_FBDIV_SEL = "false", # Disable dynamic FBDIV. + p_FBDIV_SEL = config["fdiv"]-1, # Static FBDIV value (1-64). + p_DYN_ODIV_SEL = "false", # Disable dynamic ODIV. + p_ODIV_SEL = config["odiv"], # Static ODIV value. + p_PSDA_SEL = "0000", # - + p_DYN_DA_EN = "false", # - + p_DUTYDA_SEL = "1000", # - + p_CLKOUT_FT_DIR = 1, # - + p_CLKOUTP_FT_DIR = 1, # - + p_CLKOUT_DLY_STEP = 0, # - + p_CLKOUTP_DLY_STEP = 0, # - + p_CLKFB_SEL = "internal", # Clk Feedback type (internal, external). + p_CLKOUT_BYPASS = "false", # Clk Input to CLKOUT bypass. + p_CLKOUTP_BYPASS = "false", # Clk Input to CLKOUTP bypass. + p_CLKOUTD_BYPASS = "false", # Clk Input to CLKOUTD bypass. + p_DYN_SDIV_SEL = 2, # Disable dynamic SDIV. + p_CLKOUTD_SRC = "CLKOUT", # Recopy CLKOUT to CLKOUTD. + p_CLKOUTD3_SRC = "CLKOUT", # Recopy CLKOUT to CLKOUTD3. + + # Inputs. + i_CLKIN = self.clkin, # Clk Input. + i_CLKFB = 0, # Clk Feedback. + i_RESET = self.reset, # PLL Reset. + i_RESET_P = 0, # PLL Power Down. + i_ODSEL = 0, # Dynamic ODIV control. + i_FBDSEL = 0, # Dynamic IDIV control. + i_IDSEL = 0, # Dynamic FDIV control. + i_PSDA = 0, # Dynamic phase control. + i_DUTYDA = 0, # Dynamic duty cycle control. + i_FDLY = 0, # Dynamic CLKOUTP delay control. + i_VREN = 1, + ) + clk0, f0, p0, m0 = self.clkouts[0] + self.params.update( + # Outputs. + o_LOCK = self.locked, # PLL lock status. + o_CLKOUT = clk0, # Clock output. + o_CLKOUTP = Open(), # Clock output (With phase and duty cycle adjustement). + o_CLKOUTD = Open(), # Clock divided from CLKOUT and CLKOUTP (controlled by SDIV). + o_CLKOUTD3 = Open(), # Clock divided from CLKOUT and CLKOUTP (constant division of 3). + ) + self.specials += Instance("PLLVR", **self.params)