2021-12-09 22:39:50 -07:00
|
|
|
# amaranth: UnusedElaboratable=no
|
2019-10-26 00:36:54 -06:00
|
|
|
|
2023-06-01 12:22:24 -06:00
|
|
|
import sys
|
2018-12-26 05:39:05 -07:00
|
|
|
from collections import OrderedDict
|
|
|
|
|
2024-01-30 09:43:33 -07:00
|
|
|
from amaranth.hdl._ast import *
|
|
|
|
from amaranth.hdl._cd import *
|
|
|
|
from amaranth.hdl._dsl import *
|
2023-02-20 15:58:38 -07:00
|
|
|
from amaranth.lib.enum import Enum
|
tests: move out of the main package.
Compared to tests in the repository root, tests in the package have
many downsides:
* Unless explicitly excluded in find_packages(), tests and their
support code effectively become a part of public API.
This, unfortunately, happened with FHDLTestCase, which was never
intended for downstream use.
* Even if explicitly excluded from the setuptools package, using
an editable install, or setting PYTHONPATH still allows accessing
the tests.
* Having a sub-package that is present in the source tree but not
exported (or, worse, exported only sometimes) is confusing.
* The name `nmigen.test` cannot be used for anything else, such as
testing utilities that *are* intended for downstream use.
2020-08-26 18:33:31 -06:00
|
|
|
|
2019-10-13 12:53:38 -06:00
|
|
|
from .utils import *
|
2023-02-28 07:28:41 -07:00
|
|
|
from amaranth._utils import _ignore_deprecated
|
2018-12-12 23:06:51 -07:00
|
|
|
|
|
|
|
|
2018-12-13 01:57:14 -07:00
|
|
|
class DSLTestCase(FHDLTestCase):
|
2018-12-12 23:06:51 -07:00
|
|
|
def setUp(self):
|
|
|
|
self.s1 = Signal()
|
|
|
|
self.s2 = Signal()
|
|
|
|
self.s3 = Signal()
|
|
|
|
self.c1 = Signal()
|
|
|
|
self.c2 = Signal()
|
|
|
|
self.c3 = Signal()
|
|
|
|
self.w1 = Signal(4)
|
|
|
|
|
2020-01-31 19:15:45 -07:00
|
|
|
def test_cant_inherit(self):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
(r"^Instead of inheriting from `Module`, inherit from `Elaboratable` and "
|
|
|
|
r"return a `Module` from the `elaborate\(self, platform\)` method$")):
|
2020-01-31 19:15:45 -07:00
|
|
|
class ORGate(Module):
|
|
|
|
pass
|
|
|
|
|
2018-12-12 23:06:51 -07:00
|
|
|
def test_d_comb(self):
|
|
|
|
m = Module()
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertEqual(m._driving[self.c1], "comb")
|
|
|
|
self.assertRepr(m._statements["comb"], """(
|
2018-12-12 23:06:51 -07:00
|
|
|
(eq (sig c1) (const 1'd1))
|
|
|
|
)""")
|
|
|
|
|
|
|
|
def test_d_sync(self):
|
|
|
|
m = Module()
|
|
|
|
m.d.sync += self.c1.eq(1)
|
|
|
|
m._flush()
|
|
|
|
self.assertEqual(m._driving[self.c1], "sync")
|
2024-02-08 17:53:45 -07:00
|
|
|
self.assertRepr(m._statements["sync"], """(
|
2018-12-12 23:06:51 -07:00
|
|
|
(eq (sig c1) (const 1'd1))
|
|
|
|
)""")
|
|
|
|
|
|
|
|
def test_d_pix(self):
|
|
|
|
m = Module()
|
|
|
|
m.d.pix += self.c1.eq(1)
|
|
|
|
m._flush()
|
|
|
|
self.assertEqual(m._driving[self.c1], "pix")
|
2024-02-08 17:53:45 -07:00
|
|
|
self.assertRepr(m._statements["pix"], """(
|
2018-12-12 23:06:51 -07:00
|
|
|
(eq (sig c1) (const 1'd1))
|
|
|
|
)""")
|
|
|
|
|
|
|
|
def test_d_index(self):
|
|
|
|
m = Module()
|
|
|
|
m.d["pix"] += self.c1.eq(1)
|
|
|
|
m._flush()
|
|
|
|
self.assertEqual(m._driving[self.c1], "pix")
|
2024-02-08 17:53:45 -07:00
|
|
|
self.assertRepr(m._statements["pix"], """(
|
2018-12-12 23:06:51 -07:00
|
|
|
(eq (sig c1) (const 1'd1))
|
|
|
|
)""")
|
|
|
|
|
|
|
|
def test_d_no_conflict(self):
|
|
|
|
m = Module()
|
|
|
|
m.d.comb += self.w1[0].eq(1)
|
|
|
|
m.d.comb += self.w1[1].eq(1)
|
|
|
|
|
|
|
|
def test_d_conflict(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
(r"^Driver-driver conflict: trying to drive \(sig c1\) from d\.sync, but it "
|
|
|
|
r"is already driven from d\.comb$")):
|
2018-12-12 23:06:51 -07:00
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
m.d.sync += self.c1.eq(1)
|
|
|
|
|
|
|
|
def test_d_wrong(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(AttributeError,
|
|
|
|
r"^Cannot assign 'd\.pix' attribute; did you mean 'd.pix \+='\?$"):
|
2018-12-12 23:06:51 -07:00
|
|
|
m.d.pix = None
|
|
|
|
|
|
|
|
def test_d_asgn_wrong(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
2024-03-05 21:23:47 -07:00
|
|
|
r"^Only assignments, prints, and property checks may be appended to d\.sync$"):
|
2018-12-12 23:06:51 -07:00
|
|
|
m.d.sync += Switch(self.s1, {})
|
|
|
|
|
|
|
|
def test_comb_wrong(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(AttributeError,
|
|
|
|
r"^'Module' object has no attribute 'comb'; did you mean 'd\.comb'\?$"):
|
2018-12-12 23:06:51 -07:00
|
|
|
m.comb += self.c1.eq(1)
|
|
|
|
|
|
|
|
def test_sync_wrong(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(AttributeError,
|
|
|
|
r"^'Module' object has no attribute 'sync'; did you mean 'd\.sync'\?$"):
|
2018-12-12 23:06:51 -07:00
|
|
|
m.sync += self.c1.eq(1)
|
|
|
|
|
|
|
|
def test_attr_wrong(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(AttributeError,
|
|
|
|
r"^'Module' object has no attribute 'nonexistentattr'$"):
|
2018-12-12 23:06:51 -07:00
|
|
|
m.nonexistentattr
|
|
|
|
|
2019-09-28 11:50:24 -06:00
|
|
|
def test_d_suspicious(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
(r"^Using '<module>\.d\.submodules' would add statements to clock domain "
|
|
|
|
r"'submodules'; did you mean <module>\.submodules instead\?$")):
|
2019-09-28 11:50:24 -06:00
|
|
|
m.d.submodules += []
|
|
|
|
|
2019-01-14 08:38:16 -07:00
|
|
|
def test_clock_signal(self):
|
|
|
|
m = Module()
|
|
|
|
m.d.comb += ClockSignal("pix").eq(ClockSignal())
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2019-01-14 08:38:16 -07:00
|
|
|
(
|
|
|
|
(eq (clk pix) (clk sync))
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_reset_signal(self):
|
|
|
|
m = Module()
|
|
|
|
m.d.comb += ResetSignal("pix").eq(1)
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2019-01-14 08:38:16 -07:00
|
|
|
(
|
|
|
|
(eq (rst pix) (const 1'd1))
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2018-12-12 23:06:51 -07:00
|
|
|
def test_If(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-12 23:06:51 -07:00
|
|
|
(
|
|
|
|
(switch (cat (sig s1))
|
|
|
|
(case 1 (eq (sig c1) (const 1'd1)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_If_Elif(self):
|
2024-02-08 17:53:45 -07:00
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
with m.Elif(self.s2):
|
|
|
|
m.d.comb += self.c2.eq(0)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2024-02-08 17:53:45 -07:00
|
|
|
(
|
|
|
|
(switch (cat (sig s1) (sig s2))
|
|
|
|
(case -1 (eq (sig c1) (const 1'd1)))
|
|
|
|
(case 1- (eq (sig c2) (const 1'd0)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_If_Elif_multi(self):
|
2018-12-12 23:06:51 -07:00
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
with m.Elif(self.s2):
|
|
|
|
m.d.sync += self.c2.eq(0)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-12 23:06:51 -07:00
|
|
|
(
|
|
|
|
(switch (cat (sig s1) (sig s2))
|
|
|
|
(case -1 (eq (sig c1) (const 1'd1)))
|
2024-02-08 17:53:45 -07:00
|
|
|
(case 1- )
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
self.assertRepr(m._statements["sync"], """
|
|
|
|
(
|
|
|
|
(switch (cat (sig s1) (sig s2))
|
|
|
|
(case -1 )
|
2018-12-13 11:00:05 -07:00
|
|
|
(case 1- (eq (sig c2) (const 1'd0)))
|
2018-12-12 23:06:51 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_If_Elif_Else(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
with m.Elif(self.s2):
|
|
|
|
m.d.sync += self.c2.eq(0)
|
|
|
|
with m.Else():
|
|
|
|
m.d.comb += self.c3.eq(1)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-12 23:06:51 -07:00
|
|
|
(
|
|
|
|
(switch (cat (sig s1) (sig s2))
|
|
|
|
(case -1 (eq (sig c1) (const 1'd1)))
|
2024-02-08 17:53:45 -07:00
|
|
|
(case 1- )
|
2019-06-25 11:53:09 -06:00
|
|
|
(default (eq (sig c3) (const 1'd1)))
|
2018-12-12 23:06:51 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
2024-02-08 17:53:45 -07:00
|
|
|
self.assertRepr(m._statements["sync"], """
|
|
|
|
(
|
|
|
|
(switch (cat (sig s1) (sig s2))
|
|
|
|
(case -1 )
|
|
|
|
(case 1- (eq (sig c2) (const 1'd0)))
|
|
|
|
(default )
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
2018-12-12 23:06:51 -07:00
|
|
|
|
2018-12-13 00:11:06 -07:00
|
|
|
def test_If_If(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
with m.If(self.s2):
|
|
|
|
m.d.comb += self.c2.eq(1)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-13 00:11:06 -07:00
|
|
|
(
|
|
|
|
(switch (cat (sig s1))
|
|
|
|
(case 1 (eq (sig c1) (const 1'd1)))
|
|
|
|
)
|
|
|
|
(switch (cat (sig s2))
|
|
|
|
(case 1 (eq (sig c2) (const 1'd1)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_If_nested_If(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
with m.If(self.s2):
|
|
|
|
m.d.comb += self.c2.eq(1)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-13 00:11:06 -07:00
|
|
|
(
|
|
|
|
(switch (cat (sig s1))
|
|
|
|
(case 1 (eq (sig c1) (const 1'd1))
|
|
|
|
(switch (cat (sig s2))
|
|
|
|
(case 1 (eq (sig c2) (const 1'd1)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
|
|
|
def test_If_dangling_Else(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
with m.If(self.s2):
|
|
|
|
m.d.comb += self.c2.eq(1)
|
|
|
|
with m.Else():
|
|
|
|
m.d.comb += self.c3.eq(1)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-13 00:11:06 -07:00
|
|
|
(
|
|
|
|
(switch (cat (sig s1))
|
|
|
|
(case 1
|
|
|
|
(eq (sig c1) (const 1'd1))
|
|
|
|
(switch (cat (sig s2))
|
|
|
|
(case 1 (eq (sig c2) (const 1'd1)))
|
|
|
|
)
|
|
|
|
)
|
2019-06-25 11:53:09 -06:00
|
|
|
(default
|
2018-12-13 00:11:06 -07:00
|
|
|
(eq (sig c3) (const 1'd1))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2018-12-12 23:06:51 -07:00
|
|
|
def test_Elif_wrong(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Elif without preceding If$"):
|
2018-12-12 23:06:51 -07:00
|
|
|
with m.Elif(self.s2):
|
|
|
|
pass
|
|
|
|
|
2020-10-22 07:23:06 -06:00
|
|
|
def test_Elif_wrong_nested(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Elif without preceding If$"):
|
|
|
|
with m.Elif(self.s2):
|
|
|
|
pass
|
|
|
|
|
2018-12-12 23:06:51 -07:00
|
|
|
def test_Else_wrong(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Else without preceding If\/Elif$"):
|
2018-12-12 23:06:51 -07:00
|
|
|
with m.Else():
|
|
|
|
pass
|
|
|
|
|
2021-05-10 19:59:34 -06:00
|
|
|
def test_Else_wrong_nested(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Else without preceding If/Elif$"):
|
|
|
|
with m.Else():
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_Elif_Elif_wrong_nested(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
pass
|
|
|
|
with m.Elif(self.s2):
|
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Elif without preceding If$"):
|
|
|
|
with m.Elif(self.s3):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_Else_Else_wrong_nested(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(self.s1):
|
|
|
|
pass
|
|
|
|
with m.Else():
|
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Else without preceding If/Elif$"):
|
|
|
|
with m.Else():
|
|
|
|
pass
|
|
|
|
|
2018-12-12 23:06:51 -07:00
|
|
|
def test_If_wide(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(self.w1):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-12 23:06:51 -07:00
|
|
|
(
|
|
|
|
(switch (cat (b (sig w1)))
|
|
|
|
(case 1 (eq (sig c1) (const 1'd1)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2023-06-01 12:22:24 -06:00
|
|
|
if sys.version_info < (3, 12): # upstream warning in 3.12!
|
|
|
|
def test_If_signed_suspicious(self):
|
|
|
|
m = Module()
|
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
r"^Signed values in If\/Elif conditions usually result from inverting Python "
|
2020-07-28 13:35:25 -06:00
|
|
|
r"booleans with ~, which leads to unexpected results\. Replace `~flag` with "
|
|
|
|
r"`not flag`\. \(If this is a false positive, silence this warning with "
|
2023-06-01 12:22:24 -06:00
|
|
|
r"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$"):
|
|
|
|
with m.If(~True):
|
|
|
|
pass
|
2019-08-03 08:00:29 -06:00
|
|
|
|
2023-06-01 12:22:24 -06:00
|
|
|
def test_Elif_signed_suspicious(self):
|
|
|
|
m = Module()
|
|
|
|
with m.If(0):
|
|
|
|
pass
|
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
r"^Signed values in If\/Elif conditions usually result from inverting Python "
|
2020-07-28 13:35:25 -06:00
|
|
|
r"booleans with ~, which leads to unexpected results\. Replace `~flag` with "
|
|
|
|
r"`not flag`\. \(If this is a false positive, silence this warning with "
|
2023-06-01 12:22:24 -06:00
|
|
|
r"`m\.If\(x\)` → `m\.If\(x\.bool\(\)\)`\.\)$"):
|
|
|
|
with m.Elif(~True):
|
|
|
|
pass
|
2019-08-03 08:00:29 -06:00
|
|
|
|
2020-01-30 23:37:45 -07:00
|
|
|
def test_if_If_Elif_Else(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^`if m\.If\(\.\.\.\):` does not work; use `with m\.If\(\.\.\.\)`$"):
|
2020-01-30 23:37:45 -07:00
|
|
|
if m.If(0):
|
|
|
|
pass
|
|
|
|
with m.If(0):
|
|
|
|
pass
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^`if m\.Elif\(\.\.\.\):` does not work; use `with m\.Elif\(\.\.\.\)`$"):
|
2020-01-30 23:37:45 -07:00
|
|
|
if m.Elif(0):
|
|
|
|
pass
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^`if m\.Else\(\.\.\.\):` does not work; use `with m\.Else\(\.\.\.\)`$"):
|
2020-01-30 23:37:45 -07:00
|
|
|
if m.Else():
|
|
|
|
pass
|
|
|
|
|
2018-12-13 00:11:06 -07:00
|
|
|
def test_Switch(self):
|
|
|
|
m = Module()
|
|
|
|
with m.Switch(self.w1):
|
|
|
|
with m.Case(3):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
with m.Case("11--"):
|
|
|
|
m.d.comb += self.c2.eq(1)
|
2020-02-04 00:54:54 -07:00
|
|
|
with m.Case("1 0--"):
|
|
|
|
m.d.comb += self.c2.eq(1)
|
2018-12-13 00:11:06 -07:00
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-13 00:11:06 -07:00
|
|
|
(
|
|
|
|
(switch (sig w1)
|
|
|
|
(case 0011 (eq (sig c1) (const 1'd1)))
|
|
|
|
(case 11-- (eq (sig c2) (const 1'd1)))
|
2020-02-04 00:54:54 -07:00
|
|
|
(case 10-- (eq (sig c2) (const 1'd1)))
|
2018-12-13 00:11:06 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2024-01-13 04:35:52 -07:00
|
|
|
def test_Switch_empty_Case(self):
|
2018-12-13 00:11:06 -07:00
|
|
|
m = Module()
|
|
|
|
with m.Switch(self.w1):
|
|
|
|
with m.Case(3):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
with m.Case():
|
|
|
|
m.d.comb += self.c2.eq(1)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-13 00:11:06 -07:00
|
|
|
(
|
|
|
|
(switch (sig w1)
|
|
|
|
(case 0011 (eq (sig c1) (const 1'd1)))
|
2018-12-14 09:07:25 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2019-09-08 06:24:18 -06:00
|
|
|
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()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2019-09-08 06:24:18 -06:00
|
|
|
(
|
|
|
|
(switch (sig w1)
|
|
|
|
(case 0011 (eq (sig c1) (const 1'd1)))
|
|
|
|
(default (eq (sig c2) (const 1'd1)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2018-12-14 09:07:25 -07:00
|
|
|
def test_Switch_const_test(self):
|
|
|
|
m = Module()
|
|
|
|
with m.Switch(1):
|
|
|
|
with m.Case(1):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
m._flush()
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-14 09:07:25 -07:00
|
|
|
(
|
|
|
|
(switch (const 1'd1)
|
|
|
|
(case 1 (eq (sig c1) (const 1'd1)))
|
2018-12-13 00:11:06 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2019-09-16 12:59:28 -06:00
|
|
|
def test_Switch_enum(self):
|
|
|
|
class Color(Enum):
|
|
|
|
RED = 1
|
|
|
|
BLUE = 2
|
|
|
|
m = Module()
|
2024-02-13 23:13:12 -07:00
|
|
|
se = Signal(Color, init=Color.RED)
|
2019-09-16 12:59:28 -06:00
|
|
|
with m.Switch(se):
|
|
|
|
with m.Case(Color.RED):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2019-09-16 12:59:28 -06:00
|
|
|
(
|
|
|
|
(switch (sig se)
|
|
|
|
(case 01 (eq (sig c1) (const 1'd1)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2023-02-21 03:07:55 -07:00
|
|
|
def test_Switch_const_castable(self):
|
2023-02-20 15:58:38 -07:00
|
|
|
class Color(Enum, shape=1):
|
2023-02-21 03:07:55 -07:00
|
|
|
RED = 0
|
|
|
|
BLUE = 1
|
|
|
|
m = Module()
|
|
|
|
se = Signal(2)
|
|
|
|
with m.Switch(se):
|
|
|
|
with m.Case(Cat(Color.RED, Color.BLUE)):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2023-02-21 03:07:55 -07:00
|
|
|
(
|
|
|
|
(switch (sig se)
|
|
|
|
(case 10 (eq (sig c1) (const 1'd1)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2018-12-13 00:11:06 -07:00
|
|
|
def test_Case_width_wrong(self):
|
2020-01-31 16:14:16 -07:00
|
|
|
class Color(Enum):
|
|
|
|
RED = 0b10101010
|
2018-12-13 00:11:06 -07:00
|
|
|
m = Module()
|
2024-02-08 17:53:45 -07:00
|
|
|
dummy = Signal()
|
2018-12-13 00:11:06 -07:00
|
|
|
with m.Switch(self.w1):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Case pattern '--' must have the same width as switch value \(which is 4\)$"):
|
2018-12-13 00:11:06 -07:00
|
|
|
with m.Case("--"):
|
2024-02-08 17:53:45 -07:00
|
|
|
m.d.comb += dummy.eq(0)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
2023-02-21 03:07:55 -07:00
|
|
|
r"^Case pattern '22' \(5'10110\) is wider than switch value \(which has "
|
|
|
|
r"width 4\); comparison will never be true$"):
|
2019-01-13 01:46:28 -07:00
|
|
|
with m.Case(0b10110):
|
2024-02-08 17:53:45 -07:00
|
|
|
m.d.comb += dummy.eq(0)
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
2023-02-21 03:07:55 -07:00
|
|
|
r"^Case pattern '<Color.RED: 170>' \(8'10101010\) is wider than switch value "
|
|
|
|
r"\(which has width 4\); comparison will never be true$"):
|
2020-01-31 16:14:16 -07:00
|
|
|
with m.Case(Color.RED):
|
2024-02-08 17:53:45 -07:00
|
|
|
m.d.comb += dummy.eq(0)
|
|
|
|
self.assertEqual(m._statements, {})
|
2018-12-13 00:11:06 -07:00
|
|
|
|
2024-02-13 16:04:56 -07:00
|
|
|
def test_Switch_zero_width(self):
|
|
|
|
m = Module()
|
|
|
|
s = Signal(0)
|
|
|
|
with m.Switch(s):
|
|
|
|
with m.Case(0):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
m._flush()
|
|
|
|
self.assertRepr(m._statements["comb"], """
|
|
|
|
(
|
|
|
|
(switch (sig s)
|
|
|
|
(case (eq (sig c1) (const 1'd1)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2019-09-14 14:46:10 -06:00
|
|
|
def test_Case_bits_wrong(self):
|
|
|
|
m = Module()
|
|
|
|
with m.Switch(self.w1):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
(r"^Case pattern 'abc' must consist of 0, 1, and - \(don't care\) bits, "
|
|
|
|
r"and may include whitespace$")):
|
2019-09-14 14:46:10 -06:00
|
|
|
with m.Case("abc"):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_Case_pattern_wrong(self):
|
|
|
|
m = Module()
|
|
|
|
with m.Switch(self.w1):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
2023-02-21 03:07:55 -07:00
|
|
|
r"^Case pattern must be a string or a constant-castable expression, "
|
|
|
|
r"not 1\.0$"):
|
2019-09-14 14:46:10 -06:00
|
|
|
with m.Case(1.0):
|
|
|
|
pass
|
|
|
|
|
2018-12-13 00:11:06 -07:00
|
|
|
def test_Case_outside_Switch_wrong(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Case is not permitted outside of Switch$"):
|
2018-12-13 00:11:06 -07:00
|
|
|
with m.Case():
|
|
|
|
pass
|
|
|
|
|
2024-01-29 19:49:09 -07:00
|
|
|
def test_Case_after_Default_wrong(self):
|
|
|
|
m = Module()
|
|
|
|
with m.Switch(self.w1):
|
|
|
|
with m.Default():
|
|
|
|
pass
|
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
r"^A case defined after the default case will never be active$"):
|
|
|
|
with m.Case():
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_Default_after_Default_wrong(self):
|
|
|
|
m = Module()
|
|
|
|
with m.Switch(self.w1):
|
|
|
|
with m.Default():
|
|
|
|
pass
|
|
|
|
with self.assertWarnsRegex(SyntaxWarning,
|
|
|
|
r"^A case defined after the default case will never be active$"):
|
|
|
|
with m.Default():
|
|
|
|
pass
|
|
|
|
|
2018-12-13 00:11:06 -07:00
|
|
|
def test_If_inside_Switch_wrong(self):
|
|
|
|
m = Module()
|
|
|
|
with m.Switch(self.s1):
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
(r"^If is not permitted directly inside of Switch; "
|
|
|
|
r"it is permitted inside of Switch Case$")):
|
2018-12-13 00:11:06 -07:00
|
|
|
with m.If(self.s2):
|
|
|
|
pass
|
|
|
|
|
2021-05-10 20:02:29 -06:00
|
|
|
def test_Case_wrong_nested(self):
|
|
|
|
m = Module()
|
|
|
|
with m.Switch(self.s1):
|
|
|
|
with m.Case(0):
|
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Case is not permitted outside of Switch$"):
|
|
|
|
with m.Case(1):
|
|
|
|
pass
|
|
|
|
|
2018-12-26 01:55:04 -07:00
|
|
|
def test_FSM_basic(self):
|
|
|
|
a = Signal()
|
|
|
|
b = Signal()
|
|
|
|
c = Signal()
|
|
|
|
m = Module()
|
|
|
|
with m.FSM():
|
|
|
|
with m.State("FIRST"):
|
|
|
|
m.d.comb += a.eq(1)
|
|
|
|
m.next = "SECOND"
|
|
|
|
with m.State("SECOND"):
|
|
|
|
m.d.sync += b.eq(~b)
|
|
|
|
with m.If(c):
|
|
|
|
m.next = "FIRST"
|
2024-02-27 07:03:48 -07:00
|
|
|
|
|
|
|
frag = m.elaborate(platform=None)
|
|
|
|
self.assertRepr(frag.statements["comb"], """
|
2018-12-26 01:55:04 -07:00
|
|
|
(
|
|
|
|
(switch (sig fsm_state)
|
|
|
|
(case 0
|
|
|
|
(eq (sig a) (const 1'd1))
|
2024-02-08 17:53:45 -07:00
|
|
|
)
|
|
|
|
(case 1 )
|
|
|
|
)
|
2024-03-23 10:44:44 -06:00
|
|
|
(eq (sig) (== (sig fsm_state) (const 1'd0)))
|
|
|
|
(eq (sig) (== (sig fsm_state) (const 1'd1)))
|
2024-02-08 17:53:45 -07:00
|
|
|
)
|
|
|
|
""")
|
2024-02-27 07:03:48 -07:00
|
|
|
self.assertRepr(frag.statements["sync"], """
|
2024-02-08 17:53:45 -07:00
|
|
|
(
|
|
|
|
(switch (sig fsm_state)
|
|
|
|
(case 0
|
2018-12-26 01:55:04 -07:00
|
|
|
(eq (sig fsm_state) (const 1'd1))
|
|
|
|
)
|
|
|
|
(case 1
|
|
|
|
(eq (sig b) (~ (sig b)))
|
|
|
|
(switch (cat (sig c))
|
|
|
|
(case 1
|
|
|
|
(eq (sig fsm_state) (const 1'd0)))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
2024-02-27 07:03:48 -07:00
|
|
|
self.assertEqual({repr(sig): k for k, v in frag.drivers.items() for sig in v}, {
|
2024-02-09 12:27:25 -07:00
|
|
|
"(sig a)": "comb",
|
2018-12-26 01:55:04 -07:00
|
|
|
"(sig fsm_state)": "sync",
|
|
|
|
"(sig b)": "sync",
|
2024-03-23 10:44:44 -06:00
|
|
|
"(sig)": "comb",
|
2018-12-26 01:55:04 -07:00
|
|
|
})
|
2018-12-26 05:39:05 -07:00
|
|
|
fsm = frag.find_generated("fsm")
|
|
|
|
self.assertIsInstance(fsm.state, Signal)
|
|
|
|
self.assertEqual(fsm.encoding, OrderedDict({
|
|
|
|
"FIRST": 0,
|
|
|
|
"SECOND": 1,
|
|
|
|
}))
|
|
|
|
self.assertEqual(fsm.decoding, OrderedDict({
|
|
|
|
0: "FIRST",
|
|
|
|
1: "SECOND"
|
|
|
|
}))
|
|
|
|
|
2024-02-13 23:13:12 -07:00
|
|
|
def test_FSM_init(self):
|
2018-12-26 01:55:04 -07:00
|
|
|
a = Signal()
|
|
|
|
m = Module()
|
2024-02-13 23:13:12 -07:00
|
|
|
with m.FSM(init="SECOND"):
|
2018-12-26 01:55:04 -07:00
|
|
|
with m.State("FIRST"):
|
|
|
|
m.d.comb += a.eq(0)
|
|
|
|
m.next = "SECOND"
|
|
|
|
with m.State("SECOND"):
|
|
|
|
m.next = "FIRST"
|
2024-02-27 07:03:48 -07:00
|
|
|
frag = m.elaborate(platform=None)
|
|
|
|
self.assertRepr(frag.statements["comb"], """
|
2018-12-26 01:55:04 -07:00
|
|
|
(
|
|
|
|
(switch (sig fsm_state)
|
2018-12-27 09:02:31 -07:00
|
|
|
(case 0
|
2018-12-26 01:55:04 -07:00
|
|
|
(eq (sig a) (const 1'd0))
|
2024-02-08 17:53:45 -07:00
|
|
|
)
|
|
|
|
(case 1 )
|
|
|
|
)
|
2024-03-23 10:44:44 -06:00
|
|
|
(eq (sig) (== (sig fsm_state) (const 1'd0)))
|
|
|
|
(eq (sig) (== (sig fsm_state) (const 1'd1)))
|
2024-02-08 17:53:45 -07:00
|
|
|
)
|
|
|
|
""")
|
2024-02-27 07:03:48 -07:00
|
|
|
self.assertRepr(frag.statements["sync"], """
|
2024-02-08 17:53:45 -07:00
|
|
|
(
|
|
|
|
(switch (sig fsm_state)
|
|
|
|
(case 0
|
2018-12-27 09:02:31 -07:00
|
|
|
(eq (sig fsm_state) (const 1'd1))
|
|
|
|
)
|
|
|
|
(case 1
|
2018-12-26 01:55:04 -07:00
|
|
|
(eq (sig fsm_state) (const 1'd0))
|
|
|
|
)
|
2018-12-27 09:02:31 -07:00
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
2024-02-13 23:13:12 -07:00
|
|
|
self.assertEqual(m._generated["fsm"].state.init, 1)
|
|
|
|
|
|
|
|
def test_FSM_reset(self):
|
|
|
|
a = Signal()
|
|
|
|
m = Module()
|
|
|
|
with self.assertWarnsRegex(DeprecationWarning,
|
|
|
|
r"^`reset=` is deprecated, use `init=` instead$"):
|
|
|
|
with m.FSM(reset="SECOND"):
|
|
|
|
with m.State("FIRST"):
|
|
|
|
m.d.comb += a.eq(0)
|
|
|
|
m.next = "SECOND"
|
|
|
|
with m.State("SECOND"):
|
|
|
|
m.next = "FIRST"
|
2024-02-27 07:03:48 -07:00
|
|
|
frag = m.elaborate(platform=None)
|
|
|
|
self.assertRepr(frag.statements["comb"], """
|
2024-02-13 23:13:12 -07:00
|
|
|
(
|
|
|
|
(switch (sig fsm_state)
|
|
|
|
(case 0
|
|
|
|
(eq (sig a) (const 1'd0))
|
|
|
|
)
|
|
|
|
(case 1 )
|
|
|
|
)
|
2024-03-23 10:44:44 -06:00
|
|
|
(eq (sig) (== (sig fsm_state) (const 1'd0)))
|
|
|
|
(eq (sig) (== (sig fsm_state) (const 1'd1)))
|
2024-02-13 23:13:12 -07:00
|
|
|
)
|
|
|
|
""")
|
2024-02-27 07:03:48 -07:00
|
|
|
self.assertRepr(frag.statements["sync"], """
|
2024-02-13 23:13:12 -07:00
|
|
|
(
|
|
|
|
(switch (sig fsm_state)
|
|
|
|
(case 0
|
|
|
|
(eq (sig fsm_state) (const 1'd1))
|
|
|
|
)
|
|
|
|
(case 1
|
|
|
|
(eq (sig fsm_state) (const 1'd0))
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
self.assertEqual(m._generated["fsm"].state.init, 1)
|
|
|
|
|
|
|
|
def test_FSM_reset_wrong(self):
|
|
|
|
a = Signal()
|
|
|
|
m = Module()
|
|
|
|
with self.assertRaisesRegex(ValueError,
|
|
|
|
r"^Cannot specify both `reset` and `init`$"):
|
|
|
|
with m.FSM(reset="SECOND", init="SECOND"):
|
|
|
|
with m.State("FIRST"):
|
|
|
|
m.d.comb += a.eq(0)
|
|
|
|
m.next = "SECOND"
|
|
|
|
with m.State("SECOND"):
|
|
|
|
m.next = "FIRST"
|
2018-12-27 09:02:31 -07:00
|
|
|
|
|
|
|
def test_FSM_ongoing(self):
|
|
|
|
a = Signal()
|
|
|
|
b = Signal()
|
|
|
|
m = Module()
|
|
|
|
with m.FSM() as fsm:
|
|
|
|
m.d.comb += b.eq(fsm.ongoing("SECOND"))
|
|
|
|
with m.State("FIRST"):
|
|
|
|
pass
|
|
|
|
m.d.comb += a.eq(fsm.ongoing("FIRST"))
|
|
|
|
with m.State("SECOND"):
|
|
|
|
pass
|
2024-02-27 07:03:48 -07:00
|
|
|
frag = m.elaborate(platform=None)
|
2024-02-13 23:13:12 -07:00
|
|
|
self.assertEqual(m._generated["fsm"].state.init, 1)
|
2018-12-27 09:02:31 -07:00
|
|
|
self.maxDiff = 10000
|
2024-02-27 07:03:48 -07:00
|
|
|
self.assertRepr(frag.statements["comb"], """
|
2018-12-27 09:02:31 -07:00
|
|
|
(
|
2024-03-23 10:44:44 -06:00
|
|
|
(eq (sig b) (sig))
|
|
|
|
(eq (sig a) (sig))
|
|
|
|
(eq (sig) (== (sig fsm_state) (const 1'd0)))
|
|
|
|
(eq (sig) (== (sig fsm_state) (const 1'd1)))
|
2018-12-26 01:55:04 -07:00
|
|
|
)
|
|
|
|
""")
|
|
|
|
|
2019-07-06 18:59:34 -06:00
|
|
|
def test_FSM_empty(self):
|
|
|
|
m = Module()
|
|
|
|
with m.FSM():
|
|
|
|
pass
|
2024-02-08 17:53:45 -07:00
|
|
|
self.assertEqual(m._statements, {})
|
2019-07-06 18:59:34 -06:00
|
|
|
|
2019-07-08 04:26:49 -06:00
|
|
|
def test_FSM_wrong_domain(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(ValueError,
|
|
|
|
r"^FSM may not be driven by the 'comb' domain$"):
|
2019-07-08 04:26:49 -06:00
|
|
|
with m.FSM(domain="comb"):
|
|
|
|
pass
|
|
|
|
|
2020-02-06 10:47:46 -07:00
|
|
|
def test_FSM_wrong_undefined(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(NameError,
|
|
|
|
r"^FSM state 'FOO' is referenced but not defined$"):
|
2020-02-06 10:47:46 -07:00
|
|
|
with m.FSM() as fsm:
|
|
|
|
fsm.ongoing("FOO")
|
|
|
|
|
2018-12-26 01:55:04 -07:00
|
|
|
def test_FSM_wrong_redefined(self):
|
|
|
|
m = Module()
|
|
|
|
with m.FSM():
|
|
|
|
with m.State("FOO"):
|
|
|
|
pass
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(NameError,
|
|
|
|
r"^FSM state 'FOO' is already defined$"):
|
2018-12-26 01:55:04 -07:00
|
|
|
with m.State("FOO"):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_FSM_wrong_next(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^Only assignment to `m\.next` is permitted$"):
|
2018-12-26 01:55:04 -07:00
|
|
|
m.next
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
|
2018-12-26 01:55:04 -07:00
|
|
|
m.next = "FOO"
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^`m\.next = <\.\.\.>` is only permitted inside an FSM state$"):
|
2018-12-26 05:42:43 -07:00
|
|
|
with m.FSM():
|
|
|
|
m.next = "FOO"
|
2018-12-26 01:55:04 -07:00
|
|
|
|
2019-07-06 18:59:57 -06:00
|
|
|
def test_If_inside_FSM_wrong(self):
|
|
|
|
m = Module()
|
|
|
|
with m.FSM():
|
|
|
|
with m.State("FOO"):
|
|
|
|
pass
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
(r"^If is not permitted directly inside of FSM; "
|
|
|
|
r"it is permitted inside of FSM State$")):
|
2019-07-06 18:59:57 -06:00
|
|
|
with m.If(self.s2):
|
|
|
|
pass
|
|
|
|
|
2021-05-10 20:02:29 -06:00
|
|
|
def test_State_outside_FSM_wrong(self):
|
|
|
|
m = Module()
|
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^FSM State is not permitted outside of FSM"):
|
|
|
|
with m.State("FOO"):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
def test_FSM_State_wrong_nested(self):
|
|
|
|
m = Module()
|
|
|
|
with m.FSM():
|
|
|
|
with m.State("FOO"):
|
|
|
|
with self.assertRaisesRegex(SyntaxError,
|
|
|
|
r"^FSM State is not permitted outside of FSM"):
|
|
|
|
with m.State("BAR"):
|
|
|
|
pass
|
|
|
|
|
2018-12-13 00:11:06 -07:00
|
|
|
def test_auto_pop_ctrl(self):
|
2018-12-12 23:06:51 -07:00
|
|
|
m = Module()
|
|
|
|
with m.If(self.w1):
|
|
|
|
m.d.comb += self.c1.eq(1)
|
|
|
|
m.d.comb += self.c2.eq(1)
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(m._statements["comb"], """
|
2018-12-12 23:06:51 -07:00
|
|
|
(
|
|
|
|
(switch (cat (b (sig w1)))
|
|
|
|
(case 1 (eq (sig c1) (const 1'd1)))
|
|
|
|
)
|
|
|
|
(eq (sig c2) (const 1'd1))
|
|
|
|
)
|
|
|
|
""")
|
2018-12-13 00:24:28 -07:00
|
|
|
|
|
|
|
def test_submodule_anon(self):
|
|
|
|
m1 = Module()
|
|
|
|
m2 = Module()
|
|
|
|
m1.submodules += m2
|
2024-02-16 08:16:26 -07:00
|
|
|
self.assertEqual(len(m1._anon_submodules), 1)
|
|
|
|
self.assertEqual(m1._anon_submodules[0][0], m2)
|
2019-07-19 06:39:47 -06:00
|
|
|
self.assertEqual(m1._named_submodules, {})
|
2018-12-13 00:24:28 -07:00
|
|
|
|
|
|
|
def test_submodule_anon_multi(self):
|
|
|
|
m1 = Module()
|
|
|
|
m2 = Module()
|
|
|
|
m3 = Module()
|
|
|
|
m1.submodules += m2, m3
|
2024-02-16 08:16:26 -07:00
|
|
|
self.assertEqual(len(m1._anon_submodules), 2)
|
|
|
|
self.assertEqual(m1._anon_submodules[0][0], m2)
|
|
|
|
self.assertEqual(m1._anon_submodules[1][0], m3)
|
2019-07-19 06:39:47 -06:00
|
|
|
self.assertEqual(m1._named_submodules, {})
|
2018-12-13 00:24:28 -07:00
|
|
|
|
|
|
|
def test_submodule_named(self):
|
|
|
|
m1 = Module()
|
|
|
|
m2 = Module()
|
|
|
|
m1.submodules.foo = m2
|
2019-07-19 06:39:47 -06:00
|
|
|
self.assertEqual(m1._anon_submodules, [])
|
2024-02-16 08:16:26 -07:00
|
|
|
self.assertEqual(m1._named_submodules.keys(), {"foo"})
|
|
|
|
self.assertEqual(m1._named_submodules["foo"][0], m2)
|
2018-12-13 00:24:28 -07:00
|
|
|
|
2019-06-02 20:22:55 -06:00
|
|
|
def test_submodule_named_index(self):
|
|
|
|
m1 = Module()
|
|
|
|
m2 = Module()
|
|
|
|
m1.submodules["foo"] = m2
|
2019-07-19 06:39:47 -06:00
|
|
|
self.assertEqual(m1._anon_submodules, [])
|
2024-02-16 08:16:26 -07:00
|
|
|
self.assertEqual(m1._named_submodules.keys(), {"foo"})
|
|
|
|
self.assertEqual(m1._named_submodules["foo"][0], m2)
|
2019-06-02 20:22:55 -06:00
|
|
|
|
2018-12-13 00:24:28 -07:00
|
|
|
def test_submodule_wrong(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
|
2018-12-13 00:24:28 -07:00
|
|
|
m.submodules.foo = 1
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Trying to add 1, which does not implement \.elaborate\(\), as a submodule$"):
|
2018-12-13 00:24:28 -07:00
|
|
|
m.submodules += 1
|
2018-12-13 00:33:56 -07:00
|
|
|
|
2019-07-19 06:39:47 -06:00
|
|
|
def test_submodule_named_conflict(self):
|
|
|
|
m1 = Module()
|
|
|
|
m2 = Module()
|
|
|
|
m1.submodules.foo = m2
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(NameError, r"^Submodule named 'foo' already exists$"):
|
2019-07-19 06:39:47 -06:00
|
|
|
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()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(AttributeError, r"^No submodule named 'foo' exists$"):
|
2019-07-19 06:39:47 -06:00
|
|
|
m2 = m1.submodules.foo
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(AttributeError, r"^No submodule named 'foo' exists$"):
|
2019-07-19 06:39:47 -06:00
|
|
|
m2 = m1.submodules["foo"]
|
|
|
|
|
2018-12-16 16:51:24 -07:00
|
|
|
def test_domain_named_implicit(self):
|
|
|
|
m = Module()
|
|
|
|
m.domains += ClockDomain("sync")
|
|
|
|
self.assertEqual(len(m._domains), 1)
|
|
|
|
|
|
|
|
def test_domain_named_explicit(self):
|
|
|
|
m = Module()
|
|
|
|
m.domains.foo = ClockDomain()
|
|
|
|
self.assertEqual(len(m._domains), 1)
|
2020-05-19 17:40:49 -06:00
|
|
|
self.assertEqual(m._domains["foo"].name, "foo")
|
2018-12-16 16:51:24 -07:00
|
|
|
|
2020-02-06 08:19:16 -07:00
|
|
|
def test_domain_add_wrong(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Only clock domains may be added to `m\.domains`, not 1$"):
|
2020-02-06 08:19:16 -07:00
|
|
|
m.domains.foo = 1
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(TypeError,
|
|
|
|
r"^Only clock domains may be added to `m\.domains`, not 1$"):
|
2020-02-06 08:19:16 -07:00
|
|
|
m.domains += 1
|
|
|
|
|
2020-02-06 09:13:59 -07:00
|
|
|
def test_domain_add_wrong_name(self):
|
|
|
|
m = Module()
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(NameError,
|
|
|
|
r"^Clock domain name 'bar' must match name in `m\.domains\.foo \+= \.\.\.` syntax$"):
|
2020-02-06 09:13:59 -07:00
|
|
|
m.domains.foo = ClockDomain("bar")
|
|
|
|
|
2020-05-19 17:40:49 -06:00
|
|
|
def test_domain_add_wrong_duplicate(self):
|
|
|
|
m = Module()
|
|
|
|
m.domains += ClockDomain("foo")
|
2020-07-28 13:35:25 -06:00
|
|
|
with self.assertRaisesRegex(NameError,
|
|
|
|
r"^Clock domain named 'foo' already exists$"):
|
2020-05-19 17:40:49 -06:00
|
|
|
m.domains += ClockDomain("foo")
|
|
|
|
|
2018-12-13 00:33:56 -07:00
|
|
|
def test_lower(self):
|
|
|
|
m1 = Module()
|
|
|
|
m1.d.comb += self.c1.eq(self.s1)
|
|
|
|
m2 = Module()
|
|
|
|
m2.d.comb += self.c2.eq(self.s2)
|
|
|
|
m2.d.sync += self.c3.eq(self.s3)
|
|
|
|
m1.submodules.foo = m2
|
|
|
|
|
2019-01-25 19:31:12 -07:00
|
|
|
f1 = m1.elaborate(platform=None)
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(f1.statements["comb"], """
|
2018-12-13 00:33:56 -07:00
|
|
|
(
|
|
|
|
(eq (sig c1) (sig s1))
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
self.assertEqual(f1.drivers, {
|
2024-02-09 12:27:25 -07:00
|
|
|
"comb": SignalSet((self.c1,))
|
2018-12-13 00:33:56 -07:00
|
|
|
})
|
|
|
|
self.assertEqual(len(f1.subfragments), 1)
|
2024-02-16 08:16:26 -07:00
|
|
|
(f2, f2_name, _), = f1.subfragments
|
2018-12-13 00:33:56 -07:00
|
|
|
self.assertEqual(f2_name, "foo")
|
2024-02-09 12:27:25 -07:00
|
|
|
self.assertRepr(f2.statements["comb"], """
|
2018-12-13 00:33:56 -07:00
|
|
|
(
|
|
|
|
(eq (sig c2) (sig s2))
|
2024-02-08 17:53:45 -07:00
|
|
|
)
|
|
|
|
""")
|
|
|
|
self.assertRepr(f2.statements["sync"], """
|
|
|
|
(
|
2018-12-13 00:33:56 -07:00
|
|
|
(eq (sig c3) (sig s3))
|
|
|
|
)
|
|
|
|
""")
|
|
|
|
self.assertEqual(f2.drivers, {
|
2024-02-09 12:27:25 -07:00
|
|
|
"comb": SignalSet((self.c2,)),
|
2018-12-17 10:21:12 -07:00
|
|
|
"sync": SignalSet((self.c3,))
|
2018-12-13 00:33:56 -07:00
|
|
|
})
|
|
|
|
self.assertEqual(len(f2.subfragments), 0)
|