3 from contextlib
import contextmanager
5 from nmigen
.fhdl
.ast
import *
6 from nmigen
.fhdl
.dsl
import *
9 class DSLTestCase(unittest
.TestCase
):
20 def assertRaises(self
, exception
, msg
=None):
21 with
super().assertRaises(exception
) as cm
:
24 # WTF? unittest.assertRaises is completely broken.
25 self
.assertEqual(str(cm
.exception
), msg
)
27 def assertRepr(self
, obj
, repr_str
):
28 obj
= Statement
.wrap(obj
)
29 repr_str
= re
.sub(r
"\s+", " ", repr_str
)
30 repr_str
= re
.sub(r
"\( (?=\()", "(", repr_str
)
31 repr_str
= re
.sub(r
"\) (?=\))", ")", repr_str
)
32 self
.assertEqual(repr(obj
), repr_str
.strip())
34 def test_d_comb(self
):
36 m
.d
.comb
+= self
.c1
.eq(1)
38 self
.assertEqual(m
._driving
[self
.c1
], None)
39 self
.assertRepr(m
._statements
, """(
40 (eq (sig c1) (const 1'd1))
43 def test_d_sync(self
):
45 m
.d
.sync
+= self
.c1
.eq(1)
47 self
.assertEqual(m
._driving
[self
.c1
], "sync")
48 self
.assertRepr(m
._statements
, """(
49 (eq (sig c1) (const 1'd1))
54 m
.d
.pix
+= self
.c1
.eq(1)
56 self
.assertEqual(m
._driving
[self
.c1
], "pix")
57 self
.assertRepr(m
._statements
, """(
58 (eq (sig c1) (const 1'd1))
61 def test_d_index(self
):
63 m
.d
["pix"] += self
.c1
.eq(1)
65 self
.assertEqual(m
._driving
[self
.c1
], "pix")
66 self
.assertRepr(m
._statements
, """(
67 (eq (sig c1) (const 1'd1))
70 def test_d_no_conflict(self
):
72 m
.d
.comb
+= self
.w1
[0].eq(1)
73 m
.d
.comb
+= self
.w1
[1].eq(1)
75 def test_d_conflict(self
):
77 with self
.assertRaises(SyntaxError,
78 msg
="Driver-driver conflict: trying to drive (sig c1) from d.sync, but it "
79 "is already driven from d.comb"):
80 m
.d
.comb
+= self
.c1
.eq(1)
81 m
.d
.sync
+= self
.c1
.eq(1)
83 def test_d_wrong(self
):
85 with self
.assertRaises(AttributeError,
86 msg
="Cannot assign 'd.pix' attribute; did you mean 'd.pix +='?"):
89 def test_d_asgn_wrong(self
):
91 with self
.assertRaises(SyntaxError,
92 msg
="Only assignments may be appended to d.sync"):
93 m
.d
.sync
+= Switch(self
.s1
, {})
95 def test_comb_wrong(self
):
97 with self
.assertRaises(AttributeError,
98 msg
="'Module' object has no attribute 'comb'; did you mean 'd.comb'?"):
99 m
.comb
+= self
.c1
.eq(1)
101 def test_sync_wrong(self
):
103 with self
.assertRaises(AttributeError,
104 msg
="'Module' object has no attribute 'sync'; did you mean 'd.sync'?"):
105 m
.sync
+= self
.c1
.eq(1)
107 def test_attr_wrong(self
):
109 with self
.assertRaises(AttributeError,
110 msg
="'Module' object has no attribute 'nonexistentattr'"):
116 m
.d
.comb
+= self
.c1
.eq(1)
118 self
.assertRepr(m
._statements
, """
120 (switch (cat (sig s1))
121 (case 1 (eq (sig c1) (const 1'd1)))
126 def test_If_Elif(self
):
129 m
.d
.comb
+= self
.c1
.eq(1)
130 with m
.Elif(self
.s2
):
131 m
.d
.sync
+= self
.c2
.eq(0)
133 self
.assertRepr(m
._statements
, """
135 (switch (cat (sig s1) (sig s2))
136 (case -1 (eq (sig c1) (const 1'd1)))
137 (case 1- (eq (sig c2) (const 0'd0)))
142 def test_If_Elif_Else(self
):
145 m
.d
.comb
+= self
.c1
.eq(1)
146 with m
.Elif(self
.s2
):
147 m
.d
.sync
+= self
.c2
.eq(0)
149 m
.d
.comb
+= self
.c3
.eq(1)
151 self
.assertRepr(m
._statements
, """
153 (switch (cat (sig s1) (sig s2))
154 (case -1 (eq (sig c1) (const 1'd1)))
155 (case 1- (eq (sig c2) (const 0'd0)))
156 (case -- (eq (sig c3) (const 1'd1)))
161 def test_If_If(self
):
164 m
.d
.comb
+= self
.c1
.eq(1)
166 m
.d
.comb
+= self
.c2
.eq(1)
168 self
.assertRepr(m
._statements
, """
170 (switch (cat (sig s1))
171 (case 1 (eq (sig c1) (const 1'd1)))
173 (switch (cat (sig s2))
174 (case 1 (eq (sig c2) (const 1'd1)))
179 def test_If_nested_If(self
):
182 m
.d
.comb
+= self
.c1
.eq(1)
184 m
.d
.comb
+= self
.c2
.eq(1)
186 self
.assertRepr(m
._statements
, """
188 (switch (cat (sig s1))
189 (case 1 (eq (sig c1) (const 1'd1))
190 (switch (cat (sig s2))
191 (case 1 (eq (sig c2) (const 1'd1)))
198 def test_If_dangling_Else(self
):
201 m
.d
.comb
+= self
.c1
.eq(1)
203 m
.d
.comb
+= self
.c2
.eq(1)
205 m
.d
.comb
+= self
.c3
.eq(1)
207 self
.assertRepr(m
._statements
, """
209 (switch (cat (sig s1))
211 (eq (sig c1) (const 1'd1))
212 (switch (cat (sig s2))
213 (case 1 (eq (sig c2) (const 1'd1)))
217 (eq (sig c3) (const 1'd1))
223 def test_Elif_wrong(self
):
225 with self
.assertRaises(SyntaxError,
226 msg
="Elif without preceding If"):
227 with m
.Elif(self
.s2
):
230 def test_Else_wrong(self
):
232 with self
.assertRaises(SyntaxError,
233 msg
="Else without preceding If/Elif"):
237 def test_If_wide(self
):
240 m
.d
.comb
+= self
.c1
.eq(1)
242 self
.assertRepr(m
._statements
, """
244 (switch (cat (b (sig w1)))
245 (case 1 (eq (sig c1) (const 1'd1)))
250 def test_Switch(self
):
252 with m
.Switch(self
.w1
):
254 m
.d
.comb
+= self
.c1
.eq(1)
256 m
.d
.comb
+= self
.c2
.eq(1)
258 self
.assertRepr(m
._statements
, """
261 (case 0011 (eq (sig c1) (const 1'd1)))
262 (case 11-- (eq (sig c2) (const 1'd1)))
267 def test_Switch_default(self
):
269 with m
.Switch(self
.w1
):
271 m
.d
.comb
+= self
.c1
.eq(1)
273 m
.d
.comb
+= self
.c2
.eq(1)
275 self
.assertRepr(m
._statements
, """
278 (case 0011 (eq (sig c1) (const 1'd1)))
279 (case ---- (eq (sig c2) (const 1'd1)))
284 def test_Case_width_wrong(self
):
286 with m
.Switch(self
.w1
):
287 with self
.assertRaises(SyntaxError,
288 msg
="Case value '--' must have the same width as test (which is 4)"):
292 def test_Case_outside_Switch_wrong(self
):
294 with self
.assertRaises(SyntaxError,
295 msg
="Case is not permitted outside of Switch"):
299 def test_If_inside_Switch_wrong(self
):
301 with m
.Switch(self
.s1
):
302 with self
.assertRaises(SyntaxError,
303 msg
="If is not permitted inside of Switch"):
307 def test_auto_pop_ctrl(self
):
310 m
.d
.comb
+= self
.c1
.eq(1)
311 m
.d
.comb
+= self
.c2
.eq(1)
312 self
.assertRepr(m
._statements
, """
314 (switch (cat (b (sig w1)))
315 (case 1 (eq (sig c1) (const 1'd1)))
317 (eq (sig c2) (const 1'd1))
321 def test_submodule_anon(self
):
325 self
.assertEqual(m1
._submodules
, [(m2
, None)])
327 def test_submodule_anon_multi(self
):
331 m1
.submodules
+= m2
, m3
332 self
.assertEqual(m1
._submodules
, [(m2
, None), (m3
, None)])
334 def test_submodule_named(self
):
337 m1
.submodules
.foo
= m2
338 self
.assertEqual(m1
._submodules
, [(m2
, "foo")])
340 def test_submodule_wrong(self
):
342 with self
.assertRaises(TypeError,
343 msg
="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
345 with self
.assertRaises(TypeError,
346 msg
="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
349 def test_lower(self
):
351 m1
.d
.comb
+= self
.c1
.eq(self
.s1
)
353 m2
.d
.comb
+= self
.c2
.eq(self
.s2
)
354 m2
.d
.sync
+= self
.c3
.eq(self
.s3
)
355 m1
.submodules
.foo
= m2
357 f1
= m1
.lower(platform
=None)
358 self
.assertRepr(f1
.statements
, """
360 (eq (sig c1) (sig s1))
363 self
.assertEqual(f1
.drivers
, {
364 None: ValueSet((self
.c1
,))
366 self
.assertEqual(len(f1
.subfragments
), 1)
367 (f2
, f2_name
), = f1
.subfragments
368 self
.assertEqual(f2_name
, "foo")
369 self
.assertRepr(f2
.statements
, """
371 (eq (sig c2) (sig s2))
372 (eq (sig c3) (sig s3))
375 self
.assertEqual(f2
.drivers
, {
376 None: ValueSet((self
.c2
,)),
377 "sync": ValueSet((self
.c3
,))
379 self
.assertEqual(len(f2
.subfragments
), 0)