1 # experimentation with async clocks, clock domains, and AsyncFFSynchronizer
3 from nmigen
import (Elaboratable
, Module
, Signal
, ClockDomain
, DomainRenamer
,
5 from nmigen
.lib
.cdc
import AsyncFFSynchronizer
6 from nmigen
.back
.pysim
import Simulator
, Delay
, Settle
, Tick
7 from nmutil
.util
import wrap
10 # simple "tick-driven counter" thing
11 class Domain1(Elaboratable
):
14 self
.tick_clear_wait
= Signal()
15 self
.counter
= Signal(8)
18 def elaborate(self
, platform
):
21 m
.d
.comb
+= ResetSignal().eq(self
.rst
)
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)
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)
33 # simple "counter" thing
34 class Domain2(Elaboratable
):
36 self
.counter
= Signal(8)
39 def elaborate(self
, platform
):
42 m
.d
.comb
+= ResetSignal().eq(self
.rst
)
43 m
.d
.sync
+= self
.counter
.eq(self
.counter
+ 1)
48 class AsyncThing(Elaboratable
):
51 self
.core_clk
= Signal()
52 self
.core_tick
= Signal()
54 def elaborate(self
, platform
):
56 core_sync
= ClockDomain("coresync")
57 m
.domains
+= core_sync
58 comb
, sync
, coresync
= m
.d
.comb
, m
.d
.sync
, m
.d
.coresync
60 self
.core2
= core2
= Domain2()
61 m
.submodules
.core2
= DomainRenamer("coresync")(core2
)
62 m
.submodules
.core
= self
.core
= core
= Domain1()
64 comb
+= core_sync
.clk
.eq(self
.core_clk
) # driven externally
65 comb
+= core
.rst
.eq(ResetSignal())
67 m
.submodules
+= AsyncFFSynchronizer(self
.core_tick
, core
.tick
)
72 # loops and displays the counter in the main (sync) clock-driven module
75 yield Tick("coresync")
76 counter
= (yield dut
.core
.counter
)
77 print ("count", counter
)
80 # fires the manually-driven clock at 1/3 the rate
81 def async_sim_clk(dut
):
84 yield dut
.core_clk
.eq(1)
87 yield dut
.core_clk
.eq(0)
91 counter
= yield dut
.core2
.counter
92 print ("async counter", counter
)
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)
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")
111 if __name__
== '__main__':
115 m
.submodules
.ast
= dut
118 sim
.add_clock(1e-6, domain
="sync") # standard clock
119 sim
.add_clock(3e-6, domain
="coresync") # manually-driven. 1/3 rate
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
)))
125 with sim
.write_vcd("async_sim.vcd"):