From 0f17547c5b9ecc70acc3d4382f3e824cae060d57 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Tue, 7 Apr 2020 16:59:53 +0200 Subject: [PATCH] soc/cores/clock: add initial AlteraClocking/CycloneIV support. --- litex/soc/cores/clock.py | 123 +++++++++++++++++++++++++++++++++++++++ test/test_clock.py | 8 +++ 2 files changed, 131 insertions(+) diff --git a/litex/soc/cores/clock.py b/litex/soc/cores/clock.py index d264e201e..ed4161cf0 100644 --- a/litex/soc/cores/clock.py +++ b/litex/soc/cores/clock.py @@ -698,3 +698,126 @@ class ECP5PLL(Module): 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) + +# Altera / Generic --------------------------------------------------------------------------------- + +class AlteraClocking(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.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 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) + +# Altera / CycloneIV ------------------------------------------------------------------------------- + +class CycloneIVPLL(AlteraClocking): + 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)))) + AlteraClocking.__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" : (0, 472.5e6), + "-7" : (0, 450e6), + "-8" : (0, 402.5e6), + "-8L": (0, 362e6), + "-9L": (0, 265e6), + }[speedgrade] diff --git a/test/test_clock.py b/test/test_clock.py index 630d36089..9762da7c8 100644 --- a/test/test_clock.py +++ b/test/test_clock.py @@ -67,3 +67,11 @@ class TestClock(unittest.TestCase): for i in range(pll.nclkouts_max): pll.create_clkout(ClockDomain("clkout{}".format(i)), 200e6) pll.compute_config() + + # Altera / CycloneIV + def test_cycloneivpll(self): + pll = CycloneIVPLL() + pll.register_clkin(Signal(), 50e6) + for i in range(pll.nclkouts_max): + pll.create_clkout(ClockDomain("clkout{}".format(i)), 100e6) + pll.compute_config()