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
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:
@ -104,9 +104,9 @@ class Shape:
Raises
------
TypeError
If :pc:`obj` cannot be converted to a :class:`Shape`.
If :py:`obj` cannot be converted to a :class:`Shape`.
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:
if isinstance(obj, Shape):
@ -137,7 +137,7 @@ class Shape:
def __repr__(self):
"""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:
return f"signed({self.width})"
@ -150,12 +150,12 @@ class Shape:
def unsigned(width):
"""Returns :pc:`Shape(width, signed=False)`."""
"""Returns :py:`Shape(width, signed=False)`."""
return Shape(width, signed=False)
def signed(width):
"""Returns :pc:`Shape(width, signed=True)`."""
"""Returns :py:`Shape(width, signed=True)`."""
return Shape(width, signed=True)
@ -169,7 +169,7 @@ class ShapeCastable:
extending the Amaranth language in third-party code.
To illustrate their purpose, consider constructing a signal from a shape-castable object
:pc:`shape_castable`:
:py:`shape_castable`:
.. code::
@ -184,7 +184,7 @@ class ShapeCastable:
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::
@ -216,9 +216,9 @@ class ShapeCastable:
def as_shape(self, *args, **kwargs):
"""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
another shape-like object to delegate its functionality.
@ -243,15 +243,15 @@ class ShapeCastable:
def const(self, *args, **kwargs):
"""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>`.
The object :pc:`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,
The object :py:`obj` will usually be a Python literal that can conveniently represent
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__`.
For any :pc:`obj`, the following condition must hold:
For any :py:`obj`, the following condition must hold:
.. code::
@ -277,18 +277,18 @@ class ShapeCastable:
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
:ref:`value-like object <lang-valuelike>` whose shape equals :pc:`Shape.cast(self)`,
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 :py:`Shape.cast(self)`,
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
be paired with a :class:`ValueCastable` implementation, and this method will return
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
time.
For any compliant :pc:`obj`, the following condition must hold:
For any compliant :py:`obj`, the following condition must hold:
.. code::
@ -328,7 +328,7 @@ class _ShapeLikeMeta(type):
class ShapeLike(metaclass=_ShapeLikeMeta):
"""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:`ShapeCastable` and its subclasses;
@ -337,7 +337,7 @@ class ShapeLike(metaclass=_ShapeLikeMeta):
* :class:`enum.EnumMeta` and its subclasses;
* :class:`ShapeLike` itself.
:pc:`isinstance(obj, ShapeLike)` returns :pc:`True` for:
:py:`isinstance(obj, ShapeLike)` returns :py:`True` for:
* :class:`Shape` 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
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
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.
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:
**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
of :class:`Value`. The second part (:pc:`unsigned(1)`) describes the shape of that value.
The third part, if present, indicates that the value is assignable if :pc:`self` is
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 :py:`self` is
assignable.
"""
@staticmethod
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:
* 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
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;
* a :class:`ValueCastable` instance, where the result is obtained by repeatedly calling
:meth:`obj.as_value() <ValueCastable.as_value>`.
@ -429,9 +429,9 @@ class Value(metaclass=ABCMeta):
Raises
------
TypeError
If :pc:`obj` cannot be converted to a :class:`Value`.
If :py:`obj` cannot be converted to a :class:`Value`.
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:
if isinstance(obj, Value):
@ -454,7 +454,7 @@ class Value(metaclass=ABCMeta):
@abstractmethod
def shape(self):
"""Shape of :pc:`self`.
"""Shape of :py:`self`.
Returns
-------
@ -471,7 +471,7 @@ class Value(metaclass=ABCMeta):
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])
@ -480,12 +480,12 @@ class Value(metaclass=ABCMeta):
Returns
-------
:class:`Value`, :pc:`signed(len(self))`, :ref:`assignable <lang-assignable>`
:class:`Value`, :py:`signed(len(self))`, :ref:`assignable <lang-assignable>`
Raises
------
ValueError
If :pc:`len(self) == 0`.
If :py:`len(self) == 0`.
"""
if len(self) == 0:
raise ValueError("Cannot create a 0-width signed value")
@ -494,7 +494,7 @@ class Value(metaclass=ABCMeta):
def __bool__(self):
"""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
always raises an exception.
@ -512,22 +512,22 @@ class Value(metaclass=ABCMeta):
Returns
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
return Operator("b", [self])
def __pos__(self):
"""Unary position, :pc:`+self`.
"""Unary position, :py:`+self`.
Returns
-------
:class:`Value`, :pc:`self.shape()`
:pc:`self`
:class:`Value`, :py:`self.shape()`
:py:`self`
"""
return self
def __neg__(self):
"""Unary negation, :pc:`-self`.
"""Unary negation, :py:`-self`.
..
>>> C(-1).value, C(-1).shape()
@ -537,29 +537,29 @@ class Value(metaclass=ABCMeta):
Returns
-------
:class:`Value`, :pc:`signed(len(self) + 1)`
:class:`Value`, :py:`signed(len(self) + 1)`
"""
return Operator("-", [self])
@_overridable_by_reflected("__radd__")
def __add__(self, other):
"""Addition, :pc:`self + other`.
"""Addition, :py:`self + other`.
Returns
-------
:class:`Value`, :pc:`unsigned(max(self.width(), other.width()) + 1)`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :pc:`signed(max(self.width() + 1, other.width()) + 1)`
If :pc:`self` is unsigned and :pc:`other` is signed.
:class:`Value`, :pc:`signed(max(self.width(), other.width() + 1) + 1)`
If :pc:`self` is signed and :pc:`other` is unsigned.
:class:`Value`, :pc:`signed(max(self.width(), other.width()) + 1)`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :py:`unsigned(max(self.width(), other.width()) + 1)`
If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :py:`signed(max(self.width() + 1, other.width()) + 1)`
If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :py:`signed(max(self.width(), other.width() + 1) + 1)`
If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :py:`signed(max(self.width(), other.width()) + 1)`
If both :py:`self` and :py:`other` are unsigned.
"""
return Operator("+", [self, other], src_loc_at=1)
def __radd__(self, other):
"""Addition, :pc:`other + self` (reflected).
"""Addition, :py:`other + self` (reflected).
Like :meth:`__add__`, with operands swapped.
"""
@ -567,18 +567,18 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rsub__")
def __sub__(self, other):
"""Subtraction, :pc:`self - other`.
"""Subtraction, :py:`self - other`.
Returns
-------
:class:`Value`, :pc:`signed(max(self.width(), other.width()) + 1)`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :pc:`signed(max(self.width() + 1, other.width()) + 1)`
If :pc:`self` is unsigned and :pc:`other` is signed.
:class:`Value`, :pc:`signed(max(self.width(), other.width() + 1) + 1)`
If :pc:`self` is signed and :pc:`other` is unsigned.
:class:`Value`, :pc:`signed(max(self.width(), other.width()) + 1)`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :py:`signed(max(self.width(), other.width()) + 1)`
If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :py:`signed(max(self.width() + 1, other.width()) + 1)`
If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :py:`signed(max(self.width(), other.width() + 1) + 1)`
If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :py:`signed(max(self.width(), other.width()) + 1)`
If both :py:`self` and :py:`other` are unsigned.
Returns
-------
@ -587,7 +587,7 @@ class Value(metaclass=ABCMeta):
return Operator("-", [self, other], src_loc_at=1)
def __rsub__(self, other):
"""Subtraction, :pc:`other - self` (reflected).
"""Subtraction, :py:`other - self` (reflected).
Like :meth:`__sub__`, with operands swapped.
"""
@ -595,19 +595,19 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rmul__")
def __mul__(self, other):
"""Multiplication, :pc:`self * other`.
"""Multiplication, :py:`self * other`.
Returns
-------
:class:`Value`, :pc:`unsigned(len(self) + len(other))`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :pc:`signed(len(self) + len(other))`
If either :pc:`self` or :pc:`other` are signed.
:class:`Value`, :py:`unsigned(len(self) + len(other))`
If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :py:`signed(len(self) + len(other))`
If either :py:`self` or :py:`other` are signed.
"""
return Operator("*", [self, other], src_loc_at=1)
def __rmul__(self, other):
"""Multiplication, :pc:`other * self` (reflected).
"""Multiplication, :py:`other * self` (reflected).
Like :meth:`__mul__`, with operands swapped.
"""
@ -615,27 +615,27 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rfloordiv__")
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
-------
:class:`Value`, :pc:`unsigned(len(self))`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :pc:`signed(len(self) + 1)`
If :pc:`self` is unsigned and :pc:`other` is signed.
:class:`Value`, :pc:`signed(len(self))`
If :pc:`self` is signed and :pc:`other` is unsigned.
:class:`Value`, :pc:`signed(len(self) + 1)`
If both :pc:`self` and :pc:`other` are signed.
:class:`Value`, :py:`unsigned(len(self))`
If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :py:`signed(len(self) + 1)`
If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :py:`signed(len(self))`
If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :py:`signed(len(self) + 1)`
If both :py:`self` and :py:`other` are signed.
"""
return Operator("//", [self, other], src_loc_at=1)
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.
"""
@ -643,18 +643,18 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rmod__")
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
-------
:class:`Value`, :pc:`other.shape()`
:class:`Value`, :py:`other.shape()`
"""
return Operator("%", [self, other], src_loc_at=1)
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.
"""
@ -662,66 +662,66 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__eq__")
def __eq__(self, other):
"""Equality comparison, :pc:`self == other`.
"""Equality comparison, :py:`self == other`.
Returns
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
return Operator("==", [self, other], src_loc_at=1)
@_overridable_by_reflected("__ne__")
def __ne__(self, other):
"""Inequality comparison, :pc:`self != other`.
"""Inequality comparison, :py:`self != other`.
Returns
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
return Operator("!=", [self, other], src_loc_at=1)
@_overridable_by_reflected("__gt__")
def __lt__(self, other):
"""Less than comparison, :pc:`self < other`.
"""Less than comparison, :py:`self < other`.
Returns
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
return Operator("<", [self, other], src_loc_at=1)
@_overridable_by_reflected("__ge__")
def __le__(self, other):
"""Less than or equals comparison, :pc:`self <= other`.
"""Less than or equals comparison, :py:`self <= other`.
Returns
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
return Operator("<=", [self, other], src_loc_at=1)
@_overridable_by_reflected("__lt__")
def __gt__(self, other):
"""Greater than comparison, :pc:`self > other`.
"""Greater than comparison, :py:`self > other`.
Returns
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
return Operator(">", [self, other], src_loc_at=1)
@_overridable_by_reflected("__le__")
def __ge__(self, other):
"""Greater than or equals comparison, :pc:`self >= other`.
"""Greater than or equals comparison, :py:`self >= other`.
Returns
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
return Operator(">=", [self, other], src_loc_at=1)
def __abs__(self):
"""Absolute value, :pc:`abs(self)`.
"""Absolute value, :py:`abs(self)`.
..
>>> abs(C(-1)).shape()
@ -731,7 +731,7 @@ class Value(metaclass=ABCMeta):
Return
------
:class:`Value`, :pc:`unsigned(len(self))`
:class:`Value`, :py:`unsigned(len(self))`
"""
if self.shape().signed:
return Mux(self >= 0, self, -self)[:len(self)]
@ -739,123 +739,123 @@ class Value(metaclass=ABCMeta):
return 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::
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
with the same name.
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
:pc:`~` instead; if it wasn't the case that :pc:`~C(0) == C(1)`, that would have
:py:`and`, :py:`or`, and :py:`not` operators. Amaranth uses :py:`&`, :py:`|`, and
:py:`~` instead; if it wasn't the case that :py:`~C(0) == C(1)`, that would have
been impossible.
Returns
-------
:class:`Value`, :pc:`self.shape()`
:class:`Value`, :py:`self.shape()`
"""
return Operator("~", [self])
@_overridable_by_reflected("__rand__")
def __and__(self, other):
"""Bitwise AND, :pc:`self & other`.
"""Bitwise AND, :py:`self & other`.
Returns
-------
:class:`Value`, :pc:`unsigned(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :pc:`signed(max(self.width() + 1, other.width()))`
If :pc:`self` is unsigned and :pc:`other` is signed.
:class:`Value`, :pc:`signed(max(self.width(), other.width() + 1))`
If :pc:`self` is signed and :pc:`other` is unsigned.
:class:`Value`, :pc:`signed(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :py:`unsigned(max(self.width(), other.width()))`
If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :py:`signed(max(self.width() + 1, other.width()))`
If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :py:`signed(max(self.width(), other.width() + 1))`
If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :py:`signed(max(self.width(), other.width()))`
If both :py:`self` and :py:`other` are unsigned.
"""
return Operator("&", [self, other], src_loc_at=1)
def __rand__(self, other):
"""Bitwise AND, :pc:`other & self`.
"""Bitwise AND, :py:`other & self`.
Like :meth:`__and__`, with operands swapped.
"""
return Operator("&", [other, self])
def all(self):
"""Reduction AND; are all bits :pc:`1`?
"""Reduction AND; are all bits :py:`1`?
Returns
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
return Operator("r&", [self])
@_overridable_by_reflected("__ror__")
def __or__(self, other):
"""Bitwise OR, :pc:`self | other`.
"""Bitwise OR, :py:`self | other`.
Returns
-------
:class:`Value`, :pc:`unsigned(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :pc:`signed(max(self.width() + 1, other.width()))`
If :pc:`self` is unsigned and :pc:`other` is signed.
:class:`Value`, :pc:`signed(max(self.width(), other.width() + 1))`
If :pc:`self` is signed and :pc:`other` is unsigned.
:class:`Value`, :pc:`signed(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :py:`unsigned(max(self.width(), other.width()))`
If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :py:`signed(max(self.width() + 1, other.width()))`
If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :py:`signed(max(self.width(), other.width() + 1))`
If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :py:`signed(max(self.width(), other.width()))`
If both :py:`self` and :py:`other` are unsigned.
"""
return Operator("|", [self, other], src_loc_at=1)
def __ror__(self, other):
"""Bitwise OR, :pc:`other | self`.
"""Bitwise OR, :py:`other | self`.
Like :meth:`__or__`, with operands swapped.
"""
return Operator("|", [other, self])
def any(self):
"""Reduction OR; is any bit :pc:`1`?
"""Reduction OR; is any bit :py:`1`?
Returns
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
return Operator("r|", [self])
@_overridable_by_reflected("__rxor__")
def __xor__(self, other):
"""Bitwise XOR, :pc:`self ^ other`.
"""Bitwise XOR, :py:`self ^ other`.
Returns
-------
:class:`Value`, :pc:`unsigned(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :pc:`signed(max(self.width() + 1, other.width()))`
If :pc:`self` is unsigned and :pc:`other` is signed.
:class:`Value`, :pc:`signed(max(self.width(), other.width() + 1))`
If :pc:`self` is signed and :pc:`other` is unsigned.
:class:`Value`, :pc:`signed(max(self.width(), other.width()))`
If both :pc:`self` and :pc:`other` are unsigned.
:class:`Value`, :py:`unsigned(max(self.width(), other.width()))`
If both :py:`self` and :py:`other` are unsigned.
:class:`Value`, :py:`signed(max(self.width() + 1, other.width()))`
If :py:`self` is unsigned and :py:`other` is signed.
:class:`Value`, :py:`signed(max(self.width(), other.width() + 1))`
If :py:`self` is signed and :py:`other` is unsigned.
:class:`Value`, :py:`signed(max(self.width(), other.width()))`
If both :py:`self` and :py:`other` are unsigned.
"""
return Operator("^", [self, other], src_loc_at=1)
def __rxor__(self, other):
"""Bitwise XOR, :pc:`other ^ self`.
"""Bitwise XOR, :py:`other ^ self`.
Like :meth:`__xor__`, with operands swapped.
"""
return Operator("^", [other, 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
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
return Operator("r^", [self])
@ -872,26 +872,26 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rlshift__")
def __lshift__(self, other):
"""Left shift by variable amount, :pc:`self << other`.
"""Left shift by variable amount, :py:`self << other`.
Returns
-------
:class:`Value`, :pc:`unsigned(len(self) + 2 ** len(other) - 1)`
If :pc:`self` is unsigned.
:class:`Value`, :pc:`signed(len(self) + 2 ** len(other) - 1)`
If :pc:`self` is signed.
:class:`Value`, :py:`unsigned(len(self) + 2 ** len(other) - 1)`
If :py:`self` is unsigned.
:class:`Value`, :py:`signed(len(self) + 2 ** len(other) - 1)`
If :py:`self` is signed.
Raises
------
:exc:`TypeError`
If :pc:`other` is signed.
If :py:`other` is signed.
"""
other = Value.cast(other)
other.__check_shamt()
return Operator("<<", [self, other], src_loc_at=1)
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.
"""
@ -901,14 +901,14 @@ class Value(metaclass=ABCMeta):
def shift_left(self, 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
-------
:class:`Value`, :pc:`unsigned(max(len(self) + amount, 0))`
If :pc:`self` is unsigned.
:class:`Value`, :pc:`signed(max(len(self) + amount, 1))`
If :pc:`self` is signed.
:class:`Value`, :py:`unsigned(max(len(self) + amount, 0))`
If :py:`self` is unsigned.
:class:`Value`, :py:`signed(max(len(self) + amount, 1))`
If :py:`self` is signed.
"""
if not isinstance(amount, int):
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):
"""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
-------
: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):
raise TypeError(f"Rotate amount must be an integer, not {amount!r}")
@ -936,26 +936,26 @@ class Value(metaclass=ABCMeta):
@_overridable_by_reflected("__rrshift__")
def __rshift__(self, other):
"""Right shift by variable amount, :pc:`self >> other`.
"""Right shift by variable amount, :py:`self >> other`.
Returns
-------
:class:`Value`, :pc:`unsigned(len(self))`
If :pc:`self` is unsigned.
:class:`Value`, :pc:`signed(len(self))`
If :pc:`self` is signed.
:class:`Value`, :py:`unsigned(len(self))`
If :py:`self` is unsigned.
:class:`Value`, :py:`signed(len(self))`
If :py:`self` is signed.
Raises
------
:exc:`TypeError`
If :pc:`other` is signed.
If :py:`other` is signed.
"""
other = Value.cast(other)
other.__check_shamt()
return Operator(">>", [self, other], src_loc_at=1)
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.
"""
@ -965,14 +965,14 @@ class Value(metaclass=ABCMeta):
def shift_right(self, 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
-------
:class:`Value`, :pc:`unsigned(max(len(self) - amount, 0))`
If :pc:`self` is unsigned.
:class:`Value`, :pc:`signed(max(len(self) - amount, 1))`
If :pc:`self` is signed.
:class:`Value`, :py:`unsigned(max(len(self) - amount, 0))`
If :py:`self` is unsigned.
:class:`Value`, :py:`signed(max(len(self) - amount, 1))`
If :py:`self` is signed.
"""
if not isinstance(amount, int):
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):
"""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
-------
: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):
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])
def __len__(self):
"""Bit width of :pc:`self`.
"""Bit width of :py:`self`.
Returns
-------
:class:`int`
:pc:`self.shape().width`
:py:`self.shape().width`
"""
return self.shape().width
@ -1055,13 +1055,13 @@ class Value(metaclass=ABCMeta):
def bit_select(self, offset, width):
"""Part-select with bit granularity.
Selects a constant width, variable offset part of :pc:`self`, where parts with successive
offsets overlap by :pc:`width - 1` bits. Bits above the most significant bit of :pc:`self`
may be selected; they are equal to zero if :pc:`self` is unsigned, to :pc:`self[-1]` if
:pc:`self` is signed, and assigning to them does nothing.
Selects a constant width, variable offset part of :py:`self`, where parts with successive
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 :py:`self` is unsigned, to :py:`self[-1]` if
:py:`self` is signed, and assigning to them does nothing.
When :pc:`offset` is a constant integer and :pc:`offset + width <= len(self)`,
this operation is equivalent to :pc:`self[offset:offset + width]`.
When :py:`offset` is a constant integer and :py:`offset + width <= len(self)`,
this operation is equivalent to :py:`self[offset:offset + width]`.
Parameters
----------
@ -1072,14 +1072,14 @@ class Value(metaclass=ABCMeta):
Returns
-------
:class:`Value`, :pc:`unsigned(width)`, :ref:`assignable <lang-assignable>`
:class:`Value`, :py:`unsigned(width)`, :ref:`assignable <lang-assignable>`
Raises
------
:exc:`TypeError`
If :pc:`offset` is signed.
If :py:`offset` is signed.
:exc:`TypeError`
If :pc:`width` is negative.
If :py:`width` is negative.
"""
offset = Value.cast(offset)
if type(offset) is Const and isinstance(width, int):
@ -1089,13 +1089,13 @@ class Value(metaclass=ABCMeta):
def word_select(self, offset, width):
"""Part-select with word granularity.
Selects a constant width, variable offset part of :pc:`self`, where parts with successive
offsets are adjacent but do not overlap. Bits above the most significant bit of :pc:`self`
may be selected; they are equal to zero if :pc:`self` is unsigned, to :pc:`self[-1]` if
:pc:`self` is signed, and assigning to them does nothing.
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 :py:`self`
may be selected; they are equal to zero if :py:`self` is unsigned, to :py:`self[-1]` if
: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)`,
this operation is equivalent to :pc:`self[offset * width:(offset + 1) * width]`.
When :py:`offset` is a constant integer and :py:`width:(offset + 1) * width <= len(self)`,
this operation is equivalent to :py:`self[offset * width:(offset + 1) * width]`.
Parameters
----------
@ -1106,14 +1106,14 @@ class Value(metaclass=ABCMeta):
Returns
-------
:class:`Value`, :pc:`unsigned(width)`, :ref:`assignable <lang-assignable>`
:class:`Value`, :py:`unsigned(width)`, :ref:`assignable <lang-assignable>`
Raises
------
:exc:`TypeError`
If :pc:`offset` is signed.
If :py:`offset` is signed.
:exc:`TypeError`
If :pc:`width` is negative.
If :py:`width` is negative.
"""
offset = Value.cast(offset)
if type(offset) is Const and isinstance(width, int):
@ -1123,19 +1123,19 @@ class Value(metaclass=ABCMeta):
def replicate(self, count):
"""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.
Returns
-------
:class:`Value`, :pc:`unsigned(len(self) * count)`
:class:`Value`, :py:`unsigned(len(self) * count)`
Raises
------
:exc:`TypeError`
If :pc:`count` is negative.
If :py:`count` is negative.
"""
if not isinstance(count, int) or count < 0:
raise TypeError("Replication count must be a non-negative integer, not {!r}"
@ -1145,7 +1145,7 @@ class Value(metaclass=ABCMeta):
def matches(self, *patterns):
"""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::
@ -1153,7 +1153,7 @@ class Value(metaclass=ABCMeta):
Returns
-------
:class:`Value`, :pc:`unsigned(1)`
:class:`Value`, :py:`unsigned(1)`
"""
matches = []
# 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):
""":ref:`Assignment <lang-assigns>`.
Once it is placed in a domain, an assignment changes the bit pattern of :pc:`self` to
equal :pc:`value`. If the bit width of :pc:`value` is less than that of :pc:`self`,
it is zero-extended (for unsigned :pc:`value`\\ s) or sign-extended (for signed
:pc:`value`\\ s). If the bit width of :pc:`value` is greater than that of :pc:`self`,
Once it is placed in a domain, an assignment changes the bit pattern of :py:`self` to
equal :py:`value`. If the bit width of :py:`value` is less than that of :py:`self`,
it is zero-extended (for unsigned :py:`value`\\ s) or sign-extended (for signed
:py:`value`\\ s). If the bit width of :py:`value` is greater than that of :py:`self`,
it is truncated.
Returns
@ -1211,8 +1211,8 @@ class Value(metaclass=ABCMeta):
#: Forbidden hashing.
#:
#: Python objects are :term:`python:hashable` if they provide a :pc:`__hash__` method
#: that returns an :class:`int` and an :pc:`__eq__` method that returns a :class:`bool`.
#: Python objects are :term:`python:hashable` if they provide a :py:`__hash__` method
#: 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
#: from being hashable.
#:
@ -1251,9 +1251,9 @@ class ValueCastable:
operators, which will prefer calling a reflected arithmetic operation on
the :class:`ValueCastable` argument if it defines one.
For example, if :pc:`value_castable` implements :pc:`__radd__`, then
:pc:`C(1) + value_castable` will perform :pc:`value_castable.__radd__(C(1))`, and otherwise
it will perform :pc:`C(1).__add__(value_castable.as_value())`.
For example, if :py:`value_castable` implements :py:`__radd__`, then
:py:`C(1) + value_castable` will perform :py:`value_castable.__radd__(C(1))`, and otherwise
it will perform :py:`C(1).__add__(value_castable.as_value())`.
"""
def __init__(self, *args, **kwargs):
@ -1275,9 +1275,9 @@ class ValueCastable:
def as_value(self, *args, **kwargs):
"""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
another value-like object to delegate its functionality.
@ -1304,7 +1304,7 @@ class ValueCastable:
def shape(self, *args, **kwargs):
"""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
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):
"""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:`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:`ValueLike` itself.
:pc:`isinstance(obj, ValueLike)` returns the same value as
:pc:`issubclass(type(obj), ValueLike)`.
:py:`isinstance(obj, ValueLike)` returns the same value as
:py:`issubclass(type(obj), ValueLike)`.
This class cannot be instantiated or subclassed. It can only be used for checking types of
objects.
@ -1385,8 +1385,8 @@ class ValueLike(metaclass=_ValueLikeMeta):
It is possible to define an enumeration with a member that is
:ref:`value-like <lang-valuelike>` but not :ref:`constant-castable <lang-constcasting>`,
meaning that :pc:`issubclass(BadEnum, ValueLike)` returns :pc:`True`, but
:pc:`Value.cast(BadEnum.MEMBER)` raises an exception.
meaning that :py:`issubclass(BadEnum, ValueLike)` returns :py:`True`, but
:py:`Value.cast(BadEnum.MEMBER)` raises an exception.
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

View file

@ -51,7 +51,7 @@ class Flow(enum.Enum):
Returns
-------
: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:
return In
@ -66,7 +66,7 @@ class Flow(enum.Enum):
Returns
-------
: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)
@ -103,7 +103,7 @@ class Member:
will equal ``reset``.
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):
self._flow = flow
@ -145,8 +145,8 @@ class Member:
Returns
-------
:class:`Member`
A new :pc:`member` with :pc:`member.flow` equal to :pc:`self.flow.flip()`, and identical
to :pc:`self` other than that.
A new :py:`member` with :py:`member.flow` equal to :py:`self.flow.flip()`, and identical
to :py:`self` other than that.
"""
return Member(self._flow.flip(), self._description, reset=self._reset,
_dimensions=self._dimensions)
@ -155,21 +155,21 @@ class Member:
"""Add array dimensions to this member.
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
:pc:`Out(1).array(2, 3)` and :pc:`Out(1).array(3).array(2)` both describe a two dimensional
For example, :py:`Out(1).array(2)` describes an array of 2 elements, whereas both
: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.
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
:pc:`x` members even if :pc:`member` was already an array.
The :meth:`array` method is composable: calling :py:`member.array(x)` describes an array of
:py:`x` members even if :py:`member` was already an array.
Returns
-------
:class:`Member`
A new :pc:`member` with :pc:`member.dimensions` extended by :pc:`dimensions`, and
identical to :pc:`self` other than that.
A new :py:`member` with :py:`member.dimensions` extended by :py:`dimensions`, and
identical to :py:`self` other than that.
"""
for dimension in dimensions:
if not (isinstance(dimension, int) and dimension >= 0):
@ -195,8 +195,8 @@ class Member:
Returns
-------
:class:`bool`
:pc:`True` if this is a description of a port member,
:pc:`False` if this is a description of a signature member.
:py:`True` if this is a description of a port member,
:py:`False` if this is a description of a signature member.
"""
return not isinstance(self._description, Signature)
@ -207,8 +207,8 @@ class Member:
Returns
-------
:class:`bool`
:pc:`True` if this is a description of a signature member,
:pc:`False` if this is a description of a port member.
:py:`True` if this is a description of a signature member,
:py:`False` if this is a description of a port member.
"""
return isinstance(self._description, Signature)
@ -224,7 +224,7 @@ class Member:
Raises
------
:exc:`AttributeError`
If :pc:`self` describes a signature member.
If :py:`self` describes a signature member.
"""
if self.is_signature:
raise AttributeError(f"A signature member does not have a shape")
@ -242,7 +242,7 @@ class Member:
Raises
------
:exc:`AttributeError`
If :pc:`self` describes a signature member.
If :py:`self` describes a signature member.
"""
if self.is_signature:
raise AttributeError(f"A signature member does not have a reset value")
@ -260,7 +260,7 @@ class Member:
Raises
------
:exc:`AttributeError`
If :pc:`self` describes a port member.
If :py:`self` describes a port member.
"""
if self.is_port:
raise AttributeError(f"A port member does not have a signature")
@ -316,7 +316,7 @@ class SignatureError(Exception):
class SignatureMembers(Mapping):
"""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.
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.
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
(signals and interface objects) by creating them from their descriptions. The created mapping
@ -348,7 +348,7 @@ class SignatureMembers(Mapping):
Returns
-------
: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.
"""
return FlippedSignatureMembers(self)
@ -359,7 +359,7 @@ class SignatureMembers(Mapping):
Returns
-------
: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
list(sorted(self.flatten())) == list(sorted(other.flatten())))
@ -391,11 +391,11 @@ class SignatureMembers(Mapping):
Raises
------
:exc:`TypeError`
If :pc:`name` is not a string.
If :py:`name` is not a string.
: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`
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)
if name not in self._dict:
@ -551,7 +551,7 @@ class FlippedSignatureMembers(Mapping):
Returns
-------
:class:`SignatureMembers`
:pc:`unflipped`
:py:`unflipped`
"""
return self.__unflipped
@ -637,7 +637,7 @@ class SignatureMeta(type):
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
:class:`FlippedSignature` a subclass of :class:`Signature` or any of its subclasses.
@ -651,11 +651,11 @@ class SignatureMeta(type):
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
:pc:`isinstance(obj, cls)` act as :pc:`isinstance(obj.flip(), cls)` where
:pc:`obj` is an instance of :class:`FlippedSignature`.
:py:`isinstance(obj, cls)` act as :py:`isinstance(obj.flip(), cls)` where
:py:`obj` is an instance of :class:`FlippedSignature`.
"""
# `FlippedSignature` is an instance of a `Signature` or its subclass if the unflipped
@ -668,7 +668,7 @@ class SignatureMeta(type):
class Signature(metaclass=SignatureMeta):
"""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.
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
@ -693,7 +693,7 @@ class Signature(metaclass=SignatureMeta):
Returns
-------
: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.
See the documentation for the :class:`FlippedSignature` class for a detailed discussion
@ -714,10 +714,10 @@ class Signature(metaclass=SignatureMeta):
def __eq__(self, other):
"""Compare this signature with another.
The behavior of this operator depends on the types of the arguments. If both :pc:`self`
and :pc:`other` are instances of the base :class:`Signature` class, they are compared
structurally (the result is :pc:`self.members == other.members`); otherwise they are
compared by identity (the result is :pc:`self is other`).
The behavior of this operator depends on the types of the arguments. If both :py:`self`
and :py:`other` are instances of the base :class:`Signature` class, they are compared
structurally (the result is :py:`self.members == other.members`); otherwise they are
compared by identity (the result is :py:`self is other`).
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
@ -806,9 +806,9 @@ class Signature(metaclass=SignatureMeta):
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``;
* 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
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
: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
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).
Arguments
---------
reasons : :class:`list` or :pc:`None`
reasons : :class:`list` or :py:`None`
If provided, a container that receives diagnostic messages.
path : :class:`tuple` of :class:`str`
The :ref:`path <wiring-path>` to :pc:`obj`. Could be set to improve diagnostic
messages if :pc:`obj` is nested within another object, or for clarity.
The :ref:`path <wiring-path>` to :py:`obj`. Could be set to improve diagnostic
messages if :py:`obj` is nested within another object, or for clarity.
Returns
-------
:class:`bool`
:pc:`True` if :pc:`obj` matches the description in this signature, :pc:`False`
otherwise. If :pc:`False` and :pc:`reasons` was not :pc:`None`, it will contain
:py:`True` if :py:`obj` matches the description in this signature, :py:`False`
otherwise. If :py:`False` and :py:`reasons` was not :py:`None`, it will contain
a detailed explanation why.
"""
@ -952,7 +952,7 @@ class Signature(metaclass=SignatureMeta):
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
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
the created interface objects, override this method with a similar implementation
@ -969,7 +969,7 @@ class Signature(metaclass=SignatureMeta):
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.
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
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
:pc:`isinstance(self, FlippedSignature)`:
:py:`isinstance(self, FlippedSignature)`:
.. testcode::
@ -1058,7 +1058,7 @@ class FlippedSignature:
Returns
-------
:class:`Signature`
:pc:`unflipped`
:py:`unflipped`
"""
return self.__unflipped
@ -1089,11 +1089,11 @@ class FlippedSignature:
# are two possible exits via `except AttributeError`: from `getattr` and from `.__get__()`.
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
getter or a method, its :pc:`self` argument receives the *flipped* signature. A class
method's :pc:`cls` argument receives the class of the *unflipped* signature, as usual.
Performs :py:`getattr(unflipped, name)`, ensuring that, if :py:`name` refers to a property
getter or a method, its :py:`self` argument receives the *flipped* signature. A class
method's :py:`cls` argument receives the class of the *unflipped* signature, as usual.
"""
try: # descriptor first
return _gettypeattr(self.__unflipped, name).__get__(self, type(self.__unflipped))
@ -1101,10 +1101,10 @@ class FlippedSignature:
return getattr(self.__unflipped, name)
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
a property setter, its :pc:`self` argument receives the flipped signature.
Performs :py:`setattr(unflipped, name, value)`, ensuring that, if :py:`name` refers to
a property setter, its :py:`self` argument receives the flipped signature.
"""
try: # descriptor first
_gettypeattr(self.__unflipped, name).__set__(self, value)
@ -1112,10 +1112,10 @@ class FlippedSignature:
setattr(self.__unflipped, name, value)
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
deleter, its :pc:`self` argument receives the flipped signature.
Performs :py:`delattr(unflipped, name)`, ensuring that, if :py:`name` refers to a property
deleter, its :py:`self` argument receives the flipped signature.
"""
try: # descriptor first
_gettypeattr(self.__unflipped, name).__delete__(self)
@ -1140,7 +1140,7 @@ class PureInterface:
.. 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
create an interface object, but it may be used either directly or as a base class whenever
it is convenient to do so.
@ -1150,7 +1150,7 @@ class PureInterface:
"""Create attributes from a signature.
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:
.. code::
@ -1187,7 +1187,7 @@ class FlippedInterface:
"""An interface object, with its members' directions 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
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
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
:pc:`isinstance(self, FlippedInterface)`:
:py:`isinstance(self, FlippedInterface)`:
.. testcode::
@ -1240,7 +1240,7 @@ class FlippedInterface:
Returns
-------
Signature
:pc:`unflipped.signature.flip()`
:py:`unflipped.signature.flip()`
"""
return self.__unflipped.signature.flip()
@ -1250,8 +1250,8 @@ class FlippedInterface:
Returns
-------
bool
:pc:`True` if :pc:`other` is an instance :pc:`FlippedInterface(other_unflipped)` where
:pc:`unflipped == other_unflipped`, :pc:`False` otherwise.
:py:`True` if :py:`other` is an instance :py:`FlippedInterface(other_unflipped)` where
:py:`unflipped == other_unflipped`, :py:`False` otherwise.
"""
return type(self) is type(other) and self.__unflipped == other.__unflipped
@ -1259,13 +1259,13 @@ class FlippedInterface:
# an interface member.
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.
2. If :pc:`name` refers to a property getter or a method, its :pc:`self` argument receives
the *flipped* interface. A class method's :pc:`cls` argument receives the class of
1. If :py:`name` refers to a signature member, the returned interface object is flipped.
2. If :py:`name` refers to a property getter or a method, its :py:`self` argument receives
the *flipped* interface. A class method's :py:`cls` argument receives the class of
the *unflipped* interface, as usual.
"""
if (name in self.__unflipped.signature.members and
@ -1278,12 +1278,12 @@ class FlippedInterface:
return getattr(self.__unflipped, name)
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.
2. If :pc:`name` refers to a property setter, its :pc:`self` argument receives the flipped
1. If :py:`name` refers to a signature member, the assigned interface object is flipped.
2. If :py:`name` refers to a property setter, its :py:`self` argument receives the flipped
interface.
"""
if (name in self.__unflipped.signature.members and
@ -1296,10 +1296,10 @@ class FlippedInterface:
setattr(self.__unflipped, name, value)
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
deleter, its :pc:`self` argument receives the flipped interface.
Performs :py:`delattr(unflipped, name)`, ensuring that, if :py:`name` refers to a property
deleter, its :py:`self` argument receives the flipped interface.
"""
try: # descriptor first
_gettypeattr(self.__unflipped, name).__delete__(self)
@ -1312,10 +1312,10 @@ class FlippedInterface:
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:
: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.
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
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`
is an output, then :pc:`obj2.a.b` and :pc:`obj2.a.b` must exist and be inputs. If :pc:`obj2.c`
is an input and its value is :pc:`Const(1)`, then :pc:`obj1.c` and :pc:`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,
then no ports except for those two must exist on :pc:`obj2` and :pc:`obj3` either.
For example, if :py:`obj1` is being connected to :py:`obj2` and :py:`obj3`, and :py:`obj1.a.b`
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 :py:`Const(1)`, then :py:`obj1.c` and :py:`obj3.c` must be outputs
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 :py:`obj2` and :py:`obj3` either.
Once it is determined that the interface objects can be connected, this function performs
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.)
The positions (within :pc:`args`) or names (within :pc:`kwargs`) of the arguments do not affect
the connections that are made. There is no difference in behavior between :pc:`connect(m, a, b)`
and :pc:`connect(m, b, a)` or :pc:`connect(m, arbiter=a, decoder=b)`. The names of the keyword
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 :py:`connect(m, a, b)`
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
a connection cannot be made.
"""
@ -1609,8 +1609,8 @@ class Component(Elaboratable):
Raises
------
:exc:`TypeError`
If the :pc:`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 the :py:`signature` object is neither a :class:`Signature` nor a :class:`dict`.
If neither variable annotations nor the :py:`signature` argument are present, or if
both are present.
:exc:`NameError`
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"
rst_prolog = """
.. role:: pc(code)
.. role:: py(code)
:language: python
"""

View file

@ -760,13 +760,13 @@ Amaranth operation Equivalent Python code
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.
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.
@ -997,7 +997,7 @@ Multiple assignments to the same signal bits are more useful when combined with
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
@ -1009,14 +1009,14 @@ Although it is possible to write any decision tree as a combination of :ref:`ass
with m.Else():
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::
timer = Signal(8)
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::
@ -1079,10 +1079,10 @@ Combining these cases together, the code above is equivalent to:
.. _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::
:hide:
@ -1106,17 +1106,17 @@ Conditional control flow is described using a :pc:`with m.If(cond1):` block, whi
with m.Else():
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:
: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()`?
@ -1141,13 +1141,13 @@ Case comparison, where a single value is examined against several different *pat
.. 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::
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::
@ -1162,10 +1162,10 @@ If a :pc:`Default` block is present, or the patterns in the :pc:`Case` blocks co
.. _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::
@ -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
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::
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::
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::
@ -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")):
...
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: `m.next` does the same, which is worse because adding a diagnostic is harder
.. 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.
.. 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.
@ -1235,7 +1235,7 @@ Note that in Python, assignments made using :pc:`with x() as y:` syntax persist
.. 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:
@ -1302,7 +1302,7 @@ Consider the following code:
with m.Elif(down):
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:
@ -1310,7 +1310,7 @@ Whenever there is a transition on the clock of the ``sync`` domain, the :pc:`tim
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::
@ -1329,7 +1329,7 @@ If the name of the domain is not known upfront, another, less concise, syntax ca
.. 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:
@ -1347,7 +1347,7 @@ If a clock domain is defined in a module, all of its submodules can refer to tha
.. 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::
@ -1384,7 +1384,7 @@ Clock domains are *late bound*, which means that their signals and properties ca
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
@ -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),
]
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::
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:
@ -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`.
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::
@ -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:`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:
@ -1526,7 +1526,7 @@ Consider the following code:
m = ResetInserter({"sync": rst})(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::
@ -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.
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:
@ -1590,7 +1590,7 @@ Consider the following code:
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::
@ -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 *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`.
* 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`.
* 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 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 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 :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 :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 :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``:
@ -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.
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::

View file

@ -80,9 +80,9 @@ The prelude exports exactly the following names:
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::
@ -90,7 +90,7 @@ Some call sites are not relevant for an Amaranth designer; e.g. when an Amaranth
... # additionally process `obj`...
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

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.
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.
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
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:
@ -93,21 +93,21 @@ The :mod:`amaranth.lib.wiring` module provides a solution for this problem: *com
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.
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::
>>> ComponentCounter().signature
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::
@ -130,7 +130,7 @@ The :ref:`shapes <lang-shapes>` of the ports need not be static. The :pc:`Compon
>>> GenericCounter(16).signature
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.
@ -140,7 +140,7 @@ The next section introduces the concepts of directionality and connection, and d
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::
@ -239,7 +239,7 @@ The producer and the consumer reuse the same signature, relying on flipping to m
>>> consumer.sink.signature.members['data']
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::
@ -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
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::
@ -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"]
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::
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::
@ -295,7 +295,7 @@ This explanation concludes the essential knowledge necessary for using this modu
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::
@ -318,7 +318,7 @@ Consider a case where a component includes another component as a part of its im
]
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::
@ -346,7 +346,7 @@ In some cases, *both* of the two interfaces provided to :func:`connect` must be
.. 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:
@ -443,7 +443,7 @@ When creating an adapted interface, use the :meth:`create <Signature.create>` me
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:
@ -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:
* 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 :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 :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:`__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:`__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 :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::
@ -522,11 +522,11 @@ The custom properties defined for both the signature and the interface object ca
.. 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::
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:
@ -534,7 +534,7 @@ The custom properties defined for both the signature and the interface object ca
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.