Skip to content

Commit 09967ad

Browse files
committed
Implement RFC 61: Minimal streams.
1 parent f6ed388 commit 09967ad

File tree

8 files changed

+708
-5
lines changed

8 files changed

+708
-5
lines changed

amaranth/lib/fifo.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ..utils import ceil_log2
77
from .cdc import FFSynchronizer, AsyncFFSynchronizer
88
from .memory import Memory
9+
from . import stream
910

1011

1112
__all__ = ["FIFOInterface", "SyncFIFO", "SyncFIFOBuffered", "AsyncFIFO", "AsyncFIFOBuffered"]
@@ -93,6 +94,22 @@ def __init__(self, *, width, depth):
9394
self.r_en = Signal()
9495
self.r_level = Signal(range(depth + 1))
9596

97+
@property
98+
def w_stream(self):
99+
w_stream = stream.Signature(self.width).flip().create()
100+
w_stream.payload = self.w_data
101+
w_stream.valid = self.w_en
102+
w_stream.ready = self.w_rdy
103+
return w_stream
104+
105+
@property
106+
def r_stream(self):
107+
r_stream = stream.Signature(self.width).create()
108+
r_stream.payload = self.r_data
109+
r_stream.valid = self.r_rdy
110+
r_stream.ready = self.r_en
111+
return r_stream
112+
96113

97114
def _incr(signal, modulo):
98115
if modulo == 2 ** len(signal):

amaranth/lib/stream.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
from ..hdl import *
2+
from .._utils import final
3+
from . import wiring
4+
from .wiring import In, Out
5+
6+
7+
@final
8+
class Signature(wiring.Signature):
9+
"""Signature of a unidirectional data stream.
10+
11+
.. note::
12+
13+
"Minimal streams" as defined in `RFC 61`_ lack support for complex payloads, such as
14+
multiple lanes or packetization, as well as introspection of the payload. This limitation
15+
will be lifted in a later release.
16+
17+
.. _RFC 61: https://amaranth-lang.org/rfcs/0061-minimal-streams.html
18+
19+
Parameters
20+
----------
21+
payload_shape : :class:`~.hdl.ShapeLike`
22+
Shape of the payload.
23+
always_valid : :class:`bool`
24+
Whether the stream has a payload available each cycle.
25+
always_ready : :class:`bool`
26+
Whether the stream has its payload accepted whenever it is available (i.e. whether it lacks
27+
support for backpressure).
28+
29+
Members
30+
-------
31+
payload : :py:`Out(payload_shape)`
32+
Payload.
33+
valid : :py:`Out(1)`
34+
Whether a payload is available. If the stream is :py:`always_valid`, :py:`Const(1)`.
35+
ready : :py:`In(1)`
36+
Whether a payload is accepted. If the stream is :py:`always_ready`, :py:`Const(1)`.
37+
"""
38+
def __init__(self, payload_shape: ShapeLike, *, always_valid=False, always_ready=False):
39+
Shape.cast(payload_shape)
40+
self._payload_shape = payload_shape
41+
self._always_valid = bool(always_valid)
42+
self._always_ready = bool(always_ready)
43+
44+
super().__init__({
45+
"payload": Out(payload_shape),
46+
"valid": Out(1),
47+
"ready": In(1)
48+
})
49+
50+
# payload_shape intentionally not introspectable (for now)
51+
52+
@property
53+
def always_valid(self):
54+
return self._always_valid
55+
56+
@property
57+
def always_ready(self):
58+
return self._always_ready
59+
60+
def __eq__(self, other):
61+
return (type(other) is type(self) and
62+
other._payload_shape == self._payload_shape and
63+
other.always_valid == self.always_valid and
64+
other.always_ready == self.always_ready)
65+
66+
def create(self, *, path=None, src_loc_at=0):
67+
return Interface(self, path=path, src_loc_at=1 + src_loc_at)
68+
69+
def __repr__(self):
70+
always_valid_repr = "" if not self._always_valid else ", always_valid=True"
71+
always_ready_repr = "" if not self._always_ready else ", always_ready=True"
72+
return f"stream.Signature({self._payload_shape!r}{always_valid_repr}{always_ready_repr})"
73+
74+
75+
@final
76+
class Interface:
77+
"""A unidirectional data stream.
78+
79+
Attributes
80+
----------
81+
signature : :class:`Signature`
82+
Signature of this data stream.
83+
"""
84+
85+
payload: Signal
86+
valid: 'Signal | Const'
87+
ready: 'Signal | Const'
88+
89+
def __init__(self, signature: Signature, *, path=None, src_loc_at=0):
90+
if not isinstance(signature, Signature):
91+
raise TypeError(f"Signature of stream.Interface must be a stream.Signature, not "
92+
f"{signature!r}")
93+
self._signature = signature
94+
self.__dict__.update(signature.members.create(path=path, src_loc_at=1 + src_loc_at))
95+
if signature.always_valid:
96+
self.valid = Const(1)
97+
if signature.always_ready:
98+
self.ready = Const(1)
99+
100+
@property
101+
def signature(self):
102+
return self._signature
103+
104+
@property
105+
def p(self):
106+
"""Shortcut for :py:`self.payload`.
107+
108+
This shortcut reduces repetition when manipulating the payload, for example:
109+
110+
.. code::
111+
112+
m.d.comb += [
113+
self.o_stream.p.result.eq(self.i_stream.p.first + self.i_stream.p.second),
114+
self.o_stream.valid.eq(self.i_stream.valid),
115+
self.i_stream.ready.eq(self.o_stream.ready),
116+
]
117+
"""
118+
return self.payload
119+
120+
def __repr__(self):
121+
return (f"stream.Interface(payload={self.payload!r}, valid={self.valid!r}, "
122+
f"ready={self.ready!r})")

