Skip to content

Commit feec751

Browse files
committed
event: migrate to lib.wiring interfaces.
1 parent c5e1e66 commit feec751

File tree

4 files changed

+189
-105
lines changed

4 files changed

+189
-105
lines changed

amaranth_soc/event.py

Lines changed: 103 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,131 @@
1-
import enum
2-
from collections import OrderedDict
3-
41
from amaranth import *
2+
from amaranth.lib import enum, wiring
3+
from amaranth.lib.wiring import In, Out
54

65

76
__all__ = ["Source", "EventMap", "Monitor"]
87

98

10-
class Source(Record):
9+
class Source(wiring.Interface):
1110
class Trigger(enum.Enum):
1211
"""Event trigger mode."""
1312
LEVEL = "level"
1413
RISE = "rise"
1514
FALL = "fall"
1615

16+
class Signature(wiring.Signature):
17+
"""Event source signature.
18+
19+
Parameters
20+
----------
21+
trigger : :class:`Source.Trigger`
22+
Trigger mode. An event can be edge- or level-triggered by the input line.
23+
24+
Interface attributes
25+
--------------------
26+
i : Signal()
27+
Input line. Sampled in order to detect an event.
28+
trg : Signal()
29+
Event trigger. Asserted when an event occurs, according to the trigger mode.
30+
31+
Raises
32+
------
33+
See :meth:`Source.Signature.check_parameters`.
34+
"""
35+
def __init__(self, *, trigger="level"):
36+
self.check_parameters(trigger=trigger)
37+
38+
self._trigger = Source.Trigger(trigger)
39+
40+
members = {
41+
"i": In(1),
42+
"trg": Out(1),
43+
}
44+
super().__init__(members)
45+
46+
@property
47+
def trigger(self):
48+
return self._trigger
49+
50+
def check_parameters(cls, *, trigger):
51+
"""Validate signature parameters.
52+
53+
Raises
54+
------
55+
:exc:`ValueError`
56+
If ``trigger`` is not a member of :class:`Source.Trigger`.
57+
"""
58+
# TODO(py3.9): Remove this. Python 3.8 and below use cls.__name__ in the error message
59+
# instead of cls.__qualname__.
60+
# Source.Trigger(trigger)
61+
try:
62+
Source.Trigger(trigger)
63+
except ValueError as e:
64+
raise ValueError(f"{trigger!r} is not a valid Source.Trigger") from e
65+
66+
def create(self, *, path=()):
67+
"""Create a compatible interface.
68+
69+
See :meth:`wiring.Signature.create` for details.
70+
71+
Returns
72+
-------
73+
A :class:`Source` object using this signature.
74+
"""
75+
return Source(trigger=self.trigger, path=path)
76+
77+
def __eq__(self, other):
78+
"""Compare signatures.
79+
80+
Two signatures are equal if they have the same trigger mode.
81+
"""
82+
return isinstance(other, Source.Signature) and self.trigger == other.trigger
83+
84+
def __repr__(self):
85+
return f"event.Source.Signature({self.members!r})"
86+
1787
"""Event source interface.
1888
1989
Parameters
2090
----------
21-
trigger : :class:`Trigger`
91+
trigger : :class:`Source.Trigger`
2292
Trigger mode. An event can be edge- or level-triggered by the input line.
23-
name: str
24-
Name of the underlying record.
93+
path : iter(:class:`str`)
94+
Path to this event source interface. Optional. See :class:`wiring.Interface`.
2595
2696
Attributes
2797
----------
28-
i : Signal()
29-
Input line. Sampled in order to detect an event.
30-
trg : Signal()
31-
Event trigger. Asserted when an event occurs, according to the trigger mode.
98+
event_map : :class:`EventMap`
99+
A collection of event sources.
100+
101+
Raises
102+
------
103+
See :meth:`Source.Signature.check_parameters`.
32104
"""
33-
def __init__(self, *, trigger="level", name=None, src_loc_at=0):
34-
choices = ("level", "rise", "fall")
35-
if not isinstance(trigger, Source.Trigger) and trigger not in choices:
36-
raise ValueError("Invalid trigger mode {!r}; must be one of {}"
37-
.format(trigger, ", ".join(choices)))
38-
self.trigger = Source.Trigger(trigger)
39-
self._map = None
40-
41-
super().__init__([
42-
("i", 1),
43-
("trg", 1),
44-
], name=name, src_loc_at=1 + src_loc_at)
105+
def __init__(self, *, trigger="level", path=()):
106+
super().__init__(Source.Signature(trigger=trigger), path=path)
45107

46-
@property
47-
def event_map(self):
48-
"""Event map.
108+
self._map = None
49109

50-
Return value
51-
------------
52-
A :class:`EventMap` describing subordinate event sources.
110+
@property
111+
def trigger(self):
112+
return self.signature.trigger
53113

54-
Exceptions
55-
----------
56-
Raises :exn:`NotImplementedError` if the source does not have an event map.
57-
"""
114+
@property
115+
def event_map(self):
58116
if self._map is None:
59-
raise NotImplementedError("Event source {!r} does not have an event map"
60-
.format(self))
117+
raise NotImplementedError(f"{self!r} does not have an event map")
61118
return self._map
62119

