Skip to content

Commit 0fdf254

Browse files
Deploying to main from @ amaranth-lang/amaranth@324c37f 🚀
1 parent facc373 commit 0fdf254

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1331
-296
lines changed

docs/amaranth/latest/.buildinfo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Sphinx build info version 1
22
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
3-
config: 9aa2837be947243943edbd876a51ac86
3+
config: c636b60df3401a133e587ed2203b6bbe
44
tags: 645f666f9bcd5a90fca523b33c5a78b7
Binary file not shown.
1.2 KB
Binary file not shown.
20 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

docs/amaranth/latest/_sources/guide.rst.txt

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,17 +1673,17 @@ Amaranth provides support for memories in the standard library module :mod:`amar
16731673
I/O values
16741674
==========
16751675

1676-
To interoperate with external circuitry, Amaranth provides *I/O values*, which represent bundles of wires carrying uninterpreted signals. Unlike regular :ref:`values <lang-values>`, which represent binary numbers and can be :ref:`assigned <lang-assigns>` to create a unidirectional connection or used in computations, I/O values represent electrical signals that may be digital or analog and have no :ref:`shape <lang-shapes>`, cannot be assigned, used in computations, or simulated.
1676+
To interoperate with external circuitry, Amaranth provides *core I/O values*, which represent bundles of wires carrying uninterpreted signals. Unlike regular :ref:`values <lang-values>`, which represent binary numbers and can be :ref:`assigned <lang-assigns>` to create a unidirectional connection or used in computations, core I/O values represent electrical signals that may be digital or analog and have no :ref:`shape <lang-shapes>`, cannot be assigned, used in computations, or simulated.
16771677

1678-
I/O values are only used to define connections between non-Amaranth building blocks that traverse an Amaranth design, including :ref:`instances <lang-instance>` and :ref:`I/O buffer instances <lang-iobufferinstance>`.
1678+
Core I/O values are only used to define connections between non-Amaranth building blocks that traverse an Amaranth design, including :ref:`instances <lang-instance>` and :ref:`I/O buffer instances <lang-iobufferinstance>`.
16791679

16801680

16811681
.. _lang-ioports:
16821682

16831683
I/O ports
16841684
---------
16851685

1686-
An *I/O port* is an I/O value representing a connection to a port of the topmost module in the :ref:`design hierarchy <lang-submodules>`. It can be created with an explicitly specified width.
1686+
A *core I/O port* is a core I/O value representing a connection to a port of the topmost module in the :ref:`design hierarchy <lang-submodules>`. It can be created with an explicitly specified width.
16871687

16881688
.. testcode::
16891689

@@ -1695,23 +1695,23 @@ An *I/O port* is an I/O value representing a connection to a port of the topmost
16951695
>>> port.width
16961696
4
16971697

1698-
I/O ports can be named in the same way as :ref:`signals <lang-signalname>`:
1698+
Core I/O ports can be named in the same way as :ref:`signals <lang-signalname>`:
16991699

17001700
.. doctest::
17011701

17021702
>>> clk_port = IOPort(1, name="clk")
17031703
>>> clk_port.name
17041704
'clk'
17051705

1706-
If two I/O ports with the same name exist in a design, one of them will be renamed to remove the ambiguity. Because the name of an I/O port is significant, they should be named unambiguously.
1706+
If two core I/O ports with the same name exist in a design, one of them will be renamed to remove the ambiguity. Because the name of a core I/O port is significant, they should be named unambiguously.
17071707

17081708

17091709
.. _lang-ioops:
17101710

17111711
I/O operators
17121712
-------------
17131713

1714-
I/O values support only a limited set of :ref:`sequence <python:typesseq>` operators, all of which return another I/O value. The following table lists the I/O operators provided by Amaranth:
1714+
Core I/O values support only a limited set of :ref:`sequence <python:typesseq>` operators, all of which return another core I/O value. The following table lists the operators provided by Amaranth for core I/O values:
17151715

17161716
=============== ============================== ===================
17171717
Operation Description Notes
@@ -1747,9 +1747,9 @@ An instance can be added as a submodule using the :py:`m.submodules.name = Insta
17471747

