hdl.xfrm: implement SwitchCleaner, for pruning empty switches.
[nmigen.git] / nmigen / test / test_hdl_dsl.py
1 from ..hdl.ast import *
2 from ..hdl.cd import *
3 from ..hdl.dsl import *
4 from .tools import *
5
6
7 class DSLTestCase(FHDLTestCase):
8 def setUp(self):
9 self.s1 = Signal()
10 self.s2 = Signal()
11 self.s3 = Signal()
12 self.c1 = Signal()
13 self.c2 = Signal()
14 self.c3 = Signal()
15 self.w1 = Signal(4)
16
17 def test_d_comb(self):
18 m = Module()
19 m.d.comb += self.c1.eq(1)
20 m._flush()
21 self.assertEqual(m._driving[self.c1], None)
22 self.assertRepr(m._statements, """(
23 (eq (sig c1) (const 1'd1))
24 )""")
25
26 def test_d_sync(self):
27 m = Module()
28 m.d.sync += self.c1.eq(1)
29 m._flush()
30 self.assertEqual(m._driving[self.c1], "sync")
31 self.assertRepr(m._statements, """(
32 (eq (sig c1) (const 1'd1))
33 )""")
34
35 def test_d_pix(self):
36 m = Module()
37 m.d.pix += self.c1.eq(1)
38 m._flush()
39 self.assertEqual(m._driving[self.c1], "pix")
40 self.assertRepr(m._statements, """(
41 (eq (sig c1) (const 1'd1))
42 )""")
43
44 def test_d_index(self):
45 m = Module()
46 m.d["pix"] += self.c1.eq(1)
47 m._flush()
48 self.assertEqual(m._driving[self.c1], "pix")
49 self.assertRepr(m._statements, """(
50 (eq (sig c1) (const 1'd1))
51 )""")
52
53 def test_d_no_conflict(self):
54 m = Module()
55 m.d.comb += self.w1[0].eq(1)
56 m.d.comb += self.w1[1].eq(1)
57
58 def test_d_conflict(self):
59 m = Module()
60 with self.assertRaises(SyntaxError,
61 msg="Driver-driver conflict: trying to drive (sig c1) from d.sync, but it "
62 "is already driven from d.comb"):
63 m.d.comb += self.c1.eq(1)
64 m.d.sync += self.c1.eq(1)
65
66 def test_d_wrong(self):
67 m = Module()
68 with self.assertRaises(AttributeError,
69 msg="Cannot assign 'd.pix' attribute; did you mean 'd.pix +='?"):
70 m.d.pix = None
71
72 def test_d_asgn_wrong(self):
73 m = Module()
74 with self.assertRaises(SyntaxError,
75 msg="Only assignments may be appended to d.sync"):
76 m.d.sync += Switch(self.s1, {})
77
78 def test_comb_wrong(self):
79 m = Module()
80 with self.assertRaises(AttributeError,
81 msg="'Module' object has no attribute 'comb'; did you mean 'd.comb'?"):
82 m.comb += self.c1.eq(1)
83
84 def test_sync_wrong(self):
85 m = Module()
86 with self.assertRaises(AttributeError,
87 msg="'Module' object has no attribute 'sync'; did you mean 'd.sync'?"):
88 m.sync += self.c1.eq(1)
89
90 def test_attr_wrong(self):
91 m = Module()
92 with self.assertRaises(AttributeError,
93 msg="'Module' object has no attribute 'nonexistentattr'"):
94 m.nonexistentattr
95
96 def test_If(self):
97 m = Module()
98 with m.If(self.s1):
99 m.d.comb += self.c1.eq(1)
100 m._flush()
101 self.assertRepr(m._statements, """
102 (
103 (switch (cat (sig s1))
104 (case 1 (eq (sig c1) (const 1'd1)))
105 )
106 )
107 """)
108
109 def test_If_Elif(self):
110 m = Module()
111 with m.If(self.s1):
112 m.d.comb += self.c1.eq(1)
113 with m.Elif(self.s2):
114 m.d.sync += self.c2.eq(0)
115 m._flush()
116 self.assertRepr(m._statements, """
117 (
118 (switch (cat (sig s1) (sig s2))
119 (case -1 (eq (sig c1) (const 1'd1)))
120 (case 1- (eq (sig c2) (const 1'd0)))
121 )
122 )
123 """)
124
125 def test_If_Elif_Else(self):
126 m = Module()
127 with m.If(self.s1):
128 m.d.comb += self.c1.eq(1)
129 with m.Elif(self.s2):
130 m.d.sync += self.c2.eq(0)
131 with m.Else():
132 m.d.comb += self.c3.eq(1)
133 m._flush()
134 self.assertRepr(m._statements, """
135 (
136 (switch (cat (sig s1) (sig s2))
137 (case -1 (eq (sig c1) (const 1'd1)))
138 (case 1- (eq (sig c2) (const 1'd0)))
139 (case -- (eq (sig c3) (const 1'd1)))
140 )
141 )
142 """)
143
144 def test_If_If(self):
145 m = Module()
146 with m.If(self.s1):
147 m.d.comb += self.c1.eq(1)
148 with m.If(self.s2):
149 m.d.comb += self.c2.eq(1)
150 m._flush()
151 self.assertRepr(m._statements, """
152 (
153 (switch (cat (sig s1))
154 (case 1 (eq (sig c1) (const 1'd1)))
155 )
156 (switch (cat (sig s2))
157 (case 1 (eq (sig c2) (const 1'd1)))
158 )
159 )
160 """)
161
162 def test_If_nested_If(self):
163 m = Module()
164 with m.If(self.s1):
165 m.d.comb += self.c1.eq(1)
166 with m.If(self.s2):
167 m.d.comb += self.c2.eq(1)
168 m._flush()
169 self.assertRepr(m._statements, """
170 (
171 (switch (cat (sig s1))
172 (case 1 (eq (sig c1) (const 1'd1))
173 (switch (cat (sig s2))
174 (case 1 (eq (sig c2) (const 1'd1)))
175 )
176 )
177 )
178 )
179 """)
180
181 def test_If_dangling_Else(self):
182 m = Module()
183 with m.If(self.s1):
184 m.d.comb += self.c1.eq(1)
185 with m.If(self.s2):
186 m.d.comb += self.c2.eq(1)
187 with m.Else():
188 m.d.comb += self.c3.eq(1)
189 m._flush()
190 self.assertRepr(m._statements, """
191 (
192 (switch (cat (sig s1))
193 (case 1
194 (eq (sig c1) (const 1'd1))
195 (switch (cat (sig s2))
196 (case 1 (eq (sig c2) (const 1'd1)))
197 )
198 )
199 (case -
200 (eq (sig c3) (const 1'd1))
201 )
202 )
203 )
204 """)
205
206 def test_Elif_wrong(self):
207 m = Module()
208 with self.assertRaises(SyntaxError,
209 msg="Elif without preceding If"):
210 with m.Elif(self.s2):
211 pass
212
213 def test_Else_wrong(self):
214 m = Module()
215 with self.assertRaises(SyntaxError,
216 msg="Else without preceding If/Elif"):
217 with m.Else():
218 pass
219
220 def test_If_wide(self):
221 m = Module()
222 with m.If(self.w1):
223 m.d.comb += self.c1.eq(1)
224 m._flush()
225 self.assertRepr(m._statements, """
226 (
227 (switch (cat (b (sig w1)))
228 (case 1 (eq (sig c1) (const 1'd1)))
229 )
230 )
231 """)
232
233 def test_Switch(self):
234 m = Module()
235 with m.Switch(self.w1):
236 with m.Case(3):
237 m.d.comb += self.c1.eq(1)
238 with m.Case("11--"):
239 m.d.comb += self.c2.eq(1)
240 m._flush()
241 self.assertRepr(m._statements, """
242 (
243 (switch (sig w1)
244 (case 0011 (eq (sig c1) (const 1'd1)))
245 (case 11-- (eq (sig c2) (const 1'd1)))
246 )
247 )
248 """)
249
250 def test_Switch_default(self):
251 m = Module()
252 with m.Switch(self.w1):
253 with m.Case(3):
254 m.d.comb += self.c1.eq(1)
255 with m.Case():
256 m.d.comb += self.c2.eq(1)
257 m._flush()
258 self.assertRepr(m._statements, """
259 (
260 (switch (sig w1)
261 (case 0011 (eq (sig c1) (const 1'd1)))
262 (case ---- (eq (sig c2) (const 1'd1)))
263 )
264 )
265 """)
266
267 def test_Switch_const_test(self):
268 m = Module()
269 with m.Switch(1):
270 with m.Case(1):
271 m.d.comb += self.c1.eq(1)
272 m._flush()
273 self.assertRepr(m._statements, """
274 (
275 (switch (const 1'd1)
276 (case 1 (eq (sig c1) (const 1'd1)))
277 )
278 )
279 """)
280
281 def test_Case_width_wrong(self):
282 m = Module()
283 with m.Switch(self.w1):
284 with self.assertRaises(SyntaxError,
285 msg="Case value '--' must have the same width as test (which is 4)"):
286 with m.Case("--"):
287 pass
288
289 def test_Case_outside_Switch_wrong(self):
290 m = Module()
291 with self.assertRaises(SyntaxError,
292 msg="Case is not permitted outside of Switch"):
293 with m.Case():
294 pass
295
296 def test_If_inside_Switch_wrong(self):
297 m = Module()
298 with m.Switch(self.s1):
299 with self.assertRaises(SyntaxError,
300 msg="If is not permitted inside of Switch"):
301 with m.If(self.s2):
302 pass
303
304 def test_auto_pop_ctrl(self):
305 m = Module()
306 with m.If(self.w1):
307 m.d.comb += self.c1.eq(1)
308 m.d.comb += self.c2.eq(1)
309 self.assertRepr(m._statements, """
310 (
311 (switch (cat (b (sig w1)))
312 (case 1 (eq (sig c1) (const 1'd1)))
313 )
314 (eq (sig c2) (const 1'd1))
315 )
316 """)
317
318 def test_submodule_anon(self):
319 m1 = Module()
320 m2 = Module()
321 m1.submodules += m2
322 self.assertEqual(m1._submodules, [(m2, None)])
323
324 def test_submodule_anon_multi(self):
325 m1 = Module()
326 m2 = Module()
327 m3 = Module()
328 m1.submodules += m2, m3
329 self.assertEqual(m1._submodules, [(m2, None), (m3, None)])
330
331 def test_submodule_named(self):
332 m1 = Module()
333 m2 = Module()
334 m1.submodules.foo = m2
335 self.assertEqual(m1._submodules, [(m2, "foo")])
336
337 def test_submodule_wrong(self):
338 m = Module()
339 with self.assertRaises(TypeError,
340 msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
341 m.submodules.foo = 1
342 with self.assertRaises(TypeError,
343 msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
344 m.submodules += 1
345
346 def test_domain_named_implicit(self):
347 m = Module()
348 m.domains += ClockDomain("sync")
349 self.assertEqual(len(m._domains), 1)
350
351 def test_domain_named_explicit(self):
352 m = Module()
353 m.domains.foo = ClockDomain()
354 self.assertEqual(len(m._domains), 1)
355 self.assertEqual(m._domains[0].name, "foo")
356
357 def test_lower(self):
358 m1 = Module()
359 m1.d.comb += self.c1.eq(self.s1)
360 m2 = Module()
361 m2.d.comb += self.c2.eq(self.s2)
362 m2.d.sync += self.c3.eq(self.s3)
363 m1.submodules.foo = m2
364
365 f1 = m1.lower(platform=None)
366 self.assertRepr(f1.statements, """
367 (
368 (eq (sig c1) (sig s1))
369 )
370 """)
371 self.assertEqual(f1.drivers, {
372 None: SignalSet((self.c1,))
373 })
374 self.assertEqual(len(f1.subfragments), 1)
375 (f2, f2_name), = f1.subfragments
376 self.assertEqual(f2_name, "foo")
377 self.assertRepr(f2.statements, """
378 (
379 (eq (sig c2) (sig s2))
380 (eq (sig c3) (sig s3))
381 )
382 """)
383 self.assertEqual(f2.drivers, {
384 None: SignalSet((self.c2,)),
385 "sync": SignalSet((self.c3,))
386 })
387 self.assertEqual(len(f2.subfragments), 0)