Skip to content

Commit 517638c

Browse files
Jean-François Nguyenwhitequark
Jean-François Nguyen
authored andcommitted
csr.event: add event monitor.
1 parent 0385d67 commit 517638c

File tree

3 files changed

+253
-0
lines changed

3 files changed

+253
-0
lines changed

nmigen_soc/csr/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from .bus import *
2+
from .event import *

nmigen_soc/csr/event.py

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# nmigen: UnusedElaboratable=no
2+
3+
from nmigen import *
4+
5+
from . import Element, Multiplexer
6+
from .. import event
7+
8+
9+
__all__ = ["EventMonitor"]
10+
11+
12+
class EventMonitor(Elaboratable):
13+
"""Event monitor.
14+
15+
A monitor for subordinate event sources, with a CSR bus interface.
16+
17+
CSR registers
18+
-------------
19+
enable : ``self.src.event_map.size``, read/write
20+
Enabled events. See :meth:`..event.EventMap.sources` for layout.
21+
pending : ``self.src.event_map.size``, read/clear
22+
Pending events. See :meth:`..event.EventMap.sources` for layout.
23+
24+
Parameters
25+
----------
26+
data_width : int
27+
CSR bus data width. See :class:`..csr.Interface`.
28+
alignment : int
29+
CSR address alignment. See :class:`..memory.MemoryMap`.
30+
trigger : :class:`..event.Source.Trigger`
31+
Trigger mode. See :class:`..event.Source`.
32+
"""
33+
def __init__(self, *, data_width, alignment=0, trigger="level"):
34+
choices = ("level", "rise", "fall")
35+
if not isinstance(trigger, event.Source.Trigger) and trigger not in choices:
36+
raise ValueError("Invalid trigger mode {!r}; must be one of {}"
37+
.format(trigger, ", ".join(choices)))
38+
39+
self._trigger = event.Source.Trigger(trigger)
40+
self._map = event.EventMap()
41+
self._monitor = None
42+
self._enable = None
43+
self._pending = None
44+
self._mux = Multiplexer(addr_width=1, data_width=data_width, alignment=alignment)
45+
self._frozen = False
46+
47+
def freeze(self):
48+
"""Freeze the event monitor.
49+
50+
Once the event monitor is frozen, subordinate sources cannot be added anymore.
51+
"""
52+
if self._frozen:
53+
return
54+
self._monitor = event.Monitor(self._map, trigger=self._trigger)
55+
self._enable = Element(self._map.size, "rw")
56+
self._pending = Element(self._map.size, "rw")
57+
self._mux.add(self._enable, extend=True)
58+
self._mux.add(self._pending, extend=True)
59+
self._frozen = True
60+
61+
@property
62+
def src(self):
63+
"""Event source.
64+
65+
Return value
66+
------------
67+
An :class:`..event.Source`. Its input line is asserted by the monitor when a subordinate
68+
event is enabled and pending.
69+
"""
70+
self.freeze()
71+
return self._monitor.src
72+
73+
@property
74+
def bus(self):
75+
"""CSR bus interface.
76+
77+
Return value
78+
------------
79+
A :class:`..csr.Interface` providing access to registers.
80+
"""
81+
self.freeze()
82+
return self._mux.bus
83+
84+
def add(self, src):
85+
"""Add a subordinate event source.
86+
87+
See :meth:`..event.EventMap.add` for details.
88+
"""
89+
self._map.add(src)
90+
91+
def elaborate(self, platform):
92+
self.freeze()
93+
94+
m = Module()
95+
m.submodules.monitor = self._monitor
96+
m.submodules.mux = self._mux
97+
98+
with m.If(self._enable.w_stb):
99+
m.d.sync += self._monitor.enable.eq(self._enable.w_data)
100+
m.d.comb += self._enable.r_data.eq(self._monitor.enable)
101+
102+
with m.If(self._pending.w_stb):
103+
m.d.comb += self._monitor.clear.eq(self._pending.w_data)
104+
m.d.comb += self._pending.r_data.eq(self._monitor.pending)
105+
106+
return m