17481748
* 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`.
17491749
* 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`.
1750-
* An input is specified using the :py:`i_INAME=in_val` or :py:`("i", "INAME", in_val)` syntaxes. The :py:`in_val` must be an :ref:`I/O value <lang-iovalues>` or a :ref:`value-like <lang-valuelike>` object.
1751-
* An output is specified using the :py:`o_ONAME=out_val` or :py:`("o", "ONAME", out_val)` syntaxes. The :py:`out_val` must be an :ref:`I/O value <lang-iovalues>` or a :ref:`value-like <lang-valuelike>` object that casts to a :ref:`signal <lang-signals>`, a concatenation of signals, or a slice of a signal.
1752-
* An inout is specified using the :py:`io_IONAME=inout_val` or :py:`("io", "IONAME", inout_val)` syntaxes. The :py:`inout_val` must be an :ref:`I/O value <lang-iovalues>`.
1750+
* 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:`core I/O value <lang-iovalues>` or a :ref:`value-like <lang-valuelike>` object.
1751+
* 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:`core I/O value <lang-iovalues>` or a :ref:`value-like <lang-valuelike>` object that casts to a :ref:`signal <lang-signals>`, a concatenation of signals, or a slice of a signal.
1752+
* An inout is specified using the :py:`io_IONAME=inout_val` or :py:`("io", "IONAME", inout_val)` syntaxes. The :py:`inout_val` must be a :ref:`core I/O value <lang-iovalues>`.
17531753

17541754
The two following examples use both syntaxes to add the same instance of type ``external`` as a submodule named ``processor``:
17551755

@@ -1833,31 +1833,35 @@ Although an :class:`Instance` is not an elaboratable, as a special case, it can
18331833
I/O buffer instances
18341834
====================
18351835

1836-
An *I/O buffer instance* is a submodule that allows connecting :ref:`I/O values <lang-iovalues>` and regular :ref:`values <lang-values>` without the use of an external, toolchain- and technology-dependent :ref:`instance <lang-instance>`. It can be created in four configurations: input, output, tristatable output, and bidirectional (input/output).
1836+
.. note::
1837+
1838+
I/O buffer instances are a low-level primitive which is documented to ensure that the standard library does not rely on private interfaces in the core language. Most designers should use the :mod:`amaranth.lib.io` module instead.
1839+
1840+
An *I/O buffer instance* is a submodule that allows connecting :ref:`core I/O values <lang-iovalues>` and regular :ref:`values <lang-values>` without the use of an external, toolchain- and technology-dependent :ref:`instance <lang-instance>`. It can be created in four configurations: input, output, tristatable output, and bidirectional (input/output).
18371841

18381842
.. testcode::
18391843

18401844
from amaranth.hdl import IOBufferInstance
18411845

18421846
m = Module()
18431847

1844-
In the input configuration, the buffer combinationally drives a signal :py:`i` by the port:
1848+
In the input configuration, the buffer instance combinationally drives a signal :py:`i` by the port:
18451849

18461850
.. testcode::
18471851

18481852
port = IOPort(4)
18491853
port_i = Signal(4)
18501854
m.submodules += IOBufferInstance(port, i=port_i)
18511855

1852-
In the output configuration, the buffer combinationally drives the port by a value :py:`o`:
1856+
In the output configuration, the buffer instance combinationally drives the port by a value :py:`o`:
18531857

18541858
.. testcode::
18551859

18561860
port = IOPort(4)
18571861
port_o = Signal(4)
18581862
m.submodules += IOBufferInstance(port, o=port_o)
18591863

1860-
In the tristatable output configuration, the buffer combinationally drives the port by a value :py:`o` if :py:`oe` is asserted, and does not drive (leaves in a high-impedance state, or tristates) the port otherwise:
1864+
In the tristatable output configuration, the buffer instance combinationally drives the port by a value :py:`o` if :py:`oe` is asserted, and does not drive (leaves in a high-impedance state, or tristates) the port otherwise:
18611865

18621866
.. testcode::
18631867

@@ -1866,7 +1870,7 @@ In the tristatable output configuration, the buffer combinationally drives the p
18661870
port_oe = Signal()
18671871
m.submodules += IOBufferInstance(port, o=port_o, oe=port_oe)
18681872

1869-
In the bidirectional (input/output) configuration, the buffer combinationally drives a signal :py:`i` by the port, combinationally drives the port by a value :py:`o` if :py:`oe` is asserted, and does not drive (leaves in a high-impedance state, or tristates) the port otherwise:
1873+
In the bidirectional (input/output) configuration, the buffer instance combinationally drives a signal :py:`i` by the port, combinationally drives the port by a value :py:`o` if :py:`oe` is asserted, and does not drive (leaves in a high-impedance state, or tristates) the port otherwise:
18701874

18711875
.. testcode::
18721876

