+# nmigen: UnusedElaboratable=no
+
from collections import OrderedDict
+from enum import Enum
from ..hdl.ast import *
from ..hdl.cd import *
from ..hdl.dsl import *
-from .tools import *
+from .utils import *
class DSLTestCase(FHDLTestCase):
def test_d_asgn_wrong(self):
m = Module()
with self.assertRaises(SyntaxError,
- msg="Only assignments, asserts, and assumes may be appended to d.sync"):
+ msg="Only assignments and property checks may be appended to d.sync"):
m.d.sync += Switch(self.s1, {})
def test_comb_wrong(self):
msg="'Module' object has no attribute 'nonexistentattr'"):
m.nonexistentattr
+ def test_d_suspicious(self):
+ m = Module()
+ with self.assertWarns(SyntaxWarning,
+ msg="Using '<module>.d.submodules' would add statements to clock domain "
+ "'submodules'; did you mean <module>.submodules instead?"):
+ m.d.submodules += []
+
def test_clock_signal(self):
m = Module()
m.d.comb += ClockSignal("pix").eq(ClockSignal())
)
""")
+ def test_sample_domain(self):
+ m = Module()
+ i = Signal()
+ o1 = Signal()
+ o2 = Signal()
+ o3 = Signal()
+ m.d.sync += o1.eq(Past(i))
+ m.d.pix += o2.eq(Past(i))
+ m.d.pix += o3.eq(Past(i, domain="sync"))
+ f = m.elaborate(platform=None)
+ self.assertRepr(f.statements, """
+ (
+ (eq (sig o1) (sample (sig i) @ sync[1]))
+ (eq (sig o2) (sample (sig i) @ pix[1]))
+ (eq (sig o3) (sample (sig i) @ sync[1]))
+ )
+ """)
+
def test_If(self):
m = Module()
with m.If(self.s1):
(switch (cat (sig s1) (sig s2))
(case -1 (eq (sig c1) (const 1'd1)))
(case 1- (eq (sig c2) (const 1'd0)))
- (case -- (eq (sig c3) (const 1'd1)))
+ (default (eq (sig c3) (const 1'd1)))
)
)
""")
(case 1 (eq (sig c2) (const 1'd1)))
)
)
- (case -
+ (default
(eq (sig c3) (const 1'd1))
)
)
)
""")
+ def test_If_signed_suspicious(self):
+ m = Module()
+ with self.assertWarns(SyntaxWarning,
+ msg="Signed values in If/Elif conditions usually result from inverting Python "
+ "booleans with ~, which leads to unexpected results: ~True is -2, which is "
+ "truthful. Replace `~flag` with `not flag`. (If this is a false positive, "
+ "silence this warning with `m.If(x)` → `m.If(x.bool())`.)"):
+ with m.If(~True):
+ pass
+
+ def test_Elif_signed_suspicious(self):
+ m = Module()
+ with m.If(0):
+ pass
+ with self.assertWarns(SyntaxWarning,
+ msg="Signed values in If/Elif conditions usually result from inverting Python "
+ "booleans with ~, which leads to unexpected results: ~True is -2, which is "
+ "truthful. Replace `~flag` with `not flag`. (If this is a false positive, "
+ "silence this warning with `m.If(x)` → `m.If(x.bool())`.)"):
+ with m.Elif(~True):
+ pass
+
def test_Switch(self):
m = Module()
with m.Switch(self.w1):
)
""")
- def test_Switch_default(self):
+ def test_Switch_default_Case(self):
m = Module()
with m.Switch(self.w1):
with m.Case(3):
(
(switch (sig w1)
(case 0011 (eq (sig c1) (const 1'd1)))
- (case ---- (eq (sig c2) (const 1'd1)))
+ (default (eq (sig c2) (const 1'd1)))
+ )
+ )
+ """)
+
+ def test_Switch_default_Default(self):
+ m = Module()
+ with m.Switch(self.w1):
+ with m.Case(3):
+ m.d.comb += self.c1.eq(1)
+ with m.Default():
+ m.d.comb += self.c2.eq(1)
+ m._flush()
+ self.assertRepr(m._statements, """
+ (
+ (switch (sig w1)
+ (case 0011 (eq (sig c1) (const 1'd1)))
+ (default (eq (sig c2) (const 1'd1)))
)
)
""")
)
""")
+ def test_Switch_enum(self):
+ class Color(Enum):
+ RED = 1
+ BLUE = 2
+ m = Module()
+ se = Signal(Color)
+ with m.Switch(se):
+ with m.Case(Color.RED):
+ m.d.comb += self.c1.eq(1)
+ self.assertRepr(m._statements, """
+ (
+ (switch (sig se)
+ (case 01 (eq (sig c1) (const 1'd1)))
+ )
+ )
+ """)
+
def test_Case_width_wrong(self):
m = Module()
with m.Switch(self.w1):
with self.assertRaises(SyntaxError,
- msg="Case value '--' must have the same width as test (which is 4)"):
+ msg="Case pattern '--' must have the same width as switch value (which is 4)"):
with m.Case("--"):
pass
with self.assertWarns(SyntaxWarning,
- msg="Case value '10110' is wider than test (which has width 4); comparison "
- "will never be true"):
+ msg="Case pattern '10110' is wider than switch value (which has width 4); "
+ "comparison will never be true"):
with m.Case(0b10110):
pass
self.assertRepr(m._statements, """
)
""")
+ def test_Case_bits_wrong(self):
+ m = Module()
+ with m.Switch(self.w1):
+ with self.assertRaises(SyntaxError,
+ msg="Case pattern 'abc' must consist of 0, 1, and - (don't care) bits"):
+ with m.Case("abc"):
+ pass
+
+ def test_Case_pattern_wrong(self):
+ m = Module()
+ with m.Switch(self.w1):
+ with self.assertRaises(SyntaxError,
+ msg="Case pattern must be an integer, a string, or an enumeration, not 1.0"):
+ with m.Case(1.0):
+ pass
+
def test_Case_outside_Switch_wrong(self):
m = Module()
with self.assertRaises(SyntaxError,
m = Module()
with m.Switch(self.s1):
with self.assertRaises(SyntaxError,
- msg="If is not permitted inside of Switch"):
+ msg="If is not permitted directly inside of Switch; "
+ "it is permitted inside of Switch Case"):
with m.If(self.s2):
pass
"(sig b)": "sync",
})
- frag = m.lower(platform=None)
+ frag = m.elaborate(platform=None)
fsm = frag.find_generated("fsm")
self.assertIsInstance(fsm.state, Signal)
self.assertEqual(fsm.encoding, OrderedDict({
)
""")
+ def test_FSM_empty(self):
+ m = Module()
+ with m.FSM():
+ pass
+ self.assertRepr(m._statements, """
+ ()
+ """)
+
+ def test_FSM_wrong_domain(self):
+ m = Module()
+ with self.assertRaises(ValueError,
+ msg="FSM may not be driven by the 'comb' domain"):
+ with m.FSM(domain="comb"):
+ pass
+
def test_FSM_wrong_redefined(self):
m = Module()
with m.FSM():
with m.FSM():
m.next = "FOO"
+ def test_If_inside_FSM_wrong(self):
+ m = Module()
+ with m.FSM():
+ with m.State("FOO"):
+ pass
+ with self.assertRaises(SyntaxError,
+ msg="If is not permitted directly inside of FSM; "
+ "it is permitted inside of FSM State"):
+ with m.If(self.s2):
+ pass
+
def test_auto_pop_ctrl(self):
m = Module()
with m.If(self.w1):
m1 = Module()
m2 = Module()
m1.submodules += m2
- self.assertEqual(m1._submodules, [(m2, None)])
+ self.assertEqual(m1._anon_submodules, [m2])
+ self.assertEqual(m1._named_submodules, {})
def test_submodule_anon_multi(self):
m1 = Module()
m2 = Module()
m3 = Module()
m1.submodules += m2, m3
- self.assertEqual(m1._submodules, [(m2, None), (m3, None)])
+ self.assertEqual(m1._anon_submodules, [m2, m3])
+ self.assertEqual(m1._named_submodules, {})
def test_submodule_named(self):
m1 = Module()
m2 = Module()
m1.submodules.foo = m2
- self.assertEqual(m1._submodules, [(m2, "foo")])
+ self.assertEqual(m1._anon_submodules, [])
+ self.assertEqual(m1._named_submodules, {"foo": m2})
+
+ def test_submodule_named_index(self):
+ m1 = Module()
+ m2 = Module()
+ m1.submodules["foo"] = m2
+ self.assertEqual(m1._anon_submodules, [])
+ self.assertEqual(m1._named_submodules, {"foo": m2})
def test_submodule_wrong(self):
m = Module()
with self.assertRaises(TypeError,
- msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
+ msg="Trying to add 1, which does not implement .elaborate(), as a submodule"):
m.submodules.foo = 1
with self.assertRaises(TypeError,
- msg="Trying to add '1', which does not implement .get_fragment(), as a submodule"):
+ msg="Trying to add 1, which does not implement .elaborate(), as a submodule"):
m.submodules += 1
+ def test_submodule_named_conflict(self):
+ m1 = Module()
+ m2 = Module()
+ m1.submodules.foo = m2
+ with self.assertRaises(NameError, msg="Submodule named 'foo' already exists"):
+ m1.submodules.foo = m2
+
+ def test_submodule_get(self):
+ m1 = Module()
+ m2 = Module()
+ m1.submodules.foo = m2
+ m3 = m1.submodules.foo
+ self.assertEqual(m2, m3)
+
+ def test_submodule_get_index(self):
+ m1 = Module()
+ m2 = Module()
+ m1.submodules["foo"] = m2
+ m3 = m1.submodules["foo"]
+ self.assertEqual(m2, m3)
+
+ def test_submodule_get_unset(self):
+ m1 = Module()
+ with self.assertRaises(AttributeError, msg="No submodule named 'foo' exists"):
+ m2 = m1.submodules.foo
+ with self.assertRaises(AttributeError, msg="No submodule named 'foo' exists"):
+ m2 = m1.submodules["foo"]
+
def test_domain_named_implicit(self):
m = Module()
m.domains += ClockDomain("sync")
m2.d.sync += self.c3.eq(self.s3)
m1.submodules.foo = m2
- f1 = m1.lower(platform=None)
+ f1 = m1.elaborate(platform=None)
self.assertRepr(f1.statements, """
(
(eq (sig c1) (sig s1))