nmigen_soc/test/test_csr_event.py

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# nmigen: UnusedElaboratable=no
2+
3+
import unittest
4+
from nmigen import *
5+
from nmigen.back.pysim import *
6+
7+
from ..csr import *
8+
from .. import event
9+
10+
11+
def simulation_test(dut, process):
12+
with Simulator(dut, vcd_file=open("test.vcd", "w")) as sim:
13+
sim.add_clock(1e-6)
14+
sim.add_sync_process(process)
15+
sim.run()
16+
17+
18+
class EventMonitorTestCase(unittest.TestCase):
19+
def test_params(self):
20+
monitor = EventMonitor(data_width=16, alignment=4, trigger="rise")
21+
self.assertEqual(monitor.bus.data_width, 16)
22+
self.assertEqual(monitor.bus.memory_map.alignment, 4)
23+
self.assertEqual(monitor.src.trigger, event.Source.Trigger.RISE)
24+
25+
def test_trigger_wrong(self):
26+
with self.assertRaisesRegex(ValueError,
27+
r"Invalid trigger mode 'foo'; must be one of level, rise, fall"):
28+
EventMonitor(data_width=8, trigger="foo")
29+
30+
def test_add(self):
31+
monitor = EventMonitor(data_width=8)
32+
sub = event.Source()
33+
monitor.add(sub)
34+
self.assertEqual(monitor.src.event_map.size, 1)
35+
self.assertEqual(monitor.src.event_map.index(sub), 0)
36+
37+
def test_freeze(self):
38+
monitor = EventMonitor(data_width=8)
39+
monitor.freeze()
40+
sub = event.Source()
41+
with self.assertRaisesRegex(ValueError,
42+
r"Event map has been frozen. Cannot add source."):
43+
monitor.add(sub)
44+
45+
def test_src_freeze(self):
46+
monitor = EventMonitor(data_width=8)
47+
monitor.src
48+
sub = event.Source()
49+
with self.assertRaisesRegex(ValueError,
50+
r"Event map has been frozen. Cannot add source."):
51+
monitor.add(sub)
52+
53+
def test_bus_freeze(self):
54+
monitor = EventMonitor(data_width=8)
55+
monitor.bus
56+
sub = event.Source()
57+
with self.assertRaisesRegex(ValueError,
58+
r"Event map has been frozen. Cannot add source."):
59+
monitor.add(sub)
60+
61+
def test_csr_regs(self):
62+
monitor = EventMonitor(data_width=8)
63+
sub_0 = event.Source()
64+
sub_1 = event.Source()
65+
monitor.add(sub_0)
66+
monitor.add(sub_1)
67+
resources = list(monitor.bus.memory_map.resources())
68+
self.assertEqual(len(resources), 2)
69+
enable, enable_range = resources[0]
70+
pending, pending_range = resources[1]
71+
self.assertEqual(
72+
(enable.width, enable.access, enable_range),
73+
(2, Element.Access.RW, (0, 1))
74+
)
75+
self.assertEqual(
76+
(pending.width, pending.access, pending_range),
77+
(2, Element.Access.RW, (1, 2))
78+
)
79+
80+
def test_freeze_idempotent(self):
81+
monitor = EventMonitor(data_width=8)
82+
src = monitor.src
83+
bus = monitor.bus
84+
monitor.freeze()
85+
self.assertIs(src, monitor.src)
86+
self.assertIs(bus, monitor.bus)
87+
88+
89+
class EventMonitorSimulationTestCase(unittest.TestCase):
90+
def test_simple(self):
91+
dut = EventMonitor(data_width=8)
92+
sub = event.Source()
93+
dut.add(sub)
94+
95+
addr_enable = 0x0
96+
addr_pending = 0x1
97+
98+
def process():
99+
yield sub.i.eq(1)
100+
yield Delay()
101+
self.assertEqual((yield sub.trg), 1)
102+
self.assertEqual((yield dut.src.i), 0)
103+
104+
yield dut.bus.addr.eq(addr_enable)
105+
yield dut.bus.r_stb.eq(1)
106+
yield
107+
yield dut.bus.r_stb.eq(0)
108+
yield
109+
self.assertEqual((yield dut.bus.r_data), 0b0)
110+
yield
111+
112+
yield dut.bus.addr.eq(addr_enable)
113+
yield dut.bus.w_stb.eq(1)
114+
yield dut.bus.w_data.eq(0b1)
115+
yield
116+
yield dut.bus.w_stb.eq(0)
117+
yield; yield Delay()
118+
119+
self.assertEqual((yield dut.src.i), 1)
120+
yield sub.i.eq(0)
121+
yield Delay()
122+
self.assertEqual((yield sub.trg), 0)
123+
124+
yield dut.bus.addr.eq(addr_pending)
125+
yield dut.bus.r_stb.eq(1)
126+
yield
127+
yield dut.bus.r_stb.eq(0)
128+
yield
129+
self.assertEqual((yield dut.bus.r_data), 0b1)
130+
yield
131+
132+
yield dut.bus.addr.eq(addr_pending)
133+
yield dut.bus.w_stb.eq(1)
134+
yield dut.bus.w_data.eq(0b1)
135+
yield
136+
yield dut.bus.w_stb.eq(0)
137+
yield
138+
139+
yield dut.bus.addr.eq(addr_pending)
140+
yield dut.bus.r_stb.eq(1)
141+
yield
142+
yield dut.bus.r_stb.eq(0)
143+
yield
144+
self.assertEqual((yield dut.bus.r_data), 0b0)
145+
146+
simulation_test(dut, process)

0 commit comments

Comments
 (0)