docs/amaranth/latest/_sources/stdlib.rst.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ The Amaranth standard library is separate from the Amaranth language: everything
1919
stdlib/wiring
2020
stdlib/meta
2121
stdlib/memory
22+
stdlib/io
2223
stdlib/cdc
2324
stdlib/coding
2425
stdlib/fifo
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
Input/output buffers
2+
====================
3+
4+
.. py:module:: amaranth.lib.io
5+
6+
The :mod:`amaranth.lib.io` module provides a platform-independent way to instantiate platform-specific input/output buffers: combinational, synchronous, and double data rate (DDR).
7+
8+
9+
Introduction
10+
------------
11+
12+
The Amaranth language provides :ref:`core I/O values <lang-iovalues>` that designate connections to external devices, and :ref:`I/O buffer instances <lang-iobufferinstance>` that implement platform-independent combinational I/O buffers. This low-level mechanism is foundational to all I/O in Amaranth and must be used whenever a device-specific platform is unavailable, but is limited in its capabilities. The :mod:`amaranth.lib.io` module builds on top of it to provide *library I/O ports* that specialize and annotate I/O values, and *buffer components* that connect ports to logic.
13+
14+
.. note::
15+
16+
Unfortunately, the terminology related to I/O has several ambiguities:
17+
18+
* A "port" could refer to an *interface port* (:class:`.Signal` objects created by the :mod:`amaranth.lib.wiring` module), a *core I/O port* (:class:`amaranth.hdl.IOPort` object), or a *library I/O port* (:class:`amaranth.lib.io.PortLike` object).
19+
* A "I/O buffer" could refer to an *I/O buffer instance* (:class:`amaranth.hdl.IOBufferInstance`) or a *I/O buffer component* (:class:`amaranth.lib.io.Buffer`, :class:`.FFBuffer`, or :class:`.DDRBuffer` objects).
20+
21+
Amaranth documentation always uses the least ambiguous form of these terms.
22+
23+
24+
Examples
25+
--------
26+
27+
.. testsetup::
28+
29+
from amaranth import *
30+
31+
class MockPlatform:
32+
def request(self, name, *, dir):
33+
from amaranth.hdl import IOPort
34+
from amaranth.lib import io
35+
if name == "led":
36+
return io.SingleEndedPort(IOPort(1, name=name), direction="o")
37+
if name == "clk24":
38+
return io.SingleEndedPort(IOPort(1, name=name), direction="i")
39+
if name == "d":
40+
return io.SingleEndedPort(IOPort(8, name=name), direction="io")
41+
if name == "re":
42+
return io.SingleEndedPort(IOPort(1, name=name), direction="i")
43+
if name == "we":
44+
return io.SingleEndedPort(IOPort(1, name=name), direction="i")
45+
if name == "dclk":
46+
return io.SingleEndedPort(IOPort(1, name=name), direction="o")
47+
if name == "dout":
48+
return io.SingleEndedPort(IOPort(8, name=name), direction="o")
49+
raise NameError
50+
51+
def get_io_buffer(self, buffer):
52+
return Fragment()
53+
54+
def build(self, top):
55+
from amaranth.back import rtlil
56+
return rtlil.convert(Fragment.get(top, self), ports=[])
57+
58+
59+
All of the following examples assume that one of the built-in FPGA platforms is used.
60+
61+
.. testcode::
62+
63+
from amaranth.lib import io, wiring
64+
from amaranth.lib.wiring import In, Out
65+
66+
67+
LED output
68+
++++++++++
69+
70+
In this example, a library I/O port for a LED is requested from the platform and driven to blink the LED:
71+
72+
.. testcode::
73+
74+
class Toplevel(Elaboratable):
75+
def elaborate(self, platform):
76+
m = Module()
77+
78+
delay = Signal(24)
79+
state = Signal()
80+
with m.If(delay == 0):
81+
m.d.sync += delay.eq(~0)
82+
m.d.sync += state.eq(~state)
83+
with m.Else():
84+
m.d.sync += delay.eq(delay - 1)
85+
86+
m.submodules.led = led = io.Buffer("o", platform.request("led", dir="-"))
87+
m.d.comb += led.o.eq(state)
88+
89+
return m
90+
91+
.. testcode::
92+
:hide:
93+
94+
MockPlatform().build(Toplevel())
95+
96+
97+
Clock input
98+
+++++++++++
99+
100+
In this example, a clock domain is created and driven from an external clock source:
101+
102+
.. testcode::
103+
104+
class Toplevel(Elaboratable):
105+
def elaborate(self, platform):
106+
m = Module()
107+
108+
m.domains.sync = cd_sync = ClockDomain(local=True)
109+
110+
m.submodules.clk24 = clk24 = io.Buffer("i", platform.request("clk24", dir="-"))
111+
m.d.comb += cd_sync.clk.eq(clk24.i)
112+
113+
...
114+
115+
return m
116+
117+
.. testcode::
118+
:hide:
119+
120+
MockPlatform().build(Toplevel())
121+
122+
123+
Bidirectional bus
124+
+++++++++++++++++
125+
126+
This example implements a peripheral for a clocked parallel bus. This peripheral can store and recall one byte of data. The data is stored with a write enable pulse, and recalled with a read enable pulse:
127+
128+
.. testcode::
129+
130+
class Toplevel(Elaboratable):
131+
def elaborate(self, platform):
132+
m = Module()
133+
134+
m.submodules.bus_d = bus_d = io.FFBuffer("io", platform.request("d", dir="-"))
135+
m.submodules.bus_re = bus_re = io.Buffer("i", platform.request("re", dir="-"))
136+
m.submodules.bus_we = bus_we = io.Buffer("i", platform.request("we", dir="-"))
137+
138+
data = Signal.like(bus_d.i)
139+
with m.If(bus_re.i):
140+
m.d.comb += bus_d.oe.eq(1)
141+
m.d.comb += bus_d.o.eq(data)
142+
with m.Elif(bus_we.i):
143+
m.d.sync += data.eq(bus_d.i)
144+
145+
return m
146+
147+
.. testcode::
148+
:hide:
149+
150+
MockPlatform().build(Toplevel())
151+
152+
This bus requires a turn-around time of at least 1 cycle to avoid electrical contention.
153+
154+
Note that data appears on the bus one cycle after the read enable input is asserted, and that the write enable input stores the data present on the bus in the *previous* cycle. This is called *pipelining* and is typical for clocked buses; see :class:`.FFBuffer` for a waveform diagram. Although it increases the maximum clock frequency at which the bus can run, it also makes the bus signaling more complicated.
155+
156+
157+
Clock forwarding
158+
++++++++++++++++
159+
160+
In this example of a `source-synchronous interface <https://en.wikipedia.org/wiki/Source-synchronous>`__, a clock signal is generated with the same phase as the DDR data signals associated with it:
161+
162+
.. testcode::
163+
164+
class SourceSynchronousOutput(wiring.Component):
165+
dout: In(16)
166+
167+
def elaborate(self, platform):
168+
m = Module()
169+
170+
m.submodules.bus_dclk = bus_dclk = \
171+
io.DDRBuffer("o", platform.request("dclk", dir="-"))
172+
m.d.comb += [
173+
bus_dclk.o[0].eq(1),
174+
bus_dclk.o[1].eq(0),
175+
]
176+
177+
m.submodules.bus_dout = bus_dout = \
178+
io.DDRBuffer("o", platform.request("dout", dir="-"))
179+
m.d.comb += [
180+
bus_dout.o[0].eq(self.dout[:8]),
181+
bus_dout.o[1].eq(self.dout[8:]),
182+
]
183+
184+
return m
185+
186+
.. testcode::
187+
:hide:
188+
189+
MockPlatform().build(SourceSynchronousOutput())
190+
191+
This component transmits :py:`dout` on each cycle as two halves: the low 8 bits on the rising edge of the data clock, and the high 8 bits on the falling edge of the data clock. The transmission is *edge-aligned*, meaning that the data edges exactly coincide with the clock edges.
192+
193+
194+
Ports
195+
-----
196+
197+
.. autoclass:: Direction()
198+
199+
.. autoclass:: PortLike
200+
.. autoclass:: SingleEndedPort
201+
.. autoclass:: DifferentialPort
202+
203+
204+
Buffers
205+
-------
206+
207+
.. autoclass:: Buffer(direction, port)
208+
.. autoclass:: FFBuffer(direction, port, *, i_domain=None, o_domain=None)
209+
.. autoclass:: DDRBuffer(direction, port, *, i_domain=None, o_domain=None)

docs/amaranth/latest/_static/documentation_options.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
var DOCUMENTATION_OPTIONS = {
22
URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'),
3-
VERSION: '0.5.0.dev298',
3+
VERSION: '0.5.0.dev299',
44
LANGUAGE: 'en',
55
COLLAPSE_INDEX: false,
66
BUILDER: 'html',

0 commit comments

Comments
 (0)