Implement RFC 43: Rename reset= to init=.

This commit is contained in:
Wanda 2024-02-14 07:13:12 +01:00 committed by Catherine
parent b30c87fa3e
commit 24a392887a
30 changed files with 476 additions and 276 deletions

View file

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

View file

@ -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}"

View file

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

View file

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

View file

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

View file

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

View file

@ -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.

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -29,6 +29,7 @@ Apply the following changes to code written against Amaranth 0.4 to migrate it t
* Replace uses of ``Value.matches()`` with no patterns with ``Const(1)``
* Update uses of ``amaranth.utils.log2_int(need_pow2=False)`` to :func:`amaranth.utils.ceil_log2`
* Update uses of ``amaranth.utils.log2_int(need_pow2=True)`` to :func:`amaranth.utils.exact_log2`
* Update uses of ``reset=`` keyword argument to ``init=``
* Convert uses of ``Simulator.add_sync_process`` used as testbenches to ``Simulator.add_testbench``
* Convert other uses of ``Simulator.add_sync_process`` to ``Simulator.add_process``
@ -39,11 +40,13 @@ Implemented RFCs
.. _RFC 17: https://amaranth-lang.org/rfcs/0017-remove-log2-int.html
.. _RFC 27: https://amaranth-lang.org/rfcs/0027-simulator-testbenches.html
.. _RFC 39: https://amaranth-lang.org/rfcs/0039-empty-case.html
.. _RFC 43: https://amaranth-lang.org/rfcs/0043-rename-reset-to-init.html
.. _RFC 46: https://amaranth-lang.org/rfcs/0046-shape-range-1.html
* `RFC 17`_: Remove ``log2_int``
* `RFC 27`_: Testbench processes for the simulator
* `RFC 39`_: Change semantics of no-argument ``m.Case()``
* `RFC 43`_: Rename ``reset=`` to ``init=``
* `RFC 46`_: Change ``Shape.cast(range(1))`` to ``unsigned(0)``
@ -56,9 +59,10 @@ Language changes
* Added: :func:`amaranth.utils.ceil_log2`, :func:`amaranth.utils.exact_log2`. (`RFC 17`_)
* Changed: ``m.Case()`` with no patterns is never active instead of always active. (`RFC 39`_)
* Changed: ``Value.matches()`` with no patterns is ``Const(0)`` instead of ``Const(1)``. (`RFC 39`_)
* Changed: ``Signal(range(stop), reset=stop)`` warning has been changed into a hard error and made to trigger on any out-of range value.
* Changed: ``Signal(range(stop), init=stop)`` warning has been changed into a hard error and made to trigger on any out-of range value.
* Changed: ``Signal(range(0))`` is now valid without a warning.
* Changed: ``Shape.cast(range(1))`` is now ``unsigned(0)``. (`RFC 46`_)
* Changed: the ``reset=`` argument of :class:`Signal`, :meth:`Signal.like`, :class:`amaranth.lib.wiring.Member`, :class:`amaranth.lib.cdc.FFSynchronizer`, and ``m.FSM()`` has been renamed to ``init=``. (`RFC 43`_)
* Deprecated: :func:`amaranth.utils.log2_int`. (`RFC 17`_)
* Removed: (deprecated in 0.4) :meth:`Const.normalize`. (`RFC 5`_)
* Removed: (deprecated in 0.4) :class:`Repl`. (`RFC 10`_)

View file

@ -407,19 +407,17 @@ The names do not need to be unique; if two signals with the same name end up in
Initial signal values
---------------------
Each signal has an *initial value*, specified with the ``reset=`` parameter. If the initial value is not specified explicitly, zero is used by default. An initial value can be specified with an integer or an enumeration member.
Each signal has an *initial value*, specified with the ``init=`` parameter. If the initial value is not specified explicitly, zero is used by default. An initial value can be specified with an integer or an enumeration member.
Signals :ref:`assigned <lang-assigns>` in a :ref:`combinatorial <lang-comb>` domain assume their initial value when none of the assignments are :ref:`active <lang-active>`. Signals assigned in a :ref:`synchronous <lang-sync>` domain assume their initial value after *power-on reset* and, unless the signal is :ref:`reset-less <lang-resetless>`, *explicit reset*. Signals that are used but never assigned are equivalent to constants of their initial value.
.. TODO: using "reset" for "initial value" is awful, let's rename it to "init"
.. doctest::
>>> Signal(4).reset
>>> Signal(4).init
0
>>> Signal(4, reset=5).reset
>>> Signal(4, init=5).init
5
>>> Signal(Direction, reset=Direction.LEFT).reset
>>> Signal(Direction, init=Direction.LEFT).init
1
@ -467,7 +465,7 @@ In contrast, code written in the Amaranth language *describes* computations on a
.. doctest::
>>> a = Signal(8, reset=5)
>>> a = Signal(8, init=5)
>>> a + 1
(+ (sig a) (const 1'd1))
@ -1190,11 +1188,11 @@ Simple `finite state machines <https://en.wikipedia.org/wiki/Finite-state_machin
.. TODO: FSM() should require keyword arguments, for good measure
The reset state of the FSM can be provided when defining it using the :py:`with m.FSM(reset="Name"):` argument. If not provided, it is the first state in the order of definition. For example, this definition is equivalent to the one at the beginning of this section:
The initial (and reset) state of the FSM can be provided when defining it using the :py:`with m.FSM(init="Name"):` argument. If not provided, it is the first state in the order of definition. For example, this definition is equivalent to the one at the beginning of this section:
.. testcode::
with m.FSM(reset="Set Address"):
with m.FSM(init="Set Address"):
...
The FSM belongs to a :ref:`clock domain <lang-domains>`, which is specified using the :py:`with m.FSM(domain="dom")` argument. If not specified, it is the ``sync`` domain. For example, this definition is equivalent to the one at the beginning of this section:
@ -1255,7 +1253,7 @@ Consider the following code:
.. testcode::
a = Signal(8, reset=1)
a = Signal(8, init=1)
with m.If(en):
m.d.comb += a.eq(b + 1)
@ -1534,7 +1532,7 @@ The application of control flow modifiers in it causes the behavior of the final
with m.If(en):
m.d.sync += n.eq(n + 1)
with m.If(rst):
m.d.sync += n.eq(n.reset)
m.d.sync += n.eq(n.init)
m.d.comb += z.eq(n == 0)
.. tip::

View file

@ -555,7 +555,7 @@ Signatures
.. autodata:: Out
.. autodata:: In
.. autoclass:: Member(flow, description, *, reset=None)
.. autoclass:: Member(flow, description, *, init=None)
.. autoexception:: SignatureError

View file

@ -4,7 +4,7 @@ from amaranth.cli import main
class Counter(Elaboratable):
def __init__(self, width):
self.v = Signal(width, reset=2**width-1)
self.v = Signal(width, init=2**width-1)
self.o = Signal()
def elaborate(self, platform):

View file

@ -5,7 +5,7 @@ from amaranth.back import verilog
class Counter(Elaboratable):
def __init__(self, width):
self.v = Signal(width, reset=2**width-1)
self.v = Signal(width, init=2**width-1)
self.o = Signal()
self.en = Signal()

View file

@ -7,7 +7,7 @@ cd_por = ClockDomain(reset_less=True)
cd_sync = ClockDomain()
m.domains += cd_por, cd_sync
delay = Signal(range(256), reset=255)
delay = Signal(range(256), init=255)
with m.If(delay != 0):
m.d.por += delay.eq(delay - 1)
m.d.comb += [

View file

@ -32,7 +32,7 @@ class UART(Elaboratable):
m = Module()
tx_phase = Signal(range(self.divisor))
tx_shreg = Signal(1 + self.data_bits + 1, reset=-1)
tx_shreg = Signal(1 + self.data_bits + 1, init=-1)
tx_count = Signal(range(len(tx_shreg) + 1))
m.d.comb += self.tx_o.eq(tx_shreg[0])
@ -55,7 +55,7 @@ class UART(Elaboratable):
]
rx_phase = Signal(range(self.divisor))
rx_shreg = Signal(1 + self.data_bits + 1, reset=-1)
rx_shreg = Signal(1 + self.data_bits + 1, init=-1)
rx_count = Signal(range(len(rx_shreg) + 1))
m.d.comb += self.rx_data.eq(rx_shreg[1:-1])

View file

@ -1140,24 +1140,26 @@ class SignalTestCase(FHDLTestCase):
s2 = Signal(name="sig")
self.assertEqual(s2.name, "sig")
def test_reset(self):
s1 = Signal(4, reset=0b111, reset_less=True)
self.assertEqual(s1.reset, 0b111)
def test_init(self):
s1 = Signal(4, init=0b111, reset_less=True)
self.assertEqual(s1.init, 0b111)
self.assertEqual(s1.reset_less, True)
s2 = Signal.like(s1, init=0b011)
self.assertEqual(s2.init, 0b011)
def test_reset_enum(self):
s1 = Signal(2, reset=UnsignedEnum.BAR)
self.assertEqual(s1.reset, 2)
def test_init_enum(self):
s1 = Signal(2, init=UnsignedEnum.BAR)
self.assertEqual(s1.init, 2)
with self.assertRaisesRegex(TypeError,
r"^Reset value must be a constant-castable expression, "
r"^Initial value must be a constant-castable expression, "
r"not <StringEnum\.FOO: 'a'>$"):
Signal(1, reset=StringEnum.FOO)
Signal(1, init=StringEnum.FOO)
def test_reset_const_castable(self):
s1 = Signal(4, reset=Cat(Const(0, 1), Const(1, 1), Const(0, 2)))
self.assertEqual(s1.reset, 2)
def test_init_const_castable(self):
s1 = Signal(4, init=Cat(Const(0, 1), Const(1, 1), Const(0, 2)))
self.assertEqual(s1.init, 2)
def test_reset_shape_castable_const(self):
def test_init_shape_castable_const(self):
class CastableFromHex(ShapeCastable):
def as_shape(self):
return unsigned(8)
@ -1168,54 +1170,80 @@ class SignalTestCase(FHDLTestCase):
def const(self, init):
return int(init, 16)
s1 = Signal(CastableFromHex(), reset="aa")
self.assertEqual(s1.reset, 0xaa)
s1 = Signal(CastableFromHex(), init="aa")
self.assertEqual(s1.init, 0xaa)
with self.assertRaisesRegex(ValueError,
r"^Constant returned by <.+?CastableFromHex.+?>\.const\(\) must have the shape "
r"that it casts to, unsigned\(8\), and not unsigned\(1\)$"):
Signal(CastableFromHex(), reset="01")
Signal(CastableFromHex(), init="01")
def test_reset_shape_castable_enum_wrong(self):
def test_init_shape_castable_enum_wrong(self):
class EnumA(AmaranthEnum, shape=1):
X = 1
with self.assertRaisesRegex(TypeError,
r"^Reset value must be a constant initializer of <enum 'EnumA'>$"):
Signal(EnumA) # implied reset=0
r"^Initial value must be a constant initializer of <enum 'EnumA'>$"):
Signal(EnumA) # implied init=0
def test_reset_signed_mismatch(self):
def test_init_signed_mismatch(self):
with self.assertWarnsRegex(SyntaxWarning,
r"^Reset value -2 is signed, but the signal shape is unsigned\(2\)$"):
Signal(unsigned(2), reset=-2)
r"^Initial value -2 is signed, but the signal shape is unsigned\(2\)$"):
Signal(unsigned(2), init=-2)
def test_reset_wrong_too_wide(self):
def test_init_wrong_too_wide(self):
with self.assertWarnsRegex(SyntaxWarning,
r"^Reset value 2 will be truncated to the signal shape unsigned\(1\)$"):
Signal(unsigned(1), reset=2)
r"^Initial value 2 will be truncated to the signal shape unsigned\(1\)$"):
Signal(unsigned(1), init=2)
with self.assertWarnsRegex(SyntaxWarning,
r"^Reset value 1 will be truncated to the signal shape signed\(1\)$"):
Signal(signed(1), reset=1)
r"^Initial value 1 will be truncated to the signal shape signed\(1\)$"):
Signal(signed(1), init=1)
with self.assertWarnsRegex(SyntaxWarning,
r"^Reset value -2 will be truncated to the signal shape signed\(1\)$"):
Signal(signed(1), reset=-2)
r"^Initial value -2 will be truncated to the signal shape signed\(1\)$"):
Signal(signed(1), init=-2)
def test_reset_wrong_fencepost(self):
def test_init_wrong_fencepost(self):
with self.assertRaisesRegex(SyntaxError,
r"^Reset value 10 equals the non-inclusive end of the signal shape "
r"^Initial value 10 equals the non-inclusive end of the signal shape "
r"range\(0, 10\); this is likely an off-by-one error$"):
Signal(range(0, 10), reset=10)
Signal(range(0, 10), init=10)
with self.assertRaisesRegex(SyntaxError,
r"^Reset value 0 equals the non-inclusive end of the signal shape "
r"^Initial value 0 equals the non-inclusive end of the signal shape "
r"range\(0, 0\); this is likely an off-by-one error$"):
Signal(range(0), reset=0)
Signal(range(0), init=0)
def test_reset_wrong_range(self):
def test_init_wrong_range(self):
with self.assertRaisesRegex(SyntaxError,
r"^Reset value 11 is not within the signal shape range\(0, 10\)$"):
Signal(range(0, 10), reset=11)
r"^Initial value 11 is not within the signal shape range\(0, 10\)$"):
Signal(range(0, 10), init=11)
with self.assertRaisesRegex(SyntaxError,
r"^Reset value 0 is not within the signal shape range\(1, 10\)$"):
Signal(range(1, 10), reset=0)
r"^Initial value 0 is not within the signal shape range\(1, 10\)$"):
Signal(range(1, 10), init=0)
def test_reset(self):
with self.assertWarnsRegex(DeprecationWarning,
r"^`reset=` is deprecated, use `init=` instead$"):
s1 = Signal(4, reset=0b111)
self.assertEqual(s1.init, 0b111)
with self.assertWarnsRegex(DeprecationWarning,
r"^`Signal.reset` is deprecated, use `Signal.init` instead$"):
self.assertEqual(s1.reset, 0b111)
with self.assertWarnsRegex(DeprecationWarning,
r"^`Signal.reset` is deprecated, use `Signal.init` instead$"):
s1.reset = 0b010
self.assertEqual(s1.init, 0b010)
with self.assertWarnsRegex(DeprecationWarning,
r"^`reset=` is deprecated, use `init=` instead$"):
s2 = Signal.like(s1, reset=3)
self.assertEqual(s2.init, 3)
def test_reset_wrong(self):
with self.assertRaisesRegex(ValueError,
r"^Cannot specify both `reset` and `init`$"):
Signal(4, reset=1, init=1)
s1 = Signal(4)
with self.assertRaisesRegex(ValueError,
r"^Cannot specify both `reset` and `init`$"):
Signal.like(s1, reset=1, init=1)
def test_attrs(self):
s1 = Signal()
@ -1232,8 +1260,8 @@ class SignalTestCase(FHDLTestCase):
self.assertEqual(s1.shape(), unsigned(4))
s2 = Signal.like(Signal(range(-15, 1)))
self.assertEqual(s2.shape(), signed(5))
s3 = Signal.like(Signal(4, reset=0b111, reset_less=True))
self.assertEqual(s3.reset, 0b111)
s3 = Signal.like(Signal(4, init=0b111, reset_less=True))
self.assertEqual(s3.init, 0b111)
self.assertEqual(s3.reset_less, True)
s4 = Signal.like(Signal(attrs={"no_retiming": True}))
self.assertEqual(s4.attrs, {"no_retiming": True})

View file

@ -451,7 +451,7 @@ class DSLTestCase(FHDLTestCase):
RED = 1
BLUE = 2
m = Module()
se = Signal(Color, reset=Color.RED)
se = Signal(Color, init=Color.RED)
with m.Switch(se):
with m.Case(Color.RED):
m.d.comb += self.c1.eq(1)
@ -638,10 +638,10 @@ class DSLTestCase(FHDLTestCase):
1: "SECOND"
}))
def test_FSM_reset(self):
def test_FSM_init(self):
a = Signal()
m = Module()
with m.FSM(reset="SECOND"):
with m.FSM(init="SECOND"):
with m.State("FIRST"):
m.d.comb += a.eq(0)
m.next = "SECOND"
@ -670,6 +670,55 @@ class DSLTestCase(FHDLTestCase):
)
)
""")
self.assertEqual(m._generated["fsm"].state.init, 1)
def test_FSM_reset(self):
a = Signal()
m = Module()
with self.assertWarnsRegex(DeprecationWarning,
r"^`reset=` is deprecated, use `init=` instead$"):
with m.FSM(reset="SECOND"):
with m.State("FIRST"):
m.d.comb += a.eq(0)
m.next = "SECOND"
with m.State("SECOND"):
m.next = "FIRST"
m._flush()
self.assertRepr(m._statements["comb"], """
(
(switch (sig fsm_state)
(case 0
(eq (sig a) (const 1'd0))
)
(case 1 )
)
)
""")
self.assertRepr(m._statements["sync"], """
(
(switch (sig fsm_state)
(case 0
(eq (sig fsm_state) (const 1'd1))
)
(case 1
(eq (sig fsm_state) (const 1'd0))
)
)
)
""")
self.assertEqual(m._generated["fsm"].state.init, 1)
def test_FSM_reset_wrong(self):
a = Signal()
m = Module()
with self.assertRaisesRegex(ValueError,
r"^Cannot specify both `reset` and `init`$"):
with m.FSM(reset="SECOND", init="SECOND"):
with m.State("FIRST"):
m.d.comb += a.eq(0)
m.next = "SECOND"
with m.State("SECOND"):
m.next = "FIRST"
def test_FSM_ongoing(self):
a = Signal()
@ -683,7 +732,7 @@ class DSLTestCase(FHDLTestCase):
with m.State("SECOND"):
pass
m._flush()
self.assertEqual(m._generated["fsm"].state.reset, 1)
self.assertEqual(m._generated["fsm"].state.init, 1)
self.maxDiff = 10000
self.assertRepr(m._statements["comb"], """
(

View file

@ -59,7 +59,7 @@ class MemoryTestCase(FHDLTestCase):
self.assertEqual(len(rdport.data), 8)
self.assertEqual(len(rdport.en), 1)
self.assertIsInstance(rdport.en, Signal)
self.assertEqual(rdport.en.reset, 1)
self.assertEqual(rdport.en.init, 1)
def test_read_port_non_transparent(self):
mem = Memory(width=8, depth=4)
@ -69,7 +69,7 @@ class MemoryTestCase(FHDLTestCase):
self.assertEqual(rdport.transparent, False)
self.assertEqual(len(rdport.en), 1)
self.assertIsInstance(rdport.en, Signal)
self.assertEqual(rdport.en.reset, 1)
self.assertEqual(rdport.en.init, 1)
def test_read_port_asynchronous(self):
mem = Memory(width=8, depth=4)

View file

@ -199,11 +199,11 @@ class RecordTestCase(FHDLTestCase):
self.assertEqual(r1.a.name, "r1__a")
self.assertEqual(r1.b.name, "r1__b")
self.assertEqual(r1.b.s.name, "r1__b__s")
r1.a.reset = 1
r1.b.s.reset = 1
r1.a.init = 1
r1.b.s.init = 1
r2 = Record.like(r1)
self.assertEqual(r2.a.reset, 1)
self.assertEqual(r2.b.s.reset, 1)
self.assertEqual(r2.a.init, 1)
self.assertEqual(r2.b.s.init, 1)
self.assertEqual(r2.a.name, "r2__a")
self.assertEqual(r2.b.name, "r2__b")
self.assertEqual(r2.b.s.name, "r2__b__s")

View file

@ -247,8 +247,8 @@ class DomainLowererTestCase(FHDLTestCase):
class ResetInserterTestCase(FHDLTestCase):
def setUp(self):
self.s1 = Signal()
self.s2 = Signal(reset=1)
self.s3 = Signal(reset=1, reset_less=True)
self.s2 = Signal(init=1)
self.s3 = Signal(init=1, reset_less=True)
self.c1 = Signal()
def test_reset_default(self):

View file

@ -35,10 +35,10 @@ class FFSynchronizerTestCase(FHDLTestCase):
sim.add_process(process)
sim.run()
def test_reset_value(self):
i = Signal(reset=1)
def test_init_value(self):
i = Signal(init=1)
o = Signal()
frag = FFSynchronizer(i, o, reset=1)
frag = FFSynchronizer(i, o, init=1)
sim = Simulator(frag)
sim.add_clock(1e-6)
@ -54,6 +54,34 @@ class FFSynchronizerTestCase(FHDLTestCase):
sim.add_process(process)
sim.run()
def test_reset_value(self):
i = Signal(init=1)
o = Signal()
with self.assertWarnsRegex(DeprecationWarning,
r"^`reset=` is deprecated, use `init=` instead$"):
frag = FFSynchronizer(i, o, reset=1)
sim = Simulator(frag)
sim.add_clock(1e-6)
def process():
self.assertEqual((yield o), 1)
yield i.eq(0)
yield Tick()
self.assertEqual((yield o), 1)
yield Tick()
self.assertEqual((yield o), 1)
yield Tick()
self.assertEqual((yield o), 0)
sim.add_process(process)
sim.run()
def test_reset_wrong(self):
i = Signal(init=1)
o = Signal()
with self.assertRaisesRegex(ValueError,
r"^Cannot specify both `reset` and `init`$"):
FFSynchronizer(i, o, reset=1, init=1)
class AsyncFFSynchronizerTestCase(FHDLTestCase):
def test_stages_wrong(self):
@ -115,7 +143,7 @@ class AsyncFFSynchronizerTestCase(FHDLTestCase):
sim.run()
def test_neg_edge(self):
i = Signal(reset=1)
i = Signal(init=1)
o = Signal()
m = Module()
m.domains += ClockDomain("sync")
@ -166,7 +194,7 @@ class ResetSynchronizerTestCase(FHDLTestCase):
m = Module()
m.domains += ClockDomain("sync")
m.submodules += ResetSynchronizer(arst)
s = Signal(reset=1)
s = Signal(init=1)
m.d.sync += s.eq(0)
sim = Simulator(m)

View file

@ -430,13 +430,13 @@ class LayoutTestCase(FHDLTestCase):
sl = StructLayout({"f": unsigned(1)})
self.assertRepr(sl.const({"f": Const(1)}).as_value(), "(const 1'd1)")
def test_signal_reset(self):
def test_signal_init(self):
sl = StructLayout({
"a": unsigned(1),
"b": unsigned(2)
})
self.assertEqual(Signal(sl).as_value().reset, 0)
self.assertEqual(Signal(sl, reset={"a": 0b1, "b": 0b10}).as_value().reset, 5)
self.assertEqual(Signal(sl).as_value().init, 0)
self.assertEqual(Signal(sl, init={"a": 0b1, "b": 0b10}).as_value().init, 5)
class ViewTestCase(FHDLTestCase):
@ -454,17 +454,17 @@ class ViewTestCase(FHDLTestCase):
self.assertEqual(cv.shape(), unsigned(3))
self.assertEqual(cv.name, "v")
def test_construct_signal_reset(self):
def test_construct_signal_init(self):
v1 = Signal(StructLayout({"a": unsigned(1), "b": unsigned(2)}),
reset={"a": 0b1, "b": 0b10})
self.assertEqual(Value.cast(v1).reset, 0b101)
init={"a": 0b1, "b": 0b10})
self.assertEqual(Value.cast(v1).init, 0b101)
v2 = Signal(StructLayout({"a": unsigned(1),
"b": StructLayout({"x": unsigned(1), "y": unsigned(1)})}),
reset={"a": 0b1, "b": {"x": 0b0, "y": 0b1}})
self.assertEqual(Value.cast(v2).reset, 0b101)
init={"a": 0b1, "b": {"x": 0b0, "y": 0b1}})
self.assertEqual(Value.cast(v2).init, 0b101)
v3 = Signal(ArrayLayout(unsigned(2), 2),
reset=[0b01, 0b10])
self.assertEqual(Value.cast(v3).reset, 0b1001)
init=[0b01, 0b10])
self.assertEqual(Value.cast(v3).init, 0b1001)
def test_layout_wrong(self):
with self.assertRaisesRegex(TypeError,
@ -625,13 +625,13 @@ class ViewTestCase(FHDLTestCase):
def test_bug_837_array_layout_getitem_str(self):
with self.assertRaisesRegex(TypeError,
r"^Views with array layout may only be indexed with an integer or a value, "
r"not 'reset'$"):
Signal(ArrayLayout(unsigned(1), 1), reset=[0])["reset"]
r"not 'init'$"):
Signal(ArrayLayout(unsigned(1), 1), init=[0])["init"]
def test_bug_837_array_layout_getattr(self):
with self.assertRaisesRegex(AttributeError,
r"^View of \(sig \$signal\) with an array layout does not have fields$"):
Signal(ArrayLayout(unsigned(1), 1), reset=[0]).reset
Signal(ArrayLayout(unsigned(1), 1), init=[0]).init
def test_eq(self):
s1 = Signal(StructLayout({"a": unsigned(2)}))
@ -735,7 +735,7 @@ class StructTestCase(FHDLTestCase):
self.assertRepr(v.b.q.r, "(s (slice (slice (slice (sig v) 1:9) 4:8) 0:2))")
self.assertRepr(v.b.q.s, "(s (slice (slice (slice (sig v) 1:9) 4:8) 2:4))")
def test_construct_reset(self):
def test_construct_init(self):
class S(Struct):
p: 4
q: 2 = 1
@ -744,11 +744,11 @@ class StructTestCase(FHDLTestCase):
S.q
v1 = Signal(S)
self.assertEqual(v1.as_value().reset, 0b010000)
v2 = Signal(S, reset=dict(p=0b0011))
self.assertEqual(v2.as_value().reset, 0b010011)
v3 = Signal(S, reset=dict(p=0b0011, q=0b00))
self.assertEqual(v3.as_value().reset, 0b000011)
self.assertEqual(v1.as_value().init, 0b010000)
v2 = Signal(S, init=dict(p=0b0011))
self.assertEqual(v2.as_value().init, 0b010011)
v3 = Signal(S, init=dict(p=0b0011, q=0b00))
self.assertEqual(v3.as_value().init, 0b000011)
def test_shape_undefined_wrong(self):
class S(Struct):
@ -835,33 +835,33 @@ class UnionTestCase(FHDLTestCase):
self.assertRepr(v.a, "(slice (sig v) 0:1)")
self.assertRepr(v.b, "(s (slice (sig v) 0:3))")
def test_define_reset_two_wrong(self):
def test_define_init_two_wrong(self):
with self.assertRaisesRegex(ValueError,
r"^Reset value for at most one field can be provided for a union class "
r"^Initial value for at most one field can be provided for a union class "
r"\(specified: a, b\)$"):
class U(Union):
a: unsigned(1) = 1
b: unsigned(2) = 1
def test_construct_reset_two_wrong(self):
def test_construct_init_two_wrong(self):
class U(Union):
a: unsigned(1)
b: unsigned(2)
with self.assertRaisesRegex(TypeError,
r"^Reset value must be a constant initializer of <class '.+?\.U'>$") as cm:
Signal(U, reset=dict(a=1, b=2))
r"^Initial value must be a constant initializer of <class '.+?\.U'>$") as cm:
Signal(U, init=dict(a=1, b=2))
self.assertRegex(cm.exception.__cause__.message,
r"^Initializer for at most one field can be provided for a union "
r"class \(specified: a, b\)$")
def test_construct_reset_override(self):
def test_construct_init_override(self):
class U(Union):
a: unsigned(1) = 1
b: unsigned(2)
self.assertEqual(Signal(U).as_value().reset, 0b01)
self.assertEqual(Signal(U, reset=dict(b=0b10)).as_value().reset, 0b10)
self.assertEqual(Signal(U).as_value().init, 0b01)
self.assertEqual(Signal(U, init=dict(b=0b10)).as_value().init, 0b10)
# Examples from https://github.com/amaranth-lang/amaranth/issues/693

View file

@ -36,7 +36,7 @@ class MemberTestCase(unittest.TestCase):
self.assertEqual(member.flow, In)
self.assertEqual(member.is_port, True)
self.assertEqual(member.shape, unsigned(1))
self.assertEqual(member.reset, None)
self.assertEqual(member.init, None)
self.assertEqual(member.is_signature, False)
with self.assertRaisesRegex(AttributeError,
r"^A port member does not have a signature$"):
@ -50,33 +50,58 @@ class MemberTestCase(unittest.TestCase):
r"not 'whatever'$"):
Member(In, "whatever")
def test_port_member_reset(self):
member = Member(Out, unsigned(1), reset=1)
def test_port_member_init(self):
member = Member(Out, unsigned(1), init=1)
self.assertEqual(member.flow, Out)
self.assertEqual(member.shape, unsigned(1))
self.assertEqual(member.reset, 1)
self.assertEqual(repr(member._reset_as_const), repr(Const(1, 1)))
self.assertEqual(repr(member), "Out(unsigned(1), reset=1)")
self.assertEqual(member.init, 1)
self.assertEqual(repr(member._init_as_const), repr(Const(1, 1)))
self.assertEqual(repr(member), "Out(unsigned(1), init=1)")
def test_port_member_reset_wrong(self):
def test_port_member_init_wrong(self):
with self.assertRaisesRegex(TypeError,
r"^Port member reset value 'no' is not a valid constant initializer "
r"^Port member initial value 'no' is not a valid constant initializer "
r"for unsigned\(1\)$"):
Member(In, 1, reset="no")
Member(In, 1, init="no")
def test_port_member_reset_shape_castable(self):
def test_port_member_init_shape_castable(self):
layout = data.StructLayout({"a": 32})
member = Member(In, layout, reset={"a": 1})
member = Member(In, layout, init={"a": 1})
self.assertEqual(member.flow, In)
self.assertEqual(member.shape, layout)
self.assertEqual(member.reset, {"a": 1})
self.assertEqual(repr(member), "In(StructLayout({'a': 32}), reset={'a': 1})")
self.assertEqual(member.init, {"a": 1})
self.assertEqual(repr(member), "In(StructLayout({'a': 32}), init={'a': 1})")
def test_port_member_reset_shape_castable_wrong(self):
def test_port_member_init_shape_castable_wrong(self):
with self.assertRaisesRegex(TypeError,
r"^Port member reset value 'no' is not a valid constant initializer "
r"^Port member initial value 'no' is not a valid constant initializer "
r"for StructLayout\({'a': 32}\)$"):
Member(In, data.StructLayout({"a": 32}), reset="no")
Member(In, data.StructLayout({"a": 32}), init="no")
def test_port_member_reset(self):
with self.assertWarnsRegex(DeprecationWarning,
r"^`reset=` is deprecated, use `init=` instead$"):
member = Member(Out, unsigned(1), reset=1)
self.assertEqual(member.flow, Out)
self.assertEqual(member.shape, unsigned(1))
self.assertEqual(member.init, 1)
self.assertEqual(repr(member._init_as_const), repr(Const(1, 1)))
self.assertEqual(repr(member), "Out(unsigned(1), init=1)")
with self.assertWarnsRegex(DeprecationWarning,
r"^`Member.reset` is deprecated, use `Member.init` instead$"):
self.assertEqual(member.reset, 1)
with self.assertWarnsRegex(DeprecationWarning,
r"^`reset=` is deprecated, use `init=` instead$"):
member = Out(unsigned(1), reset=1)
self.assertEqual(member.init,1)
def test_port_member_reset_wrong(self):
with self.assertRaisesRegex(ValueError,
r"^Cannot specify both `reset` and `init`$"):
Member(Out, unsigned(1), reset=1, init=1)
with self.assertRaisesRegex(ValueError,
r"^Cannot specify both `reset` and `init`$"):
Out(unsigned(1), reset=1, init=1)
def test_signature_member_out(self):
sig = Signature({"data": Out(unsigned(32))})
@ -87,8 +112,8 @@ class MemberTestCase(unittest.TestCase):
r"^A signature member does not have a shape$"):
member.shape
with self.assertRaisesRegex(AttributeError,
r"^A signature member does not have a reset value$"):
member.reset
r"^A signature member does not have an initial value$"):
member.init
self.assertEqual(member.is_signature, True)
self.assertEqual(member.signature, sig)
self.assertEqual(member.dimensions, ())
@ -103,8 +128,8 @@ class MemberTestCase(unittest.TestCase):
r"^A signature member does not have a shape$"):
member.shape
with self.assertRaisesRegex(AttributeError,
r"^A signature member does not have a reset value$"):
member.reset
r"^A signature member does not have an initial value$"):
member.init
self.assertEqual(member.is_signature, True)
self.assertEqual(member.signature, sig.flip())
self.assertEqual(member.dimensions, ())
@ -112,8 +137,8 @@ class MemberTestCase(unittest.TestCase):
def test_signature_member_wrong(self):
with self.assertRaisesRegex(ValueError,
r"^A signature member cannot have a reset value$"):
Member(In, Signature({}), reset=1)
r"^A signature member cannot have an initial value$"):
Member(In, Signature({}), init=1)
def test_array(self):
array_2 = Member(In, unsigned(1)).array(2)
@ -143,8 +168,8 @@ class MemberTestCase(unittest.TestCase):
def test_equality(self):
self.assertEqual(In(1), In(1))
self.assertNotEqual(In(1), Out(1))
self.assertNotEqual(In(1), In(1, reset=1))
self.assertNotEqual(In(1), In(1, reset=0))
self.assertNotEqual(In(1), In(1, init=1))
self.assertNotEqual(In(1), In(1, init=0))
self.assertEqual(In(1), In(1).array())
self.assertNotEqual(In(1), In(1).array(1))
sig = Signature({})
@ -237,12 +262,12 @@ class SignatureMembersTestCase(unittest.TestCase):
self.assertEqual(attrs["s"].b.shape(), unsigned(2))
self.assertEqual(attrs["s"].b.name, "attrs__s__b")
def test_create_reset(self):
def test_create_init(self):
members = SignatureMembers({
"a": In(1, reset=1),
"a": In(1, init=1),
})
attrs = members.create()
self.assertEqual(attrs["a"].reset, 1)
self.assertEqual(attrs["a"].init, 1)
def test_create_tuple(self):
sig = SignatureMembers({
@ -421,12 +446,12 @@ class SignatureTestCase(unittest.TestCase):
sig=Signature({"a": In(unsigned(1))}),
obj=NS(a=Signal(signed(1))))
self.assertNotCompliant(
r"^'obj\.a' is expected to have the reset value None, but it has the reset value 1$",
r"^'obj\.a' is expected to have the initial value None, but it has the initial value 1$",
sig=Signature({"a": In(1)}),
obj=NS(a=Signal(reset=1)))
obj=NS(a=Signal(init=1)))
self.assertNotCompliant(
r"^'obj\.a' is expected to have the reset value 1, but it has the reset value 0$",
sig=Signature({"a": In(1, reset=1)}),
r"^'obj\.a' is expected to have the initial value 1, but it has the initial value 0$",
sig=Signature({"a": In(1, init=1)}),
obj=NS(a=Signal(1)))
self.assertNotCompliant(
r"^'obj\.a' is expected to not be reset-less$",
@ -820,21 +845,21 @@ class ConnectTestCase(unittest.TestCase):
q=NS(signature=Signature({"a": In(Cycle)}),
a=Signal(Cycle)))
def test_reset_mismatch(self):
def test_init_mismatch(self):
m = Module()
with self.assertRaisesRegex(ConnectionError,
r"^Cannot connect together the member 'q\.a' with reset value 1 and the member "
r"'p\.a' with reset value 0 because the reset values do not match$"):
r"^Cannot connect together the member 'q\.a' with initial value 1 and the member "
r"'p\.a' with initial value 0 because the initial values do not match$"):
connect(m,
p=NS(signature=Signature({"a": Out(1, reset=0)}),
p=NS(signature=Signature({"a": Out(1, init=0)}),
a=Signal()),
q=NS(signature=Signature({"a": In(1, reset=1)}),
a=Signal(reset=1)))
q=NS(signature=Signature({"a": In(1, init=1)}),
a=Signal(init=1)))
def test_reset_none_match(self):
def test_init_none_match(self):
m = Module()
connect(m,
p=NS(signature=Signature({"a": Out(1, reset=0)}),
p=NS(signature=Signature({"a": Out(1, init=0)}),
a=Signal()),
q=NS(signature=Signature({"a": In(1)}),
a=Signal()))

View file

@ -18,12 +18,12 @@ from amaranth._utils import _ignore_deprecated
class SimulatorUnitTestCase(FHDLTestCase):
def assertStatement(self, stmt, inputs, output, reset=0):
def assertStatement(self, stmt, inputs, output, init=0):
inputs = [Value.cast(i) for i in inputs]
output = Value.cast(output)
isigs = [Signal(i.shape(), name=n) for i, n in zip(inputs, "abcd")]
osig = Signal(output.shape(), name="y", reset=reset)
osig = Signal(output.shape(), name="y", init=init)
stmt = stmt(osig, *isigs)
frag = Fragment()
@ -243,9 +243,9 @@ class SimulatorUnitTestCase(FHDLTestCase):
def test_slice_lhs(self):
stmt1 = lambda y, a: y[2].eq(a)
self.assertStatement(stmt1, [C(0b0, 1)], C(0b11111011, 8), reset=0b11111111)
self.assertStatement(stmt1, [C(0b0, 1)], C(0b11111011, 8), init=0b11111111)
stmt2 = lambda y, a: y[2:4].eq(a)
self.assertStatement(stmt2, [C(0b01, 2)], C(0b11110111, 8), reset=0b11111011)
self.assertStatement(stmt2, [C(0b01, 2)], C(0b11110111, 8), init=0b11111011)
def test_bit_select(self):
stmt = lambda y, a, b: y.eq(a.bit_select(b, 3))
@ -255,9 +255,9 @@ class SimulatorUnitTestCase(FHDLTestCase):
def test_bit_select_lhs(self):
stmt = lambda y, a, b: y.bit_select(a, 3).eq(b)
self.assertStatement(stmt, [C(0), C(0b100, 3)], C(0b11111100, 8), reset=0b11111111)
self.assertStatement(stmt, [C(2), C(0b101, 3)], C(0b11110111, 8), reset=0b11111111)
self.assertStatement(stmt, [C(3), C(0b110, 3)], C(0b11110111, 8), reset=0b11111111)
self.assertStatement(stmt, [C(0), C(0b100, 3)], C(0b11111100, 8), init=0b11111111)
self.assertStatement(stmt, [C(2), C(0b101, 3)], C(0b11110111, 8), init=0b11111111)
self.assertStatement(stmt, [C(3), C(0b110, 3)], C(0b11110111, 8), init=0b11111111)
def test_word_select(self):
stmt = lambda y, a, b: y.eq(a.word_select(b, 3))
@ -267,9 +267,9 @@ class SimulatorUnitTestCase(FHDLTestCase):
def test_word_select_lhs(self):
stmt = lambda y, a, b: y.word_select(a, 3).eq(b)
self.assertStatement(stmt, [C(0), C(0b100, 3)], C(0b11111100, 8), reset=0b11111111)
self.assertStatement(stmt, [C(1), C(0b101, 3)], C(0b11101111, 8), reset=0b11111111)
self.assertStatement(stmt, [C(2), C(0b110, 3)], C(0b10111111, 8), reset=0b11111111)
self.assertStatement(stmt, [C(0), C(0b100, 3)], C(0b11111100, 8), init=0b11111111)
self.assertStatement(stmt, [C(1), C(0b101, 3)], C(0b11101111, 8), init=0b11111111)
self.assertStatement(stmt, [C(2), C(0b110, 3)], C(0b10111111, 8), init=0b11111111)
def test_cat(self):
stmt = lambda y, *xs: y.eq(Cat(*xs))
@ -315,9 +315,9 @@ class SimulatorUnitTestCase(FHDLTestCase):
self.assertStatement(stmt, [C(4)], C(10))
def test_array_lhs(self):
l = Signal(3, reset=1)
m = Signal(3, reset=4)
n = Signal(3, reset=7)
l = Signal(3, init=1)
m = Signal(3, init=4)
n = Signal(3, init=7)
array = Array([l, m, n])
stmt = lambda y, a, b: [array[a].eq(b), y.eq(Cat(*array))]
self.assertStatement(stmt, [C(0), C(0b000)], C(0b111100000))
@ -426,7 +426,7 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
sim.run_until(deadline)
def setUp_counter(self):
self.count = Signal(3, reset=4)
self.count = Signal(3, init=4)
self.sync = ClockDomain()
self.m = Module()
@ -876,7 +876,7 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
def test_comb_bench_process(self):
m = Module()
a = Signal(reset=1)
a = Signal(init=1)
b = Signal()
m.d.comb += b.eq(a)
with self.assertSimulation(m) as sim:
@ -890,7 +890,7 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
def test_sync_bench_process(self):
m = Module()
a = Signal(reset=1)
a = Signal(init=1)
b = Signal()
m.d.sync += b.eq(a)
t = Signal()