From f743dd38739799c06d1bac4289016827cc953e7d Mon Sep 17 00:00:00 2001 From: Ilia Sergachev Date: Sun, 28 Nov 2021 11:59:36 +0100 Subject: [PATCH] cores/clock/gowin: merge GW1NS PLL support into GW1N; add more details to PLL frequency ranges --- litex/soc/cores/clock/gowin_gw1n.py | 41 ++++++-- litex/soc/cores/clock/gowin_gw1nsr.py | 131 -------------------------- 2 files changed, 33 insertions(+), 139 deletions(-) delete mode 100644 litex/soc/cores/clock/gowin_gw1nsr.py diff --git a/litex/soc/cores/clock/gowin_gw1n.py b/litex/soc/cores/clock/gowin_gw1n.py index 2657aef71..d3456da00 100644 --- a/litex/soc/cores/clock/gowin_gw1n.py +++ b/litex/soc/cores/clock/gowin_gw1n.py @@ -48,13 +48,13 @@ class GW1NOSC(Module): # GoWin / GW1NPLL ---------------------------------------------------------------------------------- class GW1NPLL(Module): - nclkouts_max = 1 - pfd_freq_range = ( 3e6, 400e6) - vco_freq_range = (400e6, 1000e6) - def __init__(self, device, vco_margin=0): + nclkouts_max = 1 + + def __init__(self, devicename, device, vco_margin=0): self.logger = logging.getLogger("GW1NPLL") self.logger.info("Creating GW1NPLL.".format()) self.device = device + self.devicename = devicename self.vco_margin = vco_margin self.reset = Signal() self.locked = Signal() @@ -64,6 +64,24 @@ class GW1NPLL(Module): self.clkouts = {} self.config = {} self.params = {} + if device.startswith('GW1NS'): + if 'C7/I6' in device or 'C6/I5' in device: + self.vco_freq_range = (600e6, 1200e6) # datasheet says (400, 1200) but compiler enforces (600, 1200) + self.pfd_freq_range = (3e6, 400e6) + elif 'C5/I4' in device: + self.vco_freq_range = (320e6, 960e6) # datasheet values, not tested + self.pfd_freq_range = (3e6, 320e6) + elif device.startswith('GW1N-1S'): + self.vco_freq_range = (400e6, 1200e6) + self.pfd_freq_range = (3e6, 400e6) # not verified: not found in the datasheet + elif device.startswith('GW1N-'): + self.vco_freq_range = (400e6, 900e6) + self.pfd_freq_range = (3e6, 400e6) # not verified: not found in the datasheet + + try: + self.vco_freq_range + except NameError: + self.logger.error('Unknown device name') def register_clkin(self, clkin, freq): self.clkin = Signal() @@ -117,7 +135,7 @@ class GW1NPLL(Module): # Based on UG286-1.3E Note. self.params.update( # Parameters. - p_DEVICE = self.device, # FPGA Device. + p_DEVICE = self.devicename, # 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). @@ -145,8 +163,6 @@ class GW1NPLL(Module): i_CLKFB = 0, # Clk Feedback. i_RESET = self.reset, # PLL Reset. i_RESET_P = 0, # PLL Power Down. - i_RESET_I = 0, # IDIV reset. - i_RESET_S = 0, # SDIV and DIV3 reset. i_ODSEL = 0, # Dynamic ODIV control. i_FBDSEL = 0, # Dynamic IDIV control. i_IDSEL = 0, # Dynamic FDIV control. @@ -154,6 +170,15 @@ class GW1NPLL(Module): i_DUTYDA = 0, # Dynamic duty cycle control. i_FDLY = 0, # Dynamic CLKOUTP delay control. ) + if self.device.startswith('GW1NS'): + instance_name = 'PLLVR' + self.params.update(i_VREN=1) + else: + instance_name = 'PLL' + self.params.update( + i_RESET_I = 0, # IDIV reset. + i_RESET_S = 0, # SDIV and DIV3 reset. + ) clk0, f0, p0, m0 = self.clkouts[0] self.params.update( # Outputs. @@ -163,4 +188,4 @@ class GW1NPLL(Module): 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("PLL", **self.params) + self.specials += Instance(instance_name, **self.params) diff --git a/litex/soc/cores/clock/gowin_gw1nsr.py b/litex/soc/cores/clock/gowin_gw1nsr.py deleted file mode 100644 index 948604d94..000000000 --- a/litex/soc/cores/clock/gowin_gw1nsr.py +++ /dev/null @@ -1,131 +0,0 @@ -# -# 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)