docs: use :py: role for inline Python code, not :pc:.

I originally picked :pc: as it is short for "python code", but it is
obscure and :py: is not taken, so a much more obvious role can be used
instead. Also, we all typo :pc: as :py: all the time anyway.
This commit is contained in:
Catherine 2024-02-13 10:31:44 +00:00
parent 3cb5f63aba
commit b9c9948038
6 changed files with 371 additions and 371 deletions

View file

@ -87,7 +87,7 @@ class Shape:
@staticmethod @staticmethod
def cast(obj, *, src_loc_at=0): def cast(obj, *, src_loc_at=0):
"""Cast :pc:`obj` to a shape. """Cast :py:`obj` to a shape.
Many :ref:`shape-like <lang-shapelike>` objects can be cast to a shape: Many :ref:`shape-like <lang-shapelike>` objects can be cast to a shape:
@ -104,9 +104,9 @@ class Shape:
Raises Raises
------ ------
TypeError TypeError
If :pc:`obj` cannot be converted to a :class:`Shape`. If :py:`obj` cannot be converted to a :class:`Shape`.
RecursionError RecursionError
If :pc:`obj` is a :class:`ShapeCastable` object that casts to itself. If :py:`obj` is a :class:`ShapeCastable` object that casts to itself.
""" """
while True: while True:
if isinstance(obj, Shape): if isinstance(obj, Shape):
@ -137,7 +137,7 @@ class Shape:
def __repr__(self): def __repr__(self):
"""Python code that creates this shape. """Python code that creates this shape.
Returns :pc:`f"signed({self.width})"` or :pc:`f"unsigned({self.width})"`. Returns :py:`f"signed({self.width})"` or :py:`f"unsigned({self.width})"`.
""" """
if self.signed: if self.signed:
return f"signed({self.width})" return f"signed({self.width})"
@ -150,12 +150,12 @@ class Shape:
def unsigned(width): def unsigned(width):
"""Returns :pc:`Shape(width, signed=False)`.""" """Returns :py:`Shape(width, signed=False)`."""
return Shape(width, signed=False) return Shape(width, signed=False)
def signed(width): def signed(width):
"""Returns :pc:`Shape(width, signed=True)`.""" """Returns :py:`Shape(width, signed=True)`."""
return Shape(width, signed=True) return Shape(width, signed=True)
@ -169,7 +169,7 @@ class ShapeCastable:
extending the Amaranth language in third-party code. extending the Amaranth language in third-party code.
To illustrate their purpose, consider constructing a signal from a shape-castable object To illustrate their purpose, consider constructing a signal from a shape-castable object
:pc:`shape_castable`: :py:`shape_castable`:
.. code:: .. code::
@ -184,7 +184,7 @@ class ShapeCastable:
reset=shape_castable.const(initializer) reset=shape_castable.const(initializer)
)) ))
Note that the :pc:`shape_castable(x)` syntax performs :pc:`shape_castable.__call__(x)`. Note that the :py:`shape_castable(x)` syntax performs :py:`shape_castable.__call__(x)`.
.. tip:: .. tip::
@ -216,9 +216,9 @@ class ShapeCastable:
def as_shape(self, *args, **kwargs): def as_shape(self, *args, **kwargs):
"""as_shape() """as_shape()
Convert :pc:`self` to a :ref:`shape-like object <lang-shapelike>`. Convert :py:`self` to a :ref:`shape-like object <lang-shapelike>`.
This method is called by the Amaranth language to convert :pc:`self` to a concrete This method is called by the Amaranth language to convert :py:`self` to a concrete
:class:`Shape`. It will usually return a :class:`Shape` object, but it may also return :class:`Shape`. It will usually return a :class:`Shape` object, but it may also return
another shape-like object to delegate its functionality. another shape-like object to delegate its functionality.
@ -243,15 +243,15 @@ class ShapeCastable:
def const(self, *args, **kwargs): def const(self, *args, **kwargs):
"""const(obj) """const(obj)
Convert a constant initializer :pc:`obj` to its value representation. Convert a constant initializer :py:`obj` to its value representation.
This method is called by the Amaranth language to convert :pc:`obj`, which may be an This method is called by the Amaranth language to convert :py:`obj`, which may be an
arbitrary Python object, to a concrete :ref:`value-like object <lang-valuelike>`. arbitrary Python object, to a concrete :ref:`value-like object <lang-valuelike>`.
The object :pc:`obj` will usually be a Python literal that can conveniently represent The object :py:`obj` will usually be a Python literal that can conveniently represent
a constant value whose shape is described by :pc:`self`. While not constrained here, a constant value whose shape is described by :py:`self`. While not constrained here,
the result will usually be an instance of the return type of :meth:`__call__`. the result will usually be an instance of the return type of :meth:`__call__`.
For any :pc:`obj`, the following condition must hold: For any :py:`obj`, the following condition must hold:
.. code:: .. code::
@ -277,18 +277,18 @@ class ShapeCastable:
Lift a :ref:`value-like object <lang-valuelike>` to a higher-level representation. Lift a :ref:`value-like object <lang-valuelike>` to a higher-level representation.
This method is called by the Amaranth language to lift :pc:`obj`, which may be any This method is called by the Amaranth language to lift :py:`obj`, which may be any
:ref:`value-like object <lang-valuelike>` whose shape equals :pc:`Shape.cast(self)`, :ref:`value-like object <lang-valuelike>` whose shape equals :py:`Shape.cast(self)`,
to a higher-level representation, which may be any value-like object with the same to a higher-level representation, which may be any value-like object with the same
shape. While not constrained here, usually a :class:`ShapeCastable` implementation will shape. While not constrained here, usually a :class:`ShapeCastable` implementation will
be paired with a :class:`ValueCastable` implementation, and this method will return be paired with a :class:`ValueCastable` implementation, and this method will return
an instance of the latter. an instance of the latter.
If :pc:`obj` is not as described above, this interface does not constrain the behavior If :py:`obj` is not as described above, this interface does not constrain the behavior
of this method. This may be used to implement another call-based protocol at the same of this method. This may be used to implement another call-based protocol at the same
time. time.
For any compliant :pc:`obj`, the following condition must hold: For any compliant :py:`obj`, the following condition must hold:
.. code:: .. code::
@ -328,7 +328,7 @@ class _ShapeLikeMeta(type):
class ShapeLike(metaclass=_ShapeLikeMeta): class ShapeLike(metaclass=_ShapeLikeMeta):
"""Abstract class representing all objects that can be cast to a :class:`Shape`. """Abstract class representing all objects that can be cast to a :class:`Shape`.
:pc:`issubclass(cls, ShapeLike)` returns :pc:`True` for: :py:`issubclass(cls, ShapeLike)` returns :py:`True` for:
* :class:`Shape`; * :class:`Shape`;
* :class:`ShapeCastable` and its subclasses; * :class:`ShapeCastable` and its subclasses;
@ -337,7 +337,7 @@ class ShapeLike(metaclass=_ShapeLikeMeta):
* :class:`enum.EnumMeta` and its subclasses; * :class:`enum.EnumMeta` and its subclasses;
* :class:`ShapeLike` itself. * :class:`ShapeLike` itself.
:pc:`isinstance(obj, ShapeLike)` returns :pc:`True` for: :py:`isinstance(obj, ShapeLike)` returns :py:`True` for:
* :class:`Shape` instances; * :class:`Shape` instances;
* :class:`ShapeCastable` instances; * :class:`ShapeCastable` instances;
@ -381,9 +381,9 @@ class Value(metaclass=ABCMeta):
Operations on this class interpret this bit pattern either as an integer, which can be signed Operations on this class interpret this bit pattern either as an integer, which can be signed
or unsigned depending on the value's :meth:`shape`, or as a bit container. In either case, or unsigned depending on the value's :meth:`shape`, or as a bit container. In either case,
the semantics of operations that implement Python's syntax, like :pc:`+` (also known as the semantics of operations that implement Python's syntax, like :py:`+` (also known as
:meth:`__add__`), are identical to the corresponding operation on a Python :class:`int` (or on :meth:`__add__`), are identical to the corresponding operation on a Python :class:`int` (or on
a Python sequence container). The bitwise inversion :pc:`~` (also known as :meth:`__invert__`) a Python sequence container). The bitwise inversion :py:`~` (also known as :meth:`__invert__`)
is the sole exception to this rule. is the sole exception to this rule.
Data that is not conveniently representable by a single integer or a bit container can be Data that is not conveniently representable by a single integer or a bit container can be
@ -404,24 +404,24 @@ class Value(metaclass=ABCMeta):
In each of the descriptions below, you will see a line similar to: In each of the descriptions below, you will see a line similar to:
**Return type:** :class:`Value`, :pc:`unsigned(1)`, :ref:`assignable <lang-assignable>` **Return type:** :class:`Value`, :py:`unsigned(1)`, :ref:`assignable <lang-assignable>`
The first part (:class:`Value`) indicates that the returned object's type is a subclass The first part (:class:`Value`) indicates that the returned object's type is a subclass
of :class:`Value`. The second part (:pc:`unsigned(1)`) describes the shape of that value. of :class:`Value`. The second part (:py:`unsigned(1)`) describes the shape of that value.
The third part, if present, indicates that the value is assignable if :pc:`self` is The third part, if present, indicates that the value is assignable if :py:`self` is
assignable. assignable.
""" """
@staticmethod @staticmethod
def cast(obj): def cast(obj):
"""Cast :pc:`obj` to an Amaranth value. """Cast :py:`obj` to an Amaranth value.
Many :ref:`value-like <lang-valuelike>` objects can be cast to a value: Many :ref:`value-like <lang-valuelike>` objects can be cast to a value:
* a :class:`Value` instance, where the result is itself; * a :class:`Value` instance, where the result is itself;
* a :class:`bool` or :class:`int` instance, where the result is :pc:`Const(obj)`; * a :class:`bool` or :class:`int` instance, where the result is :py:`Const(obj)`;
* an :class:`enum.IntEnum` instance, or a :class:`enum.Enum` instance whose members are * an :class:`enum.IntEnum` instance, or a :class:`enum.Enum` instance whose members are
all integers, where the result is a :class:`Const(obj, enum_shape)` where :pc:`enum_shape` all integers, where the result is a :class:`Const(obj, enum_shape)` where :py:`enum_shape`
is a shape that can represent every member of the enumeration; is a shape that can represent every member of the enumeration;
* a :class:`ValueCastable` instance, where the result is obtained by repeatedly calling * a :class:`ValueCastable` instance, where the result is obtained by repeatedly calling
:meth:`obj.as_value() <ValueCastable.as_value>`. :meth:`obj.as_value() <ValueCastable.as_value>`.
@ -429,9 +429,9 @@ class Value(metaclass=ABCMeta):
Raises Raises
------ ------
TypeError TypeError
If :pc:`obj` cannot be converted to a :class:`Value`. If :py:`obj` cannot be converted to a :class:`Value`.
RecursionError RecursionError
If :pc:`obj` is a :class:`ValueCastable` object that casts to itself. If :py:`obj` is a :class:`ValueCastable` object that casts to itself.
""" """
while True: while True:
if isinstance(obj, Value): if isinstance(obj, Value):
@ -454,7 +454,7 @@ class Value(metaclass=ABCMeta):
@abstractmethod @abstractmethod
def shape(self): def shape(self):
"""Shape of :pc:`self`. """Shape of :py:`self`.
Returns Returns
------- -------
@ -471,7 +471,7 @@ class Value(metaclass=ABCMeta):
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(len(self))`, :ref:`assignable <lang-assignable>` :class:`Value`, :py:`unsigned(len(self))`, :ref:`assignable <lang-assignable>`
""" """
return Operator("u", [self]) return Operator("u", [self])
@ -480,12 +480,12 @@ class Value(metaclass=ABCMeta):
Returns Returns
------- -------
:class:`Value`, :pc:`signed(len(self))`, :ref:`assignable <lang-assignable>` :class:`Value`, :py:`signed(len(self))`, :ref:`assignable <lang-assignable>`
Raises Raises
------ ------
ValueError ValueError
If :pc:`len(self) == 0`. If :py:`len(self) == 0`.
""" """
if len(self) == 0: if len(self) == 0:
raise ValueError("Cannot create a 0-width signed value") raise ValueError("Cannot create a 0-width signed value")
@ -494,7 +494,7 @@ class Value(metaclass=ABCMeta):
def __bool__(self): def __bool__(self):
"""Forbidden conversion to boolean. """Forbidden conversion to boolean.
Python uses this operator for its built-in semantics, e.g. :pc:`if`, and requires it to Python uses this operator for its built-in semantics, e.g. :py:`if`, and requires it to
return a :class:`bool`. Since this is not possible for Amaranth values, this operator return a :class:`bool`. Since this is not possible for Amaranth values, this operator
always raises an exception. always raises an exception.
@ -512,22 +512,22 @@ class Value(metaclass=ABCMeta):
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
return Operator("b", [self]) return Operator("b", [self])
def __pos__(self): def __pos__(self):
"""Unary position, :pc:`+self`. """Unary position, :py:`+self`.
Returns Returns
------- -------
:class:`Value`, :pc:`self.shape()` :class:`Value`, :py:`self.shape()`
:pc:`self` :py:`self`
""" """
return self return self
def __neg__(self): def __neg__(self):
"""Unary negation, :pc:`-self`. """Unary negation, :py:`-self`.
.. ..
>>> C(-1).value, C(-1).shape() >>> C(-1).value, C(-1).shape()
@ -537,29 +537,29 @@ class Value(metaclass=ABCMeta):
Returns Returns
------- -------
:class:`Value`, :pc:`signed(len(self) + 1)` :class:`Value`, :py:`signed(len(self) + 1)`
""" """
return Operator("-", [self]) return Operator("-", [self])
@_overridable_by_reflected("__radd__") @_overridable_by_reflected("__radd__")
def __add__(self, other): def __add__(self, other):
"""Addition, :pc:`self + other`. """Addition, :py:`self + other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(max(self.width(), other.width()) + 1)` :class:`Value`, :py:`unsigned(max(self.width(), other.width()) + 1)`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :pc:`signed(max(self.width() + 1, other.width()) + 1)` :class:`Value`, :py:`signed(max(self.width() + 1, other.width()) + 1)`
If :pc:`self` is unsigned and :pc:`other` is signed. If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :pc:`signed(max(self.width(), other.width() + 1) + 1)` :class:`Value`, :py:`signed(max(self.width(), other.width() + 1) + 1)`
If :pc:`self` is signed and :pc:`other` is unsigned. If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :pc:`signed(max(self.width(), other.width()) + 1)` :class:`Value`, :py:`signed(max(self.width(), other.width()) + 1)`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
""" """
return Operator("+", [self, other], src_loc_at=1) return Operator("+", [self, other], src_loc_at=1)
def __radd__(self, other): def __radd__(self, other):
"""Addition, :pc:`other + self` (reflected). """Addition, :py:`other + self` (reflected).
Like :meth:`__add__`, with operands swapped. Like :meth:`__add__`, with operands swapped.
""" """
@ -567,18 +567,18 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rsub__") @_overridable_by_reflected("__rsub__")
def __sub__(self, other): def __sub__(self, other):
"""Subtraction, :pc:`self - other`. """Subtraction, :py:`self - other`.
Returns Returns
------- -------
:class:`Value`, :pc:`signed(max(self.width(), other.width()) + 1)` :class:`Value`, :py:`signed(max(self.width(), other.width()) + 1)`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :pc:`signed(max(self.width() + 1, other.width()) + 1)` :class:`Value`, :py:`signed(max(self.width() + 1, other.width()) + 1)`
If :pc:`self` is unsigned and :pc:`other` is signed. If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :pc:`signed(max(self.width(), other.width() + 1) + 1)` :class:`Value`, :py:`signed(max(self.width(), other.width() + 1) + 1)`
If :pc:`self` is signed and :pc:`other` is unsigned. If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :pc:`signed(max(self.width(), other.width()) + 1)` :class:`Value`, :py:`signed(max(self.width(), other.width()) + 1)`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
Returns Returns
------- -------
@ -587,7 +587,7 @@ class Value(metaclass=ABCMeta):
return Operator("-", [self, other], src_loc_at=1) return Operator("-", [self, other], src_loc_at=1)
def __rsub__(self, other): def __rsub__(self, other):
"""Subtraction, :pc:`other - self` (reflected). """Subtraction, :py:`other - self` (reflected).
Like :meth:`__sub__`, with operands swapped. Like :meth:`__sub__`, with operands swapped.
""" """
@ -595,19 +595,19 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rmul__") @_overridable_by_reflected("__rmul__")
def __mul__(self, other): def __mul__(self, other):
"""Multiplication, :pc:`self * other`. """Multiplication, :py:`self * other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(len(self) + len(other))` :class:`Value`, :py:`unsigned(len(self) + len(other))`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :pc:`signed(len(self) + len(other))` :class:`Value`, :py:`signed(len(self) + len(other))`
If either :pc:`self` or :pc:`other` are signed. If either :py:`self` or :py:`other` are signed.
""" """
return Operator("*", [self, other], src_loc_at=1) return Operator("*", [self, other], src_loc_at=1)
def __rmul__(self, other): def __rmul__(self, other):
"""Multiplication, :pc:`other * self` (reflected). """Multiplication, :py:`other * self` (reflected).
Like :meth:`__mul__`, with operands swapped. Like :meth:`__mul__`, with operands swapped.
""" """
@ -615,27 +615,27 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rfloordiv__") @_overridable_by_reflected("__rfloordiv__")
def __floordiv__(self, other): def __floordiv__(self, other):
"""Flooring division, :pc:`self // other`. """Flooring division, :py:`self // other`.
If :pc:`other` is zero, the result of this operation is zero. If :py:`other` is zero, the result of this operation is zero.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(len(self))` :class:`Value`, :py:`unsigned(len(self))`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :pc:`signed(len(self) + 1)` :class:`Value`, :py:`signed(len(self) + 1)`
If :pc:`self` is unsigned and :pc:`other` is signed. If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :pc:`signed(len(self))` :class:`Value`, :py:`signed(len(self))`
If :pc:`self` is signed and :pc:`other` is unsigned. If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :pc:`signed(len(self) + 1)` :class:`Value`, :py:`signed(len(self) + 1)`
If both :pc:`self` and :pc:`other` are signed. If both :py:`self` and :py:`other` are signed.
""" """
return Operator("//", [self, other], src_loc_at=1) return Operator("//", [self, other], src_loc_at=1)
def __rfloordiv__(self, other): def __rfloordiv__(self, other):
"""Flooring division, :pc:`other // self` (reflected). """Flooring division, :py:`other // self` (reflected).
If :pc:`self` is zero, the result of this operation is zero. If :py:`self` is zero, the result of this operation is zero.
Like :meth:`__floordiv__`, with operands swapped. Like :meth:`__floordiv__`, with operands swapped.
""" """
@ -643,18 +643,18 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rmod__") @_overridable_by_reflected("__rmod__")
def __mod__(self, other): def __mod__(self, other):
"""Flooring modulo or remainder, :pc:`self % other`. """Flooring modulo or remainder, :py:`self % other`.
If :pc:`other` is zero, the result of this operation is zero. If :py:`other` is zero, the result of this operation is zero.
Returns Returns
------- -------
:class:`Value`, :pc:`other.shape()` :class:`Value`, :py:`other.shape()`
""" """
return Operator("%", [self, other], src_loc_at=1) return Operator("%", [self, other], src_loc_at=1)
def __rmod__(self, other): def __rmod__(self, other):
"""Flooring modulo or remainder, :pc:`other % self` (reflected). """Flooring modulo or remainder, :py:`other % self` (reflected).
Like :meth:`__mod__`, with operands swapped. Like :meth:`__mod__`, with operands swapped.
""" """
@ -662,66 +662,66 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__eq__") @_overridable_by_reflected("__eq__")
def __eq__(self, other): def __eq__(self, other):
"""Equality comparison, :pc:`self == other`. """Equality comparison, :py:`self == other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
return Operator("==", [self, other], src_loc_at=1) return Operator("==", [self, other], src_loc_at=1)
@_overridable_by_reflected("__ne__") @_overridable_by_reflected("__ne__")
def __ne__(self, other): def __ne__(self, other):
"""Inequality comparison, :pc:`self != other`. """Inequality comparison, :py:`self != other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
return Operator("!=", [self, other], src_loc_at=1) return Operator("!=", [self, other], src_loc_at=1)
@_overridable_by_reflected("__gt__") @_overridable_by_reflected("__gt__")
def __lt__(self, other): def __lt__(self, other):
"""Less than comparison, :pc:`self < other`. """Less than comparison, :py:`self < other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
return Operator("<", [self, other], src_loc_at=1) return Operator("<", [self, other], src_loc_at=1)
@_overridable_by_reflected("__ge__") @_overridable_by_reflected("__ge__")
def __le__(self, other): def __le__(self, other):
"""Less than or equals comparison, :pc:`self <= other`. """Less than or equals comparison, :py:`self <= other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
return Operator("<=", [self, other], src_loc_at=1) return Operator("<=", [self, other], src_loc_at=1)
@_overridable_by_reflected("__lt__") @_overridable_by_reflected("__lt__")
def __gt__(self, other): def __gt__(self, other):
"""Greater than comparison, :pc:`self > other`. """Greater than comparison, :py:`self > other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
return Operator(">", [self, other], src_loc_at=1) return Operator(">", [self, other], src_loc_at=1)
@_overridable_by_reflected("__le__") @_overridable_by_reflected("__le__")
def __ge__(self, other): def __ge__(self, other):
"""Greater than or equals comparison, :pc:`self >= other`. """Greater than or equals comparison, :py:`self >= other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
return Operator(">=", [self, other], src_loc_at=1) return Operator(">=", [self, other], src_loc_at=1)
def __abs__(self): def __abs__(self):
"""Absolute value, :pc:`abs(self)`. """Absolute value, :py:`abs(self)`.
.. ..
>>> abs(C(-1)).shape() >>> abs(C(-1)).shape()
@ -731,7 +731,7 @@ class Value(metaclass=ABCMeta):
Return Return
------ ------
:class:`Value`, :pc:`unsigned(len(self))` :class:`Value`, :py:`unsigned(len(self))`
""" """
if self.shape().signed: if self.shape().signed:
return Mux(self >= 0, self, -self)[:len(self)] return Mux(self >= 0, self, -self)[:len(self)]
@ -739,123 +739,123 @@ class Value(metaclass=ABCMeta):
return self return self
def __invert__(self): def __invert__(self):
"""Bitwise NOT, :pc:`~self`. """Bitwise NOT, :py:`~self`.
The shape of the result is the same as the shape of :pc:`self`, even for unsigned values. The shape of the result is the same as the shape of :py:`self`, even for unsigned values.
.. important:: .. important::
In Python, :pc:`~0` equals :pc:`-1`. In Amaranth, :pc:`~C(0)` equals :pc:`C(1)`. In Python, :py:`~0` equals :py:`-1`. In Amaranth, :py:`~C(0)` equals :py:`C(1)`.
This is the only case where an Amaranth operator deviates from the Python operator This is the only case where an Amaranth operator deviates from the Python operator
with the same name. with the same name.
This deviation is necessary because Python does not allow overriding the logical This deviation is necessary because Python does not allow overriding the logical
:pc:`and`, :pc:`or`, and :pc:`not` operators. Amaranth uses :pc:`&`, :pc:`|`, and :py:`and`, :py:`or`, and :py:`not` operators. Amaranth uses :py:`&`, :py:`|`, and
:pc:`~` instead; if it wasn't the case that :pc:`~C(0) == C(1)`, that would have :py:`~` instead; if it wasn't the case that :py:`~C(0) == C(1)`, that would have
been impossible. been impossible.
Returns Returns
------- -------
:class:`Value`, :pc:`self.shape()` :class:`Value`, :py:`self.shape()`
""" """
return Operator("~", [self]) return Operator("~", [self])
@_overridable_by_reflected("__rand__") @_overridable_by_reflected("__rand__")
def __and__(self, other): def __and__(self, other):
"""Bitwise AND, :pc:`self & other`. """Bitwise AND, :py:`self & other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(max(self.width(), other.width()))` :class:`Value`, :py:`unsigned(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :pc:`signed(max(self.width() + 1, other.width()))` :class:`Value`, :py:`signed(max(self.width() + 1, other.width()))`
If :pc:`self` is unsigned and :pc:`other` is signed. If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :pc:`signed(max(self.width(), other.width() + 1))` :class:`Value`, :py:`signed(max(self.width(), other.width() + 1))`
If :pc:`self` is signed and :pc:`other` is unsigned. If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :pc:`signed(max(self.width(), other.width()))` :class:`Value`, :py:`signed(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
""" """
return Operator("&", [self, other], src_loc_at=1) return Operator("&", [self, other], src_loc_at=1)
def __rand__(self, other): def __rand__(self, other):
"""Bitwise AND, :pc:`other & self`. """Bitwise AND, :py:`other & self`.
Like :meth:`__and__`, with operands swapped. Like :meth:`__and__`, with operands swapped.
""" """
return Operator("&", [other, self]) return Operator("&", [other, self])
def all(self): def all(self):
"""Reduction AND; are all bits :pc:`1`? """Reduction AND; are all bits :py:`1`?
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
return Operator("r&", [self]) return Operator("r&", [self])
@_overridable_by_reflected("__ror__") @_overridable_by_reflected("__ror__")
def __or__(self, other): def __or__(self, other):
"""Bitwise OR, :pc:`self | other`. """Bitwise OR, :py:`self | other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(max(self.width(), other.width()))` :class:`Value`, :py:`unsigned(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :pc:`signed(max(self.width() + 1, other.width()))` :class:`Value`, :py:`signed(max(self.width() + 1, other.width()))`
If :pc:`self` is unsigned and :pc:`other` is signed. If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :pc:`signed(max(self.width(), other.width() + 1))` :class:`Value`, :py:`signed(max(self.width(), other.width() + 1))`
If :pc:`self` is signed and :pc:`other` is unsigned. If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :pc:`signed(max(self.width(), other.width()))` :class:`Value`, :py:`signed(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
""" """
return Operator("|", [self, other], src_loc_at=1) return Operator("|", [self, other], src_loc_at=1)
def __ror__(self, other): def __ror__(self, other):
"""Bitwise OR, :pc:`other | self`. """Bitwise OR, :py:`other | self`.
Like :meth:`__or__`, with operands swapped. Like :meth:`__or__`, with operands swapped.
""" """
return Operator("|", [other, self]) return Operator("|", [other, self])
def any(self): def any(self):
"""Reduction OR; is any bit :pc:`1`? """Reduction OR; is any bit :py:`1`?
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
return Operator("r|", [self]) return Operator("r|", [self])
@_overridable_by_reflected("__rxor__") @_overridable_by_reflected("__rxor__")
def __xor__(self, other): def __xor__(self, other):
"""Bitwise XOR, :pc:`self ^ other`. """Bitwise XOR, :py:`self ^ other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(max(self.width(), other.width()))` :class:`Value`, :py:`unsigned(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :pc:`signed(max(self.width() + 1, other.width()))` :class:`Value`, :py:`signed(max(self.width() + 1, other.width()))`
If :pc:`self` is unsigned and :pc:`other` is signed. If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :pc:`signed(max(self.width(), other.width() + 1))` :class:`Value`, :py:`signed(max(self.width(), other.width() + 1))`
If :pc:`self` is signed and :pc:`other` is unsigned. If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :pc:`signed(max(self.width(), other.width()))` :class:`Value`, :py:`signed(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned. If both :py:`self` and :py:`other` are unsigned.
""" """
return Operator("^", [self, other], src_loc_at=1) return Operator("^", [self, other], src_loc_at=1)
def __rxor__(self, other): def __rxor__(self, other):
"""Bitwise XOR, :pc:`other ^ self`. """Bitwise XOR, :py:`other ^ self`.
Like :meth:`__xor__`, with operands swapped. Like :meth:`__xor__`, with operands swapped.
""" """
return Operator("^", [other, self]) return Operator("^", [other, self])
def xor(self): def xor(self):
"""Reduction XOR; are an odd amount of bits :pc:`1`? """Reduction XOR; are an odd amount of bits :py:`1`?
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
return Operator("r^", [self]) return Operator("r^", [self])
@ -872,26 +872,26 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rlshift__") @_overridable_by_reflected("__rlshift__")
def __lshift__(self, other): def __lshift__(self, other):
"""Left shift by variable amount, :pc:`self << other`. """Left shift by variable amount, :py:`self << other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(len(self) + 2 ** len(other) - 1)` :class:`Value`, :py:`unsigned(len(self) + 2 ** len(other) - 1)`
If :pc:`self` is unsigned. If :py:`self` is unsigned.
:class:`Value`, :pc:`signed(len(self) + 2 ** len(other) - 1)` :class:`Value`, :py:`signed(len(self) + 2 ** len(other) - 1)`
If :pc:`self` is signed. If :py:`self` is signed.
Raises Raises
------ ------
:exc:`TypeError` :exc:`TypeError`
If :pc:`other` is signed. If :py:`other` is signed.
""" """
other = Value.cast(other) other = Value.cast(other)
other.__check_shamt() other.__check_shamt()
return Operator("<<", [self, other], src_loc_at=1) return Operator("<<", [self, other], src_loc_at=1)
def __rlshift__(self, other): def __rlshift__(self, other):
"""Left shift by variable amount, :pc:`other << self`. """Left shift by variable amount, :py:`other << self`.
Like :meth:`__lshift__`, with operands swapped. Like :meth:`__lshift__`, with operands swapped.
""" """
@ -901,14 +901,14 @@ class Value(metaclass=ABCMeta):
def shift_left(self, amount): def shift_left(self, amount):
"""Left shift by constant amount. """Left shift by constant amount.
If :pc:`amount < 0`, performs the same operation as :pc:`self.shift_right(-amount)`. If :py:`amount < 0`, performs the same operation as :py:`self.shift_right(-amount)`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(max(len(self) + amount, 0))` :class:`Value`, :py:`unsigned(max(len(self) + amount, 0))`
If :pc:`self` is unsigned. If :py:`self` is unsigned.
:class:`Value`, :pc:`signed(max(len(self) + amount, 1))` :class:`Value`, :py:`signed(max(len(self) + amount, 1))`
If :pc:`self` is signed. If :py:`self` is signed.
""" """
if not isinstance(amount, int): if not isinstance(amount, int):
raise TypeError(f"Shift amount must be an integer, not {amount!r}") raise TypeError(f"Shift amount must be an integer, not {amount!r}")
@ -922,11 +922,11 @@ class Value(metaclass=ABCMeta):
def rotate_left(self, amount): def rotate_left(self, amount):
"""Left rotate by constant amount. """Left rotate by constant amount.
If :pc:`amount < 0`, performs the same operation as :pc:`self.rotate_right(-amount)`. If :py:`amount < 0`, performs the same operation as :py:`self.rotate_right(-amount)`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(len(self))`, :ref:`assignable <lang-assignable>` :class:`Value`, :py:`unsigned(len(self))`, :ref:`assignable <lang-assignable>`
""" """
if not isinstance(amount, int): if not isinstance(amount, int):
raise TypeError(f"Rotate amount must be an integer, not {amount!r}") raise TypeError(f"Rotate amount must be an integer, not {amount!r}")
@ -936,26 +936,26 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rrshift__") @_overridable_by_reflected("__rrshift__")
def __rshift__(self, other): def __rshift__(self, other):
"""Right shift by variable amount, :pc:`self >> other`. """Right shift by variable amount, :py:`self >> other`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(len(self))` :class:`Value`, :py:`unsigned(len(self))`
If :pc:`self` is unsigned. If :py:`self` is unsigned.
:class:`Value`, :pc:`signed(len(self))` :class:`Value`, :py:`signed(len(self))`
If :pc:`self` is signed. If :py:`self` is signed.
Raises Raises
------ ------
:exc:`TypeError` :exc:`TypeError`
If :pc:`other` is signed. If :py:`other` is signed.
""" """
other = Value.cast(other) other = Value.cast(other)
other.__check_shamt() other.__check_shamt()
return Operator(">>", [self, other], src_loc_at=1) return Operator(">>", [self, other], src_loc_at=1)
def __rrshift__(self, other): def __rrshift__(self, other):
"""Right shift by variable amount, :pc:`other >> self`. """Right shift by variable amount, :py:`other >> self`.
Like :meth:`__rshift__`, with operands swapped. Like :meth:`__rshift__`, with operands swapped.
""" """
@ -965,14 +965,14 @@ class Value(metaclass=ABCMeta):
def shift_right(self, amount): def shift_right(self, amount):
"""Right shift by constant amount. """Right shift by constant amount.
If :pc:`amount < 0`, performs the same operation as :pc:`self.left_right(-amount)`. If :py:`amount < 0`, performs the same operation as :py:`self.left_right(-amount)`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(max(len(self) - amount, 0))` :class:`Value`, :py:`unsigned(max(len(self) - amount, 0))`
If :pc:`self` is unsigned. If :py:`self` is unsigned.
:class:`Value`, :pc:`signed(max(len(self) - amount, 1))` :class:`Value`, :py:`signed(max(len(self) - amount, 1))`
If :pc:`self` is signed. If :py:`self` is signed.
""" """
if not isinstance(amount, int): if not isinstance(amount, int):
raise TypeError(f"Shift amount must be an integer, not {amount!r}") raise TypeError(f"Shift amount must be an integer, not {amount!r}")
@ -988,11 +988,11 @@ class Value(metaclass=ABCMeta):
def rotate_right(self, amount): def rotate_right(self, amount):
"""Right rotate by constant amount. """Right rotate by constant amount.
If :pc:`amount < 0`, performs the same operation as :pc:`self.rotate_right(-amount)`. If :py:`amount < 0`, performs the same operation as :py:`self.rotate_right(-amount)`.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(len(self))`, :ref:`assignable <lang-assignable>` :class:`Value`, :py:`unsigned(len(self))`, :ref:`assignable <lang-assignable>`
""" """
if not isinstance(amount, int): if not isinstance(amount, int):
raise TypeError(f"Rotate amount must be an integer, not {amount!r}") raise TypeError(f"Rotate amount must be an integer, not {amount!r}")
@ -1001,12 +1001,12 @@ class Value(metaclass=ABCMeta):
return Cat(self[amount:], self[:amount]) return Cat(self[amount:], self[:amount])
def __len__(self): def __len__(self):
"""Bit width of :pc:`self`. """Bit width of :py:`self`.
Returns Returns
------- -------
:class:`int` :class:`int`
:pc:`self.shape().width` :py:`self.shape().width`
""" """
return self.shape().width return self.shape().width
@ -1055,13 +1055,13 @@ class Value(metaclass=ABCMeta):
def bit_select(self, offset, width): def bit_select(self, offset, width):
"""Part-select with bit granularity. """Part-select with bit granularity.
Selects a constant width, variable offset part of :pc:`self`, where parts with successive Selects a constant width, variable offset part of :py:`self`, where parts with successive
offsets overlap by :pc:`width - 1` bits. Bits above the most significant bit of :pc:`self` offsets overlap by :py:`width - 1` bits. Bits above the most significant bit of :py:`self`
may be selected; they are equal to zero if :pc:`self` is unsigned, to :pc:`self[-1]` if may be selected; they are equal to zero if :py:`self` is unsigned, to :py:`self[-1]` if
:pc:`self` is signed, and assigning to them does nothing. :py:`self` is signed, and assigning to them does nothing.
When :pc:`offset` is a constant integer and :pc:`offset + width <= len(self)`, When :py:`offset` is a constant integer and :py:`offset + width <= len(self)`,
this operation is equivalent to :pc:`self[offset:offset + width]`. this operation is equivalent to :py:`self[offset:offset + width]`.
Parameters Parameters
---------- ----------
@ -1072,14 +1072,14 @@ class Value(metaclass=ABCMeta):
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(width)`, :ref:`assignable <lang-assignable>` :class:`Value`, :py:`unsigned(width)`, :ref:`assignable <lang-assignable>`
Raises Raises
------ ------
:exc:`TypeError` :exc:`TypeError`
If :pc:`offset` is signed. If :py:`offset` is signed.
:exc:`TypeError` :exc:`TypeError`
If :pc:`width` is negative. If :py:`width` is negative.
""" """
offset = Value.cast(offset) offset = Value.cast(offset)
if type(offset) is Const and isinstance(width, int): if type(offset) is Const and isinstance(width, int):
@ -1089,13 +1089,13 @@ class Value(metaclass=ABCMeta):
def word_select(self, offset, width): def word_select(self, offset, width):
"""Part-select with word granularity. """Part-select with word granularity.
Selects a constant width, variable offset part of :pc:`self`, where parts with successive Selects a constant width, variable offset part of :py:`self`, where parts with successive
offsets are adjacent but do not overlap. Bits above the most significant bit of :pc:`self` offsets are adjacent but do not overlap. Bits above the most significant bit of :py:`self`
may be selected; they are equal to zero if :pc:`self` is unsigned, to :pc:`self[-1]` if may be selected; they are equal to zero if :py:`self` is unsigned, to :py:`self[-1]` if
:pc:`self` is signed, and assigning to them does nothing. :py:`self` is signed, and assigning to them does nothing.
When :pc:`offset` is a constant integer and :pc:`width:(offset + 1) * width <= len(self)`, When :py:`offset` is a constant integer and :py:`width:(offset + 1) * width <= len(self)`,
this operation is equivalent to :pc:`self[offset * width:(offset + 1) * width]`. this operation is equivalent to :py:`self[offset * width:(offset + 1) * width]`.
Parameters Parameters
---------- ----------
@ -1106,14 +1106,14 @@ class Value(metaclass=ABCMeta):
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(width)`, :ref:`assignable <lang-assignable>` :class:`Value`, :py:`unsigned(width)`, :ref:`assignable <lang-assignable>`
Raises Raises
------ ------
:exc:`TypeError` :exc:`TypeError`
If :pc:`offset` is signed. If :py:`offset` is signed.
:exc:`TypeError` :exc:`TypeError`
If :pc:`width` is negative. If :py:`width` is negative.
""" """
offset = Value.cast(offset) offset = Value.cast(offset)
if type(offset) is Const and isinstance(width, int): if type(offset) is Const and isinstance(width, int):
@ -1123,19 +1123,19 @@ class Value(metaclass=ABCMeta):
def replicate(self, count): def replicate(self, count):
"""Replication. """Replication.
Equivalent to :pc:`Cat(self for _ in range(count))`, but not assignable. Equivalent to :py:`Cat(self for _ in range(count))`, but not assignable.
.. ..
Technically assignable right now, but we don't want to commit to that. Technically assignable right now, but we don't want to commit to that.
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(len(self) * count)` :class:`Value`, :py:`unsigned(len(self) * count)`
Raises Raises
------ ------
:exc:`TypeError` :exc:`TypeError`
If :pc:`count` is negative. If :py:`count` is negative.
""" """
if not isinstance(count, int) or count < 0: if not isinstance(count, int) or count < 0:
raise TypeError("Replication count must be a non-negative integer, not {!r}" raise TypeError("Replication count must be a non-negative integer, not {!r}"
@ -1145,7 +1145,7 @@ class Value(metaclass=ABCMeta):
def matches(self, *patterns): def matches(self, *patterns):
"""Pattern matching. """Pattern matching.
Matches against a set of patterns, recognizing the same grammar as :pc:`with m.Case()`. Matches against a set of patterns, recognizing the same grammar as :py:`with m.Case()`.
.. todo:: .. todo::
@ -1153,7 +1153,7 @@ class Value(metaclass=ABCMeta):
Returns Returns
------- -------
:class:`Value`, :pc:`unsigned(1)` :class:`Value`, :py:`unsigned(1)`
""" """
matches = [] matches = []
# This code should accept exactly the same patterns as `with m.Case(...):`. # This code should accept exactly the same patterns as `with m.Case(...):`.
@ -1197,10 +1197,10 @@ class Value(metaclass=ABCMeta):
def eq(self, value, *, src_loc_at=0): def eq(self, value, *, src_loc_at=0):
""":ref:`Assignment <lang-assigns>`. """:ref:`Assignment <lang-assigns>`.
Once it is placed in a domain, an assignment changes the bit pattern of :pc:`self` to Once it is placed in a domain, an assignment changes the bit pattern of :py:`self` to
equal :pc:`value`. If the bit width of :pc:`value` is less than that of :pc:`self`, equal :py:`value`. If the bit width of :py:`value` is less than that of :py:`self`,
it is zero-extended (for unsigned :pc:`value`\\ s) or sign-extended (for signed it is zero-extended (for unsigned :py:`value`\\ s) or sign-extended (for signed
:pc:`value`\\ s). If the bit width of :pc:`value` is greater than that of :pc:`self`, :py:`value`\\ s). If the bit width of :py:`value` is greater than that of :py:`self`,
it is truncated. it is truncated.
Returns Returns
@ -1211,8 +1211,8 @@ class Value(metaclass=ABCMeta):
#: Forbidden hashing. #: Forbidden hashing.
#: #:
#: Python objects are :term:`python:hashable` if they provide a :pc:`__hash__` method #: Python objects are :term:`python:hashable` if they provide a :py:`__hash__` method
#: that returns an :class:`int` and an :pc:`__eq__` method that returns a :class:`bool`. #: that returns an :class:`int` and an :py:`__eq__` method that returns a :class:`bool`.
#: Amaranth values define :meth:`__eq__` to return a :class:`Value`, which precludes them #: Amaranth values define :meth:`__eq__` to return a :class:`Value`, which precludes them
#: from being hashable. #: from being hashable.
#: #:
@ -1251,9 +1251,9 @@ class ValueCastable:
operators, which will prefer calling a reflected arithmetic operation on operators, which will prefer calling a reflected arithmetic operation on
the :class:`ValueCastable` argument if it defines one. the :class:`ValueCastable` argument if it defines one.
For example, if :pc:`value_castable` implements :pc:`__radd__`, then For example, if :py:`value_castable` implements :py:`__radd__`, then
:pc:`C(1) + value_castable` will perform :pc:`value_castable.__radd__(C(1))`, and otherwise :py:`C(1) + value_castable` will perform :py:`value_castable.__radd__(C(1))`, and otherwise
it will perform :pc:`C(1).__add__(value_castable.as_value())`. it will perform :py:`C(1).__add__(value_castable.as_value())`.
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -1275,9 +1275,9 @@ class ValueCastable:
def as_value(self, *args, **kwargs): def as_value(self, *args, **kwargs):
"""as_value() """as_value()
Convert :pc:`self` to a :ref:`value-like object <lang-valuelike>`. Convert :py:`self` to a :ref:`value-like object <lang-valuelike>`.
This method is called by the Amaranth language to convert :pc:`self` to a concrete This method is called by the Amaranth language to convert :py:`self` to a concrete
:class:`Value`. It will usually return a :class:`Value` object, but it may also return :class:`Value`. It will usually return a :class:`Value` object, but it may also return
another value-like object to delegate its functionality. another value-like object to delegate its functionality.
@ -1304,7 +1304,7 @@ class ValueCastable:
def shape(self, *args, **kwargs): def shape(self, *args, **kwargs):
"""shape() """shape()
Compute the shape of :pc:`self`. Compute the shape of :py:`self`.
This method is not called by the Amaranth language itself; whenever it needs to discover This method is not called by the Amaranth language itself; whenever it needs to discover
the shape of a value-castable object, it calls :class:`self.as_value().shape()`. However, the shape of a value-castable object, it calls :class:`self.as_value().shape()`. However,
@ -1367,7 +1367,7 @@ class _ValueLikeMeta(type):
class ValueLike(metaclass=_ValueLikeMeta): class ValueLike(metaclass=_ValueLikeMeta):
"""Abstract class representing all objects that can be cast to a :class:`Value`. """Abstract class representing all objects that can be cast to a :class:`Value`.
:pc:`issubclass(cls, ValueLike)` returns :pc:`True` for: :py:`issubclass(cls, ValueLike)` returns :py:`True` for:
* :class:`Value`; * :class:`Value`;
* :class:`ValueCastable` and its subclasses; * :class:`ValueCastable` and its subclasses;
@ -1375,8 +1375,8 @@ class ValueLike(metaclass=_ValueLikeMeta):
* :class:`enum.Enum` subclasses where all values are :ref:`value-like <lang-valuelike>`; * :class:`enum.Enum` subclasses where all values are :ref:`value-like <lang-valuelike>`;
* :class:`ValueLike` itself. * :class:`ValueLike` itself.
:pc:`isinstance(obj, ValueLike)` returns the same value as :py:`isinstance(obj, ValueLike)` returns the same value as
:pc:`issubclass(type(obj), ValueLike)`. :py:`issubclass(type(obj), ValueLike)`.
This class cannot be instantiated or subclassed. It can only be used for checking types of This class cannot be instantiated or subclassed. It can only be used for checking types of
objects. objects.
@ -1385,8 +1385,8 @@ class ValueLike(metaclass=_ValueLikeMeta):
It is possible to define an enumeration with a member that is It is possible to define an enumeration with a member that is
:ref:`value-like <lang-valuelike>` but not :ref:`constant-castable <lang-constcasting>`, :ref:`value-like <lang-valuelike>` but not :ref:`constant-castable <lang-constcasting>`,
meaning that :pc:`issubclass(BadEnum, ValueLike)` returns :pc:`True`, but meaning that :py:`issubclass(BadEnum, ValueLike)` returns :py:`True`, but
:pc:`Value.cast(BadEnum.MEMBER)` raises an exception. :py:`Value.cast(BadEnum.MEMBER)` raises an exception.
The :mod:`amaranth.lib.enum` module prevents such enumerations from being defined when The :mod:`amaranth.lib.enum` module prevents such enumerations from being defined when
the shape is specified explicitly. Using :mod:`amaranth.lib.enum` and specifying the shape the shape is specified explicitly. Using :mod:`amaranth.lib.enum` and specifying the shape

View file

@ -51,7 +51,7 @@ class Flow(enum.Enum):
Returns Returns
------- -------
:class:`Flow` :class:`Flow`
:attr:`In` if called as :pc:`Out.flip()`; :attr:`Out` if called as :pc:`In.flip()`. :attr:`In` if called as :py:`Out.flip()`; :attr:`Out` if called as :py:`In.flip()`.
""" """
if self == Out: if self == Out:
return In return In
@ -66,7 +66,7 @@ class Flow(enum.Enum):
Returns Returns
------- -------
:class:`Member` :class:`Member`
:pc:`Member(self, description, reset=reset)` :py:`Member(self, description, reset=reset)`
""" """
return Member(self, description, reset=reset, src_loc_at=src_loc_at + 1) return Member(self, description, reset=reset, src_loc_at=src_loc_at + 1)
@ -103,7 +103,7 @@ class Member:
will equal ``reset``. will equal ``reset``.
Although instances can be created directly, most often they will be created through Although instances can be created directly, most often they will be created through
:data:`In` and :data:`Out`, e.g. :pc:`In(unsigned(1))` or :pc:`Out(stream.Signature(RGBPixel))`. :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, *, reset=None, _dimensions=(), src_loc_at=0):
self._flow = flow self._flow = flow
@ -145,8 +145,8 @@ class Member:
Returns Returns
------- -------
:class:`Member` :class:`Member`
A new :pc:`member` with :pc:`member.flow` equal to :pc:`self.flow.flip()`, and identical A new :py:`member` with :py:`member.flow` equal to :py:`self.flow.flip()`, and identical
to :pc:`self` other than that. to :py:`self` other than that.
""" """
return Member(self._flow.flip(), self._description, reset=self._reset, return Member(self._flow.flip(), self._description, reset=self._reset,
_dimensions=self._dimensions) _dimensions=self._dimensions)
@ -155,21 +155,21 @@ class Member:
"""Add array dimensions to this member. """Add array dimensions to this member.
The dimensions passed to this method are `prepended` to the existing dimensions. The dimensions passed to this method are `prepended` to the existing dimensions.
For example, :pc:`Out(1).array(2)` describes an array of 2 elements, whereas both For example, :py:`Out(1).array(2)` describes an array of 2 elements, whereas both
:pc:`Out(1).array(2, 3)` and :pc:`Out(1).array(3).array(2)` both describe a two dimensional :py:`Out(1).array(2, 3)` and :py:`Out(1).array(3).array(2)` both describe a two dimensional
array of 2 by 3 elements. array of 2 by 3 elements.
Dimensions are passed to :meth:`array` in the order in which they would be indexed. Dimensions are passed to :meth:`array` in the order in which they would be indexed.
That is, :pc:`.array(x, y)` creates a member that can be indexed up to :pc:`[x-1][y-1]`. That is, :py:`.array(x, y)` creates a member that can be indexed up to :py:`[x-1][y-1]`.
The :meth:`array` method is composable: calling :pc:`member.array(x)` describes an array of The :meth:`array` method is composable: calling :py:`member.array(x)` describes an array of
:pc:`x` members even if :pc:`member` was already an array. :py:`x` members even if :py:`member` was already an array.
Returns Returns
------- -------
:class:`Member` :class:`Member`
A new :pc:`member` with :pc:`member.dimensions` extended by :pc:`dimensions`, and A new :py:`member` with :py:`member.dimensions` extended by :py:`dimensions`, and
identical to :pc:`self` other than that. identical to :py:`self` other than that.
""" """
for dimension in dimensions: for dimension in dimensions:
if not (isinstance(dimension, int) and dimension >= 0): if not (isinstance(dimension, int) and dimension >= 0):
@ -195,8 +195,8 @@ class Member:
Returns Returns
------- -------
:class:`bool` :class:`bool`
:pc:`True` if this is a description of a port member, :py:`True` if this is a description of a port member,
:pc:`False` if this is a description of a signature member. :py:`False` if this is a description of a signature member.
""" """
return not isinstance(self._description, Signature) return not isinstance(self._description, Signature)
@ -207,8 +207,8 @@ class Member:
Returns Returns
------- -------
:class:`bool` :class:`bool`
:pc:`True` if this is a description of a signature member, :py:`True` if this is a description of a signature member,
:pc:`False` if this is a description of a port member. :py:`False` if this is a description of a port member.
""" """
return isinstance(self._description, Signature) return isinstance(self._description, Signature)
@ -224,7 +224,7 @@ class Member:
Raises Raises
------ ------
:exc:`AttributeError` :exc:`AttributeError`
If :pc:`self` describes a signature member. If :py:`self` describes a signature member.
""" """
if self.is_signature: if self.is_signature:
raise AttributeError(f"A signature member does not have a shape") raise AttributeError(f"A signature member does not have a shape")
@ -242,7 +242,7 @@ class Member:
Raises Raises
------ ------
:exc:`AttributeError` :exc:`AttributeError`
If :pc:`self` describes a signature member. If :py:`self` describes a signature member.
""" """
if self.is_signature: if self.is_signature:
raise AttributeError(f"A signature member does not have a reset value") raise AttributeError(f"A signature member does not have a reset value")
@ -260,7 +260,7 @@ class Member:
Raises Raises
------ ------
:exc:`AttributeError` :exc:`AttributeError`
If :pc:`self` describes a port member. If :py:`self` describes a port member.
""" """
if self.is_port: if self.is_port:
raise AttributeError(f"A port member does not have a signature") raise AttributeError(f"A port member does not have a signature")
@ -316,7 +316,7 @@ class SignatureError(Exception):
class SignatureMembers(Mapping): class SignatureMembers(Mapping):
"""Mapping of signature member names to their descriptions. """Mapping of signature member names to their descriptions.
This container, a :class:`collections.abc.Mapping`, is used to implement the :pc:`members` This container, a :class:`collections.abc.Mapping`, is used to implement the :py:`members`
attribute of signature objects. attribute of signature objects.
The keys in this container must be valid Python attribute names that are public (do not begin The keys in this container must be valid Python attribute names that are public (do not begin
@ -326,7 +326,7 @@ class SignatureMembers(Mapping):
a container recursively freezes the members of any signatures inside. a container recursively freezes the members of any signatures inside.
In addition to the use of the superscript operator, multiple members can be added at once with In addition to the use of the superscript operator, multiple members can be added at once with
the :pc:`+=` opreator. the :py:`+=` opreator.
The :meth:`create` method converts this mapping into a mapping of names to signature members The :meth:`create` method converts this mapping into a mapping of names to signature members
(signals and interface objects) by creating them from their descriptions. The created mapping (signals and interface objects) by creating them from their descriptions. The created mapping
@ -348,7 +348,7 @@ class SignatureMembers(Mapping):
Returns Returns
------- -------
:class:`FlippedSignatureMembers` :class:`FlippedSignatureMembers`
Proxy collection :pc:`FlippedSignatureMembers(self)` that flips the data flow of Proxy collection :py:`FlippedSignatureMembers(self)` that flips the data flow of
the members that are accessed using it. the members that are accessed using it.
""" """
return FlippedSignatureMembers(self) return FlippedSignatureMembers(self)
@ -359,7 +359,7 @@ class SignatureMembers(Mapping):
Returns Returns
------- -------
:class:`bool` :class:`bool`
:pc:`True` if the mappings contain the same key-value pairs, :pc:`False` otherwise. :py:`True` if the mappings contain the same key-value pairs, :py:`False` otherwise.
""" """
return (isinstance(other, (SignatureMembers, FlippedSignatureMembers)) and return (isinstance(other, (SignatureMembers, FlippedSignatureMembers)) and
list(sorted(self.flatten())) == list(sorted(other.flatten()))) list(sorted(self.flatten())) == list(sorted(other.flatten())))
@ -391,11 +391,11 @@ class SignatureMembers(Mapping):
Raises Raises
------ ------
:exc:`TypeError` :exc:`TypeError`
If :pc:`name` is not a string. If :py:`name` is not a string.
:exc:`NameError` :exc:`NameError`
If :pc:`name` is not a valid, public Python attribute name. If :py:`name` is not a valid, public Python attribute name.
:exc:`SignatureError` :exc:`SignatureError`
If a member called :pc:`name` does not exist in the collection. If a member called :py:`name` does not exist in the collection.
""" """
self._check_name(name) self._check_name(name)
if name not in self._dict: if name not in self._dict:
@ -551,7 +551,7 @@ class FlippedSignatureMembers(Mapping):
Returns Returns
------- -------
:class:`SignatureMembers` :class:`SignatureMembers`
:pc:`unflipped` :py:`unflipped`
""" """
return self.__unflipped return self.__unflipped
@ -637,7 +637,7 @@ class SignatureMeta(type):
def __subclasscheck__(cls, subclass): def __subclasscheck__(cls, subclass):
""" """
Override of :pc:`issubclass(cls, Signature)`. Override of :py:`issubclass(cls, Signature)`.
In addition to the standard behavior of :func:`issubclass`, this override makes In addition to the standard behavior of :func:`issubclass`, this override makes
:class:`FlippedSignature` a subclass of :class:`Signature` or any of its subclasses. :class:`FlippedSignature` a subclass of :class:`Signature` or any of its subclasses.
@ -651,11 +651,11 @@ class SignatureMeta(type):
def __instancecheck__(cls, instance): def __instancecheck__(cls, instance):
""" """
Override of :pc:`isinstance(obj, Signature)`. Override of :py:`isinstance(obj, Signature)`.
In addition to the standard behavior of :func:`isinstance`, this override makes In addition to the standard behavior of :func:`isinstance`, this override makes
:pc:`isinstance(obj, cls)` act as :pc:`isinstance(obj.flip(), cls)` where :py:`isinstance(obj, cls)` act as :py:`isinstance(obj.flip(), cls)` where
:pc:`obj` is an instance of :class:`FlippedSignature`. :py:`obj` is an instance of :class:`FlippedSignature`.
""" """
# `FlippedSignature` is an instance of a `Signature` or its subclass if the unflipped # `FlippedSignature` is an instance of a `Signature` or its subclass if the unflipped
@ -668,7 +668,7 @@ class SignatureMeta(type):
class Signature(metaclass=SignatureMeta): class Signature(metaclass=SignatureMeta):
"""Description of an interface object. """Description of an interface object.
An interface object is a Python object that has a :pc:`signature` attribute containing An interface object is a Python object that has a :py:`signature` attribute containing
a :class:`Signature` object, as well as an attribute for every member of its signature. a :class:`Signature` object, as well as an attribute for every member of its signature.
Signatures and interface objects are tightly linked: an interface object can be created out Signatures and interface objects are tightly linked: an interface object can be created out
of a signature, and the signature is used when :func:`connect` ing two interface objects of a signature, and the signature is used when :func:`connect` ing two interface objects
@ -693,7 +693,7 @@ class Signature(metaclass=SignatureMeta):
Returns Returns
------- -------
:class:`FlippedSignature` :class:`FlippedSignature`
Proxy object :pc:`FlippedSignature(self)` that flips the data flow of the attributes Proxy object :py:`FlippedSignature(self)` that flips the data flow of the attributes
corresponding to the members that are accessed using it. corresponding to the members that are accessed using it.
See the documentation for the :class:`FlippedSignature` class for a detailed discussion See the documentation for the :class:`FlippedSignature` class for a detailed discussion
@ -714,10 +714,10 @@ class Signature(metaclass=SignatureMeta):
def __eq__(self, other): def __eq__(self, other):
"""Compare this signature with another. """Compare this signature with another.
The behavior of this operator depends on the types of the arguments. If both :pc:`self` The behavior of this operator depends on the types of the arguments. If both :py:`self`
and :pc:`other` are instances of the base :class:`Signature` class, they are compared and :py:`other` are instances of the base :class:`Signature` class, they are compared
structurally (the result is :pc:`self.members == other.members`); otherwise they are structurally (the result is :py:`self.members == other.members`); otherwise they are
compared by identity (the result is :pc:`self is other`). compared by identity (the result is :py:`self is other`).
Subclasses of :class:`Signature` are expected to override this method to take into account Subclasses of :class:`Signature` are expected to override this method to take into account
the specifics of the domain. If the subclass has additional properties that do not influence the specifics of the domain. If the subclass has additional properties that do not influence
@ -806,9 +806,9 @@ class Signature(metaclass=SignatureMeta):
It verifies that: It verifies that:
* :pc:`obj` has a :pc:`signature` attribute whose value a :class:`Signature` instance * :py:`obj` has a :py:`signature` attribute whose value a :class:`Signature` instance
such that ``self == obj.signature``; such that ``self == obj.signature``;
* for each member, :pc:`obj` has an attribute with the same name, whose value: * for each member, :py:`obj` has an attribute with the same name, whose value:
* for members with :meth:`dimensions <Member.dimensions>` specified, contains a list or * for members with :meth:`dimensions <Member.dimensions>` specified, contains a list or
a tuple (or several levels of nested lists or tuples, for multiple dimensions) a tuple (or several levels of nested lists or tuples, for multiple dimensions)
@ -820,24 +820,24 @@ class Signature(metaclass=SignatureMeta):
* for signature members, matches the description in the signature as verified by * for signature members, matches the description in the signature as verified by
:meth:`Signature.is_compliant`. :meth:`Signature.is_compliant`.
If the verification fails, this method reports the reason(s) by filling the :pc:`reasons` If the verification fails, this method reports the reason(s) by filling the :py:`reasons`
container. These reasons are intended to be human-readable: more than one reason may be container. These reasons are intended to be human-readable: more than one reason may be
reported but only in cases where this is helpful (e.g. the same error message will not reported but only in cases where this is helpful (e.g. the same error message will not
repeat 10 times for each of the 10 ports in a list). repeat 10 times for each of the 10 ports in a list).
Arguments Arguments
--------- ---------
reasons : :class:`list` or :pc:`None` reasons : :class:`list` or :py:`None`
If provided, a container that receives diagnostic messages. If provided, a container that receives diagnostic messages.
path : :class:`tuple` of :class:`str` path : :class:`tuple` of :class:`str`
The :ref:`path <wiring-path>` to :pc:`obj`. Could be set to improve diagnostic The :ref:`path <wiring-path>` to :py:`obj`. Could be set to improve diagnostic
messages if :pc:`obj` is nested within another object, or for clarity. messages if :py:`obj` is nested within another object, or for clarity.
Returns Returns
------- -------
:class:`bool` :class:`bool`
:pc:`True` if :pc:`obj` matches the description in this signature, :pc:`False` :py:`True` if :py:`obj` matches the description in this signature, :py:`False`
otherwise. If :pc:`False` and :pc:`reasons` was not :pc:`None`, it will contain otherwise. If :py:`False` and :py:`reasons` was not :py:`None`, it will contain
a detailed explanation why. a detailed explanation why.
""" """
@ -952,7 +952,7 @@ class Signature(metaclass=SignatureMeta):
This implementation creates an interface object from this signature that serves purely This implementation creates an interface object from this signature that serves purely
as a container for the attributes corresponding to the signature members, and implements as a container for the attributes corresponding to the signature members, and implements
no behavior. Such an implementation is sufficient for signatures created ad-hoc using no behavior. Such an implementation is sufficient for signatures created ad-hoc using
the :pc:`Signature({ ... })` constructor as well as simple signature subclasses. the :py:`Signature({ ... })` constructor as well as simple signature subclasses.
When defining a :class:`Signature` subclass that needs to customize the behavior of When defining a :class:`Signature` subclass that needs to customize the behavior of
the created interface objects, override this method with a similar implementation the created interface objects, override this method with a similar implementation
@ -969,7 +969,7 @@ class Signature(metaclass=SignatureMeta):
def my_property(self): def my_property(self):
... ...
The :pc:`path` and :pc:`src_loc_at` arguments are necessary to ensure the generated signals The :py:`path` and :py:`src_loc_at` arguments are necessary to ensure the generated signals
have informative names and accurate source location information. have informative names and accurate source location information.
The custom :meth:`create` method may take positional or keyword arguments in addition to The custom :meth:`create` method may take positional or keyword arguments in addition to
@ -1033,9 +1033,9 @@ class FlippedSignature:
It is not possible to inherit from :class:`FlippedSignature` and :meth:`Signature.flip` must not It is not possible to inherit from :class:`FlippedSignature` and :meth:`Signature.flip` must not
be overridden. If a :class:`Signature` subclass defines a method and this method is called on be overridden. If a :class:`Signature` subclass defines a method and this method is called on
a flipped instance of the subclass, it receives the flipped instance as its :pc:`self` argument. a flipped instance of the subclass, it receives the flipped instance as its :py:`self` argument.
To distinguish being called on the flipped instance from being called on the unflipped one, use To distinguish being called on the flipped instance from being called on the unflipped one, use
:pc:`isinstance(self, FlippedSignature)`: :py:`isinstance(self, FlippedSignature)`:
.. testcode:: .. testcode::
@ -1058,7 +1058,7 @@ class FlippedSignature:
Returns Returns
------- -------
:class:`Signature` :class:`Signature`
:pc:`unflipped` :py:`unflipped`
""" """
return self.__unflipped return self.__unflipped
@ -1089,11 +1089,11 @@ class FlippedSignature:
# are two possible exits via `except AttributeError`: from `getattr` and from `.__get__()`. # are two possible exits via `except AttributeError`: from `getattr` and from `.__get__()`.
def __getattr__(self, name): def __getattr__(self, name):
"""Retrieves attribute or method :pc:`name` of the unflipped signature. """Retrieves attribute or method :py:`name` of the unflipped signature.
Performs :pc:`getattr(unflipped, name)`, ensuring that, if :pc:`name` refers to a property Performs :py:`getattr(unflipped, name)`, ensuring that, if :py:`name` refers to a property
getter or a method, its :pc:`self` argument receives the *flipped* signature. A class getter or a method, its :py:`self` argument receives the *flipped* signature. A class
method's :pc:`cls` argument receives the class of the *unflipped* signature, as usual. method's :py:`cls` argument receives the class of the *unflipped* signature, as usual.
""" """
try: # descriptor first try: # descriptor first
return _gettypeattr(self.__unflipped, name).__get__(self, type(self.__unflipped)) return _gettypeattr(self.__unflipped, name).__get__(self, type(self.__unflipped))
@ -1101,10 +1101,10 @@ class FlippedSignature:
return getattr(self.__unflipped, name) return getattr(self.__unflipped, name)
def __setattr__(self, name, value): def __setattr__(self, name, value):
"""Assigns attribute :pc:`name` of the unflipped signature to ``value``. """Assigns attribute :py:`name` of the unflipped signature to ``value``.
Performs :pc:`setattr(unflipped, name, value)`, ensuring that, if :pc:`name` refers to Performs :py:`setattr(unflipped, name, value)`, ensuring that, if :py:`name` refers to
a property setter, its :pc:`self` argument receives the flipped signature. a property setter, its :py:`self` argument receives the flipped signature.
""" """
try: # descriptor first try: # descriptor first
_gettypeattr(self.__unflipped, name).__set__(self, value) _gettypeattr(self.__unflipped, name).__set__(self, value)
@ -1112,10 +1112,10 @@ class FlippedSignature:
setattr(self.__unflipped, name, value) setattr(self.__unflipped, name, value)
def __delattr__(self, name): def __delattr__(self, name):
"""Removes attribute :pc:`name` of the unflipped signature. """Removes attribute :py:`name` of the unflipped signature.
Performs :pc:`delattr(unflipped, name)`, ensuring that, if :pc:`name` refers to a property Performs :py:`delattr(unflipped, name)`, ensuring that, if :py:`name` refers to a property
deleter, its :pc:`self` argument receives the flipped signature. deleter, its :py:`self` argument receives the flipped signature.
""" """
try: # descriptor first try: # descriptor first
_gettypeattr(self.__unflipped, name).__delete__(self) _gettypeattr(self.__unflipped, name).__delete__(self)
@ -1140,7 +1140,7 @@ class PureInterface:
.. important:: .. important::
Any object can be an interface object; it only needs a :pc:`signature` property containing Any object can be an interface object; it only needs a :py:`signature` property containing
a compliant signature. It is **not** necessary to use :class:`PureInterface` in order to a compliant signature. It is **not** necessary to use :class:`PureInterface` in order to
create an interface object, but it may be used either directly or as a base class whenever create an interface object, but it may be used either directly or as a base class whenever
it is convenient to do so. it is convenient to do so.
@ -1150,7 +1150,7 @@ class PureInterface:
"""Create attributes from a signature. """Create attributes from a signature.
The sole method defined by this helper is its constructor, which only defines The sole method defined by this helper is its constructor, which only defines
the :pc:`self.signature` attribute as well as the attributes created from the signature the :py:`self.signature` attribute as well as the attributes created from the signature
members: members:
.. code:: .. code::
@ -1187,7 +1187,7 @@ class FlippedInterface:
"""An interface object, with its members' directions flipped. """An interface object, with its members' directions flipped.
An instance of :class:`FlippedInterface` should only be created by calling :func:`flipped`, An instance of :class:`FlippedInterface` should only be created by calling :func:`flipped`,
which ensures that a :pc:`FlippedInterface(FlippedInterface(...))` object is never created. which ensures that a :py:`FlippedInterface(FlippedInterface(...))` object is never created.
This proxy wraps any interface object and forwards attribute and method access to the wrapped This proxy wraps any interface object and forwards attribute and method access to the wrapped
interface object while flipping its signature and the values of any attributes corresponding to interface object while flipping its signature and the values of any attributes corresponding to
@ -1211,9 +1211,9 @@ class FlippedInterface:
It is not possible to inherit from :class:`FlippedInterface`. If an interface object class It is not possible to inherit from :class:`FlippedInterface`. If an interface object class
defines a method or a property and it is called on the flipped interface object, the method defines a method or a property and it is called on the flipped interface object, the method
receives the flipped interface object as its :pc:`self` argument. To distinguish being called receives the flipped interface object as its :py:`self` argument. To distinguish being called
on the flipped interface object from being called on the unflipped one, use on the flipped interface object from being called on the unflipped one, use
:pc:`isinstance(self, FlippedInterface)`: :py:`isinstance(self, FlippedInterface)`:
.. testcode:: .. testcode::
@ -1240,7 +1240,7 @@ class FlippedInterface:
Returns Returns
------- -------
Signature Signature
:pc:`unflipped.signature.flip()` :py:`unflipped.signature.flip()`
""" """
return self.__unflipped.signature.flip() return self.__unflipped.signature.flip()
@ -1250,8 +1250,8 @@ class FlippedInterface:
Returns Returns
------- -------
bool bool
:pc:`True` if :pc:`other` is an instance :pc:`FlippedInterface(other_unflipped)` where :py:`True` if :py:`other` is an instance :py:`FlippedInterface(other_unflipped)` where
:pc:`unflipped == other_unflipped`, :pc:`False` otherwise. :py:`unflipped == other_unflipped`, :py:`False` otherwise.
""" """
return type(self) is type(other) and self.__unflipped == other.__unflipped return type(self) is type(other) and self.__unflipped == other.__unflipped
@ -1259,13 +1259,13 @@ class FlippedInterface:
# an interface member. # an interface member.
def __getattr__(self, name): def __getattr__(self, name):
"""Retrieves attribute or method :pc:`name` of the unflipped interface. """Retrieves attribute or method :py:`name` of the unflipped interface.
Performs :pc:`getattr(unflipped, name)`, with the following caveats: Performs :py:`getattr(unflipped, name)`, with the following caveats:
1. If :pc:`name` refers to a signature member, the returned interface object is flipped. 1. If :py:`name` refers to a signature member, the returned interface object is flipped.
2. If :pc:`name` refers to a property getter or a method, its :pc:`self` argument receives 2. If :py:`name` refers to a property getter or a method, its :py:`self` argument receives
the *flipped* interface. A class method's :pc:`cls` argument receives the class of the *flipped* interface. A class method's :py:`cls` argument receives the class of
the *unflipped* interface, as usual. the *unflipped* interface, as usual.
""" """
if (name in self.__unflipped.signature.members and if (name in self.__unflipped.signature.members and
@ -1278,12 +1278,12 @@ class FlippedInterface:
return getattr(self.__unflipped, name) return getattr(self.__unflipped, name)
def __setattr__(self, name, value): def __setattr__(self, name, value):
"""Assigns attribute :pc:`name` of the unflipped interface to ``value``. """Assigns attribute :py:`name` of the unflipped interface to ``value``.
Performs :pc:`setattr(unflipped, name, value)`, with the following caveats: Performs :py:`setattr(unflipped, name, value)`, with the following caveats:
1. If :pc:`name` refers to a signature member, the assigned interface object is flipped. 1. If :py:`name` refers to a signature member, the assigned interface object is flipped.
2. If :pc:`name` refers to a property setter, its :pc:`self` argument receives the flipped 2. If :py:`name` refers to a property setter, its :py:`self` argument receives the flipped
interface. interface.
""" """
if (name in self.__unflipped.signature.members and if (name in self.__unflipped.signature.members and
@ -1296,10 +1296,10 @@ class FlippedInterface:
setattr(self.__unflipped, name, value) setattr(self.__unflipped, name, value)
def __delattr__(self, name): def __delattr__(self, name):
"""Removes attribute :pc:`name` of the unflipped interface. """Removes attribute :py:`name` of the unflipped interface.
Performs :pc:`delattr(unflipped, name)`, ensuring that, if :pc:`name` refers to a property Performs :py:`delattr(unflipped, name)`, ensuring that, if :py:`name` refers to a property
deleter, its :pc:`self` argument receives the flipped interface. deleter, its :py:`self` argument receives the flipped interface.
""" """
try: # descriptor first try: # descriptor first
_gettypeattr(self.__unflipped, name).__delete__(self) _gettypeattr(self.__unflipped, name).__delete__(self)
@ -1312,10 +1312,10 @@ class FlippedInterface:
def flipped(interface): def flipped(interface):
""" """
Flip the data flow of the members of the interface object :pc:`interface`. Flip the data flow of the members of the interface object :py:`interface`.
If an interface object is flipped twice, returns the original object: If an interface object is flipped twice, returns the original object:
:pc:`flipped(flipped(interface)) is interface`. Otherwise, wraps :pc:`interface` in :py:`flipped(flipped(interface)) is interface`. Otherwise, wraps :py:`interface` in
a :class:`FlippedInterface` proxy object that flips the directions of its members. a :class:`FlippedInterface` proxy object that flips the directions of its members.
See the documentation for the :class:`FlippedInterface` class for a detailed discussion of how See the documentation for the :class:`FlippedInterface` class for a detailed discussion of how
@ -1352,11 +1352,11 @@ def connect(m, *args, **kwargs):
to a constant value, then the rest of the interface objects must have output port members to a constant value, then the rest of the interface objects must have output port members
corresponding to the same constant value. corresponding to the same constant value.
For example, if :pc:`obj1` is being connected to :pc:`obj2` and :pc:`obj3`, and :pc:`obj1.a.b` For example, if :py:`obj1` is being connected to :py:`obj2` and :py:`obj3`, and :py:`obj1.a.b`
is an output, then :pc:`obj2.a.b` and :pc:`obj2.a.b` must exist and be inputs. If :pc:`obj2.c` is an output, then :py:`obj2.a.b` and :py:`obj2.a.b` must exist and be inputs. If :py:`obj2.c`
is an input and its value is :pc:`Const(1)`, then :pc:`obj1.c` and :pc:`obj3.c` must be outputs is an input and its value is :py:`Const(1)`, then :py:`obj1.c` and :py:`obj3.c` must be outputs
whose value is also :pc:`Const(1)`. If no ports besides :pc:`obj1.a.b` and :pc:`obj1.c` exist, whose value is also :py:`Const(1)`. If no ports besides :py:`obj1.a.b` and :py:`obj1.c` exist,
then no ports except for those two must exist on :pc:`obj2` and :pc:`obj3` either. then no ports except for those two must exist on :py:`obj2` and :py:`obj3` either.
Once it is determined that the interface objects can be connected, this function performs Once it is determined that the interface objects can be connected, this function performs
an equivalent of: an equivalent of:
@ -1369,12 +1369,12 @@ def connect(m, *args, **kwargs):
... ...
] ]
Where :pc:`out1` is an output and :pc:`in1`, :pc:`in2`, ... are the inputs that have the same Where :py:`out1` is an output and :py:`in1`, :py:`in2`, ... are the inputs that have the same
path. (If no interface object has an output for a given path, **no connection at all** is made.) path. (If no interface object has an output for a given path, **no connection at all** is made.)
The positions (within :pc:`args`) or names (within :pc:`kwargs`) of the arguments do not affect The positions (within :py:`args`) or names (within :py:`kwargs`) of the arguments do not affect
the connections that are made. There is no difference in behavior between :pc:`connect(m, a, b)` the connections that are made. There is no difference in behavior between :py:`connect(m, a, b)`
and :pc:`connect(m, b, a)` or :pc:`connect(m, arbiter=a, decoder=b)`. The names of the keyword and :py:`connect(m, b, a)` or :py:`connect(m, arbiter=a, decoder=b)`. The names of the keyword
arguments serve only a documentation purpose: they clarify the diagnostic messages when arguments serve only a documentation purpose: they clarify the diagnostic messages when
a connection cannot be made. a connection cannot be made.
""" """
@ -1609,8 +1609,8 @@ class Component(Elaboratable):
Raises Raises
------ ------
:exc:`TypeError` :exc:`TypeError`
If the :pc:`signature` object is neither a :class:`Signature` nor a :class:`dict`. If the :py:`signature` object is neither a :class:`Signature` nor a :class:`dict`.
If neither variable annotations nor the :pc:`signature` argument are present, or if If neither variable annotations nor the :py:`signature` argument are present, or if
both are present. both are present.
:exc:`NameError` :exc:`NameError`
If a name conflict is detected between two variable annotations, or between a member If a name conflict is detected between two variable annotations, or between a member

View file

@ -47,6 +47,6 @@ html_css_files = ["custom.css"]
html_logo = "_static/logo.png" html_logo = "_static/logo.png"
rst_prolog = """ rst_prolog = """
.. role:: pc(code) .. role:: py(code)
:language: python :language: python
""" """

View file

@ -760,13 +760,13 @@ Amaranth operation Equivalent Python code
Match operator Match operator
-------------- --------------
The :pc:`val.matches(*patterns)` operator examines a value against a set of patterns. It evaluates to :pc:`Const(1)` if the value *matches* any of the patterns, and to :pc:`Const(0)` otherwise. What it means for a value to match a pattern depends on the type of the pattern. The :py:`val.matches(*patterns)` operator examines a value against a set of patterns. It evaluates to :py:`Const(1)` if the value *matches* any of the patterns, and to :py:`Const(0)` otherwise. What it means for a value to match a pattern depends on the type of the pattern.
If the pattern is a :class:`str`, it is treated as a bit mask with "don't care" bits. After removing whitespace, each character of the pattern is compared to the corresponding bit of the value, where the leftmost character of the pattern (with the lowest index) corresponds to the most significant bit of the value. If the pattern character is ``'0'`` or ``'1'``, the comparison succeeds if the bit equals ``0`` or ``1`` correspondingly. If the pattern character is ``'-'``, the comparison always succeeds. Aside from spaces and tabs, which are ignored, no other characters are accepted. If the pattern is a :class:`str`, it is treated as a bit mask with "don't care" bits. After removing whitespace, each character of the pattern is compared to the corresponding bit of the value, where the leftmost character of the pattern (with the lowest index) corresponds to the most significant bit of the value. If the pattern character is ``'0'`` or ``'1'``, the comparison succeeds if the bit equals ``0`` or ``1`` correspondingly. If the pattern character is ``'-'``, the comparison always succeeds. Aside from spaces and tabs, which are ignored, no other characters are accepted.
Otherwise, the pattern is :ref:`cast to a constant <lang-constcasting>` and compared to :pc:`val` using the :ref:`equality operator <lang-cmpops>`. Otherwise, the pattern is :ref:`cast to a constant <lang-constcasting>` and compared to :py:`val` using the :ref:`equality operator <lang-cmpops>`.
For example, given a 8-bit value :pc:`val`, :pc:`val.matches(1, '---- -01-')` is equivalent to :pc:`(val == 1) | ((val & 0b0000_0110) == 0b0000_0010)`. Bit patterns in this operator are treated similarly to :ref:`bit sequence operators <lang-bitops>`. For example, given a 8-bit value :py:`val`, :py:`val.matches(1, '---- -01-')` is equivalent to :py:`(val == 1) | ((val & 0b0000_0110) == 0b0000_0010)`. Bit patterns in this operator are treated similarly to :ref:`bit sequence operators <lang-bitops>`.
The :ref:`Case <lang-switch>` control flow block accepts the same patterns, with the same meaning, as the match operator. The :ref:`Case <lang-switch>` control flow block accepts the same patterns, with the same meaning, as the match operator.
@ -997,7 +997,7 @@ Multiple assignments to the same signal bits are more useful when combined with
Control flow Control flow
============ ============
Although it is possible to write any decision tree as a combination of :ref:`assignments <lang-assigns>` and :ref:`choice expressions <lang-muxop>`, Amaranth provides *control flow syntax* tailored for this task: :ref:`If/Elif/Else <lang-if>`, :ref:`Switch/Case <lang-switch>`, and :ref:`FSM/State <lang-fsm>`. The control flow syntax uses :pc:`with` blocks (it is implemented using :ref:`context managers <python:context-managers>`), for example: Although it is possible to write any decision tree as a combination of :ref:`assignments <lang-assigns>` and :ref:`choice expressions <lang-muxop>`, Amaranth provides *control flow syntax* tailored for this task: :ref:`If/Elif/Else <lang-if>`, :ref:`Switch/Case <lang-switch>`, and :ref:`FSM/State <lang-fsm>`. The control flow syntax uses :py:`with` blocks (it is implemented using :ref:`context managers <python:context-managers>`), for example:
.. TODO: link to relevant subsections .. TODO: link to relevant subsections
@ -1009,14 +1009,14 @@ Although it is possible to write any decision tree as a combination of :ref:`ass
with m.Else(): with m.Else():
m.d.sync += timer.eq(timer - 1) m.d.sync += timer.eq(timer - 1)
While some Amaranth control structures are superficially similar to imperative control flow statements (such as Python's :pc:`if`), their function---together with :ref:`expressions <lang-abstractexpr>` and :ref:`assignments <lang-assigns>`---is to describe circuits. The code above is equivalent to: While some Amaranth control structures are superficially similar to imperative control flow statements (such as Python's :py:`if`), their function---together with :ref:`expressions <lang-abstractexpr>` and :ref:`assignments <lang-assigns>`---is to describe circuits. The code above is equivalent to:
.. testcode:: .. testcode::
timer = Signal(8) timer = Signal(8)
m.d.sync += timer.eq(Mux(timer == 0, 10, timer - 1)) m.d.sync += timer.eq(Mux(timer == 0, 10, timer - 1))
Because all branches of a decision tree affect the generated circuit, all of the Python code inside Amaranth control structures is always evaluated in the order in which it appears in the program. This can be observed through Python code with side effects, such as :pc:`print()`: Because all branches of a decision tree affect the generated circuit, all of the Python code inside Amaranth control structures is always evaluated in the order in which it appears in the program. This can be observed through Python code with side effects, such as :py:`print()`:
.. testcode:: .. testcode::
@ -1079,10 +1079,10 @@ Combining these cases together, the code above is equivalent to:
.. _lang-if: .. _lang-if:
:pc:`If`/:pc:`Elif`/:pc:`Else` control blocks :py:`If`/:py:`Elif`/:py:`Else` control blocks
--------------------------------------------- ---------------------------------------------
Conditional control flow is described using a :pc:`with m.If(cond1):` block, which may be followed by one or more :pc:`with m.Elif(cond2):` blocks, and optionally a final :pc:`with m.Else():` block. This structure parallels Python's own :ref:`if/elif/else <python:if>` control flow syntax. For example: Conditional control flow is described using a :py:`with m.If(cond1):` block, which may be followed by one or more :py:`with m.Elif(cond2):` blocks, and optionally a final :py:`with m.Else():` block. This structure parallels Python's own :ref:`if/elif/else <python:if>` control flow syntax. For example:
.. testcode:: .. testcode::
:hide: :hide:
@ -1106,17 +1106,17 @@ Conditional control flow is described using a :pc:`with m.If(cond1):` block, whi
with m.Else(): with m.Else():
m.d.sync += x_coord.eq(0) m.d.sync += x_coord.eq(0)
Within a single :pc:`If`/:pc:`Elif`/:pc:`Else` sequence of blocks, the statements within at most one block will be active at any time. This will be the first block in the order of definition whose condition, :ref:`converted to boolean <lang-bool>`, is true. Within a single :py:`If`/:py:`Elif`/:py:`Else` sequence of blocks, the statements within at most one block will be active at any time. This will be the first block in the order of definition whose condition, :ref:`converted to boolean <lang-bool>`, is true.
If an :pc:`Else` block is present, then the statements within exactly one block will be active at any time, and the sequence as a whole is called a *full condition*. If an :py:`Else` block is present, then the statements within exactly one block will be active at any time, and the sequence as a whole is called a *full condition*.
.. _lang-switch: .. _lang-switch:
:pc:`Switch`/:pc:`Case` control blocks :py:`Switch`/:py:`Case` control blocks
-------------------------------------- --------------------------------------
Case comparison, where a single value is examined against several different *patterns*, is described using a :pc:`with m.Switch(value):` block. This block can contain any amount of :pc:`with m.Case(*patterns)` and :pc:`with m.Default():` blocks. This structure parallels Python's own :ref:`match/case <python:match>` control flow syntax. For example: Case comparison, where a single value is examined against several different *patterns*, is described using a :py:`with m.Switch(value):` block. This block can contain any amount of :py:`with m.Case(*patterns)` and :py:`with m.Default():` blocks. This structure parallels Python's own :ref:`match/case <python:match>` control flow syntax. For example:
.. TODO: rename `Switch` to `Match`, to mirror `Value.matches()`? .. TODO: rename `Switch` to `Match`, to mirror `Value.matches()`?
@ -1141,13 +1141,13 @@ Case comparison, where a single value is examined against several different *pat
.. TODO: diagnostic for `Case` blocks after `Default`? .. TODO: diagnostic for `Case` blocks after `Default`?
Within a single :pc:`Switch` block, the statements within at most one block will be active at any time. This will be the first :pc:`Case` block in the order of definition whose pattern :ref:`matches <lang-matchop>` the value, or the first :pc:`Default` block, whichever is earlier. Within a single :py:`Switch` block, the statements within at most one block will be active at any time. This will be the first :py:`Case` block in the order of definition whose pattern :ref:`matches <lang-matchop>` the value, or the first :py:`Default` block, whichever is earlier.
If a :pc:`Default` block is present, or the patterns in the :pc:`Case` blocks cover every possible :pc:`Switch` value, then the statements within exactly one block will be active at any time, and the sequence as a whole is called a *full condition*. If a :py:`Default` block is present, or the patterns in the :py:`Case` blocks cover every possible :py:`Switch` value, then the statements within exactly one block will be active at any time, and the sequence as a whole is called a *full condition*.
.. tip:: .. tip::
While all Amaranth control flow syntax can be generated programmatically, the :pc:`Switch` control block is particularly easy to use in this way: While all Amaranth control flow syntax can be generated programmatically, the :py:`Switch` control block is particularly easy to use in this way:
.. testcode:: .. testcode::
@ -1162,10 +1162,10 @@ If a :pc:`Default` block is present, or the patterns in the :pc:`Case` blocks co
.. _lang-fsm: .. _lang-fsm:
:pc:`FSM`/:pc:`State` control blocks :py:`FSM`/:py:`State` control blocks
------------------------------------ ------------------------------------
Simple `finite state machines <https://en.wikipedia.org/wiki/Finite-state_machine>`_ are described using a :pc:`with m.FSM():` block. This block can contain one or more :pc:`with m.State("Name")` blocks. In addition to these blocks, the :pc:`m.next = "Name"` syntax chooses which state the FSM enters on the next clock cycle. For example, this FSM performs a bus read transaction once after reset: Simple `finite state machines <https://en.wikipedia.org/wiki/Finite-state_machine>`_ are described using a :py:`with m.FSM():` block. This block can contain one or more :py:`with m.State("Name")` blocks. In addition to these blocks, the :py:`m.next = "Name"` syntax chooses which state the FSM enters on the next clock cycle. For example, this FSM performs a bus read transaction once after reset:
.. testcode:: .. testcode::
@ -1190,21 +1190,21 @@ Simple `finite state machines <https://en.wikipedia.org/wiki/Finite-state_machin
.. TODO: FSM() should require keyword arguments, for good measure .. TODO: FSM() should require keyword arguments, for good measure
The reset state of the FSM can be provided when defining it using the :pc:`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 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:
.. testcode:: .. testcode::
with m.FSM(reset="Set Address"): with m.FSM(reset="Set Address"):
... ...
The FSM belongs to a :ref:`clock domain <lang-domains>`, which is specified using the :pc:`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: 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:
.. testcode:: .. testcode::
with m.FSM(domain="sync"): with m.FSM(domain="sync"):
... ...
To determine (from code that is outside the FSM definition) whether it is currently in a particular state, the FSM can be captured; its :pc:`.ongoing("Name")` method returns a value that is true whenever the FSM is in the corresponding state. For example: To determine (from code that is outside the FSM definition) whether it is currently in a particular state, the FSM can be captured; its :py:`.ongoing("Name")` method returns a value that is true whenever the FSM is in the corresponding state. For example:
.. testcode:: .. testcode::
@ -1214,20 +1214,20 @@ To determine (from code that is outside the FSM definition) whether it is curren
with m.If(fsm.ongoing("Set Address")): with m.If(fsm.ongoing("Set Address")):
... ...
Note that in Python, assignments made using :pc:`with x() as y:` syntax persist past the end of the block. Note that in Python, assignments made using :py:`with x() as y:` syntax persist past the end of the block.
.. TODO: `ongoing` currently creates a state if it doesn't exist, which seems clearly wrong but maybe some depend on it? add a diagnostic here .. TODO: `ongoing` currently creates a state if it doesn't exist, which seems clearly wrong but maybe some depend on it? add a diagnostic here
.. TODO: `m.next` does the same, which is worse because adding a diagnostic is harder .. TODO: `m.next` does the same, which is worse because adding a diagnostic is harder
.. warning:: .. warning::
If you make a typo in the state name provided to :pc:`m.next = ...` or :pc:`fsm.ongoing(...)`, an empty and unreachable state with that name will be created with no diagnostic message. If you make a typo in the state name provided to :py:`m.next = ...` or :py:`fsm.ongoing(...)`, an empty and unreachable state with that name will be created with no diagnostic message.
This hazard will be eliminated in the future. This hazard will be eliminated in the future.
.. warning:: .. warning::
If a non-string object is provided as a state name to :pc:`with m.State(...):`, it is cast to a string first, which may lead to surprising behavior. :pc:`with m.State(...):` **does not** treat an enumeration value specially; if one is provided, it is cast to a string, and its numeric value will have no correspondence to the numeric value of the generated state signal. If a non-string object is provided as a state name to :py:`with m.State(...):`, it is cast to a string first, which may lead to surprising behavior. :py:`with m.State(...):` **does not** treat an enumeration value specially; if one is provided, it is cast to a string, and its numeric value will have no correspondence to the numeric value of the generated state signal.
This hazard will be eliminated in the future. This hazard will be eliminated in the future.
@ -1235,7 +1235,7 @@ Note that in Python, assignments made using :pc:`with x() as y:` syntax persist
.. note:: .. note::
If you are nesting two state machines within each other, the :pc:`m.next = ...` syntax always refers to the innermost one. To change the state of the outer state machine from within the inner one, use an intermediate signal. If you are nesting two state machines within each other, the :py:`m.next = ...` syntax always refers to the innermost one. To change the state of the outer state machine from within the inner one, use an intermediate signal.
.. _lang-comb: .. _lang-comb:
@ -1302,7 +1302,7 @@ Consider the following code:
with m.Elif(down): with m.Elif(down):
m.d.sync += timer.eq(timer - 1) m.d.sync += timer.eq(timer - 1)
Whenever there is a transition on the clock of the ``sync`` domain, the :pc:`timer` signal is incremented by one if :pc:`up` is true, decremented by one if :pc:`down` is true, and retains its value otherwise. Whenever there is a transition on the clock of the ``sync`` domain, the :py:`timer` signal is incremented by one if :py:`up` is true, decremented by one if :py:`down` is true, and retains its value otherwise.
.. _lang-clockdomains: .. _lang-clockdomains:
@ -1310,7 +1310,7 @@ Whenever there is a transition on the clock of the ``sync`` domain, the :pc:`tim
Clock domains Clock domains
============= =============
A new synchronous :ref:`control domain <lang-domains>`, which is more often called a *clock domain*, can be defined in a design by creating a :class:`ClockDomain` object and adding it to the :pc:`m.domains` collection: A new synchronous :ref:`control domain <lang-domains>`, which is more often called a *clock domain*, can be defined in a design by creating a :class:`ClockDomain` object and adding it to the :py:`m.domains` collection:
.. testcode:: .. testcode::
@ -1329,7 +1329,7 @@ If the name of the domain is not known upfront, another, less concise, syntax ca
.. note:: .. note::
Whenever the created :class:`ClockDomain` object is immediately assigned using the :pc:`domain_name = ClockDomain(...)` or :pc:`m.domains.domain_name = ClockDomain(...)` syntax, the name of the domain may be omitted from the :pc:`ClockDomain()` invocation. In other cases, it must be provided as the first argument. Whenever the created :class:`ClockDomain` object is immediately assigned using the :py:`domain_name = ClockDomain(...)` or :py:`m.domains.domain_name = ClockDomain(...)` syntax, the name of the domain may be omitted from the :py:`ClockDomain()` invocation. In other cases, it must be provided as the first argument.
A clock domain always has a clock signal, which can be accessed through the :attr:`cd.clk <ClockDomain.clk>` attribute. By default, the *active edge* of the clock domain is positive; this means that the signals in the domain change when the clock signal transitions from 0 to 1. A clock domain can be configured to have a negative active edge so that signals in it change when the clock signal transitions from 1 to 0: A clock domain always has a clock signal, which can be accessed through the :attr:`cd.clk <ClockDomain.clk>` attribute. By default, the *active edge* of the clock domain is positive; this means that the signals in the domain change when the clock signal transitions from 0 to 1. A clock domain can be configured to have a negative active edge so that signals in it change when the clock signal transitions from 1 to 0:
@ -1347,7 +1347,7 @@ If a clock domain is defined in a module, all of its submodules can refer to tha
.. warning:: .. warning::
Always provide the :pc:`local=True` keyword argument when defining a clock domain. The behavior of clock domains defined without this keyword argument is subject to change in near future, and is intentionally left undocumented. Always provide the :py:`local=True` keyword argument when defining a clock domain. The behavior of clock domains defined without this keyword argument is subject to change in near future, and is intentionally left undocumented.
.. warning:: .. warning::
@ -1384,7 +1384,7 @@ Clock domains are *late bound*, which means that their signals and properties ca
ResetSignal().eq(~bus_rstn), ResetSignal().eq(~bus_rstn),
] ]
In this example, once the design is processed, the clock signal of the clock domain ``sync`` found in this module or one of its containing modules will be equal to :pc:`bus_clk`. The reset signal of the same clock domain will be equal to the negated :pc:`bus_rstn`. With the ``sync`` domain created in the same module, these statements become equivalent to: In this example, once the design is processed, the clock signal of the clock domain ``sync`` found in this module or one of its containing modules will be equal to :py:`bus_clk`. The reset signal of the same clock domain will be equal to the negated :py:`bus_rstn`. With the ``sync`` domain created in the same module, these statements become equivalent to:
.. TODO: explain the difference (or lack thereof, eventually) between m.d, m.domain, and m.domains .. TODO: explain the difference (or lack thereof, eventually) between m.d, m.domain, and m.domains
@ -1396,13 +1396,13 @@ In this example, once the design is processed, the clock signal of the clock dom
cd_sync.rst.eq(~bus_rstn), cd_sync.rst.eq(~bus_rstn),
] ]
The :class:`ClockSignal` and :class:`ResetSignal` values may also be assigned to other signals and used in expressions. They take a single argument, which is the name of the domain; if not specified, it defaults to :pc:`"sync"`. The :class:`ClockSignal` and :class:`ResetSignal` values may also be assigned to other signals and used in expressions. They take a single argument, which is the name of the domain; if not specified, it defaults to :py:`"sync"`.
.. warning:: .. warning::
Be especially careful when using :class:`ClockSignal` or :attr:`cd.clk <ClockDomain.clk>` in expressions. Assigning to and from a clock signal is usually safe; any other operations may have unpredictable results. Consult the documentation for your synthesis toolchain and platform to understand which operations with a clock signal are permitted. Be especially careful when using :class:`ClockSignal` or :attr:`cd.clk <ClockDomain.clk>` in expressions. Assigning to and from a clock signal is usually safe; any other operations may have unpredictable results. Consult the documentation for your synthesis toolchain and platform to understand which operations with a clock signal are permitted.
FPGAs usually have dedicated clocking facilities that can be used to disable, divide, or multiplex clock signals. When targeting an FPGA, these facilities should be used if at all possible, and expressions like :pc:`ClockSignal() & en` or :pc:`Mux(sel, ClockSignal("a"), ClockSignal("b"))` should be avoided. FPGAs usually have dedicated clocking facilities that can be used to disable, divide, or multiplex clock signals. When targeting an FPGA, these facilities should be used if at all possible, and expressions like :py:`ClockSignal() & en` or :py:`Mux(sel, ClockSignal("a"), ClockSignal("b"))` should be avoided.
.. _lang-elaboration: .. _lang-elaboration:
@ -1430,7 +1430,7 @@ The :meth:`~Elaboratable.elaborate` method must either return an instance of :cl
Instances of :class:`Module` also implement the :meth:`~Elaboratable.elaborate` method, which returns a special object that represents a fragment of a netlist. Such an object cannot be constructed without using :class:`Module`. Instances of :class:`Module` also implement the :meth:`~Elaboratable.elaborate` method, which returns a special object that represents a fragment of a netlist. Such an object cannot be constructed without using :class:`Module`.
The :pc:`platform` argument received by the :meth:`~Elaboratable.elaborate` method can be :pc:`None`, an instance of :ref:`a built-in platform <platform>`, or a custom object. It is used for `dependency injection <https://en.wikipedia.org/wiki/Dependency_injection>`_ and to contain the state of a design while it is being elaborated. The :py:`platform` argument received by the :meth:`~Elaboratable.elaborate` method can be :py:`None`, an instance of :ref:`a built-in platform <platform>`, or a custom object. It is used for `dependency injection <https://en.wikipedia.org/wiki/Dependency_injection>`_ and to contain the state of a design while it is being elaborated.
.. important:: .. important::
@ -1483,7 +1483,7 @@ Control flow within an elaboratable can be altered without introducing a new clo
* :class:`ResetInserter` introduces a synchronous reset input (or inputs), updating all of the signals in the specified domains to their :ref:`initial value <lang-initial>` whenever the active edge occurs on the clock of the domain *if* the synchronous reset input is asserted. * :class:`ResetInserter` introduces a synchronous reset input (or inputs), updating all of the signals in the specified domains to their :ref:`initial value <lang-initial>` whenever the active edge occurs on the clock of the domain *if* the synchronous reset input is asserted.
* :class:`EnableInserter` introduces a synchronous enable input (or inputs), preventing any of the signals in the specified domains from changing value whenever the active edge occurs on the clock of the domain *unless* the synchronous enable input is asserted. * :class:`EnableInserter` introduces a synchronous enable input (or inputs), preventing any of the signals in the specified domains from changing value whenever the active edge occurs on the clock of the domain *unless* the synchronous enable input is asserted.
Control flow modifiers use the syntax :pc:`Modifier(controls)(elaboratable)`, where :pc:`controls` is a mapping from :ref:`clock domain <lang-clockdomains>` names to 1-wide :ref:`values <lang-values>` and :pc:`elaboratable` is any :ref:`elaboratable <lang-elaboration>` object. When only the ``sync`` domain is involved, instead of writing :pc:`Modifier({"sync": input})(elaboratable)`, the equivalent but shorter :pc:`Modifier(input)(elaboratable)` syntax can be used. Control flow modifiers use the syntax :py:`Modifier(controls)(elaboratable)`, where :py:`controls` is a mapping from :ref:`clock domain <lang-clockdomains>` names to 1-wide :ref:`values <lang-values>` and :py:`elaboratable` is any :ref:`elaboratable <lang-elaboration>` object. When only the ``sync`` domain is involved, instead of writing :py:`Modifier({"sync": input})(elaboratable)`, the equivalent but shorter :py:`Modifier(input)(elaboratable)` syntax can be used.
The result of applying a control flow modifier to an elaboratable is, itself, an elaboratable object. A common way to use a control flow modifier is to apply it to another elaboratable while adding it as a submodule: The result of applying a control flow modifier to an elaboratable is, itself, an elaboratable object. A common way to use a control flow modifier is to apply it to another elaboratable while adding it as a submodule:
@ -1526,7 +1526,7 @@ Consider the following code:
m = ResetInserter({"sync": rst})(m) m = ResetInserter({"sync": rst})(m)
m = EnableInserter({"sync": en})(m) m = EnableInserter({"sync": en})(m)
The application of control flow modifiers in it causes the behavior of the final :pc:`m` to be identical to that of this module: The application of control flow modifiers in it causes the behavior of the final :py:`m` to be identical to that of this module:
.. testcode:: .. testcode::
@ -1551,7 +1551,7 @@ Renaming domains
A reusable :ref:`elaboratable <lang-elaboration>` usually specifies the use of one or more :ref:`clock domains <lang-clockdomains>` while leaving the details of clocking and initialization to a later phase in the design process. :class:`DomainRenamer` can be used to alter a reusable elaboratable for integration in a specific design. Most elaboratables use a single clock domain named ``sync``, and :class:`DomainRenamer` makes it easy to place such elaboratables in any clock domain of a design. A reusable :ref:`elaboratable <lang-elaboration>` usually specifies the use of one or more :ref:`clock domains <lang-clockdomains>` while leaving the details of clocking and initialization to a later phase in the design process. :class:`DomainRenamer` can be used to alter a reusable elaboratable for integration in a specific design. Most elaboratables use a single clock domain named ``sync``, and :class:`DomainRenamer` makes it easy to place such elaboratables in any clock domain of a design.
Clock domains can be renamed using the syntax :pc:`DomainRenamer(domains)(elaboratable)`, where :pc:`domains` is a mapping from clock domain names to clock domain names and :pc:`elaboratable` is any :ref:`elaboratable <lang-elaboration>` object. The keys of :pc:`domains` correspond to existing clock domain names specified by :pc:`elaboratable`, and the values of :pc:`domains` correspond to the clock domain names from the containing elaboratable that will be used instead. When only the ``sync`` domain is being renamed, instead of writing :pc:`DomainRenamer({"sync": name})(elaboratable)`, the equivalent but shorter :pc:`DomainRenamer(name)(elaboratable)` syntax can be used. Clock domains can be renamed using the syntax :py:`DomainRenamer(domains)(elaboratable)`, where :py:`domains` is a mapping from clock domain names to clock domain names and :py:`elaboratable` is any :ref:`elaboratable <lang-elaboration>` object. The keys of :py:`domains` correspond to existing clock domain names specified by :py:`elaboratable`, and the values of :py:`domains` correspond to the clock domain names from the containing elaboratable that will be used instead. When only the ``sync`` domain is being renamed, instead of writing :py:`DomainRenamer({"sync": name})(elaboratable)`, the equivalent but shorter :py:`DomainRenamer(name)(elaboratable)` syntax can be used.
The result of renaming clock domains in an elaboratable is, itself, an elaboratable object. A common way to rename domains is to apply :class:`DomainRenamer` to another elaboratable while adding it as a submodule: The result of renaming clock domains in an elaboratable is, itself, an elaboratable object. A common way to rename domains is to apply :class:`DomainRenamer` to another elaboratable while adding it as a submodule:
@ -1590,7 +1590,7 @@ Consider the following code:
m = DomainRenamer({"sync": "video"})(m) m = DomainRenamer({"sync": "video"})(m)
The renaming of the ``sync`` clock domain in it causes the behavior of the final :pc:`m` to be identical to that of this module: The renaming of the ``sync`` clock domain in it causes the behavior of the final :py:`m` to be identical to that of this module:
.. testcode:: .. testcode::
@ -1626,12 +1626,12 @@ A submodule written in a non-Amaranth language is called an *instance*. An insta
* The *parameters* of an instance correspond to parameters of a (System)Verilog module instance, or a generic constant of a VHDL entity or component instance. Not all HDLs allow their design units to be parameterized during instantiation. * The *parameters* of an instance correspond to parameters of a (System)Verilog module instance, or a generic constant of a VHDL entity or component instance. Not all HDLs allow their design units to be parameterized during instantiation.
* The *inputs* and *outputs* of an instance correspond to inputs and outputs of the external design unit. * The *inputs* and *outputs* of an instance correspond to inputs and outputs of the external design unit.
An instance can be added as a submodule using the :pc:`m.submodules.name = Instance("type", ...)` syntax, where :pc:`"type"` is the type of the instance as a string (which is passed to the synthesis toolchain uninterpreted), and :pc:`...` is a list of parameters, inputs, and outputs. Depending on whether the name of an attribute, parameter, input, or output can be written as a part of a Python identifier or not, one of two possible syntaxes is used to specify them: An instance can be added as a submodule using the :py:`m.submodules.name = Instance("type", ...)` syntax, where :py:`"type"` is the type of the instance as a string (which is passed to the synthesis toolchain uninterpreted), and :py:`...` is a list of parameters, inputs, and outputs. Depending on whether the name of an attribute, parameter, input, or output can be written as a part of a Python identifier or not, one of two possible syntaxes is used to specify them:
* An attribute is specified using the :pc:`a_ANAME=attr` or :pc:`("a", "ANAME", attr)` syntaxes. The :pc:`attr` must be an :class:`int`, a :class:`str`, or a :class:`Const`. * An attribute is specified using the :py:`a_ANAME=attr` or :py:`("a", "ANAME", attr)` syntaxes. The :py:`attr` must be an :class:`int`, a :class:`str`, or a :class:`Const`.
* A parameter is specified using the :pc:`p_PNAME=param` or :pc:`("p", "PNAME", param)` syntaxes. The :pc:`param` must be an :class:`int`, a :class:`str`, or a :class:`Const`. * A parameter is specified using the :py:`p_PNAME=param` or :py:`("p", "PNAME", param)` syntaxes. The :py:`param` must be an :class:`int`, a :class:`str`, or a :class:`Const`.
* An input is specified using the :pc:`i_INAME=in_val` or :pc:`("i", "INAME", in_val)` syntaxes. The :pc:`in_val` must be a :ref:`value-like <lang-valuelike>` object. * An input is specified using the :py:`i_INAME=in_val` or :py:`("i", "INAME", in_val)` syntaxes. The :py:`in_val` must be a :ref:`value-like <lang-valuelike>` object.
* An output is specified using the :pc:`o_ONAME=out_val` or :pc:`("o", "ONAME", out_val)` syntaxes. The :pc:`out_val` must be a :ref:`value-like <lang-valuelike>` object that casts to a :class:`Signal`. * An output is specified using the :py:`o_ONAME=out_val` or :py:`("o", "ONAME", out_val)` syntaxes. The :py:`out_val` must be a :ref:`value-like <lang-valuelike>` object that casts to a :class:`Signal`.
The two following examples use both syntaxes to add the same instance of type ``external`` as a submodule named ``processor``: The two following examples use both syntaxes to add the same instance of type ``external`` as a submodule named ``processor``:
@ -1683,7 +1683,7 @@ Like a regular submodule, an instance can also be added without specifying a nam
If a name is not explicitly specified for a submodule, one will be generated and assigned automatically. Designs with many autogenerated names can be difficult to debug, so a name should usually be supplied. If a name is not explicitly specified for a submodule, one will be generated and assigned automatically. Designs with many autogenerated names can be difficult to debug, so a name should usually be supplied.
Although an :class:`Instance` is not an elaboratable, as a special case, it can be returned from the :pc:`elaborate()` method. This is conveinent for implementing an elaboratable that adorns an instance with an Amaranth interface: Although an :class:`Instance` is not an elaboratable, as a special case, it can be returned from the :py:`elaborate()` method. This is conveinent for implementing an elaboratable that adorns an instance with an Amaranth interface:
.. testcode:: .. testcode::

View file

@ -80,9 +80,9 @@ The prelude exports exactly the following names:
Source locations Source locations
================ ================
Many functions and methods in Amaranth take the :pc:`src_loc_at=0` keyword argument. These language constructs may inspect the call stack to determine the file and line of its call site, which will be used to annotate generated code when a netlist is generated or to improve diagnostic messages. Many functions and methods in Amaranth take the :py:`src_loc_at=0` keyword argument. These language constructs may inspect the call stack to determine the file and line of its call site, which will be used to annotate generated code when a netlist is generated or to improve diagnostic messages.
Some call sites are not relevant for an Amaranth designer; e.g. when an Amaranth language construct is called from a user-defined utility function, the source location of the call site within this utility function is usually not interesting to the designer. In these cases, one or more levels of function calls can be removed from consideration using the :pc:`src_loc_at` argument as follows (using :meth:`Shape.cast` to demonstrate the concept): Some call sites are not relevant for an Amaranth designer; e.g. when an Amaranth language construct is called from a user-defined utility function, the source location of the call site within this utility function is usually not interesting to the designer. In these cases, one or more levels of function calls can be removed from consideration using the :py:`src_loc_at` argument as follows (using :meth:`Shape.cast` to demonstrate the concept):
.. testcode:: .. testcode::
@ -90,7 +90,7 @@ Some call sites are not relevant for an Amaranth designer; e.g. when an Amaranth
... # additionally process `obj`... ... # additionally process `obj`...
return Shape.cast(obj, src_loc_at=1 + src_loc_at) return Shape.cast(obj, src_loc_at=1 + src_loc_at)
The number :pc:`1` corresponds to the number of call stack frames that should be skipped. The number :py:`1` corresponds to the number of call stack frames that should be skipped.
Shapes Shapes

View file

@ -22,7 +22,7 @@ This module provides four related facilities:
1. Description and construction of interface objects via :class:`Flow` (:data:`In` and :data:`Out`), :class:`Member`, and :class:`Signature`, as well as the associated container class :class:`SignatureMembers`. These classes provide the syntax used in defining components, and are also useful for introspection. 1. Description and construction of interface objects via :class:`Flow` (:data:`In` and :data:`Out`), :class:`Member`, and :class:`Signature`, as well as the associated container class :class:`SignatureMembers`. These classes provide the syntax used in defining components, and are also useful for introspection.
2. Flipping of signatures and interface objects via :class:`FlippedSignature` and :class:`FlippedInterface`, as well as the associated container class :class:`FlippedSignatureMembers`. This facility reduces boilerplate by adapting existing signatures and interface objects: the flip operation changes the :data:`In` data flow of a member to :data:`Out` and vice versa. 2. Flipping of signatures and interface objects via :class:`FlippedSignature` and :class:`FlippedInterface`, as well as the associated container class :class:`FlippedSignatureMembers`. This facility reduces boilerplate by adapting existing signatures and interface objects: the flip operation changes the :data:`In` data flow of a member to :data:`Out` and vice versa.
3. Connecting interface objects together via :func:`connect`. The :func:`connect` function ensures that the provided interface objects can be connected to each other, and adds the necessary :pc:`.eq()` statements to a :class:`Module`. 3. Connecting interface objects together via :func:`connect`. The :func:`connect` function ensures that the provided interface objects can be connected to each other, and adds the necessary :py:`.eq()` statements to a :class:`Module`.
4. Defining reusable, self-contained components via :class:`Component`. Components are :class:`Elaboratable` objects that interact with the rest of the design through an interface specified by their signature. 4. Defining reusable, self-contained components via :class:`Component`. Components are :class:`Elaboratable` objects that interact with the rest of the design through an interface specified by their signature.
To use this module, add the following imports to the beginning of the file: To use this module, add the following imports to the beginning of the file:
@ -66,7 +66,7 @@ Consider a reusable counter with an enable input, configurable limit, and an ove
return m return m
Nothing in this implementation indicates the directions of its ports (:pc:`en`, :pc:`count`, :pc:`limit`, and :pc:`overflow`) in relation to other parts of the design. To understand whether the value of a port is expected to be provided externally or generated internally, it is first necessary to read the body of the :pc:`elaborate` method. If the port is not used within that method in a particular elaboratable, it is not possible to determine its direction, or whether it is even meant to be connected. Nothing in this implementation indicates the directions of its ports (:py:`en`, :py:`count`, :py:`limit`, and :py:`overflow`) in relation to other parts of the design. To understand whether the value of a port is expected to be provided externally or generated internally, it is first necessary to read the body of the :py:`elaborate` method. If the port is not used within that method in a particular elaboratable, it is not possible to determine its direction, or whether it is even meant to be connected.
The :mod:`amaranth.lib.wiring` module provides a solution for this problem: *components*. A component is an elaboratable that declares the shapes and directions of its ports in its *signature*. The example above can be rewritten to use the :class:`Component` base class (which itself inherits from :class:`Elaboratable`) to be: The :mod:`amaranth.lib.wiring` module provides a solution for this problem: *components*. A component is an elaboratable that declares the shapes and directions of its ports in its *signature*. The example above can be rewritten to use the :class:`Component` base class (which itself inherits from :class:`Elaboratable`) to be:
@ -93,21 +93,21 @@ The :mod:`amaranth.lib.wiring` module provides a solution for this problem: *com
return m return m
The code in the constructor *creating* the signals of the counter's interface one by one is now gone, replaced with the :term:`variable annotations <python:variable annotation>` *declaring* the counter's interface. The inherited constructor, :meth:`Component.__init__`, creates the same attributes with the same values as before, and the :pc:`elaborate` method is unchanged. The code in the constructor *creating* the signals of the counter's interface one by one is now gone, replaced with the :term:`variable annotations <python:variable annotation>` *declaring* the counter's interface. The inherited constructor, :meth:`Component.__init__`, creates the same attributes with the same values as before, and the :py:`elaborate` method is unchanged.
The major difference between the two examples is that the :pc:`ComponentCounter` provides unambiguous answers to two questions that previously required examining the :pc:`elaborate` method: The major difference between the two examples is that the :py:`ComponentCounter` provides unambiguous answers to two questions that previously required examining the :py:`elaborate` method:
1. Which of the Python object's attributes are ports that are intended to be connected to the rest of the design. 1. Which of the Python object's attributes are ports that are intended to be connected to the rest of the design.
2. What is the direction of the flow of information through the port. 2. What is the direction of the flow of information through the port.
This information, aside from being clear from the source code, can now be retrieved from the :pc:`.signature` attribute, which contains an instance of the :class:`Signature` class: This information, aside from being clear from the source code, can now be retrieved from the :py:`.signature` attribute, which contains an instance of the :class:`Signature` class:
.. doctest:: .. doctest::
>>> ComponentCounter().signature >>> ComponentCounter().signature
Signature({'en': In(1), 'count': Out(8), 'limit': In(8), 'overflow': Out(1)}) Signature({'en': In(1), 'count': Out(8), 'limit': In(8), 'overflow': Out(1)})
The :ref:`shapes <lang-shapes>` of the ports need not be static. The :pc:`ComponentCounter` can be made generic, with its range specified when it is constructed, by creating the signature explicitly in its constructor: The :ref:`shapes <lang-shapes>` of the ports need not be static. The :py:`ComponentCounter` can be made generic, with its range specified when it is constructed, by creating the signature explicitly in its constructor:
.. testcode:: .. testcode::
@ -130,7 +130,7 @@ The :ref:`shapes <lang-shapes>` of the ports need not be static. The :pc:`Compon
>>> GenericCounter(16).signature >>> GenericCounter(16).signature
Signature({'en': In(1), 'count': Out(16), 'limit': In(16), 'overflow': Out(1)}) Signature({'en': In(1), 'count': Out(16), 'limit': In(16), 'overflow': Out(1)})
Instances of the :class:`ComponentCounter` and :class:`GenericCounter` class are two examples of *interface objects*. An interface object is a Python object of any type whose a :pc:`signature` attribute contains a :class:`Signature` with which the interface object is compliant (as determined by the :meth:`is_compliant <Signature.is_compliant>` method of the signature). Instances of the :class:`ComponentCounter` and :class:`GenericCounter` class are two examples of *interface objects*. An interface object is a Python object of any type whose a :py:`signature` attribute contains a :class:`Signature` with which the interface object is compliant (as determined by the :meth:`is_compliant <Signature.is_compliant>` method of the signature).
The next section introduces the concepts of directionality and connection, and discusses interface objects in more detail. The next section introduces the concepts of directionality and connection, and discusses interface objects in more detail.
@ -140,7 +140,7 @@ The next section introduces the concepts of directionality and connection, and d
Reusable interfaces Reusable interfaces
+++++++++++++++++++ +++++++++++++++++++
Consider a more complex example where two components are communicating with a *stream* that is using *ready/valid signaling*, where the :pc:`valid` signal indicates that the value of :pc:`data` provided by the source is meaningful, and the :pc:`ready` signal indicates that the sink has consumed the data word: Consider a more complex example where two components are communicating with a *stream* that is using *ready/valid signaling*, where the :py:`valid` signal indicates that the value of :py:`data` provided by the source is meaningful, and the :py:`ready` signal indicates that the sink has consumed the data word:
.. testcode:: .. testcode::
@ -239,7 +239,7 @@ The producer and the consumer reuse the same signature, relying on flipping to m
>>> consumer.sink.signature.members['data'] >>> consumer.sink.signature.members['data']
In(8) In(8)
In the :pc:`StreamConsumer` definition above, the :pc:`sink` member has its direction flipped explicitly because the sink is a stream input; this is the case for every interface due to how port directions are defined. Since this operation is so ubiquitous, it is also performed when :pc:`In(...)` is used with a signature rather than a shape. The :pc:`StreamConsumer` definition above should normally be written as: In the :py:`StreamConsumer` definition above, the :py:`sink` member has its direction flipped explicitly because the sink is a stream input; this is the case for every interface due to how port directions are defined. Since this operation is so ubiquitous, it is also performed when :py:`In(...)` is used with a signature rather than a shape. The :py:`StreamConsumer` definition above should normally be written as:
.. testcode:: .. testcode::
@ -255,7 +255,7 @@ The data flow directions of the ports are identical between the two definitions:
>>> consumer.sink.signature.members == StreamConsumerUsingIn().sink.signature.members >>> consumer.sink.signature.members == StreamConsumerUsingIn().sink.signature.members
True True
If signatures are nested within each other multiple levels deep, the final port direction is determined by how many nested :pc:`In(...)` members there are. For each :pc:`In(...)` signature wrapping a port, the data flow direction of the port is flipped once: If signatures are nested within each other multiple levels deep, the final port direction is determined by how many nested :py:`In(...)` members there are. For each :py:`In(...)` signature wrapping a port, the data flow direction of the port is flipped once:
.. doctest:: .. doctest::
@ -269,13 +269,13 @@ If signatures are nested within each other multiple levels deep, the final port
>>> in2.members["sig"].signature.members["sig"].signature.members["port"] >>> in2.members["sig"].signature.members["sig"].signature.members["port"]
Out(1) Out(1)
Going back to the stream example, the producer and the consumer now communicate with one another using the same set of ports with identical shapes and complementary directions (the auxiliary :pc:`en` port being outside of the stream signature), and can be *connected* using the :func:`connect` function: Going back to the stream example, the producer and the consumer now communicate with one another using the same set of ports with identical shapes and complementary directions (the auxiliary :py:`en` port being outside of the stream signature), and can be *connected* using the :func:`connect` function:
.. testcode:: .. testcode::
wiring.connect(m, producer.source, consumer.sink) wiring.connect(m, producer.source, consumer.sink)
This function examines the signatures of the two provided interface objects, ensuring that they are exactly complementary, and then adds combinatorial :pc:`.eq()` statements to the module for each of the port pairs to form the connection. Aside from the *connectability* check, the single line above is equivalent to: This function examines the signatures of the two provided interface objects, ensuring that they are exactly complementary, and then adds combinatorial :py:`.eq()` statements to the module for each of the port pairs to form the connection. Aside from the *connectability* check, the single line above is equivalent to:
.. testcode:: .. testcode::
@ -295,7 +295,7 @@ This explanation concludes the essential knowledge necessary for using this modu
Forwarding interior interfaces Forwarding interior interfaces
++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++
Consider a case where a component includes another component as a part of its implementation, and where it is necessary to *forward* the ports of the inner component, that is, expose them within the outer component's signature. To use the :pc:`SimpleStreamSignature` definition above in an example: Consider a case where a component includes another component as a part of its implementation, and where it is necessary to *forward* the ports of the inner component, that is, expose them within the outer component's signature. To use the :py:`SimpleStreamSignature` definition above in an example:
.. testcode:: .. testcode::
@ -318,7 +318,7 @@ Consider a case where a component includes another component as a part of its im
] ]
return m return m
Because forwarding the ports requires assigning an output to an output and an input to an input, the :func:`connect` function, which connects outputs to inputs and vice versa, cannot be used---at least not directly. The :func:`connect` function is designed to cover the usual case of connecting the interfaces of modules *from outside* those modules. In order to connect an interface *from inside* a module, it is necessary to flip that interface first using the :func:`flipped` function. The :pc:`DataProcessorWrapper` should instead be implemented as: Because forwarding the ports requires assigning an output to an output and an input to an input, the :func:`connect` function, which connects outputs to inputs and vice versa, cannot be used---at least not directly. The :func:`connect` function is designed to cover the usual case of connecting the interfaces of modules *from outside* those modules. In order to connect an interface *from inside* a module, it is necessary to flip that interface first using the :func:`flipped` function. The :py:`DataProcessorWrapper` should instead be implemented as:
.. testcode:: .. testcode::
@ -346,7 +346,7 @@ In some cases, *both* of the two interfaces provided to :func:`connect` must be
.. warning:: .. warning::
It is important to wrap an interface with the :func:`flipped` function whenever it is being connected from inside the module. If the :pc:`elaborate` function above had made a connection using :pc:`wiring.connect(m, self.sink, self.source)`, it would not work correctly. No diagnostic is emitted in this case. It is important to wrap an interface with the :func:`flipped` function whenever it is being connected from inside the module. If the :py:`elaborate` function above had made a connection using :py:`wiring.connect(m, self.sink, self.source)`, it would not work correctly. No diagnostic is emitted in this case.
.. _wiring-constant-inputs: .. _wiring-constant-inputs:
@ -443,7 +443,7 @@ When creating an adapted interface, use the :meth:`create <Signature.create>` me
Customizing signatures and interfaces Customizing signatures and interfaces
+++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++
The :mod:`amaranth.lib.wiring` module encourages creation of reusable building blocks. In the examples above, a custom signature, :pc:`SimpleStreamSignature`, was introduced to illustrate the essential concepts necessary to use this module. While sufficient for that goal, it does not demonstrate the full capabilities provided by the module. The :mod:`amaranth.lib.wiring` module encourages creation of reusable building blocks. In the examples above, a custom signature, :py:`SimpleStreamSignature`, was introduced to illustrate the essential concepts necessary to use this module. While sufficient for that goal, it does not demonstrate the full capabilities provided by the module.
Consider a simple System-on-Chip memory bus with a configurable address width. In an application like that, additional properties and methods could be usefully defined both on the signature (for example, properties to retrieve the parameters of the interface) and on the created interface object (for example, methods to examine the control and status signals). These can be defined as follows: Consider a simple System-on-Chip memory bus with a configurable address width. In an application like that, additional properties and methods could be usefully defined both on the signature (for example, properties to retrieve the parameters of the interface) and on the created interface object (for example, methods to examine the control and status signals). These can be defined as follows:
@ -492,9 +492,9 @@ Consider a simple System-on-Chip memory bus with a configurable address width. I
This example demonstrates several important principles of use: This example demonstrates several important principles of use:
* Defining additional properties for a custom signature. The :class:`Signature` objects are mutable in a restricted way, and can be frozen with the :meth:`freeze <Signature.freeze>` method. In almost all cases, the newly defined properties must be immutable, as shown above. * Defining additional properties for a custom signature. The :class:`Signature` objects are mutable in a restricted way, and can be frozen with the :meth:`freeze <Signature.freeze>` method. In almost all cases, the newly defined properties must be immutable, as shown above.
* Defining a signature-specific :pc:`__eq__` method. While anonymous (created from a dictionary of members) instances of :class:`Signature` compare structurally, instances of :class:`Signature`-derived classes compare by identity unless the equality operator is overridden. In almost all cases, the equality operator should compare the parameters of the signatures rather than their structures. * Defining a signature-specific :py:`__eq__` method. While anonymous (created from a dictionary of members) instances of :class:`Signature` compare structurally, instances of :class:`Signature`-derived classes compare by identity unless the equality operator is overridden. In almost all cases, the equality operator should compare the parameters of the signatures rather than their structures.
* Defining a signature-specific :pc:`__repr__` method. Similarly to :pc:`__eq__`, the default implementation for :class:`Signature`-derived classes uses the signature's identity. In almost all cases, the representation conversion operator should return an expression that constructs an equivalent signature. * Defining a signature-specific :py:`__repr__` method. Similarly to :py:`__eq__`, the default implementation for :class:`Signature`-derived classes uses the signature's identity. In almost all cases, the representation conversion operator should return an expression that constructs an equivalent signature.
* Defining a signature-specific :pc:`create` method. The default implementation used in anonymous signatures, :meth:`Signature.create`, returns a new instance of :class:`PureInterface`. Whenever the custom signature has a corresponding custom interface object class, this method should return a new instance of that class. It should not have any required arguments beyond the ones that :meth:`Signature.create` has (required parameters should be provided when creating the signature and not the interface), but may take additional optional arguments, forwarding them to the interface object constructor. * Defining a signature-specific :py:`create` method. The default implementation used in anonymous signatures, :meth:`Signature.create`, returns a new instance of :class:`PureInterface`. Whenever the custom signature has a corresponding custom interface object class, this method should return a new instance of that class. It should not have any required arguments beyond the ones that :meth:`Signature.create` has (required parameters should be provided when creating the signature and not the interface), but may take additional optional arguments, forwarding them to the interface object constructor.
.. doctest:: .. doctest::
@ -522,11 +522,11 @@ The custom properties defined for both the signature and the interface object ca
.. note:: .. note::
Unusually for Python, when the implementation of a property or method is invoked through a flipped object, the :pc:`self` argument receives the flipped object that has the type :class:`FlippedSignature` or :class:`FlippedInterface`. This wrapper object proxies all attribute accesses and method calls to the original signature or interface, the only change being that of the data flow directions. See the documentation for these classes for a more detailed explanation. Unusually for Python, when the implementation of a property or method is invoked through a flipped object, the :py:`self` argument receives the flipped object that has the type :class:`FlippedSignature` or :class:`FlippedInterface`. This wrapper object proxies all attribute accesses and method calls to the original signature or interface, the only change being that of the data flow directions. See the documentation for these classes for a more detailed explanation.
.. warning:: .. warning::
While the wrapper object forwards attribute accesses and method calls, it does not currently proxy special methods such as :pc:`__getitem__` or :pc:`__add__` that are rarely, if ever, used with interface objects. This limitation may be lifted in the future. While the wrapper object forwards attribute accesses and method calls, it does not currently proxy special methods such as :py:`__getitem__` or :py:`__add__` that are rarely, if ever, used with interface objects. This limitation may be lifted in the future.
.. _wiring-path: .. _wiring-path:
@ -534,7 +534,7 @@ The custom properties defined for both the signature and the interface object ca
Paths Paths
+++++ +++++
Whenever an operation in this module needs to refer to the interior of an object, it accepts or produces a *path*: a tuple of strings and integers denoting the attribute names and indexes through which an interior value can be extracted. For example, the path :pc:`("buses", 0, "cyc")` into the object :pc:`obj` corresponds to the Python expression :pc:`obj.buses[0].cyc`. Whenever an operation in this module needs to refer to the interior of an object, it accepts or produces a *path*: a tuple of strings and integers denoting the attribute names and indexes through which an interior value can be extracted. For example, the path :py:`("buses", 0, "cyc")` into the object :py:`obj` corresponds to the Python expression :py:`obj.buses[0].cyc`.
When they appear in diagnostics, paths are printed as the corresponding Python expression. When they appear in diagnostics, paths are printed as the corresponding Python expression.