Implement RFC 43: Rename reset= to init=.
This commit is contained in:
parent
b30c87fa3e
commit
24a392887a
30 changed files with 476 additions and 276 deletions
|
|
@ -173,7 +173,7 @@ class ShapeCastable:
|
|||
|
||||
.. code::
|
||||
|
||||
value_like = Signal(shape_castable, reset=initializer)
|
||||
value_like = Signal(shape_castable, init=initializer)
|
||||
|
||||
The code above is equivalent to:
|
||||
|
||||
|
|
@ -181,7 +181,7 @@ class ShapeCastable:
|
|||
|
||||
value_like = shape_castable(Signal(
|
||||
shape_castable.as_shape(),
|
||||
reset=shape_castable.const(initializer)
|
||||
init=shape_castable.const(initializer)
|
||||
))
|
||||
|
||||
Note that the :py:`shape_castable(x)` syntax performs :py:`shape_castable.__call__(x)`.
|
||||
|
|
@ -1731,15 +1731,15 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
|
|||
name : str
|
||||
Name hint for this signal. If ``None`` (default) the name is inferred from the variable
|
||||
name this ``Signal`` is assigned to.
|
||||
reset : int or integral Enum
|
||||
init : int or integral Enum
|
||||
Reset (synchronous) or default (combinatorial) value.
|
||||
When this ``Signal`` is assigned to in synchronous context and the corresponding clock
|
||||
domain is reset, the ``Signal`` assumes the given value. When this ``Signal`` is unassigned
|
||||
in combinatorial context (due to conditional assignments not being taken), the ``Signal``
|
||||
assumes its ``reset`` value. Defaults to 0.
|
||||
assumes its ``init`` value. Defaults to 0.
|
||||
reset_less : bool
|
||||
If ``True``, do not generate reset logic for this ``Signal`` in synchronous statements.
|
||||
The ``reset`` value is only used as a combinatorial default or as the initial value.
|
||||
The ``init`` value is only used as a combinatorial default or as the initial value.
|
||||
Defaults to ``False``.
|
||||
attrs : dict
|
||||
Dictionary of synthesis attributes.
|
||||
|
|
@ -1754,13 +1754,13 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
|
|||
width : int
|
||||
signed : bool
|
||||
name : str
|
||||
reset : int
|
||||
init : int
|
||||
reset_less : bool
|
||||
attrs : dict
|
||||
decoder : function
|
||||
"""
|
||||
|
||||
def __init__(self, shape=None, *, name=None, reset=None, reset_less=False,
|
||||
def __init__(self, shape=None, *, name=None, init=None, reset=None, reset_less=False,
|
||||
attrs=None, decoder=None, src_loc_at=0):
|
||||
super().__init__(src_loc_at=src_loc_at)
|
||||
|
||||
|
|
@ -1776,53 +1776,61 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
|
|||
self.width = shape.width
|
||||
self.signed = shape.signed
|
||||
|
||||
orig_reset = reset
|
||||
# TODO(amaranth-0.7): remove
|
||||
if reset is not None:
|
||||
if init is not None:
|
||||
raise ValueError("Cannot specify both `reset` and `init`")
|
||||
warnings.warn("`reset=` is deprecated, use `init=` instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
init = reset
|
||||
|
||||
orig_init = init
|
||||
if isinstance(orig_shape, ShapeCastable):
|
||||
try:
|
||||
reset = Const.cast(orig_shape.const(reset))
|
||||
init = Const.cast(orig_shape.const(init))
|
||||
except Exception:
|
||||
raise TypeError("Reset value must be a constant initializer of {!r}"
|
||||
raise TypeError("Initial value must be a constant initializer of {!r}"
|
||||
.format(orig_shape))
|
||||
if reset.shape() != Shape.cast(orig_shape):
|
||||
if init.shape() != Shape.cast(orig_shape):
|
||||
raise ValueError("Constant returned by {!r}.const() must have the shape that "
|
||||
"it casts to, {!r}, and not {!r}"
|
||||
.format(orig_shape, Shape.cast(orig_shape),
|
||||
reset.shape()))
|
||||
init.shape()))
|
||||
else:
|
||||
if reset is None:
|
||||
reset = 0
|
||||
if init is None:
|
||||
init = 0
|
||||
try:
|
||||
reset = Const.cast(reset)
|
||||
init = Const.cast(init)
|
||||
except TypeError:
|
||||
raise TypeError("Reset value must be a constant-castable expression, not {!r}"
|
||||
.format(orig_reset))
|
||||
raise TypeError("Initial value must be a constant-castable expression, not {!r}"
|
||||
.format(orig_init))
|
||||
# Avoid false positives for all-zeroes and all-ones
|
||||
if orig_reset is not None and not (isinstance(orig_reset, int) and orig_reset in (0, -1)):
|
||||
if reset.shape().signed and not self.signed:
|
||||
if orig_init is not None and not (isinstance(orig_init, int) and orig_init in (0, -1)):
|
||||
if init.shape().signed and not self.signed:
|
||||
warnings.warn(
|
||||
message="Reset value {!r} is signed, but the signal shape is {!r}"
|
||||
.format(orig_reset, shape),
|
||||
message="Initial value {!r} is signed, but the signal shape is {!r}"
|
||||
.format(orig_init, shape),
|
||||
category=SyntaxWarning,
|
||||
stacklevel=2)
|
||||
elif (reset.shape().width > self.width or
|
||||
reset.shape().width == self.width and
|
||||
self.signed and not reset.shape().signed):
|
||||
elif (init.shape().width > self.width or
|
||||
init.shape().width == self.width and
|
||||
self.signed and not init.shape().signed):
|
||||
warnings.warn(
|
||||
message="Reset value {!r} will be truncated to the signal shape {!r}"
|
||||
.format(orig_reset, shape),
|
||||
message="Initial value {!r} will be truncated to the signal shape {!r}"
|
||||
.format(orig_init, shape),
|
||||
category=SyntaxWarning,
|
||||
stacklevel=2)
|
||||
self.reset = reset.value
|
||||
self.init = init.value
|
||||
self.reset_less = bool(reset_less)
|
||||
|
||||
if isinstance(orig_shape, range) and orig_reset is not None and orig_reset not in orig_shape:
|
||||
if orig_reset == orig_shape.stop:
|
||||
if isinstance(orig_shape, range) and orig_init is not None and orig_init not in orig_shape:
|
||||
if orig_init == orig_shape.stop:
|
||||
raise SyntaxError(
|
||||
f"Reset value {orig_reset!r} equals the non-inclusive end of the signal "
|
||||
f"Initial value {orig_init!r} equals the non-inclusive end of the signal "
|
||||
f"shape {orig_shape!r}; this is likely an off-by-one error")
|
||||
else:
|
||||
raise SyntaxError(
|
||||
f"Reset value {orig_reset!r} is not within the signal shape {orig_shape!r}")
|
||||
f"Initial value {orig_init!r} is not within the signal shape {orig_shape!r}")
|
||||
|
||||
self.attrs = OrderedDict(() if attrs is None else attrs)
|
||||
|
||||
|
|
@ -1850,6 +1858,18 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
|
|||
# Any other case is formatted as a plain integer.
|
||||
self._value_repr = (Repr(FormatInt(), self),)
|
||||
|
||||
@property
|
||||
def reset(self):
|
||||
warnings.warn("`Signal.reset` is deprecated, use `Signal.init` instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return self.init
|
||||
|
||||
@reset.setter
|
||||
def reset(self, value):
|
||||
warnings.warn("`Signal.reset` is deprecated, use `Signal.init` instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
self.init = value
|
||||
|
||||
@property
|
||||
def decoder(self):
|
||||
return self._decoder
|
||||
|
|
@ -1873,7 +1893,7 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
|
|||
self._decoder = enum_decoder
|
||||
|
||||
@classmethod
|
||||
def like(cls, other, *, name=None, name_suffix=None, src_loc_at=0, **kwargs):
|
||||
def like(cls, other, *, name=None, name_suffix=None, init=None, reset=None, src_loc_at=0, **kwargs):
|
||||
"""Create Signal based on another.
|
||||
|
||||
Parameters
|
||||
|
|
@ -1887,15 +1907,24 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
|
|||
new_name = other.name + str(name_suffix)
|
||||
else:
|
||||
new_name = tracer.get_var_name(depth=2 + src_loc_at, default="$like")
|
||||
# TODO(amaranth-0.7): remove
|
||||
if reset is not None:
|
||||
if init is not None:
|
||||
raise ValueError("Cannot specify both `reset` and `init`")
|
||||
warnings.warn("`reset=` is deprecated, use `init=` instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
init = reset
|
||||
if isinstance(other, ValueCastable):
|
||||
shape = other.shape()
|
||||
else:
|
||||
shape = Value.cast(other).shape()
|
||||
kw = dict(shape=shape, name=new_name)
|
||||
if isinstance(other, Signal):
|
||||
kw.update(reset=other.reset, reset_less=other.reset_less,
|
||||
kw.update(init=other.init, reset_less=other.reset_less,
|
||||
attrs=other.attrs, decoder=other.decoder)
|
||||
kw.update(kwargs)
|
||||
if init is not None:
|
||||
kw["init"] = init
|
||||
return cls(**kw, src_loc_at=1 + src_loc_at)
|
||||
|
||||
def shape(self):
|
||||
|
|
|
|||
|
|
@ -374,14 +374,21 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
|||
self._statements = _outer_case
|
||||
|
||||
@contextmanager
|
||||
def FSM(self, reset=None, domain="sync", name="fsm"):
|
||||
def FSM(self, init=None, domain="sync", name="fsm", *, reset=None):
|
||||
self._check_context("FSM", context=None)
|
||||
if domain == "comb":
|
||||
raise ValueError(f"FSM may not be driven by the '{domain}' domain")
|
||||
# TODO(amaranth-0.7): remove
|
||||
if reset is not None:
|
||||
if init is not None:
|
||||
raise ValueError("Cannot specify both `reset` and `init`")
|
||||
warnings.warn("`reset=` is deprecated, use `init=` instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
init = reset
|
||||
fsm_data = self._set_ctrl("FSM", {
|
||||
"name": name,
|
||||
"signal": Signal(name=f"{name}_state", src_loc_at=2),
|
||||
"reset": reset,
|
||||
"init": init,
|
||||
"domain": domain,
|
||||
"encoding": OrderedDict(),
|
||||
"decoding": OrderedDict(),
|
||||
|
|
@ -489,17 +496,17 @@ class Module(_ModuleBuilderRoot, Elaboratable):
|
|||
src_loc=src_loc, case_src_locs=switch_case_src_locs))
|
||||
|
||||
if name == "FSM":
|
||||
fsm_signal, fsm_reset, fsm_encoding, fsm_decoding, fsm_states = \
|
||||
data["signal"], data["reset"], data["encoding"], data["decoding"], data["states"]
|
||||
fsm_signal, fsm_init, fsm_encoding, fsm_decoding, fsm_states = \
|
||||
data["signal"], data["init"], data["encoding"], data["decoding"], data["states"]
|
||||
fsm_state_src_locs = data["state_src_locs"]
|
||||
if not fsm_states:
|
||||
return
|
||||
fsm_signal.width = bits_for(len(fsm_encoding) - 1)
|
||||
if fsm_reset is None:
|
||||
fsm_signal.reset = fsm_encoding[next(iter(fsm_states))]
|
||||
if fsm_init is None:
|
||||
fsm_signal.init = fsm_encoding[next(iter(fsm_states))]
|
||||
else:
|
||||
fsm_signal.reset = fsm_encoding[fsm_reset]
|
||||
# The FSM is encoded such that the state with encoding 0 is always the reset state.
|
||||
fsm_signal.init = fsm_encoding[fsm_init]
|
||||
# The FSM is encoded such that the state with encoding 0 is always the init state.
|
||||
fsm_decoding.update((n, s) for s, n in fsm_encoding.items())
|
||||
fsm_signal.decoder = lambda n: f"{fsm_decoding[n]}/{n}"
|
||||
|
||||
|
|
|
|||
|
|
@ -691,8 +691,8 @@ class NetlistDriver:
|
|||
|
||||
def emit_value(self, builder):
|
||||
if self.domain is None:
|
||||
reset = _ast.Const(self.signal.reset, self.signal.width)
|
||||
default, _signed = builder.emit_rhs(self.module_idx, reset)
|
||||
init = _ast.Const(self.signal.init, self.signal.width)
|
||||
default, _signed = builder.emit_rhs(self.module_idx, init)
|
||||
else:
|
||||
default = builder.emit_signal(self.signal)
|
||||
if len(self.assignments) == 1:
|
||||
|
|
@ -1184,7 +1184,7 @@ class NetlistEmitter:
|
|||
arst = _nir.Net.from_const(0)
|
||||
cell = _nir.FlipFlop(driver.module_idx,
|
||||
data=value,
|
||||
init=driver.signal.reset,
|
||||
init=driver.signal.init,
|
||||
clk=clk,
|
||||
clk_edge=driver.domain.clk_edge,
|
||||
arst=arst,
|
||||
|
|
@ -1198,12 +1198,12 @@ class NetlistEmitter:
|
|||
src_loc = driver.signal.src_loc
|
||||
self.connect(self.emit_signal(driver.signal), value, src_loc=src_loc)
|
||||
|
||||
# Connect all undriven signal bits to their reset values. This can only happen for entirely
|
||||
# Connect all undriven signal bits to their initial values. This can only happen for entirely
|
||||
# undriven signals, or signals that are partially driven by instances.
|
||||
for signal, value in self.netlist.signals.items():
|
||||
for bit, net in enumerate(value):
|
||||
if net.is_late and net not in self.netlist.connections:
|
||||
self.netlist.connections[net] = _nir.Net.from_const((signal.reset >> bit) & 1)
|
||||
self.netlist.connections[net] = _nir.Net.from_const((signal.init >> bit) & 1)
|
||||
|
||||
def emit_fragment(self, fragment: _ir.Fragment, parent_module_idx: 'int | None'):
|
||||
from . import _mem
|
||||
|
|
|
|||
|
|
@ -262,7 +262,7 @@ class ReadPort(Elaboratable):
|
|||
self.data = Signal(memory.width,
|
||||
name=f"{memory.name}_r_data", src_loc_at=1 + src_loc_at)
|
||||
if self.domain != "comb":
|
||||
self.en = Signal(name=f"{memory.name}_r_en", reset=1,
|
||||
self.en = Signal(name=f"{memory.name}_r_en", init=1,
|
||||
src_loc_at=1 + src_loc_at)
|
||||
else:
|
||||
self.en = Const(1)
|
||||
|
|
|
|||
|
|
@ -521,7 +521,7 @@ class DomainLowerer(FragmentTransformer, ValueTransformer, StatementTransformer)
|
|||
domain = fragment.domains[domain_name]
|
||||
if domain.rst is None:
|
||||
continue
|
||||
stmts = [signal.eq(Const(signal.reset, signal.width))
|
||||
stmts = [signal.eq(Const(signal.init, signal.width))
|
||||
for signal in signals if not signal.reset_less]
|
||||
fragment.add_statements(domain_name, Switch(domain.rst, {1: stmts}))
|
||||
|
||||
|
|
@ -559,7 +559,7 @@ class _ControlInserter(FragmentTransformer):
|
|||
|
||||
class ResetInserter(_ControlInserter):
|
||||
def _insert_control(self, fragment, domain, signals):
|
||||
stmts = [s.eq(Const(s.reset, s.width)) for s in signals if not s.reset_less]
|
||||
stmts = [s.eq(Const(s.init, s.width)) for s in signals if not s.reset_less]
|
||||
fragment.add_statements(domain, Switch(self.controls[domain], {1: stmts}, src_loc=self.src_loc))
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import warnings
|
||||
|
||||
from .. import *
|
||||
|
||||
|
||||
|
|
@ -26,8 +28,8 @@ class FFSynchronizer(Elaboratable):
|
|||
Signal connected to synchroniser output.
|
||||
o_domain : str
|
||||
Name of output clock domain.
|
||||
reset : int
|
||||
Reset value of the flip-flops. On FPGAs, even if ``reset_less`` is ``True``,
|
||||
init : int
|
||||
Initial and reset value of the flip-flops. On FPGAs, even if ``reset_less`` is ``True``,
|
||||
the :class:`FFSynchronizer` is still set to this value during initialization.
|
||||
reset_less : bool
|
||||
If ``True`` (the default), this :class:`FFSynchronizer` is unaffected by ``o_domain``
|
||||
|
|
@ -62,14 +64,24 @@ class FFSynchronizer(Elaboratable):
|
|||
Define the ``get_ff_sync`` platform method to override the implementation of
|
||||
:class:`FFSynchronizer`, e.g. to instantiate library cells directly.
|
||||
"""
|
||||
def __init__(self, i, o, *, o_domain="sync", reset=0, reset_less=True, stages=2,
|
||||
def __init__(self, i, o, *, o_domain="sync", init=None, reset=None, reset_less=True, stages=2,
|
||||
max_input_delay=None):
|
||||
_check_stages(stages)
|
||||
|
||||
self.i = i
|
||||
self.o = o
|
||||
|
||||
self._reset = reset
|
||||
# TODO(amaranth-0.7): remove
|
||||
if reset is not None:
|
||||
if init is not None:
|
||||
raise ValueError("Cannot specify both `reset` and `init`")
|
||||
warnings.warn("`reset=` is deprecated, use `init=` instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
init = reset
|
||||
if init is None:
|
||||
init = 0
|
||||
|
||||
self._init = init
|
||||
self._reset_less = reset_less
|
||||
self._o_domain = o_domain
|
||||
self._stages = stages
|
||||
|
|
@ -87,7 +99,7 @@ class FFSynchronizer(Elaboratable):
|
|||
|
||||
m = Module()
|
||||
flops = [Signal(self.i.shape(), name=f"stage{index}",
|
||||
reset=self._reset, reset_less=self._reset_less)
|
||||
init=self._init, 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)
|
||||
|
|
@ -161,7 +173,7 @@ class AsyncFFSynchronizer(Elaboratable):
|
|||
|
||||
m = Module()
|
||||
m.domains += ClockDomain("async_ff", async_reset=True, local=True)
|
||||
flops = [Signal(1, name=f"stage{index}", reset=1)
|
||||
flops = [Signal(1, name=f"stage{index}", init=1)
|
||||
for index in range(self._stages)]
|
||||
for i, o in zip((0, *flops), flops):
|
||||
m.d.async_ff += o.eq(i)
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ class Processor(Elaboratable):
|
|||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
crc_reg = Signal(self._crc_width, reset=self._initial_crc.value)
|
||||
crc_reg = Signal(self._crc_width, init=self._initial_crc.value)
|
||||
data_in = Signal(self._data_width)
|
||||
|
||||
# Optionally bit-reflect input words.
|
||||
|
|
|
|||
|
|
@ -812,7 +812,7 @@ class _AggregateMeta(ShapeCastable, type):
|
|||
cls = type.__new__(metacls, name, bases, namespace)
|
||||
if cls.__layout_cls is UnionLayout:
|
||||
if len(default) > 1:
|
||||
raise ValueError("Reset value for at most one field can be provided for "
|
||||
raise ValueError("Initial value for at most one field can be provided for "
|
||||
"a union class (specified: {})"
|
||||
.format(", ".join(default.keys())))
|
||||
cls.__layout = cls.__layout_cls(layout)
|
||||
|
|
@ -855,12 +855,12 @@ class Struct(View, metaclass=_AggregateMeta):
|
|||
"""Structures defined with annotations.
|
||||
|
||||
The :class:`Struct` base class is a subclass of :class:`View` that provides a concise way
|
||||
to describe the structure layout and reset values for the fields using Python
|
||||
to describe the structure layout and initial values for the fields using Python
|
||||
:term:`variable annotations <python:variable annotation>`.
|
||||
|
||||
Any annotations containing :ref:`shape-like <lang-shapelike>` objects are used,
|
||||
in the order in which they appear in the source code, to construct a :class:`StructLayout`.
|
||||
The values assigned to such annotations are used to populate the reset value of the signal
|
||||
The values assigned to such annotations are used to populate the initial value of the signal
|
||||
created by the view. Any other annotations are kept as-is.
|
||||
|
||||
.. testsetup::
|
||||
|
|
@ -907,15 +907,15 @@ class Struct(View, metaclass=_AggregateMeta):
|
|||
>>> flt.is_subnormal()
|
||||
(== (slice (sig flt) 23:31) (const 1'd0))
|
||||
|
||||
The reset values for individual fields can be overridden during instantiation:
|
||||
The initial values for individual fields can be overridden during instantiation:
|
||||
|
||||
.. doctest::
|
||||
|
||||
>>> hex(Signal(IEEE754Single).as_value().reset)
|
||||
>>> hex(Signal(IEEE754Single).as_value().init)
|
||||
'0x3f800000'
|
||||
>>> hex(Signal(IEEE754Single, reset={'sign': 1}).as_value().reset)
|
||||
>>> hex(Signal(IEEE754Single, init={'sign': 1}).as_value().init)
|
||||
'0xbf800000'
|
||||
>>> hex(Signal(IEEE754Single, reset={'exponent': 0}).as_value().reset)
|
||||
>>> hex(Signal(IEEE754Single, init={'exponent': 0}).as_value().init)
|
||||
'0x0'
|
||||
|
||||
Classes inheriting from :class:`Struct` can be used as base classes. The only restrictions
|
||||
|
|
@ -964,8 +964,8 @@ class Union(View, metaclass=_AggregateMeta):
|
|||
annotation>`. It is very similar to the :class:`Struct` class, except that its layout
|
||||
is a :class:`UnionLayout`.
|
||||
|
||||
A :class:`Union` can have only one field with a specified reset value. If a reset value is
|
||||
explicitly provided during instantiation, it overrides the reset value specified with
|
||||
A :class:`Union` can have only one field with a specified initial value. If an initial value is
|
||||
explicitly provided during instantiation, it overrides the initial value specified with
|
||||
an annotation:
|
||||
|
||||
.. testcode::
|
||||
|
|
@ -976,9 +976,9 @@ class Union(View, metaclass=_AggregateMeta):
|
|||
|
||||
.. doctest::
|
||||
|
||||
>>> Signal(VarInt).as_value().reset
|
||||
>>> Signal(VarInt).as_value().init
|
||||
256
|
||||
>>> Signal(VarInt, reset={'int8': 10}).as_value().reset
|
||||
>>> Signal(VarInt, init={'int8': 10}).as_value().init
|
||||
10
|
||||
"""
|
||||
_AggregateMeta__layout_cls = UnionLayout
|
||||
|
|
|
|||
|
|
@ -165,8 +165,8 @@ class EnumMeta(ShapeCastable, py_enum.EnumMeta):
|
|||
def const(cls, init):
|
||||
# Same considerations apply as above.
|
||||
if init is None:
|
||||
# Signal with unspecified reset value passes ``None`` to :meth:`const`.
|
||||
# Before RFC 9 was implemented, the unspecified reset value was 0, so this keeps
|
||||
# Signal with unspecified initial value passes ``None`` to :meth:`const`.
|
||||
# Before RFC 9 was implemented, the unspecified initial value was 0, so this keeps
|
||||
# the old behavior intact.
|
||||
member = cls(0)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -59,16 +59,23 @@ class Flow(enum.Enum):
|
|||
return Out
|
||||
assert False # :nocov:
|
||||
|
||||
def __call__(self, description, *, reset=None, src_loc_at=0):
|
||||
def __call__(self, description, *, init=None, reset=None, src_loc_at=0):
|
||||
"""Create a :class:`Member` with this data flow and the provided description and
|
||||
reset value.
|
||||
initial value.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:class:`Member`
|
||||
:py:`Member(self, description, reset=reset)`
|
||||
:py:`Member(self, description, init=init)`
|
||||
"""
|
||||
return Member(self, description, reset=reset, src_loc_at=src_loc_at + 1)
|
||||
# TODO(amaranth-0.7): remove
|
||||
if reset is not None:
|
||||
if init is not None:
|
||||
raise ValueError("Cannot specify both `reset` and `init`")
|
||||
warnings.warn("`reset=` is deprecated, use `init=` instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
init = reset
|
||||
return Member(self, description, init=init, src_loc_at=src_loc_at + 1)
|
||||
|
||||
def __repr__(self):
|
||||
return self.name
|
||||
|
|
@ -98,17 +105,24 @@ class Member:
|
|||
object (in which case it is created as a port member). After creation the :class:`Member`
|
||||
instance cannot be modified.
|
||||
|
||||
When a :class:`Signal` is created from a description of a port member, the signal's reset value
|
||||
When a :class:`Signal` is created from a description of a port member, the signal's initial value
|
||||
is taken from the member description. If this signal is never explicitly assigned a value, it
|
||||
will equal ``reset``.
|
||||
will equal ``init``.
|
||||
|
||||
Although instances can be created directly, most often they will be created through
|
||||
:data:`In` and :data:`Out`, e.g. :py:`In(unsigned(1))` or :py:`Out(stream.Signature(RGBPixel))`.
|
||||
"""
|
||||
def __init__(self, flow, description, *, reset=None, _dimensions=(), src_loc_at=0):
|
||||
def __init__(self, flow, description, *, init=None, reset=None, _dimensions=(), src_loc_at=0):
|
||||
# TODO(amaranth-0.7): remove
|
||||
if reset is not None:
|
||||
if init is not None:
|
||||
raise ValueError("Cannot specify both `reset` and `init`")
|
||||
warnings.warn("`reset=` is deprecated, use `init=` instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
init = reset
|
||||
self._flow = flow
|
||||
self._description = description
|
||||
self._reset = reset
|
||||
self._init = init
|
||||
self._dimensions = _dimensions
|
||||
self.src_loc = tracer.get_src_loc(src_loc_at=src_loc_at)
|
||||
|
||||
|
|
@ -121,23 +135,23 @@ class Member:
|
|||
except TypeError as e:
|
||||
raise TypeError(f"Port member description must be a shape-castable object or "
|
||||
f"a signature, not {description!r}") from e
|
||||
# This mirrors the logic that handles Signal(reset=).
|
||||
# This mirrors the logic that handles Signal(init=).
|
||||
# TODO: We need a simpler way to check for "is this a valid constant initializer"
|
||||
if issubclass(type(self._description), ShapeCastable):
|
||||
try:
|
||||
self._reset_as_const = Const.cast(self._description.const(self._reset))
|
||||
self._init_as_const = Const.cast(self._description.const(self._init))
|
||||
except Exception as e:
|
||||
raise TypeError(f"Port member reset value {self._reset!r} is not a valid "
|
||||
raise TypeError(f"Port member initial value {self._init!r} is not a valid "
|
||||
f"constant initializer for {self._description}") from e
|
||||
else:
|
||||
try:
|
||||
self._reset_as_const = Const.cast(reset or 0)
|
||||
self._init_as_const = Const.cast(init or 0)
|
||||
except TypeError:
|
||||
raise TypeError(f"Port member reset value {self._reset!r} is not a valid "
|
||||
raise TypeError(f"Port member initial value {self._init!r} is not a valid "
|
||||
f"constant initializer for {shape}")
|
||||
if self.is_signature:
|
||||
if self._reset is not None:
|
||||
raise ValueError(f"A signature member cannot have a reset value")
|
||||
if self._init is not None:
|
||||
raise ValueError(f"A signature member cannot have an initial value")
|
||||
|
||||
def flip(self):
|
||||
"""Flip the data flow of this member.
|
||||
|
|
@ -148,7 +162,7 @@ class Member:
|
|||
A new :py:`member` with :py:`member.flow` equal to :py:`self.flow.flip()`, and identical
|
||||
to :py:`self` other than that.
|
||||
"""
|
||||
return Member(self._flow.flip(), self._description, reset=self._reset,
|
||||
return Member(self._flow.flip(), self._description, init=self._init,
|
||||
_dimensions=self._dimensions)
|
||||
|
||||
def array(self, *dimensions):
|
||||
|
|
@ -175,7 +189,7 @@ class Member:
|
|||
if not (isinstance(dimension, int) and dimension >= 0):
|
||||
raise TypeError(f"Member array dimensions must be non-negative integers, "
|
||||
f"not {dimension!r}")
|
||||
return Member(self._flow, self._description, reset=self._reset,
|
||||
return Member(self._flow, self._description, init=self._init,
|
||||
_dimensions=(*dimensions, *self._dimensions))
|
||||
|
||||
@property
|
||||
|
|
@ -231,13 +245,13 @@ class Member:
|
|||
return self._description
|
||||
|
||||
@property
|
||||
def reset(self):
|
||||
"""Reset value of a port member.
|
||||
def init(self):
|
||||
"""Initial value of a port member.
|
||||
|
||||
Returns
|
||||
-------
|
||||
:ref:`const-castable object <lang-constcasting>`
|
||||
The reset value that was provided when constructing this :class:`Member`.
|
||||
The initial value that was provided when constructing this :class:`Member`.
|
||||
|
||||
Raises
|
||||
------
|
||||
|
|
@ -245,8 +259,14 @@ class Member:
|
|||
If :py:`self` describes a signature member.
|
||||
"""
|
||||
if self.is_signature:
|
||||
raise AttributeError(f"A signature member does not have a reset value")
|
||||
return self._reset
|
||||
raise AttributeError(f"A signature member does not have an initial value")
|
||||
return self._init
|
||||
|
||||
@property
|
||||
def reset(self):
|
||||
warnings.warn("`Member.reset` is deprecated, use `Member.init` instead",
|
||||
DeprecationWarning, stacklevel=2)
|
||||
return self.init
|
||||
|
||||
@property
|
||||
def signature(self):
|
||||
|
|
@ -288,16 +308,16 @@ class Member:
|
|||
return (type(other) is Member and
|
||||
self._flow == other._flow and
|
||||
self._description == other._description and
|
||||
self._reset == other._reset and
|
||||
self._init == other._init and
|
||||
self._dimensions == other._dimensions)
|
||||
|
||||
def __repr__(self):
|
||||
reset_repr = dimensions_repr = ""
|
||||
if self._reset:
|
||||
reset_repr = f", reset={self._reset!r}"
|
||||
init_repr = dimensions_repr = ""
|
||||
if self._init:
|
||||
init_repr = f", init={self._init!r}"
|
||||
if self._dimensions:
|
||||
dimensions_repr = f".array({', '.join(map(str, self._dimensions))})"
|
||||
return f"{self._flow!r}({self._description!r}{reset_repr}){dimensions_repr}"
|
||||
return f"{self._flow!r}({self._description!r}{init_repr}){dimensions_repr}"
|
||||
|
||||
|
||||
@final
|
||||
|
|
@ -470,7 +490,7 @@ class SignatureMembers(Mapping):
|
|||
def create(self, *, path=None, src_loc_at=0):
|
||||
"""Create members from their descriptions.
|
||||
|
||||
For each port member, this function creates a :class:`Signal` with the shape and reset
|
||||
For each port member, this function creates a :class:`Signal` with the shape and initial
|
||||
value taken from the member description, and the name constructed from
|
||||
the :ref:`paths <wiring-path>` to the member (by concatenating path items with a double
|
||||
underscore, ``__``).
|
||||
|
|
@ -497,7 +517,7 @@ class SignatureMembers(Mapping):
|
|||
# Ideally we would keep both source locations here, but the .src_loc attribute
|
||||
# data structure doesn't currently support that, so keep the more important one:
|
||||
# the one with the name of the signal (the `In()`/`Out()` call site.)
|
||||
signal = Signal(member.shape, reset=member.reset, src_loc_at=1 + src_loc_at,
|
||||
signal = Signal(member.shape, init=member.init, src_loc_at=1 + src_loc_at,
|
||||
name="__".join(str(item) for item in path))
|
||||
signal.src_loc = member.src_loc
|
||||
return signal
|
||||
|
|
@ -775,7 +795,7 @@ class Signature(metaclass=SignatureMeta):
|
|||
|
||||
def iter_member(value, *, path):
|
||||
if member.is_port:
|
||||
yield path, Member(member.flow, member.shape, reset=member.reset), value
|
||||
yield path, Member(member.flow, member.shape, init=member.init), value
|
||||
elif member.is_signature:
|
||||
for sub_path, sub_member, sub_value in member.signature.flatten(value):
|
||||
if member.flow == In:
|
||||
|
|
@ -816,7 +836,7 @@ class Signature(metaclass=SignatureMeta):
|
|||
* for port members, is a :ref:`value-like <lang-valuelike>` object casting to
|
||||
a :class:`Signal` or a :class:`Const` whose width and signedness is the same as that
|
||||
of the member, and (in case of a :class:`Signal`) which is not reset-less and whose
|
||||
reset value is that of the member;
|
||||
initial value is that of the member;
|
||||
* for signature members, matches the description in the signature as verified by
|
||||
:meth:`Signature.is_compliant`.
|
||||
|
||||
|
|
@ -878,11 +898,11 @@ class Signature(metaclass=SignatureMeta):
|
|||
f"the shape {_format_shape(attr_shape)}")
|
||||
return False
|
||||
if isinstance(attr_value_cast, Signal):
|
||||
if attr_value_cast.reset != member._reset_as_const.value:
|
||||
if attr_value_cast.init != member._init_as_const.value:
|
||||
if reasons is not None:
|
||||
reasons.append(f"{_format_path(path)} is expected to have "
|
||||
f"the reset value {member.reset!r}, but it has "
|
||||
f"the reset value {attr_value_cast.reset!r}")
|
||||
f"the initial value {member.init!r}, but it has "
|
||||
f"the initial value {attr_value_cast.init!r}")
|
||||
return False
|
||||
if attr_value_cast.reset_less:
|
||||
if reasons is not None:
|
||||
|
|
@ -1343,7 +1363,7 @@ def connect(m, *args, **kwargs):
|
|||
|
||||
* Every interface object must have the same set of port members, and they must have the same
|
||||
:meth:`dimensions <Member.dimensions>`.
|
||||
* For each path, the port members of every interface object must have the same width and reset
|
||||
* For each path, the port members of every interface object must have the same width and initial
|
||||
value (for port members corresponding to signals) or constant value (for port members
|
||||
corresponding to constants). Signedness may differ.
|
||||
* For each path, at most one interface object must have the corresponding port member be
|
||||
|
|
@ -1486,8 +1506,8 @@ def connect(m, *args, **kwargs):
|
|||
is_first = False
|
||||
first_path = path
|
||||
first_member_shape = member.shape
|
||||
first_member_reset = member.reset
|
||||
first_member_reset_as_const = member._reset_as_const
|
||||
first_member_init = member.init
|
||||
first_member_init_as_const = member._init_as_const
|
||||
continue
|
||||
if Shape.cast(first_member_shape).width != Shape.cast(member_shape).width:
|
||||
raise ConnectionError(
|
||||
|
|
@ -1496,13 +1516,13 @@ def connect(m, *args, **kwargs):
|
|||
f"shape {_format_shape(member_shape)} because the shape widths "
|
||||
f"({Shape.cast(first_member_shape).width} and "
|
||||
f"{Shape.cast(member_shape).width}) do not match")
|
||||
if first_member_reset_as_const.value != member._reset_as_const.value:
|
||||
if first_member_init_as_const.value != member._init_as_const.value:
|
||||
raise ConnectionError(
|
||||
f"Cannot connect together the member {_format_path(first_path)} with reset "
|
||||
f"value {first_member_reset!r} and the member {_format_path(path)} with reset "
|
||||
f"value {member.reset} because the reset values do not match")
|
||||
f"Cannot connect together the member {_format_path(first_path)} with initial "
|
||||
f"value {first_member_init!r} and the member {_format_path(path)} with initial "
|
||||
f"value {member.init} because the initial values do not match")
|
||||
# If there are no Out members, there is nothing to connect. The In members, while not
|
||||
# explicitly connected, will stay at the same value since we ensured their reset values
|
||||
# explicitly connected, will stay at the same value since we ensured their initial values
|
||||
# are all identical.
|
||||
if len(out_kind) == 0:
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -436,7 +436,7 @@ class _FragmentCompiler:
|
|||
if domain_name == "comb":
|
||||
for signal in domain_signals:
|
||||
signal_index = self.state.get_signal(signal)
|
||||
emitter.append(f"next_{signal_index} = {signal.reset}")
|
||||
emitter.append(f"next_{signal_index} = {signal.init}")
|
||||
|
||||
inputs = SignalSet()
|
||||
_StatementCompiler(self.state, emitter, inputs=inputs)(domain_stmts)
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ class Simulator:
|
|||
def add_process(self, process):
|
||||
process = self._check_process(process)
|
||||
def wrapper():
|
||||
# Only start a bench process after comb settling, so that the reset values are correct.
|
||||
# Only start a bench process after comb settling, so that the initial values are correct.
|
||||
yield object.__new__(Settle)
|
||||
yield from process()
|
||||
self._engine.add_coroutine_process(wrapper, default_cmd=None)
|
||||
|
|
@ -188,7 +188,7 @@ class Simulator:
|
|||
|
||||
if phase is None:
|
||||
# By default, delay the first edge by half period. This causes any synchronous activity
|
||||
# to happen at a non-zero time, distinguishing it from the reset values in the waveform
|
||||
# to happen at a non-zero time, distinguishing it from the initial values in the waveform
|
||||
# viewer.
|
||||
phase = period // 2
|
||||
else:
|
||||
|
|
@ -199,7 +199,7 @@ class Simulator:
|
|||
def reset(self):
|
||||
"""Reset the simulation.
|
||||
|
||||
Assign the reset value to every signal in the simulation, and restart every user process.
|
||||
Assign the initial value to every signal and memory in the simulation, and restart every user process.
|
||||
"""
|
||||
self._engine.reset()
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ class _VCDWriter:
|
|||
self.vcd_signal_vars[signal] = []
|
||||
self.gtkw_signal_names[signal] = []
|
||||
for repr in signal._value_repr:
|
||||
var_init = self.eval_field(repr.value, signal, signal.reset)
|
||||
var_init = self.eval_field(repr.value, signal, signal.init)
|
||||
if isinstance(repr.format, FormatInt):
|
||||
var_type = "wire"
|
||||
var_size = repr.value.shape().width
|
||||
|
|
@ -250,7 +250,7 @@ class _PySignalState(BaseSignalState):
|
|||
self.signal = signal
|
||||
self.pending = pending
|
||||
self.waiters = {}
|
||||
self.curr = self.next = signal.reset
|
||||
self.curr = self.next = signal.init
|
||||
|
||||
def set(self, value):
|
||||
if self.next == value:
|
||||
|
|
@ -342,7 +342,7 @@ class _PySimulation(BaseSimulation):
|
|||
for signal, index in self.signals.items():
|
||||
state = self.slots[index]
|
||||
assert isinstance(state, _PySignalState)
|
||||
state.curr = state.next = signal.reset
|
||||
state.curr = state.next = signal.init
|
||||
for index in self.memories.values():
|
||||
state = self.slots[index]
|
||||
assert isinstance(state, _PyMemoryState)
|
||||
|
|
|
|||
|
|
@ -1174,7 +1174,7 @@ class XilinxPlatform(TemplatedPlatform):
|
|||
def get_ff_sync(self, ff_sync):
|
||||
m = Module()
|
||||
flops = [Signal(ff_sync.i.shape(), name=f"stage{index}",
|
||||
reset=ff_sync._reset, reset_less=ff_sync._reset_less,
|
||||
init=ff_sync._reset, reset_less=ff_sync._reset_less,
|
||||
attrs={"ASYNC_REG": "TRUE"})
|
||||
for index in range(ff_sync._stages)]
|
||||
if self.toolchain == "Vivado":
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue