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:
whitequark 2019-10-09 20:02:33 +00:00
parent 2512a9a12d
commit b9e57fd67b
4 changed files with 27 additions and 20 deletions

View file

@ -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):