Skip to content

Commit 3195de9

Browse files
committed
wishbone.bus: add Decoder.
1 parent 921d740 commit 3195de9

File tree

8 files changed

+371
-27
lines changed

8 files changed

+371
-27
lines changed

nmigen_soc/csr/bus.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ def add(self, sub_bus, *, addr=None):
328328
.format(sub_bus))
329329
if sub_bus.data_width != self.bus.data_width:
330330
raise ValueError("Subordinate bus has data width {}, which is not the same as "
331-
"multiplexer data width {}"
331+
"decoder data width {}"
332332
.format(sub_bus.data_width, self.bus.data_width))
333333
self._subs[sub_bus.memory_map] = sub_bus
334334
return self._map.add_window(sub_bus.memory_map, addr=addr)
@@ -340,7 +340,9 @@ def elaborate(self, platform):
340340
r_data_fanin = 0
341341

342342
with m.Switch(self.bus.addr):
343-
for sub_map, sub_pat in self._map.window_patterns():
343+
for sub_map, (sub_pat, sub_ratio) in self._map.window_patterns():
344+
assert sub_ratio == 1
345+
344346
sub_bus = self._subs[sub_map]
345347
m.d.comb += sub_bus.addr.eq(self.bus.addr[:sub_bus.addr_width])
346348

nmigen_soc/csr/wishbone.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,10 @@ def segment(index):
8080
with m.Default():
8181
m.d.sync += wb_bus.dat_r[segment(index)].eq(csr_bus.r_data)
8282
m.d.sync += wb_bus.ack.eq(1)
83+
m.d.sync += cycle.eq(0)
8384

8485
with m.Else():
85-
m.d.sync += cycle.eq(0)
8686
m.d.sync += wb_bus.ack.eq(0)
87+
m.d.sync += cycle.eq(0)
8788

8889
return m

nmigen_soc/memory.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -328,15 +328,18 @@ def window_patterns(self):
328328
329329
Yield values
330330
------------
331-
A tuple ``window, pattern`` describing the address range assigned to the window.
331+
A tuple ``window, (pattern, ratio)`` describing the address range assigned to the window.
332332
``pattern`` is a ``self.addr_width`` wide pattern that may be used in ``Case`` or ``match``
333-
to determine if an address signal is within the address range of ``window``.
333+
to determine if an address signal is within the address range of ``window``. When bridging
334+
buses of unequal data width, ``ratio`` is the amount of contiguous addresses on
335+
the narrower bus that are accessed for each transaction on the wider bus. Otherwise,
336+
it is always 1.
334337
"""
335338
for window, window_range in self._windows.items():
336339
pattern = "{:0{}b}{}".format(window_range.start >> window.addr_width,
337340
self.addr_width - window.addr_width,
338341
"-" * window.addr_width)
339-
yield window, pattern
342+
yield window, (pattern, window_range.step)
340343

341344
@staticmethod
342345
def _translate(start, end, width, window, window_range):

nmigen_soc/test/test_csr_bus.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# nmigen: UnusedElaboratable=no
2+
13
import unittest
24
from nmigen import *
35
from nmigen.hdl.rec import Layout
@@ -85,7 +87,6 @@ def test_wrong_data_width(self):
8587
class MultiplexerTestCase(unittest.TestCase):
8688
def setUp(self):
8789
self.dut = Multiplexer(addr_width=16, data_width=8)
88-
Fragment.get(self.dut, platform=None) # silence UnusedElaboratable
8990

9091
def test_add_4b(self):
9192
self.assertEqual(self.dut.add(Element(4, "rw")),
@@ -201,7 +202,6 @@ def sim_test():
201202
class MultiplexerAlignedTestCase(unittest.TestCase):
202203
def setUp(self):
203204
self.dut = Multiplexer(addr_width=16, data_width=8, alignment=2)
204-
Fragment.get(self.dut, platform=None) # silence UnusedElaboratable
205205

206206
def test_add_two(self):
207207
self.assertEqual(self.dut.add(Element(8, "rw")),
@@ -261,7 +261,6 @@ def sim_test():
261261
class DecoderTestCase(unittest.TestCase):
262262
def setUp(self):
263263
self.dut = Decoder(addr_width=16, data_width=8)
264-
Fragment.get(self.dut, platform=None) # silence UnusedElaboratable
265264

266265
def test_align_to(self):
267266
self.assertEqual(self.dut.add(Interface(addr_width=10, data_width=8)),
@@ -281,7 +280,7 @@ def test_add_wrong_data_width(self):
281280

282281
with self.assertRaisesRegex(ValueError,
283282
r"Subordinate bus has data width 16, which is not the same as "
284-
r"multiplexer data width 8"):
283+
r"decoder data width 8"):
285284
self.dut.add(mux.bus)
286285

287286
def test_sim(self):

nmigen_soc/test/test_csr_wishbone.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# nmigen: UnusedElaboratable=no
2+
13
import unittest
24
from nmigen import *
35
from nmigen.back.pysim import *

nmigen_soc/test/test_memory.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,8 @@ def test_iter_window_patterns(self):
216216
window_2 = MemoryMap(addr_width=12, data_width=16)
217217
memory_map.add_window(window_2)
218218
self.assertEqual(list(memory_map.window_patterns()), [
219-
(window_1, "000000----------"),
220-
(window_2, "0001------------"),
219+
(window_1, ("000000----------", 2)),
220+
(window_2, ("0001------------", 1)),
221221
])
222222

223223
def test_align_to(self):

nmigen_soc/test/test_wishbone_bus.py

Lines changed: 214 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
# nmigen: UnusedElaboratable=no
2+
13
import unittest
24
from nmigen import *
35
from nmigen.hdl.rec import *
6+
from nmigen.back.pysim import *
47

58
from ..wishbone.bus import *
69

@@ -42,9 +45,9 @@ def test_granularity(self):
4245
("ack", 1, DIR_FANIN),
4346
]))
4447

45-
def test_optional(self):
48+
def test_features(self):
4649
iface = Interface(addr_width=32, data_width=32,
47-
optional={"rty", "err", "stall", "lock", "cti", "bte"})
50+
features={"rty", "err", "stall", "lock", "cti", "bte"})
4851
self.assertEqual(iface.layout, Layout.cast([
4952
("adr", 32, DIR_FANOUT),
5053
("dat_w", 32, DIR_FANOUT),
@@ -82,7 +85,214 @@ def test_wrong_granularity_wide(self):
8285
r"Granularity 32 may not be greater than data width 8"):
8386
Interface(addr_width=0, data_width=8, granularity=32)
8487

85-
def test_wrong_optional(self):
88+
def test_wrong_features(self):
8689
with self.assertRaisesRegex(ValueError,
8790
r"Optional signal\(s\) 'foo' are not supported"):
88-
Interface(addr_width=0, data_width=8, optional={"foo"})
91+
Interface(addr_width=0, data_width=8, features={"foo"})
92+
93+
94+
class DecoderTestCase(unittest.TestCase):
95+
def setUp(self):
96+
self.dut = Decoder(addr_width=31, data_width=32, granularity=16)
97+
98+
def test_add_align_to(self):
99+
sub_1 = Interface(addr_width=15, data_width=32, granularity=16)
100+
sub_2 = Interface(addr_width=15, data_width=32, granularity=16)
101+
self.assertEqual(self.dut.add(sub_1), (0x00000000, 0x00010000, 1))
102+
self.assertEqual(self.dut.align_to(18), 0x000040000)
103+
self.assertEqual(self.dut.add(sub_2), (0x00040000, 0x00050000, 1))
104+
105+
def test_add_wrong(self):
106+
with self.assertRaisesRegex(TypeError,
107+
r"Subordinate bus must be an instance of wishbone\.Interface, not 'foo'"):
108+
self.dut.add("foo")
109+
110+
def test_add_wrong_granularity(self):
111+
with self.assertRaisesRegex(ValueError,
112+
r"Subordinate bus has granularity 32, which is greater than "
113+
r"the decoder granularity 16"):
114+
self.dut.add(Interface(addr_width=15, data_width=32, granularity=32))
115+
116+
def test_add_wrong_width_dense(self):
117+
with self.assertRaisesRegex(ValueError,
118+
r"Subordinate bus has data width 16, which is not the same as decoder "
119+
r"data width 32 \(required for dense address translation\)"):
120+
self.dut.add(Interface(addr_width=15, data_width=16, granularity=16))
121+
122+
def test_add_wrong_granularity_sparse(self):
123+
with self.assertRaisesRegex(ValueError,
124+
r"Subordinate bus has data width 64, which is not the same as subordinate "
125+
r"bus granularity 16 \(required for sparse address translation\)"):
126+
self.dut.add(Interface(addr_width=15, data_width=64, granularity=16), sparse=True)
127+
128+
def test_add_wrong_optional_output(self):
129+
with self.assertRaisesRegex(ValueError,
130+
r"Subordinate bus has optional output 'err', but the decoder does "
131+
r"not have a corresponding input"):
132+
self.dut.add(Interface(addr_width=15, data_width=32, granularity=16, features={"err"}))
133+
134+
135+
class DecoderSimulationTestCase(unittest.TestCase):
136+
def test_simple(self):
137+
dut = Decoder(addr_width=30, data_width=32, granularity=8,
138+
features={"err", "rty", "stall", "lock", "cti", "bte"})
139+
sub_1 = Interface(addr_width=14, data_width=32, granularity=8)
140+
dut.add(sub_1, addr=0x10000)
141+
sub_2 = Interface(addr_width=14, data_width=32, granularity=8,
142+
features={"err", "rty", "stall", "lock", "cti", "bte"})
143+
dut.add(sub_2)
144+
145+
def sim_test():
146+
yield dut.bus.adr.eq(0x10400 >> 2)
147+
yield dut.bus.cyc.eq(1)
148+
yield dut.bus.stb.eq(1)
149+
yield dut.bus.sel.eq(0b11)
150+
yield dut.bus.dat_w.eq(0x12345678)
151+
yield dut.bus.lock.eq(1)
152+
yield dut.bus.cti.eq(CycleType.INCR_BURST)
153+
yield dut.bus.bte.eq(BurstTypeExt.WRAP_4)
154+
yield sub_1.ack.eq(1)
155+
yield sub_1.dat_r.eq(0xabcdef01)
156+
yield sub_2.dat_r.eq(0x5678abcd)
157+
yield Delay(1e-6)
158+
self.assertEqual((yield sub_1.adr), 0x400 >> 2)
159+
self.assertEqual((yield sub_1.cyc), 1)
160+
self.assertEqual((yield sub_2.cyc), 0)
161+
self.assertEqual((yield sub_1.stb), 1)
162+
self.assertEqual((yield sub_1.sel), 0b11)
163+
self.assertEqual((yield sub_1.dat_w), 0x12345678)
164+
self.assertEqual((yield dut.bus.ack), 1)
165+
self.assertEqual((yield dut.bus.err), 0)
166+
self.assertEqual((yield dut.bus.rty), 0)
167+
self.assertEqual((yield dut.bus.dat_r), 0xabcdef01)
168+
169+
yield dut.bus.adr.eq(0x20400 >> 2)
170+
yield sub_1.ack.eq(0)
171+
yield sub_2.err.eq(1)
172+
yield sub_2.rty.eq(1)
173+
yield sub_2.stall.eq(1)
174+
yield Delay(1e-6)
175+
self.assertEqual((yield sub_2.adr), 0x400 >> 2)
176+
self.assertEqual((yield sub_1.cyc), 0)
177+
self.assertEqual((yield sub_2.cyc), 1)
178+
self.assertEqual((yield sub_1.stb), 1)
179+
self.assertEqual((yield sub_1.sel), 0b11)
180+
self.assertEqual((yield sub_1.dat_w), 0x12345678)
181+
self.assertEqual((yield sub_2.lock), 1)
182+
self.assertEqual((yield sub_2.cti), CycleType.INCR_BURST.value)
183+
self.assertEqual((yield sub_2.bte), BurstTypeExt.WRAP_4.value)
184+
self.assertEqual((yield dut.bus.ack), 0)
185+
self.assertEqual((yield dut.bus.err), 1)
186+
self.assertEqual((yield dut.bus.rty), 1)
187+
self.assertEqual((yield dut.bus.stall), 1)
188+
self.assertEqual((yield dut.bus.dat_r), 0x5678abcd)
189+
190+
with Simulator(dut, vcd_file=open("test.vcd", "w")) as sim:
191+
sim.add_process(sim_test())
192+
sim.run()
193+
194+
def test_addr_translate(self):
195+
class AddressLoopback(Elaboratable):
196+
def __init__(self, **kwargs):
197+
self.bus = Interface(**kwargs)
198+
199+
def elaborate(self, platform):
200+
m = Module()
201+
202+
for index, sel_bit in enumerate(self.bus.sel):
203+
with m.If(sel_bit):
204+
segment = self.bus.dat_r.word_select(index, self.bus.granularity)
205+
m.d.comb += segment.eq(self.bus.adr + index)
206+
207+
return m
208+
209+
dut = Decoder(addr_width=20, data_width=32, granularity=16)
210+
loop_1 = AddressLoopback(addr_width=7, data_width=32, granularity=16)
211+
self.assertEqual(dut.add(loop_1.bus, addr=0x10000),
212+
(0x10000, 0x10100, 1))
213+
loop_2 = AddressLoopback(addr_width=6, data_width=32, granularity=8)
214+
self.assertEqual(dut.add(loop_2.bus, addr=0x20000),
215+
(0x20000, 0x20080, 2))
216+
loop_3 = AddressLoopback(addr_width=8, data_width=16, granularity=16)
217+
self.assertEqual(dut.add(loop_3.bus, addr=0x30000, sparse=True),
218+
(0x30000, 0x30100, 1))
219+
loop_4 = AddressLoopback(addr_width=8, data_width=8, granularity=8)
220+
self.assertEqual(dut.add(loop_4.bus, addr=0x40000, sparse=True),
221+
(0x40000, 0x40100, 1))
222+
223+
def sim_test():
224+
yield dut.bus.cyc.eq(1)
225+
226+
yield dut.bus.adr.eq(0x10010 >> 1)
227+
228+
yield dut.bus.sel.eq(0b11)
229+
yield Delay(1e-6)
230+
self.assertEqual((yield dut.bus.dat_r), 0x00090008)
231+
232+
yield dut.bus.sel.eq(0b01)
233+
yield Delay(1e-6)
234+
self.assertEqual((yield dut.bus.dat_r), 0x00000008)
235+
236+
yield dut.bus.sel.eq(0b10)
237+
yield Delay(1e-6)
238+
self.assertEqual((yield dut.bus.dat_r), 0x00090000)
239+
240+
yield dut.bus.adr.eq(0x20010 >> 1)
241+
242+
yield dut.bus.sel.eq(0b11)
243+
yield Delay(1e-6)
244+
self.assertEqual((yield dut.bus.dat_r), 0x13121110)
245+
246+
yield dut.bus.sel.eq(0b01)
247+
yield Delay(1e-6)
248+
self.assertEqual((yield dut.bus.dat_r), 0x00001110)
249+
250+
yield dut.bus.sel.eq(0b10)
251+
yield Delay(1e-6)
252+
self.assertEqual((yield dut.bus.dat_r), 0x13120000)
253+
254+
yield dut.bus.adr.eq(0x30010 >> 1)
255+
256+
yield dut.bus.sel.eq(0b11)
257+
yield Delay(1e-6)
258+
self.assertEqual((yield dut.bus.dat_r), 0x0008)
259+
260+
yield dut.bus.sel.eq(0b01)
261+
yield Delay(1e-6)
262+
self.assertEqual((yield dut.bus.dat_r), 0x0008)
263+
264+
yield dut.bus.sel.eq(0b10)
265+
yield Delay(1e-6)
266+
self.assertEqual((yield dut.bus.dat_r), 0x0000)
267+
268+
yield dut.bus.adr.eq(0x30012 >> 1)
269+
270+
yield dut.bus.sel.eq(0b11)
271+
yield Delay(1e-6)
272+
self.assertEqual((yield dut.bus.dat_r), 0x0009)
273+
274+
yield dut.bus.adr.eq(0x40010 >> 1)
275+
276+
yield dut.bus.sel.eq(0b11)
277+
yield Delay(1e-6)
278+
self.assertEqual((yield dut.bus.dat_r), 0x08)
279+
280+
yield dut.bus.sel.eq(0b01)
281+
yield Delay(1e-6)
282+
self.assertEqual((yield dut.bus.dat_r), 0x08)
283+
284+
yield dut.bus.sel.eq(0b10)
285+
yield Delay(1e-6)
286+
self.assertEqual((yield dut.bus.dat_r), 0x00)
287+
288+
yield dut.bus.adr.eq(0x40012 >> 1)
289+
290+
yield dut.bus.sel.eq(0b11)
291+
yield Delay(1e-6)
292+
self.assertEqual((yield dut.bus.dat_r), 0x09)
293+
294+
m = Module()
295+
m.submodules += dut, loop_1, loop_2, loop_3, loop_4
296+
with Simulator(m, vcd_file=open("test.vcd", "w")) as sim:
297+
sim.add_process(sim_test())
298+
sim.run()

0 commit comments

Comments
 (0)