Skip to content

Commit ec94f31

Browse files
committed
[WIP]wishbone.Connector class
Implementation of a module that allows to connect one initiator to one subordinate.bus. Currently connecting initiator bus with data width greater than the subordinate bus is not implemented. Implementation of that feature will need to instantiate multiple subordinate bus cycles for one initiator bus cycle.
1 parent 5509bd2 commit ec94f31

File tree

2 files changed

+373
-1
lines changed

2 files changed

+373
-1
lines changed

nmigen_soc/test/test_wishbone_bus.py

+255
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,261 @@ def test_set_map_wrong_addr_width(self):
123123
iface.memory_map = MemoryMap(addr_width=30, data_width=8)
124124

125125

126+
class ConnectorTestCase(unittest.TestCase):
127+
def test_wrong_intr(self):
128+
sub_bus = Interface(addr_width=10, data_width=8)
129+
with self.assertRaisesRegexp(TypeError,
130+
r"Initiator bus must be an instance of wishbone.Interface, not 'foo'"):
131+
Connector(intr_bus="foo", sub_bus=sub_bus)
132+
133+
def test_wrong_sub(self):
134+
intr_bus = Interface(addr_width=10, data_width=8)
135+
with self.assertRaisesRegexp(TypeError,
136+
r"Subordinate bus must be an instance of wishbone.Interface, not 'foo'"):
137+
Connector(intr_bus=intr_bus, sub_bus="foo")
138+
139+
def test_wrong_bitsize(self):
140+
intr_bus = Interface(addr_width=10, data_width=32)
141+
sub_bus = Interface(addr_width=10, data_width=8)
142+
with self.assertRaisesRegexp(ValueError,
143+
r"Total bit size of initiator and subordinate bus have to be the same"):
144+
Connector(intr_bus=intr_bus, sub_bus=sub_bus)
145+
146+
def test_wrong_granularity(self):
147+
intr_bus = Interface(addr_width=12, data_width=8)
148+
sub_bus = Interface(addr_width=10, data_width=32)
149+
with self.assertRaisesRegexp(ValueError,
150+
r"Granularity of subordinate bus has to be smaller or equal to "
151+
r"granulariy of initiator bus"):
152+
Connector(intr_bus=intr_bus, sub_bus=sub_bus)
153+
154+
def test_lock_mismatch(self):
155+
intr_bus = Interface(addr_width=10, data_width=8, features={"lock"})
156+
sub_bus = Interface(addr_width=10, data_width=8)
157+
with self.assertRaisesRegexp(ValueError,
158+
r"Initiator bus has optional output 'lock', but the subordinate bus "
159+
r"does not have a corresponding input"):
160+
Connector(intr_bus=intr_bus, sub_bus=sub_bus)
161+
162+
def test_err_mismatch(self):
163+
intr_bus = Interface(addr_width=10, data_width=8)
164+
sub_bus = Interface(addr_width=10, data_width=8, features={"err"})
165+
with self.assertRaisesRegexp(ValueError,
166+
r"Subordinate bus has optional output 'err', but the initiator bus "
167+
r"does not have a corresponding input"):
168+
Connector(intr_bus=intr_bus, sub_bus=sub_bus)
169+
170+
def test_rty_mismatch(self):
171+
intr_bus = Interface(addr_width=10, data_width=8)
172+
sub_bus = Interface(addr_width=10, data_width=8, features={"rty"})
173+
with self.assertRaisesRegexp(ValueError,
174+
r"Subordinate bus has optional output 'rty', but the initiator bus "
175+
r"does not have a corresponding input"):
176+
Connector(intr_bus=intr_bus, sub_bus=sub_bus)
177+
178+
def test_not_implemented_multicycle(self):
179+
intr_bus = Interface(addr_width=10, data_width=32)
180+
sub_bus = Interface(addr_width=12, data_width=8)
181+
with self.assertRaisesRegexp(NotImplementedError,
182+
r"Support for multi-cycle bus operation when initiator data_width is"
183+
r"bigger than the subordinate one is not implemented."):
184+
Connector(intr_bus=intr_bus, sub_bus=sub_bus)
185+
186+
187+
class ConnectorSimulationTestCase(unittest.TestCase):
188+
def test_same(self):
189+
intr_bus = Interface(addr_width=10, data_width=32, granularity=8)
190+
sub_bus = Interface(addr_width=10, data_width=32, granularity=8)
191+
dut = Connector(intr_bus=intr_bus, sub_bus=sub_bus)
192+
193+
def sim_test():
194+
yield intr_bus.adr.eq(1)
195+
yield intr_bus.we.eq(0)
196+
yield intr_bus.cyc.eq(1)
197+
yield intr_bus.stb.eq(1)
198+
yield intr_bus.sel.eq(5)
199+
yield Delay(1e-6)
200+
self.assertEqual((yield sub_bus.adr), 1)
201+
self.assertEqual((yield sub_bus.we), 0)
202+
self.assertEqual((yield sub_bus.cyc), 1)
203+
self.assertEqual((yield sub_bus.stb), 1)
204+
self.assertEqual((yield sub_bus.sel), 5)
205+
yield sub_bus.ack.eq(1)
206+
yield Delay(1e-6)
207+
self.assertEqual((yield intr_bus.ack), 1)
208+
yield intr_bus.adr.eq(127)
209+
yield intr_bus.we.eq(1)
210+
yield intr_bus.cyc.eq(1)
211+
yield intr_bus.stb.eq(0)
212+
yield intr_bus.sel.eq(10)
213+
yield Delay(1e-6)
214+
self.assertEqual((yield sub_bus.adr), 127)
215+
self.assertEqual((yield sub_bus.we), 1)
216+
self.assertEqual((yield sub_bus.cyc), 1)
217+
self.assertEqual((yield sub_bus.stb), 0)
218+
self.assertEqual((yield sub_bus.sel), 10)
219+
220+
with Simulator(dut, vcd_file=open("test.vcd", "w")) as sim:
221+
sim.add_process(sim_test())
222+
sim.run()
223+
224+
def test_same_pipelined(self):
225+
intr_bus = Interface(addr_width=10, data_width=8, features={"stall"})
226+
sub_bus = Interface(addr_width=10, data_width=8, features={"stall"})
227+
dut = Connector(intr_bus=intr_bus, sub_bus=sub_bus)
228+
229+
def sim_test():
230+
yield intr_bus.adr.eq(1)
231+
yield intr_bus.we.eq(0)
232+
yield intr_bus.cyc.eq(1)
233+
yield intr_bus.stb.eq(1)
234+
yield intr_bus.sel.eq(1)
235+
yield sub_bus.stall.eq(1)
236+
yield Delay(1e-6)
237+
self.assertEqual((yield sub_bus.adr), 1)
238+
self.assertEqual((yield sub_bus.we), 0)
239+
self.assertEqual((yield sub_bus.cyc), 1)
240+
self.assertEqual((yield sub_bus.stb), 1)
241+
self.assertEqual((yield sub_bus.sel), 1)
242+
self.assertEqual((yield intr_bus.stall), 1)
243+
yield sub_bus.stall.eq(0)
244+
yield Delay(1e-6)
245+
self.assertEqual((yield intr_bus.stall), 0)
246+
yield sub_bus.ack.eq(1)
247+
yield Delay(1e-6)
248+
self.assertEqual((yield intr_bus.ack), 1)
249+
250+
with Simulator(dut, vcd_file=open("test.vcd", "w")) as sim:
251+
sim.add_process(sim_test())
252+
sim.run()
253+
254+
def test_default(self):
255+
intr_bus = Interface(addr_width=10, data_width=8, features={"err", "rty"})
256+
sub_bus = Interface(addr_width=10, data_width=8, features={"lock", "cti", "bte"})
257+
dut = Connector(intr_bus=intr_bus, sub_bus=sub_bus)
258+
259+
def sim_test():
260+
yield Delay(1e-6)
261+
self.assertEqual((yield intr_bus.err), 0)
262+
self.assertEqual((yield intr_bus.rty), 0)
263+
self.assertEqual((yield sub_bus.lock), 0)
264+
self.assertEqual((yield sub_bus.cti), CycleType.CLASSIC.value)
265+
self.assertEqual((yield sub_bus.bte), BurstTypeExt.LINEAR.value)
266+
267+
with Simulator(dut, vcd_file=open("test.vcd", "w")) as sim:
268+
sim.add_process(sim_test())
269+
sim.run()
270+
271+
def test_conv_granularity(self):
272+
intr_bus = Interface(addr_width=10, data_width=32)
273+
sub_bus = Interface(addr_width=10, data_width=32, granularity=8)
274+
dut = Connector(intr_bus=intr_bus, sub_bus=sub_bus)
275+
276+
def sim_test():
277+
yield intr_bus.sel.eq(1)
278+
yield Delay(1e-6)
279+
self.assertEqual((yield sub_bus.sel), 0b1111)
280+
yield intr_bus.sel.eq(0)
281+
yield Delay(1e-6)
282+
self.assertEqual((yield sub_bus.sel), 0b0000)
283+
284+
with Simulator(dut, vcd_file=open("test.vcd", "w")) as sim:
285+
sim.add_process(sim_test())
286+
sim.run()
287+
288+
def test_conv_addr_width(self):
289+
intr_bus = Interface(addr_width=12, data_width=8)
290+
sub_bus = Interface(addr_width=10, data_width=32, granularity=8)
291+
dut = Connector(intr_bus=intr_bus, sub_bus=sub_bus)
292+
293+
def sim_test():
294+
yield intr_bus.adr.eq(1)
295+
yield intr_bus.sel.eq(1)
296+
yield intr_bus.dat_w.eq(0xA5)
297+
yield sub_bus.dat_r.eq(0x03020100)
298+
yield Delay(1e-6)
299+
self.assertEqual((yield sub_bus.sel), 0b0010)
300+
self.assertEqual((yield sub_bus.dat_w), 0xA5A5A5A5)
301+
self.assertEqual((yield intr_bus.dat_r), 0x01)
302+
303+
with Simulator(dut, vcd_file=open("test.vcd", "w")) as sim:
304+
sim.add_process(sim_test())
305+
sim.run()
306+
307+
def test_conv_granularity_addr_width(self):
308+
intr_bus = Interface(addr_width=11, data_width=16)
309+
sub_bus = Interface(addr_width=10, data_width=32, granularity=8)
310+
dut = Connector(intr_bus=intr_bus, sub_bus=sub_bus)
311+
312+
def sim_test():
313+
yield intr_bus.adr.eq(3)
314+
yield intr_bus.sel.eq(1)
315+
yield intr_bus.dat_w.eq(0xA55A)
316+
yield sub_bus.dat_r.eq(0x03020100)
317+
yield Delay(1e-6)
318+
self.assertEqual((yield sub_bus.sel), 0b1100)
319+
self.assertEqual((yield sub_bus.dat_w), 0xA55AA55A)
320+
self.assertEqual((yield intr_bus.dat_r), 0x0302)
321+
322+
with Simulator(dut, vcd_file=open("test.vcd", "w")) as sim:
323+
sim.add_process(sim_test())
324+
sim.run()
325+
326+
def test_pipelined_initiator(self):
327+
intr_bus = Interface(addr_width=10, data_width=8, features={"stall"})
328+
sub_bus = Interface(addr_width=10, data_width=8)
329+
dut = Connector(intr_bus=intr_bus, sub_bus=sub_bus)
330+
331+
def sim_test():
332+
yield intr_bus.adr.eq(1)
333+
yield intr_bus.sel.eq(1)
334+
yield intr_bus.cyc.eq(1)
335+
yield intr_bus.stb.eq(1)
336+
yield Delay(1e-7)
337+
self.assertEqual((yield sub_bus.cyc), 1)
338+
self.assertEqual((yield sub_bus.stb), 1)
339+
self.assertEqual((yield intr_bus.ack), 0)
340+
self.assertEqual((yield intr_bus.stall), 1)
341+
yield Delay(1e-7)
342+
yield sub_bus.ack.eq(1)
343+
yield Delay(1e-7)
344+
self.assertEqual((yield intr_bus.stall), 0)
345+
346+
with Simulator(dut, vcd_file=open("test.vcd", "w")) as sim:
347+
sim.add_process(sim_test())
348+
sim.run()
349+
350+
def test_pipelined_subordinate(self):
351+
intr_bus = Interface(addr_width=10, data_width=8)
352+
sub_bus = Interface(addr_width=10, data_width=8, features={"stall"})
353+
dut = Connector(intr_bus=intr_bus, sub_bus=sub_bus)
354+
355+
def sim_test():
356+
yield intr_bus.adr.eq(1)
357+
yield intr_bus.sel.eq(1)
358+
yield intr_bus.cyc.eq(1)
359+
yield intr_bus.stb.eq(1)
360+
yield Delay(1e-8)
361+
self.assertEqual((yield sub_bus.cyc), 1)
362+
self.assertEqual((yield sub_bus.stb), 1)
363+
self.assertEqual((yield intr_bus.ack), 0)
364+
yield
365+
yield sub_bus.ack.eq(1)
366+
yield Delay(1e-8)
367+
self.assertEqual((yield intr_bus.ack), 1)
368+
yield intr_bus.stb.eq(0)
369+
yield
370+
self.assertEqual((yield intr_bus.ack), 1)
371+
yield sub_bus.ack.eq(0)
372+
yield
373+
self.assertEqual((yield intr_bus.ack), 0)
374+
375+
with Simulator(dut, vcd_file=open("test_debug.vcd", "w")) as sim:
376+
sim.add_clock(1e-6)
377+
sim.add_sync_process(sim_test())
378+
sim.run()
379+
380+
126381
class DecoderTestCase(unittest.TestCase):
127382
def setUp(self):
128383
self.dut = Decoder(addr_width=31, data_width=32, granularity=16)

nmigen_soc/wishbone/bus.py

+118-1
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from enum import Enum
22
from nmigen import *
3+
from nmigen.lib import coding
34
from nmigen.hdl.rec import Direction
45
from nmigen.utils import log2_int
56

67
from ..memory import MemoryMap
78

89

9-
__all__ = ["CycleType", "BurstTypeExt", "Interface", "Decoder", "Arbiter"]
10+
__all__ = ["CycleType", "BurstTypeExt", "Interface", "Connector", "Decoder", "Arbiter"]
1011

1112

1213
class CycleType(Enum):
@@ -166,6 +167,122 @@ def memory_map(self, memory_map):
166167
self._map = memory_map
167168

168169

170+
class Connector(Elaboratable):
171+
"""Module to connect one Wishbone initiator bus to one Wishbone subordinate bus
172+
173+
Ths class also handles data_width conversion if the two buses have compatible
174+
granularity. This means that granularity of subordinate bus has to be smaller or
175+
equal to the granularity of the initiator bus.
176+
Currently initiating multiple subordinate bus cycles for initiator bus cycle is
177+
not implement. As a consequence an initiator bus data width bigger than the
178+
subordinate bus data width is not supported.
179+
180+
Parameters:
181+
-----------
182+
intr_bus : :class:`Interface`
183+
The initiator bus
184+
sub_bus : :class:`Interface`
185+
The subordinate bus
186+
"""
187+
def __init__(self, *, intr_bus, sub_bus):
188+
if not isinstance(intr_bus, Interface):
189+
raise TypeError("Initiator bus must be an instance of wishbone.Interface, not {!r}"
190+
.format(intr_bus))
191+
if not isinstance(sub_bus, Interface):
192+
raise TypeError("Subordinate bus must be an instance of wishbone.Interface, not {!r}"
193+
.format(sub_bus))
194+
intr_size = (2**intr_bus.addr_width)*intr_bus.data_width
195+
sub_size = (2**sub_bus.addr_width)*sub_bus.data_width
196+
if intr_size != sub_size:
197+
raise ValueError("Total bit size of initiator and subordinate bus have to be the same")
198+
if sub_bus.granularity > intr_bus.granularity:
199+
raise ValueError(
200+
"Granularity of subordinate bus has to be smaller or equal to "
201+
"granulariy of initiator bus"
202+
)
203+
for opt_output in {"lock"}:
204+
if hasattr(intr_bus, opt_output) and not hasattr(sub_bus, opt_output):
205+
raise ValueError("Initiator bus has optional output {!r}, but the subordinate bus "
206+
"does not have a corresponding input"
207+
.format(opt_output))
208+
for opt_output in {"err", "rty"}:
209+
if hasattr(sub_bus, opt_output) and not hasattr(intr_bus, opt_output):
210+
raise ValueError("Subordinate bus has optional output {!r}, but the initiator bus "
211+
"does not have a corresponding input"
212+
.format(opt_output))
213+
if intr_bus.data_width > sub_bus.data_width:
214+
raise NotImplementedError(
215+
"Support for multi-cycle bus operation when initiator data_width is"
216+
"bigger than the subordinate one is not implemented."
217+
)
218+
219+
self.intr_bus = intr_bus
220+
self.sub_bus = sub_bus
221+
222+
def elaborate(self, platform):
223+
m = Module()
224+
225+
common_addr_width = min(self.intr_bus.addr_width, self.sub_bus.addr_width)
226+
m.d.comb += [
227+
self.sub_bus.cyc.eq(self.intr_bus.cyc),
228+
self.sub_bus.we.eq(self.intr_bus.we),
229+
self.sub_bus.adr[(self.sub_bus.addr_width-common_addr_width):self.sub_bus.addr_width].eq(
230+
self.intr_bus.adr[(self.intr_bus.addr_width-common_addr_width):self.intr_bus.addr_width]
231+
),
232+
self.intr_bus.ack.eq(self.sub_bus.ack),
233+
]
234+
if hasattr(self.intr_bus, "err"):
235+
m.d.comb += self.intr_bus.err.eq(getattr(self.sub_bus, "err", 0))
236+
if hasattr(self.intr_bus, "rty"):
237+
m.d.comb += self.intr_bus.rty.eq(getattr(self.sub_bus, "rty", 0))
238+
if hasattr(self.sub_bus, "lock"):
239+
m.d.comb += self.sub_bus.lock.eq(getattr(self.intr_bus, "lock", 0))
240+
if hasattr(self.sub_bus, "cti"):
241+
m.d.comb += self.sub_bus.cti.eq(getattr(self.intr_bus, "cti", CycleType.CLASSIC))
242+
if hasattr(self.sub_bus, "bte"):
243+
m.d.comb += self.sub_bus.bte.eq(getattr(self.intr_bus, "bte", BurstTypeExt.LINEAR))
244+
245+
# stb and stall for different pipeline combinations of initiator and subordinate bus
246+
if hasattr(self.intr_bus, "stall") == hasattr(self.sub_bus, "stall"):
247+
m.d.comb += self.sub_bus.stb.eq(self.intr_bus.stb)
248+
if hasattr(self.intr_bus, "stall"):
249+
m.d.comb += self.intr_bus.stall.eq(self.sub_bus.stall)
250+
elif hasattr(self.intr_bus, "stall"):
251+
# See Wishbone B4 spec: 5.2 Pipelined master connected to standard slave
252+
m.d.comb += [
253+
self.sub_bus.stb.eq(self.intr_bus.stb),
254+
self.intr_bus.stall.eq(self.intr_bus.cyc & ~self.sub_bus.ack),
255+
]
256+
else:
257+
# See Wishbone B4 spec: 5.1 Standard master connected to pipelined slave
258+
wait4ack = Signal()
259+
m.d.sync += wait4ack.eq(self.intr_bus.stb & ~(wait4ack & self.sub_bus.ack))
260+
m.d.comb += self.sub_bus.stb.eq(self.intr_bus.stb & ~wait4ack)
261+
262+
# Data and sel width conversion
263+
m.submodules.sel_decoder = sel_decoder = coding.Decoder(width=1<<(self.intr_bus.addr_width-self.sub_bus.addr_width))
264+
sel_from_granularity = Const(-1, self.intr_bus.granularity//self.sub_bus.granularity)
265+
m.d.comb += [
266+
sel_decoder.i.eq(self.intr_bus.adr[:self.intr_bus.addr_width-self.sub_bus.addr_width]),
267+
self.sub_bus.dat_w.eq(Repl(self.intr_bus.dat_w, self.sub_bus.data_width//self.intr_bus.data_width)),
268+
self.intr_bus.dat_r.eq(self.sub_bus.dat_r.word_select(sel_decoder.i, self.intr_bus.data_width)),
269+
self.sub_bus.sel.eq(
270+
Cat(
271+
Cat(
272+
Cat(
273+
sel_a & sel_i & sel_g
274+
for sel_g in sel_from_granularity
275+
)
276+
for sel_i in self.intr_bus.sel
277+
)
278+
for sel_a in sel_decoder.o
279+
)
280+
),
281+
]
282+
283+
return m
284+
285+
169286
class Decoder(Elaboratable):
170287
"""Wishbone bus decoder.
171288

0 commit comments

Comments
 (0)