add clock selection mechanism
[soc.git] / src / soc / clock / select.py
1 """Clock selection.
2
3 * PLL @ 300mhz input generates a div-6 "test" output
4 * clock select sets the source
5 - 0b000 - SYS_CLK (direct)
6 - 0b001 - PLL / 6
7 - 0b010 - PLL / 4
8 - 0b011 - PLL / 3
9 - 0b100 - PLL / 2
10 - 0b101 - PLL
11 - 0b110 - ZERO (direct driving in combination with ONE)
12 - 0b111 - ONE
13 * this is all assumed to be driven by the "PLL CLK".
14 the SYS_CLK is the default in case PLL is unstable
15 """
16
17 from nmigen import (Module, Array, Signal, Mux, Elaboratable, ClockSignal)
18 from nmigen.cli import rtlil
19
20 SYS_CLK = 0b000
21 PLL6 = 0b001
22 PLL4 = 0b010
23 PLL3 = 0b011
24 PLL2 = 0b100
25 PLL = 0b101
26 ZERO = 0b110
27 ONE = 0b111
28
29
30 class ClockSelect(Elaboratable):
31 def __init__(self):
32
33 self.sys_clk_i = Signal() # 24 mhz PLL incoming
34 self.pll_48_o = Signal() # 6-divide (test signal) from PLL
35 self.clk_sel_i = Signal(3) # clock source selection
36 self.core_clk_o = Signal() # main core clock (selectable)
37
38 def elaborate(self, platform):
39 m = Module()
40 comb, sync = m.d.comb, m.d.sync
41
42 # array of clocks (selectable by clk_sel_i)
43 clkgen = Array([Signal(name="clk%d" % i) for i in range(8)])
44 counter3 = Signal(2) # for divide-by-3
45
46 # set up system, zero and one clocks
47 comb += clkgen[SYS_CLK].eq(self.sys_clk_i) # 1st is external 24mhz
48 comb += clkgen[ZERO].eq(0) # LOW (use with ONE for direct driving)
49 comb += clkgen[ONE].eq(1) # HI
50
51 # (always) generate PLL-driven signals: /2/3/4/6
52 sync += clkgen[PLL2].eq(~clkgen[PLL2]) # half PLL rate
53 with m.If(clkgen[PLL2]):
54 sync += clkgen[PLL4].eq(~clkgen[PLL4]) # quarter PLL rate
55 with m.If(counter3 == 2):
56 sync += counter3.eq(0)
57 sync += clkgen[PLL3].eq(~clkgen[PLL3]) # 1/3 PLL rate
58 with m.If(clkgen[PLL3]):
59 sync += clkgen[PLL6].eq(~clkgen[PLL6]) # 1/6 PLL rate
60 with m.Else():
61 sync += counter3.eq(counter3+1)
62
63 # select from available array of clock sources
64 comb += self.core_clk_o.eq(clkgen[self.clk_sel_i])
65
66 # 48mhz output is PLL/6
67 comb += self.pll_48_o.eq(clkgen[PLL6])
68
69 return m
70
71 def ports(self):
72 return [self.sys_clk_i, self.pll_48_o, self.clk_sel_i, self.core_clk_o]
73
74
75 if __name__ == '__main__':
76 dut = ClockSelect()
77
78 vl = rtlil.convert(dut, ports=dut.ports())
79 with open("test_clk_sel.il", "w") as f:
80 f.write(vl)
81