From 8954041a9314c53c5f2b395aef00bd367384a2af Mon Sep 17 00:00:00 2001 From: George Hilliard Date: Thu, 8 Jul 2021 08:11:47 -0500 Subject: [PATCH] clock/lattice_ecp5/ECP5PLL: Only consider non-dpa clocks as feedback Dynamically adjusting the phase of a feedback will cause it to unlock. The phase adjust ports are shared by all the outputs, so there is no technical way to prevent this. Allow the user to indicate that they will not adjust a clock when requesting an output by setting uses_dpa=False, and only consider those that the user has promised not to use. --- litex/soc/cores/clock/lattice_ecp5.py | 15 ++++++++++----- test/test_clock.py | 3 ++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/litex/soc/cores/clock/lattice_ecp5.py b/litex/soc/cores/clock/lattice_ecp5.py index 12692d519..05fe6f694 100644 --- a/litex/soc/cores/clock/lattice_ecp5.py +++ b/litex/soc/cores/clock/lattice_ecp5.py @@ -30,6 +30,7 @@ class ECP5PLL(Module): self.stdby = Signal() self.clkin_freq = None self.vcxo_freq = None + self.dpa_en = False self.nclkouts = 0 self.clkouts = {} self.config = {} @@ -47,13 +48,13 @@ class ECP5PLL(Module): 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): + def create_clkout(self, cd, freq, phase=0, margin=1e-2, with_reset=True, uses_dpa=True): (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, phase, margin) + self.clkouts[self.nclkouts] = (clkout, freq, phase, margin, uses_dpa) if with_reset: self.specials += AsyncResetSynchronizer(cd, ~self.locked) self.comb += cd.clk.eq(clkout) @@ -80,7 +81,10 @@ class ECP5PLL(Module): for clkfb_div in range(*self.clkfb_div_range): # pick a suitable feedback clock found_fb = None - for n, (clk, f, p, m) in sorted(self.clkouts.items()): + for n, (clk, f, p, m, dpa) in sorted(self.clkouts.items()): + if dpa and self.dpa_en: + # cannot use clocks whose phase the user will change + continue for d in range(*self.clko_div_range): vco_freq = self.clkin_freq/clki_div*clkfb_div*d clk_freq = vco_freq/d @@ -107,7 +111,7 @@ class ECP5PLL(Module): # vco_freq is known, compute remaining clocks' output settings all_valid = True - for n, (clk, f, p, m) in sorted(self.clkouts.items()): + for n, (clk, f, p, m, dpa) in sorted(self.clkouts.items()): if n == found_fb: continue # already picked this one for d in range(*self.clko_div_range): @@ -128,6 +132,7 @@ class ECP5PLL(Module): raise ValueError("No PLL config found") def expose_dpa(self): + self.dpa_en = True self.phase_sel = Signal(2) self.phase_dir = Signal() self.phase_step = Signal() @@ -164,7 +169,7 @@ class ECP5PLL(Module): p_CLKI_DIV = config["clki_div"] ) self.comb += self.locked.eq(locked & ~self.reset) - for n, (clk, f, p, m) in sorted(self.clkouts.items()): + for n, (clk, f, p, m, dpa) in sorted(self.clkouts.items()): div = config["clko{}_div".format(n)] cphase = int(p*(div + 1)/360 + div - 1) self.params["p_CLKO{}_ENABLE".format(n_to_l[n])] = "ENABLED" diff --git a/test/test_clock.py b/test/test_clock.py index f0a3ed1e4..b23726f0c 100644 --- a/test/test_clock.py +++ b/test/test_clock.py @@ -117,7 +117,8 @@ class TestClock(unittest.TestCase): pll = ECP5PLL() pll.register_clkin(Signal(), 100e6) for i in range(pll.nclkouts_max): - pll.create_clkout(ClockDomain("clkout{}".format(i)), 200e6) + pll.create_clkout(ClockDomain("clkout{}".format(i)), 200e6, uses_dpa=(i != 0)) + pll.expose_dpa() pll.compute_config() # Lattice / NX