83e9b7c1b32e48ded179232853c14958a2b8dc74
[nmigen-soc.git] / nmigen_soc / test / test_csr_bus.py
1 # nmigen: UnusedElaboratable=no
2
3 import unittest
4 from nmigen import *
5 from nmigen.hdl.rec import Layout
6 from nmigen.back.pysim import *
7
8 from ..csr.bus import *
9
10
11 class ElementTestCase(unittest.TestCase):
12 def test_layout_1_ro(self):
13 elem = Element(1, "r")
14 self.assertEqual(elem.width, 1)
15 self.assertEqual(elem.access, Element.Access.R)
16 self.assertEqual(elem.layout, Layout.cast([
17 ("r_data", 1),
18 ("r_stb", 1),
19 ]))
20
21 def test_layout_8_rw(self):
22 elem = Element(8, access="rw")
23 self.assertEqual(elem.width, 8)
24 self.assertEqual(elem.access, Element.Access.RW)
25 self.assertEqual(elem.layout, Layout.cast([
26 ("r_data", 8),
27 ("r_stb", 1),
28 ("w_data", 8),
29 ("w_stb", 1),
30 ]))
31
32 def test_layout_10_wo(self):
33 elem = Element(10, "w")
34 self.assertEqual(elem.width, 10)
35 self.assertEqual(elem.access, Element.Access.W)
36 self.assertEqual(elem.layout, Layout.cast([
37 ("w_data", 10),
38 ("w_stb", 1),
39 ]))
40
41 def test_layout_0_rw(self): # degenerate but legal case
42 elem = Element(0, access=Element.Access.RW)
43 self.assertEqual(elem.width, 0)
44 self.assertEqual(elem.access, Element.Access.RW)
45 self.assertEqual(elem.layout, Layout.cast([
46 ("r_data", 0),
47 ("r_stb", 1),
48 ("w_data", 0),
49 ("w_stb", 1),
50 ]))
51
52 def test_width_wrong(self):
53 with self.assertRaisesRegex(ValueError,
54 r"Width must be a non-negative integer, not -1"):
55 Element(-1, "rw")
56
57 def test_access_wrong(self):
58 with self.assertRaisesRegex(ValueError,
59 r"Access mode must be one of \"r\", \"w\", or \"rw\", not 'wo'"):
60 Element(1, "wo")
61
62
63 class InterfaceTestCase(unittest.TestCase):
64 def test_layout(self):
65 iface = Interface(addr_width=12, data_width=8)
66 self.assertEqual(iface.addr_width, 12)
67 self.assertEqual(iface.data_width, 8)
68 self.assertEqual(iface.layout, Layout.cast([
69 ("addr", 12),
70 ("r_data", 8),
71 ("r_stb", 1),
72 ("w_data", 8),
73 ("w_stb", 1),
74 ]))
75
76 def test_wrong_addr_width(self):
77 with self.assertRaisesRegex(ValueError,
78 r"Address width must be a positive integer, not -1"):
79 Interface(addr_width=-1, data_width=8)
80
81 def test_wrong_data_width(self):
82 with self.assertRaisesRegex(ValueError,
83 r"Data width must be a positive integer, not -1"):
84 Interface(addr_width=16, data_width=-1)
85
86
87 class MultiplexerTestCase(unittest.TestCase):
88 def setUp(self):
89 self.dut = Multiplexer(addr_width=16, data_width=8)
90
91 def test_add_4b(self):
92 self.assertEqual(self.dut.add(Element(4, "rw")),
93 (0, 1))
94
95 def test_add_8b(self):
96 self.assertEqual(self.dut.add(Element(8, "rw")),
97 (0, 1))
98
99 def test_add_12b(self):
100 self.assertEqual(self.dut.add(Element(12, "rw")),
101 (0, 2))
102
103 def test_add_16b(self):
104 self.assertEqual(self.dut.add(Element(16, "rw")),
105 (0, 2))
106
107 def test_add_two(self):
108 self.assertEqual(self.dut.add(Element(16, "rw")),
109 (0, 2))
110 self.assertEqual(self.dut.add(Element(8, "rw")),
111 (2, 3))
112
113 def test_add_wrong(self):
114 with self.assertRaisesRegex(TypeError,
115 r"Element must be an instance of csr\.Element, not 'foo'"):
116 self.dut.add("foo")
117
118 def test_align_to(self):
119 self.assertEqual(self.dut.add(Element(8, "rw")),
120 (0, 1))
121 self.assertEqual(self.dut.align_to(2), 4)
122 self.assertEqual(self.dut.add(Element(8, "rw")),
123 (4, 5))
124
125 def test_sim(self):
126 bus = self.dut.bus
127
128 elem_4_r = Element(4, "r")
129 self.dut.add(elem_4_r)
130 elem_8_w = Element(8, "w")
131 self.dut.add(elem_8_w)
132 elem_16_rw = Element(16, "rw")
133 self.dut.add(elem_16_rw)
134
135 def sim_test():
136 yield elem_4_r.r_data.eq(0xa)
137 yield elem_16_rw.r_data.eq(0x5aa5)
138
139 yield bus.addr.eq(0)
140 yield bus.r_stb.eq(1)
141 yield
142 yield bus.r_stb.eq(0)
143 self.assertEqual((yield elem_4_r.r_stb), 1)
144 self.assertEqual((yield elem_16_rw.r_stb), 0)
145 yield
146 self.assertEqual((yield bus.r_data), 0xa)
147
148 yield bus.addr.eq(2)
149 yield bus.r_stb.eq(1)
150 yield
151 yield bus.r_stb.eq(0)
152 self.assertEqual((yield elem_4_r.r_stb), 0)
153 self.assertEqual((yield elem_16_rw.r_stb), 1)
154 yield
155 yield bus.addr.eq(3) # pipeline a read
156 self.assertEqual((yield bus.r_data), 0xa5)
157
158 yield bus.r_stb.eq(1)
159 yield
160 yield bus.r_stb.eq(0)
161 self.assertEqual((yield elem_4_r.r_stb), 0)
162 self.assertEqual((yield elem_16_rw.r_stb), 0)
163 yield
164 self.assertEqual((yield bus.r_data), 0x5a)
165
166 yield bus.addr.eq(1)
167 yield bus.w_data.eq(0x3d)
168 yield bus.w_stb.eq(1)
169 yield
170 yield bus.w_stb.eq(0)
171 yield bus.addr.eq(2) # change address
172 yield
173 self.assertEqual((yield elem_8_w.w_stb), 1)
174 self.assertEqual((yield elem_8_w.w_data), 0x3d)
175 self.assertEqual((yield elem_16_rw.w_stb), 0)
176 yield
177 self.assertEqual((yield elem_8_w.w_stb), 0)
178
179 yield bus.addr.eq(2)
180 yield bus.w_data.eq(0x55)
181 yield bus.w_stb.eq(1)
182 yield
183 self.assertEqual((yield elem_8_w.w_stb), 0)
184 self.assertEqual((yield elem_16_rw.w_stb), 0)
185 yield bus.addr.eq(3) # pipeline a write
186 yield bus.w_data.eq(0xaa)
187 yield
188 self.assertEqual((yield elem_8_w.w_stb), 0)
189 self.assertEqual((yield elem_16_rw.w_stb), 0)
190 yield bus.w_stb.eq(0)
191 yield
192 self.assertEqual((yield elem_8_w.w_stb), 0)
193 self.assertEqual((yield elem_16_rw.w_stb), 1)
194 self.assertEqual((yield elem_16_rw.w_data), 0xaa55)
195
196 with Simulator(self.dut, vcd_file=open("test.vcd", "w")) as sim:
197 sim.add_clock(1e-6)
198 sim.add_sync_process(sim_test())
199 sim.run()
200
201
202 class MultiplexerAlignedTestCase(unittest.TestCase):
203 def setUp(self):
204 self.dut = Multiplexer(addr_width=16, data_width=8, alignment=2)
205
206 def test_add_two(self):
207 self.assertEqual(self.dut.add(Element(8, "rw")),
208 (0, 4))
209 self.assertEqual(self.dut.add(Element(16, "rw")),
210 (4, 8))
211
212 def test_over_align_to(self):
213 self.assertEqual(self.dut.add(Element(8, "rw")),
214 (0, 4))
215 self.assertEqual(self.dut.align_to(3), 8)
216 self.assertEqual(self.dut.add(Element(8, "rw")),
217 (8, 12))
218
219 def test_under_align_to(self):
220 self.assertEqual(self.dut.add(Element(8, "rw")),
221 (0, 4))
222 self.assertEqual(self.dut.align_to(1), 4)
223 self.assertEqual(self.dut.add(Element(8, "rw")),
224 (4, 8))
225
226 def test_sim(self):
227 bus = self.dut.bus
228
229 elem_20_rw = Element(20, "rw")
230 self.dut.add(elem_20_rw)
231
232 def sim_test():
233 yield bus.w_stb.eq(1)
234 yield bus.addr.eq(0)
235 yield bus.w_data.eq(0x55)
236 yield
237 self.assertEqual((yield elem_20_rw.w_stb), 0)
238 yield bus.addr.eq(1)
239 yield bus.w_data.eq(0xaa)
240 yield
241 self.assertEqual((yield elem_20_rw.w_stb), 0)
242 yield bus.addr.eq(2)
243 yield bus.w_data.eq(0x33)
244 yield
245 self.assertEqual((yield elem_20_rw.w_stb), 0)
246 yield bus.addr.eq(3)
247 yield bus.w_data.eq(0xdd)
248 yield
249 self.assertEqual((yield elem_20_rw.w_stb), 0)
250 yield bus.w_stb.eq(0)
251 yield
252 self.assertEqual((yield elem_20_rw.w_stb), 1)
253 self.assertEqual((yield elem_20_rw.w_data), 0x3aa55)
254
255 with Simulator(self.dut, vcd_file=open("test.vcd", "w")) as sim:
256 sim.add_clock(1e-6)
257 sim.add_sync_process(sim_test())
258 sim.run()
259
260
261 class DecoderTestCase(unittest.TestCase):
262 def setUp(self):
263 self.dut = Decoder(addr_width=16, data_width=8)
264
265 def test_align_to(self):
266 self.assertEqual(self.dut.add(Interface(addr_width=10, data_width=8)),
267 (0, 0x400, 1))
268 self.assertEqual(self.dut.align_to(12), 0x1000)
269 self.assertEqual(self.dut.add(Interface(addr_width=10, data_width=8)),
270 (0x1000, 0x1400, 1))
271
272 def test_add_wrong_sub_bus(self):
273 with self.assertRaisesRegex(TypeError,
274 r"Subordinate bus must be an instance of csr\.Interface, not 1"):
275 self.dut.add(1)
276
277 def test_add_wrong_data_width(self):
278 mux = Multiplexer(addr_width=10, data_width=16)
279 Fragment.get(mux, platform=None) # silence UnusedElaboratable
280
281 with self.assertRaisesRegex(ValueError,
282 r"Subordinate bus has data width 16, which is not the same as "
283 r"decoder data width 8"):
284 self.dut.add(mux.bus)
285
286 def test_sim(self):
287 mux_1 = Multiplexer(addr_width=10, data_width=8)
288 self.dut.add(mux_1.bus)
289 elem_1 = Element(8, "rw")
290 mux_1.add(elem_1)
291
292 mux_2 = Multiplexer(addr_width=10, data_width=8)
293 self.dut.add(mux_2.bus)
294 elem_2 = Element(8, "rw")
295 mux_2.add(elem_2, addr=2)
296
297 elem_1_addr, _, _ = self.dut.bus.memory_map.find_resource(elem_1)
298 elem_2_addr, _, _ = self.dut.bus.memory_map.find_resource(elem_2)
299 self.assertEqual(elem_1_addr, 0x0000)
300 self.assertEqual(elem_2_addr, 0x0402)
301
302 bus = self.dut.bus
303
304 def sim_test():
305 yield bus.addr.eq(elem_1_addr)
306 yield bus.w_stb.eq(1)
307 yield bus.w_data.eq(0x55)
308 yield
309 yield bus.w_stb.eq(0)
310 yield
311 self.assertEqual((yield elem_1.w_data), 0x55)
312
313 yield bus.addr.eq(elem_2_addr)
314 yield bus.w_stb.eq(1)
315 yield bus.w_data.eq(0xaa)
316 yield
317 yield bus.w_stb.eq(0)
318 yield
319 self.assertEqual((yield elem_2.w_data), 0xaa)
320
321 yield elem_1.r_data.eq(0x55)
322 yield elem_2.r_data.eq(0xaa)
323
324 yield bus.addr.eq(elem_1_addr)
325 yield bus.r_stb.eq(1)
326 yield
327 yield bus.addr.eq(elem_2_addr)
328 yield
329 self.assertEqual((yield bus.r_data), 0x55)
330 yield
331 self.assertEqual((yield bus.r_data), 0xaa)
332
333 m = Module()
334 m.submodules += self.dut, mux_1, mux_2
335 with Simulator(m, vcd_file=open("test.vcd", "w")) as sim:
336 sim.add_clock(1e-6)
337 sim.add_sync_process(sim_test())
338 sim.run()