diff --git a/litex/soc/cores/clock.py b/litex/soc/cores/clock.py index d5a8f4929..2bafb9802 100644 --- a/litex/soc/cores/clock.py +++ b/litex/soc/cores/clock.py @@ -382,6 +382,101 @@ class USIDELAYCTRL(Module): i_REFCLK=cd.clk, i_RST=ic_reset) +# 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): + 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 + + def create_clkout(self, cd, freq, margin=1e-2): + (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) + self.nclkouts += 1 + self.comb += cd.clk.eq(clkout) + + 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["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 + 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, + i_REFERENCECLK=self.clkin, + o_LOCK=self.locked, + ) + 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("SB_PLL40_CORE", **self.params) + # Lattice / ECP5 ----------------------------------------------------------------------------------- # TODO: