Skip to content

Commit 4f59a0b

Browse files
committed
csr.wishbone: add WishboneCSRBridge.
1 parent d52d0b9 commit 4f59a0b

File tree

3 files changed

+308
-2
lines changed

3 files changed

+308
-2
lines changed

nmigen_soc/csr/bus.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ class Multiplexer(Elaboratable):
201201
CSR bus providing access to registers.
202202
"""
203203
def __init__(self, *, addr_width, data_width, alignment=0):
204-
self.bus = Interface(addr_width=addr_width, data_width=data_width, alignment=alignment)
204+
self.bus = Interface(addr_width=addr_width, data_width=data_width, alignment=alignment,
205+
name="csr")
205206
self._map = self.bus.memory_map
206207

207208
def align_to(self, alignment):
@@ -305,7 +306,8 @@ class Decoder(Elaboratable):
305306
CSR bus providing access to subordinate buses.
306307
"""
307308
def __init__(self, *, addr_width, data_width, alignment=0):
308-
self.bus = Interface(addr_width=addr_width, data_width=data_width, alignment=alignment)
309+
self.bus = Interface(addr_width=addr_width, data_width=data_width, alignment=alignment,
310+
name="csr")
309311
self._map = self.bus.memory_map
310312
self._subs = dict()
311313

nmigen_soc/csr/wishbone.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from nmigen import *
2+
from nmigen.utils import log2_int
3+
4+
from . import Interface as CSRInterface
5+
from ..wishbone import Interface as WishboneInterface
6+
7+
8+
__all__ = ["WishboneCSRBridge"]
9+
10+
11+
class WishboneCSRBridge(Elaboratable):
12+
"""Wishbone to CSR bridge.
13+
14+
A bus bridge for accessing CSR registers from Wishbone. This bridge supports any Wishbone
15+
data width greater or equal to CSR data width and performs appropriate address translation.
16+
17+
Latency
18+
-------
19+
20+
Reads and writes always take ``self.data_width // csr_bus.data_width + 1`` cycles to complete,
21+
regardless of the select inputs. Write side effects occur simultaneously with acknowledgement.
22+
23+
Parameters
24+
----------
25+
csr_bus : :class:`..csr.Interface`
26+
CSR bus driven by the bridge.
27+
data_width : int or None
28+
Wishbone bus data width. If not specified, defaults to ``csr_bus.data_width``.
29+
30+
Attributes
31+
----------
32+
wb_bus : :class:`..wishbone.Interface`
33+
Wishbone bus provided by the bridge.
34+
"""
35+
def __init__(self, csr_bus, *, data_width=None):
36+
if not isinstance(csr_bus, CSRInterface):
37+
raise ValueError("CSR bus must be an instance of CSRInterface, not {!r}"
38+
.format(csr_bus))
39+
if csr_bus.data_width not in (8, 16, 32, 64):
40+
raise ValueError("CSR bus data width must be one of 8, 16, 32, 64, not {!r}"
41+
.format(csr_bus.data_width))
42+
if data_width is None:
43+
data_width = csr_bus.data_width
44+
45+
self.csr_bus = csr_bus
46+
self.wb_bus = WishboneInterface(
47+
addr_width=max(0, csr_bus.addr_width - log2_int(data_width // csr_bus.data_width)),
48+
data_width=data_width,
49+
granularity=csr_bus.data_width,
50+
name="wb")
51+
52+
# Since granularity of the Wishbone interface matches the data width of the CSR bus,
53+
# no width conversion is performed, even if the Wishbone data width is greater.
54+
self.wb_bus.memory_map.add_window(self.csr_bus.memory_map)
55+
56+
def elaborate(self, platform):
57+
csr_bus = self.csr_bus
58+
wb_bus = self.wb_bus
59+
60+
m = Module()
61+
62+
cycle = Signal(range(len(wb_bus.sel) + 1))
63+
m.d.comb += csr_bus.addr.eq(Cat(cycle[:log2_int(len(wb_bus.sel))], wb_bus.adr))
64+
65+
with m.If(wb_bus.cyc & wb_bus.stb):
66+
with m.Switch(cycle):
67+
def segment(index):
68+
return slice(index * wb_bus.granularity, (index + 1) * wb_bus.granularity)
69+
70+
for index, sel_index in enumerate(wb_bus.sel):
71+
with m.Case(index):
72+
if index > 0:
73+
# CSR reads are registered, and we need to re-register them.
74+
m.d.sync += wb_bus.dat_r[segment(index - 1)].eq(csr_bus.r_data)
75+
m.d.comb += csr_bus.r_stb.eq(sel_index & ~wb_bus.we)
76+
m.d.comb += csr_bus.w_data.eq(wb_bus.dat_w[segment(index)])
77+
m.d.comb += csr_bus.w_stb.eq(sel_index & wb_bus.we)
78+
m.d.sync += cycle.eq(index + 1)
79+
80+
with m.Default():
81+
m.d.sync += wb_bus.dat_r[segment(index)].eq(csr_bus.r_data)
82+
m.d.sync += wb_bus.ack.eq(1)
83+
84+
with m.Else():
85+
m.d.sync += cycle.eq(0)
86+
m.d.sync += wb_bus.ack.eq(0)
87+
88+
return m
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
import unittest
2+
from nmigen import *
3+
from nmigen.back.pysim import *
4+
5+
from .. import csr
6+
from ..csr.wishbone import *
7+
8+
9+
class MockRegister(Elaboratable):
10+
def __init__(self, width):
11+
self.element = csr.Element(width, "rw")
12+
self.r_count = Signal(8)
13+
self.w_count = Signal(8)
14+
self.data = Signal(width)
15+
16+
def elaborate(self, platform):
17+
m = Module()
18+
19+
with m.If(self.element.r_stb):
20+
m.d.sync += self.r_count.eq(self.r_count + 1)
21+
m.d.comb += self.element.r_data.eq(self.data)
22+
23+
with m.If(self.element.w_stb):
24+
m.d.sync += self.w_count.eq(self.w_count + 1)
25+
m.d.sync += self.data.eq(self.element.w_data)
26+
27+
return m
28+
29+
30+
class WishboneCSRBridgeTestCase(unittest.TestCase):
31+
def test_wrong_csr_bus(self):
32+
with self.assertRaisesRegex(ValueError,
33+
r"CSR bus must be an instance of CSRInterface, not 'foo'"):
34+
WishboneCSRBridge(csr_bus="foo")
35+
36+
def test_wrong_csr_bus_data_width(self):
37+
with self.assertRaisesRegex(ValueError,
38+
r"CSR bus data width must be one of 8, 16, 32, 64, not 7"):
39+
WishboneCSRBridge(csr_bus=csr.Interface(addr_width=10, data_width=7))
40+
41+
def test_narrow(self):
42+
mux = csr.Multiplexer(addr_width=10, data_width=8)
43+
reg_1 = MockRegister(8)
44+
mux.add(reg_1.element)
45+
reg_2 = MockRegister(16)
46+
mux.add(reg_2.element)
47+
dut = WishboneCSRBridge(mux.bus)
48+
49+
def sim_test():
50+
yield dut.wb_bus.cyc.eq(1)
51+
yield dut.wb_bus.sel.eq(0b1)
52+
53+
yield dut.wb_bus.we.eq(1)
54+
55+
yield dut.wb_bus.adr.eq(0)
56+
yield dut.wb_bus.stb.eq(1)
57+
yield dut.wb_bus.dat_w.eq(0x55)
58+
yield
59+
yield
60+
yield dut.wb_bus.stb.eq(0)
61+
yield
62+
self.assertEqual((yield dut.wb_bus.ack), 1)
63+
self.assertEqual((yield reg_1.r_count), 0)
64+
self.assertEqual((yield reg_1.w_count), 1)
65+
self.assertEqual((yield reg_1.data), 0x55)
66+
67+
yield dut.wb_bus.adr.eq(1)
68+
yield dut.wb_bus.stb.eq(1)
69+
yield dut.wb_bus.dat_w.eq(0xaa)
70+
yield
71+
yield
72+
yield dut.wb_bus.stb.eq(0)
73+
yield
74+
self.assertEqual((yield dut.wb_bus.ack), 1)
75+
self.assertEqual((yield reg_2.r_count), 0)
76+
self.assertEqual((yield reg_2.w_count), 0)
77+
self.assertEqual((yield reg_2.data), 0)
78+
79+
yield dut.wb_bus.adr.eq(2)
80+
yield dut.wb_bus.stb.eq(1)
81+
yield dut.wb_bus.dat_w.eq(0xbb)
82+
yield
83+
yield
84+
yield dut.wb_bus.stb.eq(0)
85+
yield
86+
self.assertEqual((yield dut.wb_bus.ack), 1)
87+
self.assertEqual((yield reg_2.r_count), 0)
88+
self.assertEqual((yield reg_2.w_count), 1)
89+
self.assertEqual((yield reg_2.data), 0xbbaa)
90+
91+
yield dut.wb_bus.we.eq(0)
92+
93+
yield dut.wb_bus.adr.eq(0)
94+
yield dut.wb_bus.stb.eq(1)
95+
yield
96+
yield
97+
yield dut.wb_bus.stb.eq(0)
98+
yield
99+
self.assertEqual((yield dut.wb_bus.ack), 1)
100+
self.assertEqual((yield dut.wb_bus.dat_r), 0x55)
101+
self.assertEqual((yield reg_1.r_count), 1)
102+
self.assertEqual((yield reg_1.w_count), 1)
103+
104+
yield dut.wb_bus.adr.eq(1)
105+
yield dut.wb_bus.stb.eq(1)
106+
yield
107+
yield
108+
yield dut.wb_bus.stb.eq(0)
109+
yield
110+
self.assertEqual((yield dut.wb_bus.ack), 1)
111+
self.assertEqual((yield dut.wb_bus.dat_r), 0xaa)
112+
self.assertEqual((yield reg_2.r_count), 1)
113+
self.assertEqual((yield reg_2.w_count), 1)
114+
115+
yield reg_2.data.eq(0x33333)
116+
117+
yield dut.wb_bus.adr.eq(2)
118+
yield dut.wb_bus.stb.eq(1)
119+
yield
120+
yield
121+
yield dut.wb_bus.stb.eq(0)
122+
yield
123+
self.assertEqual((yield dut.wb_bus.ack), 1)
124+
self.assertEqual((yield dut.wb_bus.dat_r), 0xbb)
125+
self.assertEqual((yield reg_2.r_count), 1)
126+
self.assertEqual((yield reg_2.w_count), 1)
127+
128+
m = Module()
129+
m.submodules += mux, reg_1, reg_2, dut
130+
with Simulator(m, vcd_file=open("test.vcd", "w")) as sim:
131+
sim.add_clock(1e-6)
132+
sim.add_sync_process(sim_test())
133+
sim.run()
134+
135+
def test_wide(self):
136+
mux = csr.Multiplexer(addr_width=10, data_width=8)
137+
reg = MockRegister(32)
138+
mux.add(reg.element)
139+
dut = WishboneCSRBridge(mux.bus, data_width=32)
140+
141+
def sim_test():
142+
yield dut.wb_bus.cyc.eq(1)
143+
yield dut.wb_bus.adr.eq(0)
144+
145+
yield dut.wb_bus.we.eq(1)
146+
147+
yield dut.wb_bus.dat_w.eq(0x44332211)
148+
yield dut.wb_bus.sel.eq(0b1111)
149+
yield dut.wb_bus.stb.eq(1)
150+
yield
151+
yield
152+
yield
153+
yield
154+
yield
155+
yield dut.wb_bus.stb.eq(0)
156+
yield
157+
self.assertEqual((yield dut.wb_bus.ack), 1)
158+
self.assertEqual((yield reg.r_count), 0)
159+
self.assertEqual((yield reg.w_count), 1)
160+
self.assertEqual((yield reg.data), 0x44332211)
161+
162+
# partial write
163+
yield dut.wb_bus.dat_w.eq(0xaabbccdd)
164+
yield dut.wb_bus.sel.eq(0b0110)
165+
yield dut.wb_bus.stb.eq(1)
166+
yield
167+
yield
168+
yield
169+
yield
170+
yield
171+
yield dut.wb_bus.stb.eq(0)
172+
yield
173+
self.assertEqual((yield dut.wb_bus.ack), 1)
174+
self.assertEqual((yield reg.r_count), 0)
175+
self.assertEqual((yield reg.w_count), 1)
176+
self.assertEqual((yield reg.data), 0x44332211)
177+
178+
yield dut.wb_bus.we.eq(0)
179+
180+
yield dut.wb_bus.sel.eq(0b1111)
181+
yield dut.wb_bus.stb.eq(1)
182+
yield
183+
yield
184+
yield
185+
yield
186+
yield
187+
yield dut.wb_bus.stb.eq(0)
188+
yield
189+
self.assertEqual((yield dut.wb_bus.ack), 1)
190+
self.assertEqual((yield dut.wb_bus.dat_r), 0x44332211)
191+
self.assertEqual((yield reg.r_count), 1)
192+
self.assertEqual((yield reg.w_count), 1)
193+
194+
yield reg.data.eq(0xaaaaaaaa)
195+
196+
# partial read
197+
yield dut.wb_bus.sel.eq(0b0110)
198+
yield dut.wb_bus.stb.eq(1)
199+
yield
200+
yield
201+
yield
202+
yield
203+
yield
204+
yield dut.wb_bus.stb.eq(0)
205+
yield
206+
self.assertEqual((yield dut.wb_bus.ack), 1)
207+
self.assertEqual((yield dut.wb_bus.dat_r), 0x00332200)
208+
self.assertEqual((yield reg.r_count), 1)
209+
self.assertEqual((yield reg.w_count), 1)
210+
211+
m = Module()
212+
m.submodules += mux, reg, dut
213+
with Simulator(m, vcd_file=open("test.vcd", "w")) as sim:
214+
sim.add_clock(1e-6)
215+
sim.add_sync_process(sim_test())
216+
sim.run()

0 commit comments

Comments
 (0)