63120
@event_map.setter
64121
def event_map(self, event_map):
65122
if not isinstance(event_map, EventMap):
66-
raise TypeError("Event map must be an instance of EventMap, not {!r}"
67-
.format(event_map))
123+
raise TypeError(f"Event map must be an instance of EventMap, not {event_map!r}")
68124
event_map.freeze()
69125
self._map = event_map
70126

71-
# FIXME: get rid of this
72-
__hash__ = object.__hash__
127+
def __repr__(self):
128+
return f"event.Source({self.signature!r})"
73129

74130

75131
class EventMap:
@@ -80,7 +136,7 @@ class EventMap:
80136
increment, starting at 0.
81137
"""
82138
def __init__(self):
83-
self._sources = OrderedDict()
139+
self._sources = dict()
84140
self._frozen = False
85141

86142
@property
@@ -117,8 +173,8 @@ def add(self, src):
117173
if not isinstance(src, Source):
118174
raise TypeError("Event source must be an instance of event.Source, not {!r}"
119175
.format(src))
120-
if src not in self._sources:
121-
self._sources[src] = self.size
176+
if id(src) not in self._sources:
177+
self._sources[id(src)] = src, self.size
122178

123179
def index(self, src):
124180
"""Get the index corresponding to an event source.
@@ -139,7 +195,8 @@ def index(self, src):
139195
if not isinstance(src, Source):
140196
raise TypeError("Event source must be an instance of event.Source, not {!r}"
141197
.format(src))
142-
return self._sources[src]
198+
_, index = self._sources[id(src)]
199+
return index
143200

144201
def sources(self):
145202
"""Iterate event sources.
@@ -148,8 +205,7 @@ def sources(self):
148205
------------
149206
A tuple ``src, index`` corresponding to an event source and its index.
150207
"""
151-
for src, index in self._sources.items():
152-
yield src, index
208+
yield from self._sources.values()
153209

154210

155211
class Monitor(Elaboratable):
@@ -160,7 +216,7 @@ class Monitor(Elaboratable):
160216
Parameters
161217
----------
162218
event_map : :class:`EventMap`
163-
Event map.
219+
A collection of event sources.
164220
trigger : :class:`Source.Trigger`
165221
Trigger mode. See :class:`Source`.
166222
@@ -176,7 +232,7 @@ class Monitor(Elaboratable):
176232
Clear selected pending events.
177233
"""
178234
def __init__(self, event_map, *, trigger="level"):
179-
self.src = Source(trigger=trigger)
235+
self.src = Source(trigger=trigger, path=("src",))
180236
self.src.event_map = event_map
181237

182238
self.enable = Signal(event_map.size)

tests/test_csr_event.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,39 +30,39 @@ def test_trigger_wrong(self):
3030

3131
def test_add(self):
3232
monitor = EventMonitor(data_width=8)
33-
sub = event.Source()
33+
sub = event.Source.Signature().create()
3434
monitor.add(sub)
3535
self.assertEqual(monitor.src.event_map.size, 1)
3636
self.assertEqual(monitor.src.event_map.index(sub), 0)
3737

3838
def test_freeze(self):
3939
monitor = EventMonitor(data_width=8)
4040
monitor.freeze()
41-
sub = event.Source()
41+
sub = event.Source.Signature().create()
4242
with self.assertRaisesRegex(ValueError,
4343
r"Event map has been frozen. Cannot add source."):
4444
monitor.add(sub)
4545

4646
def test_src_freeze(self):
4747
monitor = EventMonitor(data_width=8)
4848
monitor.src
49-
sub = event.Source()
49+
sub = event.Source.Signature().create()
5050
with self.assertRaisesRegex(ValueError,
5151
r"Event map has been frozen. Cannot add source."):
5252
monitor.add(sub)
5353

5454
def test_bus_freeze(self):
5555
monitor = EventMonitor(data_width=8)
5656
monitor.bus
57-
sub = event.Source()
57+
sub = event.Source.Signature().create()
5858
with self.assertRaisesRegex(ValueError,
5959
r"Event map has been frozen. Cannot add source."):
6060
monitor.add(sub)
6161

6262
def test_csr_regs(self):
6363
monitor = EventMonitor(data_width=8)
64-
sub_0 = event.Source()
65-
sub_1 = event.Source()
64+
sub_0 = event.Source.Signature().create()
65+
sub_1 = event.Source.Signature().create()
6666
monitor.add(sub_0)
6767
monitor.add(sub_1)
6868
resources = list(monitor.bus.memory_map.resources())
@@ -90,7 +90,7 @@ def test_freeze_idempotent(self):
9090
class EventMonitorSimulationTestCase(unittest.TestCase):
9191
def test_simple(self):
9292
dut = EventMonitor(data_width=8)
93-
sub = event.Source()
93+
sub = event.Source.Signature().create(path=("sub",))
9494
dut.add(sub)
9595

9696
addr_enable = 0x0

0 commit comments

Comments
 (0)