This change achieves two related goals.
First, default_rst is no longer assumed to be synchronous to
default_clk, which is the safer option, since it can be connected to
e.g. buttons on some evaluation boards.
Second, since the power-on / configuration reset is inherently
asynchronous to any user clock, the default create_missing_domain()
behavior is to use a reset synchronizer with `0` as input. Since,
like all reset synchronizers, it uses Signal(reset=1) for its
synchronization stages, after power-on reset it keeps its subordinate
clock domain in reset, and releases it after fabric flops start
toggling.
The latter change is helpful to architectures that lack an end-of-
configuration signal, i.e. most of them. ECP5 was already using
a similar scheme (and is not changed here). Xilinx devices with EOS
use EOS to drive a BUFGMUX, which is more efficient than using
a global reset when the design does not need one; Xilinx devices
without EOS use the new scheme. iCE40 requires a post-configuration
timer because of BRAM silicon bug, and was changed to add a reset
synchronizer if user clock is provided.
from .. import __version__
from .._toolchain import *
-from ..hdl.ast import *
-from ..hdl.cd import *
-from ..hdl.dsl import *
-from ..hdl.ir import *
+from ..hdl import *
+from ..lib.cdc import ResetSynchronizer
from ..back import rtlil, verilog
from .res import *
from .run import *
return True
return all(has_tool(name) for name in self.required_tools)
- @abstractmethod
def create_missing_domain(self, name):
# Simple instantiation of a clock domain driven directly by the board clock and reset.
- # Because of device-specific considerations, this implementation generally does NOT provide
- # reliable power-on/post-configuration reset, and the logic should be replaced with family
- # specific logic based on vendor recommendations.
+ # This implementation uses a single ResetSynchronizer to ensure that:
+ # * an external reset is definitely synchronized to the system clock;
+ # * release of power-on reset, which is inherently asynchronous, is synchronized to
+ # the system clock.
+ # Many device families provide advanced primitives for tackling reset. If these exist,
+ # they should be used instead.
if name == "sync" and self.default_clk is not None:
clk_i = self.request(self.default_clk).i
if self.default_rst is not None:
rst_i = self.request(self.default_rst).i
+ else:
+ rst_i = Const(0)
m = Module()
m.domains += ClockDomain("sync", reset_less=self.default_rst is None)
m.d.comb += ClockSignal("sync").eq(clk_i)
- if self.default_rst is not None:
- m.d.comb += ResetSignal("sync").eq(rst_i)
+ m.submodules.reset_sync = ResetSynchronizer(rst_i, domain="sync")
return m
def prepare(self, elaboratable, name="top", **kwargs):
from abc import abstractproperty
from ..hdl import *
+from ..lib.cdc import ResetSynchronizer
from ..build import *
clk_i = self.request(self.default_clk).i
if self.default_rst is not None:
rst_i = self.request(self.default_rst).i
+ else:
+ rst_i = Const(0)
m = Module()
# Power-on-reset domain
m.domains += ClockDomain("sync")
m.d.comb += ClockSignal("sync").eq(clk_i)
if self.default_rst is not None:
- m.d.comb += ResetSignal("sync").eq(~ready | rst_i)
+ m.submodules.reset_sync = ResetSynchronizer(~ready | rst_i, domain="sync")
else:
m.d.comb += ResetSignal("sync").eq(~ready)
return m
from abc import abstractproperty
from ..hdl import *
+from ..lib.cdc import ResetSynchronizer
from ..build import *
m.domains += ClockDomain("sync", reset_less=self.default_rst is None)
m.submodules += Instance("BUFGCE", i_CE=ready, i_I=clk_i, o_O=ClockSignal("sync"))
if self.default_rst is not None:
- m.d.comb += ResetSignal("sync").eq(rst_i)
+ m.submodules.reset_sync = ResetSynchronizer(rst_i, domain="sync")
return m
def _get_xdr_buffer(self, m, pin, *, i_invert=False, o_invert=False):
from abc import abstractproperty
from ..hdl import *
+from ..lib.cdc import ResetSynchronizer
from ..build import *
# signal (if available). For details, see:
# * https://www.xilinx.com/support/answers/44174.html
# * https://www.xilinx.com/support/documentation/white_papers/wp272.pdf
+ if self.family != "6":
+ # Spartan 3 lacks a STARTUP primitive with EOS output; use a simple ResetSynchronizer
+ # in that case, as is the default.
+ return super().create_missing_domain(name)
+
if name == "sync" and self.default_clk is not None:
clk_i = self.request(self.default_clk).i
if self.default_rst is not None:
rst_i = self.request(self.default_rst).i
m = Module()
- ready = Signal()
- if self.family == "6":
- m.submodules += Instance("STARTUP_SPARTAN6", o_EOS=ready)
- else:
- raise NotImplementedError("Spartan 3 devices lack an end-of-startup signal; "
- "ensure the design has an appropriate reset")
+ eos = Signal()
+ m.submodules += Instance("STARTUP_SPARTAN6", o_EOS=eos)
m.domains += ClockDomain("sync", reset_less=self.default_rst is None)
- m.submodules += Instance("BUFGCE", i_CE=ready, i_I=clk_i, o_O=ClockSignal("sync"))
+ m.submodules += Instance("BUFGCE", i_CE=eos, i_I=clk_i, o_O=ClockSignal("sync"))
if self.default_rst is not None:
- m.d.comb += ResetSignal("sync").eq(rst_i)
+ m.submodules.reset_sync = ResetSynchronizer(rst_i, domain="sync")
return m
def _get_xdr_buffer(self, m, pin, *, i_invert=False, o_invert=False):