cores/clock: add initial iCE40 support
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 23 Jul 2019 07:27:20 +0000 (09:27 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 23 Jul 2019 07:27:20 +0000 (09:27 +0200)
litex/soc/cores/clock.py

index d5a8f4929e856b1fa864187a8d122d768c9d59eb..2bafb980238bf5bc7b5d992f1fb4c7e8b5a164f0 100644 (file)
@@ -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: