Implement RFC 53: Low-level I/O primitives.

Co-authored-by: Catherine <whitequark@whitequark.org>
Co-authored-by: mcclure <mcclure@users.noreply.github.com>
This commit is contained in:
Wanda 2024-03-15 06:37:17 +01:00 committed by Catherine
parent 18b54ded0a
commit 744576011f
16 changed files with 1364 additions and 436 deletions

View file

@ -1,11 +1,6 @@
# amaranth: UnusedElaboratable=no
import warnings
from amaranth import *
with warnings.catch_warnings():
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
from amaranth.hdl.rec import *
from amaranth.hdl import *
from amaranth.lib.wiring import *
from amaranth.lib.io import *
from amaranth.build.dsl import *
@ -77,14 +72,11 @@ class ResourceManagerTestCase(FHDLTestCase):
def test_request_with_dir(self):
i2c = self.cm.request("i2c", 0, dir={"sda": "o"})
self.assertIsInstance(i2c, PureInterface)
self.assertTrue(i2c.signature.is_compliant(i2c))
self.assertIsInstance(flipped(i2c.sda), Pin)
self.assertEqual(i2c.sda.dir, "o")
def test_request_tristate(self):
i2c = self.cm.request("i2c", 0)
self.assertTrue(i2c.signature.is_compliant(i2c))
self.assertEqual(i2c.sda.dir, "io")
ports = list(self.cm.iter_ports())
@ -158,7 +150,7 @@ class ResourceManagerTestCase(FHDLTestCase):
def test_request_raw(self):
clk50 = self.cm.request("clk50", 0, dir="-")
self.assertIsInstance(clk50.io, Signal)
self.assertIsInstance(clk50.io, IOPort)
ports = list(self.cm.iter_ports())
self.assertEqual(len(ports), 1)
@ -166,8 +158,8 @@ class ResourceManagerTestCase(FHDLTestCase):
def test_request_raw_diffpairs(self):
clk100 = self.cm.request("clk100", 0, dir="-")
self.assertIsInstance(clk100.p, Signal)
self.assertIsInstance(clk100.n, Signal)
self.assertIsInstance(clk100.p, IOPort)
self.assertIsInstance(clk100.n, IOPort)
ports = list(self.cm.iter_ports())
self.assertEqual(len(ports), 2)

View file

@ -308,7 +308,7 @@ class ValueTestCase(FHDLTestCase):
self.assertEqual(s2.start, 1)
self.assertEqual(s2.stop, 2)
s3 = Const(31)[::2]
self.assertIsInstance(s3, Cat)
self.assertIsInstance(s3, Concat)
self.assertIsInstance(s3.parts[0], Slice)
self.assertEqual(s3.parts[0].start, 0)
self.assertEqual(s3.parts[0].stop, 1)
@ -1679,3 +1679,109 @@ class SwitchTestCase(FHDLTestCase):
def test_two_cases(self):
s = Switch(Const(0, 8), {("00001111", 123): []})
self.assertEqual(s.cases, {("00001111", "01111011"): []})
class IOValueTestCase(FHDLTestCase):
def test_ioport(self):
a = IOPort(4)
self.assertEqual(len(a), 4)
self.assertEqual(a.attrs, {})
self.assertEqual(a.metadata, (None, None, None, None))
self.assertEqual(a._ioports(), {a})
self.assertRepr(a, "(io-port a)")
b = IOPort(3, name="b", attrs={"a": "b"}, metadata=["x", "y", "z"])
self.assertEqual(len(b), 3)
self.assertEqual(b.attrs, {"a": "b"})
self.assertEqual(b.metadata, ("x", "y", "z"))
self.assertEqual(b._ioports(), {b})
self.assertRepr(b, "(io-port b)")
def test_ioport_wrong(self):
with self.assertRaisesRegex(TypeError,
r"^Name must be a string, not 3$"):
a = IOPort(2, name=3)
with self.assertRaises(TypeError):
a = IOPort("a")
with self.assertRaises(TypeError):
a = IOPort(8, attrs=3)
with self.assertRaises(TypeError):
a = IOPort(8, metadata=3)
with self.assertRaisesRegex(ValueError,
r"^Metadata length \(3\) doesn't match port width \(2\)$"):
a = IOPort(2, metadata=["a", "b", "c"])
def test_ioslice(self):
a = IOPort(8, metadata=["a", "b", "c", "d", "e", "f", "g", "h"])
s = a[2:5]
self.assertEqual(len(s), 3)
self.assertEqual(s.metadata, ("c", "d", "e"))
self.assertEqual(s._ioports(), {a})
self.assertRepr(s, "(io-slice (io-port a) 2:5)")
s = a[-5:-2]
self.assertEqual(len(s), 3)
self.assertEqual(s.metadata, ("d", "e", "f"))
self.assertEqual(s._ioports(), {a})
self.assertRepr(s, "(io-slice (io-port a) 3:6)")
s = IOSlice(a, -5, -2)
self.assertEqual(len(s), 3)
self.assertEqual(s.metadata, ("d", "e", "f"))
self.assertEqual(s._ioports(), {a})
self.assertRepr(s, "(io-slice (io-port a) 3:6)")
s = a[5]
self.assertEqual(len(s), 1)
self.assertEqual(s.metadata, ("f",))
self.assertEqual(s._ioports(), {a})
self.assertRepr(s, "(io-slice (io-port a) 5:6)")
s = a[-1]
self.assertEqual(len(s), 1)
self.assertEqual(s.metadata, ("h",))
self.assertEqual(s._ioports(), {a})
self.assertRepr(s, "(io-slice (io-port a) 7:8)")
s = a[::2]
self.assertEqual(len(s), 4)
self.assertEqual(s.metadata, ("a", "c", "e", "g"))
self.assertEqual(s._ioports(), {a})
self.assertRepr(s, "(io-cat (io-slice (io-port a) 0:1) (io-slice (io-port a) 2:3) (io-slice (io-port a) 4:5) (io-slice (io-port a) 6:7))")
def test_ioslice_wrong(self):
a = IOPort(8)
with self.assertRaises(IndexError):
a[8]
with self.assertRaises(IndexError):
a[-9]
with self.assertRaises(TypeError):
a["a"]
with self.assertRaises(IndexError):
IOSlice(a, 0, 9)
with self.assertRaises(IndexError):
IOSlice(a, -10, 8)
with self.assertRaises(TypeError):
IOSlice(a, 0, "a")
with self.assertRaises(TypeError):
IOSlice(a, "a", 8)
with self.assertRaises(IndexError):
a[5:3]
def test_iocat(self):
a = IOPort(3, name="a", metadata=["a", "b", "c"])
b = IOPort(2, name="b", metadata=["x", "y"])
c = Cat(a, b)
self.assertEqual(len(c), 5)
self.assertEqual(c.metadata, ("a", "b", "c", "x", "y"))
self.assertEqual(c._ioports(), {a, b})
self.assertRepr(c, "(io-cat (io-port a) (io-port b))")
c = Cat(a, Cat())
self.assertEqual(len(c), 3)
self.assertEqual(c.metadata, ("a", "b", "c"))
self.assertEqual(c._ioports(), {a})
self.assertRepr(c, "(io-cat (io-port a) (io-cat ))")
c = Cat(a, Cat()[:])
self.assertEqual(len(c), 3)
self.assertRepr(c, "(io-cat (io-port a) (io-cat ))")
def test_iocat_wrong(self):
a = IOPort(3, name="a")
b = Signal()
with self.assertRaisesRegex(TypeError,
r"^Object \(sig b\) cannot be converted to an IO value$"):
Cat(a, b)

View file

@ -92,7 +92,7 @@ class FragmentPortsTestCase(FHDLTestCase):
self.assertRepr(nl, """
(
(module 0 None ('top'))
(cell 0 0 (top ))
(cell 0 0 (top))
)
""")
@ -177,21 +177,21 @@ class FragmentPortsTestCase(FHDLTestCase):
(output 'c2' 2.0)
(output 'c3' 1.0))
(module 1 0 ('top' 'f1')
(input 'port$0$2' 0.2)
(output 'port$1$0' 1.0)
(output 'port$2$0' 2.0)
(output 'port$5$0' 5.0)
(input 'port$10$0' 10.0))
(module 2 1 ('top' 'f1' 'f11')
(input 'port$0$2' 0.2)
(output 'port$1$0' 1.0)
(output 'port$2$0' 2.0)
(input 'port$6$0' 6.0))
(module 3 2 ('top' 'f1' 'f11' 'f111')
(input 'port$0$2' 0.2)
(input 's1' 0.2)
(output 'c3' 1.0)
(output 'c2' 2.0)
(input 'port$6$0' 6.0))
(output 'c1' 5.0)
(input 's2' 10.0))
(module 2 1 ('top' 'f1' 'f11')
(input 's1' 0.2)
(output 'c3' 1.0)
(output 'c2' 2.0)
(input 's3' 6.0))
(module 3 2 ('top' 'f1' 'f11' 'f111')
(input 's1' 0.2)
(output 'c3' 1.0)
(output 'c2' 2.0)
(input 's3' 6.0))
(module 4 3 ('top' 'f1' 'f11' 'f111' 'f1111')
(input 's1' 0.2)
(output 'c2' 2.0)
@ -200,9 +200,9 @@ class FragmentPortsTestCase(FHDLTestCase):
(output 'c1' 5.0)
(input 's3' 6.0))
(module 6 1 ('top' 'f1' 'f13')
(input 'port$0$2' 0.2)
(output 'port$6$0' 6.0)
(input 'port$10$0' 10.0))
(input 's1' 0.2)
(output 's3' 6.0)
(input 's2' 10.0))
(module 7 6 ('top' 'f1' 'f13' 'f131')
(input 's1' 0.2)
(output 's3' 6.0)
@ -229,12 +229,12 @@ class FragmentPortsTestCase(FHDLTestCase):
nl = build_netlist(f, ports={
"a": (self.s1, PortDirection.Output),
"b": (self.s2, PortDirection.Input),
"c": (self.s3, PortDirection.Inout),
"c": (IOPort(1, name="io3"), PortDirection.Inout),
})
self.assertRepr(nl, """
(
(module 0 None ('top') (input 'b' 0.2) (inout 'c' 0.3) (output 'a' 1'd0))
(cell 0 0 (top (input 'b' 2:3) (output 'a' 1'd0) (inout 'c' 3:4)))
(module 0 None ('top') (input 'b' 0.2) (output 'a' 1'd0) (io inout 'c' 0.0))
(cell 0 0 (top (input 'b' 2:3) (output 'a' 1'd0)))
)
""")
@ -308,6 +308,65 @@ class FragmentPortsTestCase(FHDLTestCase):
)
""")
def test_port_io(self):
io = IOPort(8)
f = Fragment()
f1 = Fragment()
f1.add_subfragment(Instance("t", i_io=io[:2]), "i")
f.add_subfragment(f1, "f1")
f2 = Fragment()
f2.add_subfragment(Instance("t", o_io=io[2:4]), "i")
f.add_subfragment(f2, "f2")
f3 = Fragment()
f3.add_subfragment(Instance("t", io_io=io[4:6]), "i")
f.add_subfragment(f3, "f3")
nl = build_netlist(f, ports=[])
self.assertRepr(nl, """
(
(module 0 None ('top')
(io inout 'io' 0.0:8)
)
(module 1 0 ('top' 'f1')
(io input 'ioport$0$0' 0.0:2)
)
(module 2 0 ('top' 'f2')
(io output 'ioport$0$2' 0.2:4)
)
(module 3 0 ('top' 'f3')
(io inout 'ioport$0$4' 0.4:6)
)
(cell 0 0 (top))
(cell 1 1 (instance 't' 'i' (io input 'io' 0.0:2)))
(cell 2 2 (instance 't' 'i' (io output 'io' 0.2:4)))
(cell 3 3 (instance 't' 'i' (io inout 'io' 0.4:6)))
)
""")
def test_port_io_part(self):
io = IOPort(4)
f = Fragment()
f1 = Fragment()
f1.add_subfragment(Instance("t", i_i=io[0], o_o=io[1], io_io=io[2]), "i")
f.add_subfragment(f1, "f1")
nl = build_netlist(f, ports=[])
self.assertRepr(nl, """
(
(module 0 None ('top')
(io inout 'io' 0.0:4)
)
(module 1 0 ('top' 'f1')
(io input 'ioport$0$0' 0.0)
(io output 'ioport$0$1' 0.1)
(io inout 'ioport$0$2' 0.2)
)
(cell 0 0 (top))
(cell 1 1 (instance 't' 'i'
(io input 'i' 0.0)
(io output 'o' 0.1)
(io inout 'io' 0.2)
))
)
""")
def test_port_instance(self):
f = Fragment()
@ -316,40 +375,53 @@ class FragmentPortsTestCase(FHDLTestCase):
a = Signal(4)
b = Signal(4)
c = Signal(4)
d = Signal(4)
ioa = IOPort(4)
iob = IOPort(4)
ioc = IOPort(4)
f1.add_subfragment(Instance("t",
p_p = "meow",
a_a = True,
i_aa=a,
io_bb=b,
o_bb=b,
o_cc=c,
o_dd=d,
i_aaa=ioa,
o_bbb=iob,
io_ccc=ioc,
), "i")
nl = build_netlist(f, ports=[a, b, c, d])
nl = build_netlist(f, ports=[a, b, c])
self.assertRepr(nl, """
(
(module 0 None ('top')
(input 'a' 0.2:6)
(inout 'b' 0.6:10)
(output 'c' 1.0:4)
(output 'd' 1.4:8))
(output 'b' 1.0:4)
(output 'c' 1.4:8)
(io input 'ioa' 0.0:4)
(io output 'iob' 1.0:4)
(io inout 'ioc' 2.0:4)
)
(module 1 0 ('top' 'f1')
(input 'port$0$2' 0.2:6)
(inout 'port$0$6' 0.6:10)
(output 'port$1$0' 1.0:4)
(output 'port$1$4' 1.4:8))
(input 'a' 0.2:6)
(output 'b' 1.0:4)
(output 'c' 1.4:8)
(io input 'ioa' 0.0:4)
(io output 'iob' 1.0:4)
(io inout 'ioc' 2.0:4)
)
(cell 0 0 (top
(input 'a' 2:6)
(output 'c' 1.0:4)
(output 'd' 1.4:8)
(inout 'b' 6:10)))
(output 'b' 1.0:4)
(output 'c' 1.4:8)
))
(cell 1 1 (instance 't' 'i'
(param 'p' 'meow')
(attr 'a' True)
(input 'aa' 0.2:6)
(output 'cc' 0:4)
(output 'dd' 4:8)
(inout 'bb' 0.6:10)))
(output 'bb' 0:4)
(output 'cc' 4:8)
(io input 'aaa' 0.0:4)
(io output 'bbb' 1.0:4)
(io inout 'ccc' 2.0:4)
))
)
""")
@ -357,7 +429,7 @@ class FragmentPortsTestCase(FHDLTestCase):
f = Fragment()
a = Signal()
with self.assertRaisesRegex(TypeError,
r"^Only signals may be added as ports, not \(const 1'd1\)$"):
r"^Only signals and IO ports may be added as ports, not \(const 1'd1\)$"):
build_netlist(f, ports=(Const(1),))
with self.assertRaisesRegex(TypeError,
r"^Port name must be a string, not 1$"):
@ -619,19 +691,27 @@ class InstanceTestCase(FHDLTestCase):
s2 = Signal()
s3 = Signal()
s4 = Signal()
s5 = Signal()
s6 = Signal()
io1 = IOPort(1)
io2 = IOPort(1)
io3 = IOPort(1)
io4 = IOPort(1)
io5 = IOPort(1)
io6 = IOPort(1)
inst = Instance("foo",
("a", "ATTR1", 1),
("p", "PARAM1", 0x1234),
("i", "s1", s1),
("o", "s2", s2),
("io", "s3", s3),
("i", "io1", io1),
("o", "io2", io2),
("io", "io3", io3),
a_ATTR2=2,
p_PARAM2=0x5678,
i_s4=s4,
o_s5=s5,
io_s6=s6,
i_s3=s3,
o_s4=s4,
i_io4=io4,
o_io5=io5,
io_io6=io6,
)
self.assertEqual(inst.attrs, OrderedDict([
("ATTR1", 1),
@ -644,27 +724,27 @@ class InstanceTestCase(FHDLTestCase):
self.assertEqual(inst.named_ports, OrderedDict([
("s1", (s1, "i")),
("s2", (s2, "o")),
("s3", (s3, "io")),
("s4", (s4, "i")),
("s5", (s5, "o")),
("s6", (s6, "io")),
("io1", (io1, "i")),
("io2", (io2, "o")),
("io3", (io3, "io")),
("s3", (s3, "i")),
("s4", (s4, "o")),
("io4", (io4, "i")),
("io5", (io5, "o")),
("io6", (io6, "io")),
]))
def test_cast_ports(self):
inst = Instance("foo",
("i", "s1", 1),
("o", "s2", 2),
("io", "s3", 3),
i_s4=4,
o_s5=5,
io_s6=6,
("io", "s2", Cat()),
i_s3=3,
io_s4=Cat(),
)
self.assertRepr(inst.named_ports["s1"][0], "(const 1'd1)")
self.assertRepr(inst.named_ports["s2"][0], "(const 2'd2)")
self.assertRepr(inst.named_ports["s2"][0], "(io-cat )")
self.assertRepr(inst.named_ports["s3"][0], "(const 2'd3)")
self.assertRepr(inst.named_ports["s4"][0], "(const 3'd4)")
self.assertRepr(inst.named_ports["s5"][0], "(const 3'd5)")
self.assertRepr(inst.named_ports["s6"][0], "(const 3'd6)")
self.assertRepr(inst.named_ports["s4"][0], "(io-cat )")
def test_wrong_construct_arg(self):
s = Signal()
@ -683,7 +763,7 @@ class InstanceTestCase(FHDLTestCase):
def setUp_cpu(self):
self.rst = Signal()
self.stb = Signal()
self.pins = Signal(8)
self.pins = IOPort(8)
self.datal = Signal(4)
self.datah = Signal(4)
self.inst = Instance("cpu",
@ -716,33 +796,40 @@ class InstanceTestCase(FHDLTestCase):
f = Fragment()
i = Signal(3)
o = Signal(4)
io = Signal(5)
ioa = IOPort(5)
iob = IOPort(6)
ioc = IOPort(7)
f.add_subfragment(Instance("gadget",
i_i=i,
o_o=o,
io_io=io,
i_ioa=ioa,
o_iob=iob,
io_ioc=ioc,
p_param="TEST",
a_attr=1234,
), "my_gadget")
nl = build_netlist(f, [i, o, io])
nl = build_netlist(f, [i, o])
self.assertRepr(nl, """
(
(module 0 None ('top')
(input 'i' 0.2:5)
(inout 'io' 0.5:10)
(output 'o' 1.0:4)
(io input 'ioa' 0.0:5)
(io output 'iob' 1.0:6)
(io inout 'ioc' 2.0:7)
)
(cell 0 0 (top
(input 'i' 2:5)
(output 'o' 1.0:4)
(inout 'io' 5:10)
))
(cell 1 0 (instance 'gadget' 'my_gadget'
(param 'param' 'TEST')
(attr 'attr' 1234)
(input 'i' 0.2:5)
(output 'o' 0:4)
(inout 'io' 0.5:10)
(io input 'ioa' 0.0:5)
(io output 'iob' 1.0:6)
(io inout 'ioc' 2.0:7)
))
)
""")
@ -798,33 +885,72 @@ class InstanceTestCase(FHDLTestCase):
)
""")
def test_nir_io_slice(self):
f = Fragment()
io = IOPort(8)
f.add_subfragment(Instance("test",
i_i=io[:2],
o_o=io[2:4],
io_io=io[4:6],
), "t1")
nl = build_netlist(f, [])
self.assertRepr(nl, """
(
(module 0 None ('top')
(io inout 'io' 0.0:8)
)
(cell 0 0 (top))
(cell 1 0 (instance 'test' 't1'
(io input 'i' 0.0:2)
(io output 'o' 0.2:4)
(io inout 'io' 0.4:6)
))
)
""")
def test_nir_io_concat(self):
f = Fragment()
io1 = IOPort(4)
io2 = IOPort(4)
f.add_subfragment(Instance("test",
io_io=Cat(io1, io2),
))
nl = build_netlist(f, [io1, io2])
self.assertRepr(nl, """
(
(module 0 None ('top')
(io inout 'io1' 0.0:4)
(io inout 'io2' 1.0:4)
)
(cell 0 0 (top))
(cell 1 0 (instance 'test' 'U$0'
(io inout 'io' (io-cat 0.0:4 1.0:4))
))
)
""")
def test_nir_operator(self):
f = Fragment()
i = Signal(3)
o = Signal(4)
io = Signal(5)
f.add_subfragment(Instance("gadget",
i_i=i.as_signed(),
o_o=o.as_signed(),
io_io=io.as_signed(),
), "my_gadget")
nl = build_netlist(f, [i, o, io])
nl = build_netlist(f, [i, o])
self.assertRepr(nl, """
(
(module 0 None ('top')
(input 'i' 0.2:5)
(inout 'io' 0.5:10)
(output 'o' 1.0:4)
)
(cell 0 0 (top
(input 'i' 2:5)
(output 'o' 1.0:4)
(inout 'io' 5:10)
))
(cell 1 0 (instance 'gadget' 'my_gadget'
(input 'i' 0.2:5)
(output 'o' 0:4)
(inout 'io' 0.5:10)
))
)
""")
@ -856,7 +982,7 @@ class NamesTestCase(FHDLTestCase):
"o3": (o3, PortDirection.Output),
}
design = f.prepare(ports)
self.assertEqual(design.signal_names[design.fragment], SignalDict([
self.assertEqual(design.fragments[design.fragment].signal_names, SignalDict([
(i, "i"),
(rst, "rst"),
(o1, "o1"),
@ -865,7 +991,7 @@ class NamesTestCase(FHDLTestCase):
(cd_sync.clk, "clk"),
(cd_sync.rst, "rst$6"),
(cd_sync_norst.clk, "sync_norst_clk"),
(i1, "i$8"),
(i1, "i$7"),
]))
def test_assign_names_to_fragments(self):
@ -874,11 +1000,9 @@ class NamesTestCase(FHDLTestCase):
f.add_subfragment(b := Fragment(), name="b")
design = Design(f, ports=(), hierarchy=("top",))
self.assertEqual(design.fragment_names, {
f: ("top",),
a: ("top", "U$0"),
b: ("top", "b")
})
self.assertEqual(design.fragments[f].name, ("top",))
self.assertEqual(design.fragments[a].name, ("top", "U$0"))
self.assertEqual(design.fragments[b].name, ("top", "b"))
def test_assign_names_to_fragments_rename_top(self):
f = Fragment()
@ -886,11 +1010,9 @@ class NamesTestCase(FHDLTestCase):
f.add_subfragment(b := Fragment(), name="b")
design = Design(f, ports=[], hierarchy=("bench", "cpu"))
self.assertEqual(design.fragment_names, {
f: ("bench", "cpu",),
a: ("bench", "cpu", "U$0"),
b: ("bench", "cpu", "b")
})
self.assertEqual(design.fragments[f].name, ("bench", "cpu",))
self.assertEqual(design.fragments[a].name, ("bench", "cpu", "U$0"))
self.assertEqual(design.fragments[b].name, ("bench", "cpu", "b"))
def test_assign_names_to_fragments_collide_with_signal(self):
f = Fragment()
@ -898,10 +1020,8 @@ class NamesTestCase(FHDLTestCase):
a_s = Signal(name="a")
design = Design(f, ports=[("a", a_s, None)], hierarchy=("top",))
self.assertEqual(design.fragment_names, {
f: ("top",),
a_f: ("top", "a$U$0")
})
self.assertEqual(design.fragments[f].name, ("top",))
self.assertEqual(design.fragments[a_f].name, ("top", "a$1"))
def test_assign_names_to_fragments_duplicate(self):
f = Fragment()
@ -909,11 +1029,9 @@ class NamesTestCase(FHDLTestCase):
f.add_subfragment(a2_f := Fragment(), name="a")
design = Design(f, ports=[], hierarchy=("top",))
self.assertEqual(design.fragment_names, {
f: ("top",),
a1_f: ("top", "a"),
a2_f: ("top", "a$U$1"),
})
self.assertEqual(design.fragments[f].name, ("top",))
self.assertEqual(design.fragments[a1_f].name, ("top", "a"))
self.assertEqual(design.fragments[a2_f].name, ("top", "a$1"))
class ElaboratesTo(Elaboratable):
@ -944,122 +1062,137 @@ class OriginsTestCase(FHDLTestCase):
class IOBufferTestCase(FHDLTestCase):
def test_nir_i(self):
pad = Signal(4)
port = IOPort(4)
i = Signal(4)
f = Fragment()
f.add_subfragment(IOBufferInstance(pad, i=i))
nl = build_netlist(f, ports=[pad, i])
f.add_subfragment(IOBufferInstance(port, i=i))
nl = build_netlist(f, ports=[i])
self.assertRepr(nl, """
(
(module 0 None ('top')
(inout 'pad' 0.2:6)
(output 'i' 1.0:4)
(io input 'port' 0.0:4)
)
(cell 0 0 (top
(output 'i' 1.0:4)
(inout 'pad' 2:6)
))
(cell 1 0 (iob 0.2:6 4'd0 0))
(cell 1 0 (iob input 0.0:4))
)
""")
def test_nir_o(self):
pad = Signal(4)
port = IOPort(4)
o = Signal(4)
f = Fragment()
f.add_subfragment(IOBufferInstance(pad, o=o))
nl = build_netlist(f, ports=[pad, o])
f.add_subfragment(IOBufferInstance(port, o=o))
nl = build_netlist(f, ports=[o])
self.assertRepr(nl, """
(
(module 0 None ('top')
(input 'o' 0.6:10)
(inout 'pad' 0.2:6)
(input 'o' 0.2:6)
(io output 'port' 0.0:4)
)
(cell 0 0 (top
(input 'o' 6:10)
(inout 'pad' 2:6)
(input 'o' 2:6)
))
(cell 1 0 (iob 0.2:6 0.6:10 1))
(cell 1 0 (iob output 0.0:4 0.2:6 1))
)
""")
def test_nir_oe(self):
pad = Signal(4)
port = IOPort(4)
o = Signal(4)
oe = Signal()
f = Fragment()
f.add_subfragment(IOBufferInstance(pad, o=o, oe=oe))
nl = build_netlist(f, ports=[pad, o, oe])
f.add_subfragment(IOBufferInstance(port, o=o, oe=oe))
nl = build_netlist(f, ports=[ o, oe])
self.assertRepr(nl, """
(
(module 0 None ('top')
(input 'o' 0.6:10)
(input 'oe' 0.10)
(inout 'pad' 0.2:6)
(input 'o' 0.2:6)
(input 'oe' 0.6)
(io output 'port' 0.0:4)
)
(cell 0 0 (top
(input 'o' 6:10)
(input 'oe' 10:11)
(inout 'pad' 2:6)
(input 'o' 2:6)
(input 'oe' 6:7)
))
(cell 1 0 (iob 0.2:6 0.6:10 0.10))
(cell 1 0 (iob output 0.0:4 0.2:6 0.6))
)
""")
def test_nir_io(self):
pad = Signal(4)
port = IOPort(4)
i = Signal(4)
o = Signal(4)
oe = Signal()
f = Fragment()
f.add_subfragment(IOBufferInstance(pad, i=i, o=o, oe=oe))
nl = build_netlist(f, ports=[pad, i, o, oe])
f.add_subfragment(IOBufferInstance(port, i=i, o=o, oe=oe))
nl = build_netlist(f, ports=[i, o, oe])
self.assertRepr(nl, """
(
(module 0 None ('top')
(input 'o' 0.6:10)
(input 'oe' 0.10)
(inout 'pad' 0.2:6)
(input 'o' 0.2:6)
(input 'oe' 0.6)
(output 'i' 1.0:4)
(io inout 'port' 0.0:4)
)
(cell 0 0 (top
(input 'o' 6:10)
(input 'oe' 10:11)
(input 'o' 2:6)
(input 'oe' 6:7)
(output 'i' 1.0:4)
(inout 'pad' 2:6)
))
(cell 1 0 (iob 0.2:6 0.6:10 0.10))
(cell 1 0 (iob inout 0.0:4 0.2:6 0.6))
)
""")
def test_wrong_port(self):
port = Signal(4)
i = Signal(4)
with self.assertRaisesRegex(TypeError,
r"^Object \(sig port\) cannot be converted to an IO value"):
IOBufferInstance(port, i=i)
def test_wrong_i(self):
pad = Signal(4)
port = IOPort(4)
i = Signal()
with self.assertRaisesRegex(ValueError,
r"^`pad` length \(4\) doesn't match `i` length \(1\)"):
IOBufferInstance(pad, i=i)
r"^'port' length \(4\) doesn't match 'i' length \(1\)"):
IOBufferInstance(port, i=i)
def test_wrong_o(self):
pad = Signal(4)
port = IOPort(4)
o = Signal()
with self.assertRaisesRegex(ValueError,
r"^`pad` length \(4\) doesn't match `o` length \(1\)"):
IOBufferInstance(pad, o=o)
r"^'port' length \(4\) doesn't match 'o' length \(1\)"):
IOBufferInstance(port, o=o)
def test_wrong_oe(self):
pad = Signal(4)
port = IOPort(4)
o = Signal(4)
oe = Signal(4)
with self.assertRaisesRegex(ValueError,
r"^`oe` length \(4\) must be 1"):
IOBufferInstance(pad, o=o, oe=oe)
r"^'oe' length \(4\) must be 1"):
IOBufferInstance(port, o=o, oe=oe)
def test_wrong_oe_without_o(self):
pad = Signal(4)
port = IOPort(4)
oe = Signal()
with self.assertRaisesRegex(ValueError,
r"^`oe` must not be used if `o` is not used"):
IOBufferInstance(pad, oe=oe)
r"^'oe' must not be used if 'o' is not used"):
IOBufferInstance(port, oe=oe)
def test_conflict(self):
port = IOPort(4)
i1 = Signal(4)
i2 = Signal(4)
f = Fragment()
f.add_subfragment(IOBufferInstance(port, i=i1))
f.add_subfragment(IOBufferInstance(port, i=i2))
with self.assertRaisesRegex(DriverConflict,
r"^Bit 0 of I/O port \(io-port port\) used twice, at .*test_hdl_ir.py:\d+ and "
r".*test_hdl_ir.py:\d+$"):
build_netlist(f, ports=[i1, i2])
class AssignTestCase(FHDLTestCase):