build.plat,vendor: always synchronize reset in default sync domain.
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.
This commit is contained in:
parent
2512a9a12d
commit
b9e57fd67b
|
@ -7,10 +7,8 @@ import jinja2
|
|||
|
||||
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 *
|
||||
|
@ -87,22 +85,25 @@ class Platform(ResourceManager, metaclass=ABCMeta):
|
|||
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):
|
||||
|
|
5
nmigen/vendor/lattice_ice40.py
vendored
5
nmigen/vendor/lattice_ice40.py
vendored
|
@ -1,6 +1,7 @@
|
|||
from abc import abstractproperty
|
||||
|
||||
from ..hdl import *
|
||||
from ..lib.cdc import ResetSynchronizer
|
||||
from ..build import *
|
||||
|
||||
|
||||
|
@ -346,6 +347,8 @@ class LatticeICE40Platform(TemplatedPlatform):
|
|||
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
|
||||
|
@ -362,7 +365,7 @@ class LatticeICE40Platform(TemplatedPlatform):
|
|||
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
|
||||
|
|
3
nmigen/vendor/xilinx_7series.py
vendored
3
nmigen/vendor/xilinx_7series.py
vendored
|
@ -1,6 +1,7 @@
|
|||
from abc import abstractproperty
|
||||
|
||||
from ..hdl import *
|
||||
from ..lib.cdc import ResetSynchronizer
|
||||
from ..build import *
|
||||
|
||||
|
||||
|
@ -164,7 +165,7 @@ class Xilinx7SeriesPlatform(TemplatedPlatform):
|
|||
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):
|
||||
|
|
18
nmigen/vendor/xilinx_spartan_3_6.py
vendored
18
nmigen/vendor/xilinx_spartan_3_6.py
vendored
|
@ -1,6 +1,7 @@
|
|||
from abc import abstractproperty
|
||||
|
||||
from ..hdl import *
|
||||
from ..lib.cdc import ResetSynchronizer
|
||||
from ..build import *
|
||||
|
||||
|
||||
|
@ -187,22 +188,23 @@ class XilinxSpartan3Or6Platform(TemplatedPlatform):
|
|||
# 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):
|
||||
|
|
Loading…
Reference in a new issue