Skip to content

Commit

Permalink
lib.cdc: avoid modifying synchronizers in their elaborate() method.
Browse files Browse the repository at this point in the history
  • Loading branch information
whitequark committed Sep 23, 2019
1 parent 51f03bb commit 86f0f12
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 22 deletions.
24 changes: 14 additions & 10 deletions nmigen/lib/cdc.py
Expand Up @@ -57,19 +57,22 @@ def __init__(self, i, o, *, o_domain="sync", stages=2, reset=0, reset_less=True)
self.i = i
self.o = o

self._o_domain = o_domain
self._stages = [Signal(self.i.shape(), name="stage{}".format(index),
reset=reset, reset_less=reset_less)
for index in range(stages)]
self._reset = reset
self._reset_less = reset_less
self._o_domain = o_domain
self._stages = stages

def elaborate(self, platform):
if hasattr(platform, "get_ff_sync"):
return platform.get_ff_sync(self)

m = Module()
for i, o in zip((self.i, *self._stages), self._stages):
flops = [Signal(self.i.shape(), name="stage{}".format(index),
reset=self._reset, reset_less=self._reset_less)
for index in range(self._stages)]
for i, o in zip((self.i, *flops), flops):
m.d[self._o_domain] += o.eq(i)
m.d.comb += self.o.eq(self._stages[-1])
m.d.comb += self.o.eq(flops[-1])
return m


Expand All @@ -82,20 +85,21 @@ def __init__(self, arst, *, domain="sync", stages=2):
self.arst = arst

self._domain = domain
self._stages = [Signal(1, name="stage{}".format(i), reset=1)
for i in range(stages)]
self._stages = stages

def elaborate(self, platform):
if hasattr(platform, "get_reset_sync"):
return platform.get_reset_sync(self)

m = Module()
m.domains += ClockDomain("reset_sync", async_reset=True, local=True)
for i, o in zip((0, *self._stages), self._stages):
flops = [Signal(1, name="stage{}".format(index), reset=1)
for index in range(self._stages)]
for i, o in zip((0, *flops), flops):
m.d.reset_sync += o.eq(i)
m.d.comb += [
ClockSignal("reset_sync").eq(ClockSignal(self._domain)),
ResetSignal("reset_sync").eq(self.arst),
ResetSignal(self._domain).eq(self._stages[-1])
ResetSignal(self._domain).eq(flops[-1])
]
return m
9 changes: 6 additions & 3 deletions nmigen/vendor/xilinx_7series.py
Expand Up @@ -363,8 +363,11 @@ def get_diff_input_output(self, pin, p_port, n_port, attrs, invert):

def get_ff_sync(self, ff_sync):
m = Module()
for i, o in zip((ff_sync.i, *ff_sync._stages), ff_sync._stages):
o.attrs["ASYNC_REG"] = "TRUE"
flops = [Signal(ff_sync.i.shape(), name="stage{}".format(index),
reset=ff_sync._reset, reset_less=ff_sync._reset_less,
attrs={"ASYNC_REG": "TRUE"})
for index in range(ff_sync._stages)]
for i, o in zip((ff_sync.i, *flops), flops):
m.d[ff_sync._o_domain] += o.eq(i)
m.d.comb += ff_sync.o.eq(ff_sync._stages[-1])
m.d.comb += ff_sync.o.eq(flops[-1])
return m
23 changes: 14 additions & 9 deletions nmigen/vendor/xilinx_spartan_3_6.py
Expand Up @@ -413,22 +413,27 @@ def get_diff_input_output(self, pin, p_port, n_port, attrs, invert):

def get_ff_sync(self, ff_sync):
m = Module()
for i, o in zip((ff_sync.i, *ff_sync._stages), ff_sync._stages):
o.attrs["ASYNC_REG"] = "TRUE"
flops = [Signal(ff_sync.i.shape(), name="stage{}".format(index),
reset=ff_sync._reset, reset_less=ff_sync._reset_less,
attrs={"ASYNC_REG": "TRUE"})
for index in range(ff_sync._stages)]
for i, o in zip((ff_sync.i, *flops), flops):
m.d[ff_sync._o_domain] += o.eq(i)
m.d.comb += ff_sync.o.eq(multireg._stages[-1])
m.d.comb += ff_sync.o.eq(flops[-1])
return m

def get_reset_sync(self, resetsync):
def get_reset_sync(self, reset_sync):
m = Module()
m.domains += ClockDomain("reset_sync", async_reset=True, local=True)
for i, o in zip((0, *resetsync._stages), resetsync._stages):
o.attrs["ASYNC_REG"] = "TRUE"
flops = [Signal(1, name="stage{}".format(index), reset=1,
attrs={"ASYNC_REG": "TRUE"})
for index in range(reset_sync._stages)]
for i, o in zip((0, *flops), flops):
m.d.reset_sync += o.eq(i)
m.d.comb += [
ClockSignal("reset_sync").eq(ClockSignal(resetsync._domain)),
ResetSignal("reset_sync").eq(resetsync.arst),
ResetSignal(resetsync._domain).eq(resetsync._stages[-1])
ClockSignal("reset_sync").eq(ClockSignal(reset_sync._domain)),
ResetSignal("reset_sync").eq(reset_sync.arst),
ResetSignal(reset_sync._domain).eq(flops[-1])
]
return m

Expand Down

2 comments on commit 86f0f12

@whitequark
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dlharmon I redid this in a slightly cleaner way. If you happen to synthesize the same design for multiple different platforms (something nMigen aims to support) then not mutating the Signals is useful. Please let me know if I made a mistake somewhere here.

@dlharmon
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I built my multi clock 7series project with it and all was fine.

I've merged it with my #227 pull request and made the required changes there.

Please sign in to comment.