add clock selection mechanism
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 27 Sep 2020 08:08:25 +0000 (09:08 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Sun, 27 Sep 2020 08:08:25 +0000 (09:08 +0100)
src/soc/clock/__init__.py [new file with mode: 0644]
src/soc/clock/select.py [new file with mode: 0644]

diff --git a/src/soc/clock/__init__.py b/src/soc/clock/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/soc/clock/select.py b/src/soc/clock/select.py
new file mode 100644 (file)
index 0000000..b08a2ea
--- /dev/null
@@ -0,0 +1,81 @@
+"""Clock selection.
+
+* PLL @ 300mhz input generates a div-6 "test" output
+* clock select sets the source
+  - 0b000 - SYS_CLK (direct)
+  - 0b001 - PLL / 6
+  - 0b010 - PLL / 4
+  - 0b011 - PLL / 3
+  - 0b100 - PLL / 2
+  - 0b101 - PLL
+  - 0b110 - ZERO (direct driving in combination with ONE)
+  - 0b111 - ONE
+* this is all assumed to be driven by the "PLL CLK".
+  the SYS_CLK is the default in case PLL is unstable
+"""
+
+from nmigen import (Module, Array, Signal, Mux, Elaboratable, ClockSignal)
+from nmigen.cli import rtlil
+
+SYS_CLK = 0b000
+PLL6 = 0b001
+PLL4 = 0b010
+PLL3 = 0b011
+PLL2 = 0b100
+PLL  = 0b101
+ZERO = 0b110
+ONE  = 0b111
+
+
+class ClockSelect(Elaboratable):
+    def __init__(self):
+
+        self.sys_clk_i = Signal() # 24 mhz PLL incoming
+        self.pll_48_o = Signal()  # 6-divide (test signal) from PLL
+        self.clk_sel_i = Signal(3) # clock source selection
+        self.core_clk_o = Signal() # main core clock (selectable)
+
+    def elaborate(self, platform):
+        m = Module()
+        comb, sync = m.d.comb, m.d.sync
+
+        # array of clocks (selectable by clk_sel_i)
+        clkgen = Array([Signal(name="clk%d" % i) for i in range(8)])
+        counter3 = Signal(2) # for divide-by-3
+
+        # set up system, zero and one clocks
+        comb += clkgen[SYS_CLK].eq(self.sys_clk_i) # 1st is external 24mhz
+        comb += clkgen[ZERO].eq(0) # LOW (use with ONE for direct driving)
+        comb += clkgen[ONE].eq(1) # HI
+
+        # (always) generate PLL-driven signals: /2/3/4/6
+        sync += clkgen[PLL2].eq(~clkgen[PLL2]) # half PLL rate
+        with m.If(clkgen[PLL2]):
+            sync += clkgen[PLL4].eq(~clkgen[PLL4]) # quarter PLL rate
+        with m.If(counter3 == 2):
+            sync += counter3.eq(0)
+            sync += clkgen[PLL3].eq(~clkgen[PLL3]) # 1/3 PLL rate
+            with m.If(clkgen[PLL3]):
+                sync += clkgen[PLL6].eq(~clkgen[PLL6]) # 1/6 PLL rate
+        with m.Else():
+            sync += counter3.eq(counter3+1)
+
+        # select from available array of clock sources
+        comb += self.core_clk_o.eq(clkgen[self.clk_sel_i])
+
+        # 48mhz output is PLL/6
+        comb += self.pll_48_o.eq(clkgen[PLL6])
+
+        return m
+
+    def ports(self):
+        return [self.sys_clk_i, self.pll_48_o, self.clk_sel_i, self.core_clk_o]
+
+
+if __name__ == '__main__':
+    dut = ClockSelect()
+
+    vl = rtlil.convert(dut, ports=dut.ports())
+    with open("test_clk_sel.il", "w") as f:
+        f.write(vl)
+