still experimenting with async FF sync
[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(64)
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(64)
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 domain="coresync")
69
70 return m
71
72
73 # loops and displays the counter in the main (sync) clock-driven module
74 def domain_sim(dut):
75 for i in range(50):
76 yield Tick("coresync")
77 counter = (yield dut.core.counter)
78 print ("count i", i, counter)
79
80
81 # fires the manually-driven clock at 1/3 the rate
82 def async_sim_clk(dut):
83
84 for i in range(100):
85 yield dut.core_clk.eq(1)
86 yield Tick("sync")
87 yield Tick("sync")
88 yield Tick("sync")
89 yield Tick("sync")
90 yield Tick("sync")
91 yield Tick("sync")
92 yield dut.core_clk.eq(0)
93 yield Tick("sync")
94 yield Tick("sync")
95 yield Tick("sync")
96 yield Tick("sync")
97 yield Tick("sync")
98 yield Tick("sync")
99
100 counter = yield dut.core2.counter
101 print ("async counter", counter)
102
103
104 # runs at the *sync* simulation rate but yields *coresync*-sized ticks,
105 # arbitrarily switching core_tick on and off
106 # this deliberately does not quite match up with when the *clock* ticks
107 # (see async_sim_clk above)
108 #
109 # experimenting by deleting some of these coresyncs (both the on and off ones)
110 # does in fact "miss" things.
111
112 def async_sim(dut):
113 for i in range(5):
114 yield dut.core_tick.eq(1)
115 yield Tick("coresync")
116 yield Tick("coresync")
117 yield Tick("coresync")
118 yield Tick("coresync")
119 yield Tick("coresync")
120 yield Tick("coresync")
121
122 # switch off but must wait at least 3 coresync ticks because
123 # otherwise the coresync domain that the counter is in might
124 # miss it (actually AsyncFFSynchronizer would)
125 yield dut.core_tick.eq(0)
126 yield Tick("coresync")
127 yield Tick("coresync")
128 yield Tick("coresync")
129
130 if __name__ == '__main__':
131
132 dut = AsyncThing()
133 m = Module()
134 m.submodules.ast = dut
135
136 sim = Simulator(m)
137 sim.add_clock(1e-6, domain="sync") # standard clock
138 sim.add_clock(3e-6, domain="coresync") # manually-driven. 1/3 rate
139
140 sim.add_sync_process(wrap(domain_sim(dut)))
141 sim.add_sync_process(wrap(async_sim(dut)), domain="coresync")
142 sim.add_sync_process(wrap(async_sim_clk(dut)))
143
144 with sim.write_vcd("async_sim.vcd"):
145 sim.run()
146
147