From 515c06219acbfa46582c69de27acce5044710c63 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Sat, 24 Nov 2018 00:47:38 +0100 Subject: [PATCH] cores/clock: add ECP5PLL --- litex/soc/cores/clock.py | 120 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/litex/soc/cores/clock.py b/litex/soc/cores/clock.py index a4cbb6336..fc14fc075 100644 --- a/litex/soc/cores/clock.py +++ b/litex/soc/cores/clock.py @@ -15,6 +15,7 @@ from litex.soc.interconnect.csr import * def period_ns(freq): return 1e9/freq +# Xilinx class S7Clocking(Module, AutoCSR): clkfbout_mult_frange = (2, 64+1) @@ -203,3 +204,122 @@ class S7IDELAYCTRL(Module): ic_reset.eq(0) ) self.specials += Instance("IDELAYCTRL", i_REFCLK=cd.clk, i_RST=ic_reset) + +# Lattice + +# TODO: +# - test on hardware +# - add phase shift support + +class ECP5PLL(Module): + nclkouts_max = 4 + 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.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): + self.comb += self.clkin.eq(clkin) + else: + raise ValueError + self.clkin_freq = freq + + def create_clkout(self, cd, freq, phase=0, margin=1e-2): + (clko_freq_min, clko_freq_max) = self.clko_freq_range + assert freq >= clko_freq_min + assert freq <= clko_freq_max + assert phase == 0 + assert self.nclkouts < self.nclkouts_max + clkout = Signal() + self.clkouts[self.nclkouts] = (clkout, freq, phase, margin) + self.nclkouts += 1 + self.comb += cd.clk.eq(clkout) + + def compute_config(self): + config = {} + config["clki_div"] = 1 + for clkfb_div in range(*self.clkfb_div_range): + all_valid = True + vco_freq = self.clkin_freq*clkfb_div + (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{}_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 + return config + raise ValueError("No PLL config found") + + def do_finalize(self): + config = self.compute_config() + clkfb = Signal() + self.params.update( + attr=[ + ("ICP_CURRENT", "6"), + ("LPF_RESISTOR", "16"), + ("MFG_ENABLE_FILTEROPAMP", "1"), + ("MFG_GMCREF_SEL", "2")], + p_PLL_LOCK_MODE=0, + + p_FEEDBK_PATH="CLKOP", + p_OUTDIVIDER_MUXB="DIVB", + p_CLKOP_ENABLE="ENABLED", + p_CLKOS_ENABLE="ENABLED", + p_CLKOS2_ENABLE="ENABLED", + p_CLKOS3_ENABLE="ENABLED", + + p_CLKFB_DIV=config["clkfb_div"], + p_CLKI_DIV=1, + + i_CLKI=self.clkin, + i_CLKFB=clkfb, + o_LOCK=self.locked, + + i_STDBY=0, + i_PLLWAKESYNC=0, + i_RST=self.reset, + + i_PHASESEL1=0, + i_PHASESEL0=0, + i_PHASEDIR=0, + i_PHASESTEP=0, + i_PHASELOADREG=0, + ) + for n, (clk, f, p, m) in sorted(self.clkouts.items()): + if n == 0: + self.comb += clkfb.eq(clk) + n_to_l = {0: "P", 1: "S", 2: "S2", 3: "S3"} + self.params["i_ENCLKO{}".format(n_to_l[n])] = 0 + self.params["p_CLKO{}_DIV".format(n)] = config["clko{}_div".format(n)] + self.params["p_CLKO{}_FPHASE".format(n_to_l[n])] = 0 + self.params["p_CLK0{}_CPHASE".format(n_to_l[n])] = 0 + self.params["o_CLKO{}".format(n_to_l[n])] = clk + self.specials += Instance("EHXPLLL", **self.params)