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

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