docs/stdlib.rst

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ Standard library
33

44
The :mod:`amaranth.lib` module, also known as the standard library, provides modules that falls into one of the three categories:
55

6-
1. Modules that will used by essentially all idiomatic Amaranth code, or which are necessary for interoperability. This includes :mod:`amaranth.lib.enum` (enumerations), :mod:`amaranth.lib.data` (data structures), :mod:`amaranth.lib.wiring` (interfaces and components), and :mod:`amaranth.lib.meta` (interface metadata).
7-
2. Modules that abstract common functionality whose implementation differs between hardware platforms. This includes :mod:`amaranth.lib.cdc`, :mod:`amaranth.lib.memory`.
6+
1. Modules that will used by essentially all idiomatic Amaranth code, or which are necessary for interoperability. This includes :mod:`amaranth.lib.enum` (enumerations), :mod:`amaranth.lib.data` (data structures), :mod:`amaranth.lib.wiring` (interfaces and components), :mod:`amaranth.lib.meta` (interface metadata), and :mod:`amaranth.lib.stream` (data streams).
7+
2. Modules that abstract common functionality whose implementation differs between hardware platforms. This includes :mod:`amaranth.lib.memory` and :mod:`amaranth.lib.cdc`.
88
3. Modules that have essentially one correct implementation and are of broad utility in digital designs. This includes :mod:`amaranth.lib.coding`, :mod:`amaranth.lib.fifo`, and :mod:`amaranth.lib.crc`.
99

1010
As part of the Amaranth backwards compatibility guarantee, any behaviors described in these documents will not change from a version to another without at least one version including a warning about the impending change. Any nontrivial change to these behaviors must also go through the public review as a part of the `Amaranth Request for Comments process <https://amaranth-lang.org/rfcs/>`_.
@@ -18,6 +18,7 @@ The Amaranth standard library is separate from the Amaranth language: everything
1818
stdlib/data
1919
stdlib/wiring
2020
stdlib/meta
21+
stdlib/stream
2122
stdlib/memory
2223
stdlib/io
2324
stdlib/cdc
56.3 KB
Loading

0 commit comments

Comments
 (0)