add an async clock synchronizer experiment
[soc.git] / src / soc / experiment / test / async_sim.py
1 # experimentation with async clocks, clock domains, and AsyncFFSynchronizer
2
3 from nmigen import (Elaboratable, Module, Signal, ClockDomain, DomainRenamer,
4 ResetSignal)
5 from nmigen.lib.cdc import AsyncFFSynchronizer
6 from nmigen.back.pysim import Simulator, Delay, Settle, Tick
7 from nmutil.util import wrap
8
9
10 # simple "tick-driven counter" thing
11 class Domain1(Elaboratable):
12 def __init__(self):
13 self.tick = Signal()
14 self.tick_clear_wait = Signal()
15 self.counter = Signal(8)
16 self.rst = Signal()
17
18 def elaborate(self, platform):
19 m = Module()
20
21 m.d.comb += ResetSignal().eq(self.rst)
22
23 # increments the counter each time "tick" goes HI
24 with m.If(self.tick & ~self.tick_clear_wait):
25 m.d.sync += self.counter.eq(self.counter + 1)
26 m.d.sync += self.tick_clear_wait.eq(1)
27
28 # clears waiting when tick is LO
29 with m.If(~self.tick & self.tick_clear_wait):
30 m.d.sync += self.tick_clear_wait.eq(0)
31 return m
32
33 # simple "counter" thing
34 class Domain2(Elaboratable):
35 def __init__(self):
36 self.counter = Signal(8)
37 self.rst = Signal()
38
39 def elaborate(self, platform):
40 m = Module()
41
42 m.d.comb += ResetSignal().eq(self.rst)
43 m.d.sync += self.counter.eq(self.counter + 1)
44
45 return m
46
47
48 class AsyncThing(Elaboratable):
49 def __init__(self):
50
51 self.core_clk = Signal()
52 self.core_tick = Signal()
53
54 def elaborate(self, platform):
55 m = Module()
56 core_sync = ClockDomain("coresync")
57 m.domains += core_sync
58 comb, sync, coresync = m.d.comb, m.d.sync, m.d.coresync
59
60 self.core2 = core2 = Domain2()
61 m.submodules.core2 = DomainRenamer("coresync")(core2)
62 m.submodules.core = self.core = core = Domain1()
63
64 comb += core_sync.clk.eq(self.core_clk) # driven externally
65 comb += core.rst.eq(ResetSignal())
66
67 m.submodules += AsyncFFSynchronizer(self.core_tick, core.tick)
68
69 return m
70
71
72 # loops and displays the counter in the main (sync) clock-driven module
73 def domain_sim(dut):
74 for i in range(50):
75 yield Tick("coresync")
76 counter = (yield dut.core.counter)
77 print ("count", counter)
78
79
80 # fires the manually-driven clock at 1/3 the rate
81 def async_sim_clk(dut):
82
83 for i in range(100):
84 yield dut.core_clk.eq(1)
85 yield Tick("sync")
86 yield Tick("sync")
87 yield dut.core_clk.eq(0)
88 yield Tick("sync")
89 yield Tick("sync")
90
91 counter = yield dut.core2.counter
92 print ("async counter", counter)
93
94
95 # runs at the coresync tick-rate, arbitrarily switching core_tick on and off
96 # this deliberately does not quite match up with when the *clock* ticks
97 # (see async_sim_clk above)
98 def async_sim(dut):
99 for i in range(5):
100 yield dut.core_tick.eq(1)
101 yield Tick("coresync")
102 yield Tick("coresync")
103 yield Tick("coresync")
104 yield Tick("coresync")
105 yield Tick("coresync")
106 yield Tick("coresync")
107 yield dut.core_tick.eq(0)
108 yield Tick("coresync")
109 yield Tick("coresync")
110
111 if __name__ == '__main__':
112
113 dut = AsyncThing()
114 m = Module()
115 m.submodules.ast = dut
116
117 sim = Simulator(m)
118 sim.add_clock(1e-6, domain="sync") # standard clock
119 sim.add_clock(3e-6, domain="coresync") # manually-driven. 1/3 rate
120
121 sim.add_sync_process(wrap(domain_sim(dut)))
122 sim.add_sync_process(wrap(async_sim(dut)), domain="coresync")
123 sim.add_sync_process(wrap(async_sim_clk(dut)))
124
125 with sim.write_vcd("async_sim.vcd"):
126 sim.run()
127
128