939 lines
34 KiB
Python
939 lines
34 KiB
Python
# amaranth: UnusedElaboratable=no
|
|
|
|
from amaranth.hdl import *
|
|
from amaranth.sim import *
|
|
from amaranth.hdl._ir import build_netlist
|
|
from amaranth.lib.io import *
|
|
from amaranth.lib.wiring import *
|
|
from amaranth.lib import wiring, data
|
|
|
|
from .utils import *
|
|
|
|
|
|
class DirectionTestCase(FHDLTestCase):
|
|
def test_or(self):
|
|
self.assertIs(Direction.Input | Direction.Input, Direction.Input)
|
|
self.assertIs(Direction.Input | Direction.Output, Direction.Bidir)
|
|
self.assertIs(Direction.Input | Direction.Bidir, Direction.Bidir)
|
|
self.assertIs(Direction.Output | Direction.Input, Direction.Bidir)
|
|
self.assertIs(Direction.Output | Direction.Output, Direction.Output)
|
|
self.assertIs(Direction.Output | Direction.Bidir, Direction.Bidir)
|
|
self.assertIs(Direction.Bidir | Direction.Input, Direction.Bidir)
|
|
self.assertIs(Direction.Bidir | Direction.Output, Direction.Bidir)
|
|
self.assertIs(Direction.Bidir | Direction.Bidir, Direction.Bidir)
|
|
with self.assertRaises(TypeError):
|
|
Direction.Bidir | 3
|
|
|
|
def test_and(self):
|
|
self.assertIs(Direction.Input & Direction.Input, Direction.Input)
|
|
self.assertIs(Direction.Input & Direction.Bidir, Direction.Input)
|
|
self.assertIs(Direction.Output & Direction.Output, Direction.Output)
|
|
self.assertIs(Direction.Output & Direction.Bidir, Direction.Output)
|
|
self.assertIs(Direction.Bidir & Direction.Input, Direction.Input)
|
|
self.assertIs(Direction.Bidir & Direction.Output, Direction.Output)
|
|
self.assertIs(Direction.Bidir & Direction.Bidir, Direction.Bidir)
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"Cannot combine input port with output port"):
|
|
Direction.Output & Direction.Input
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"Cannot combine input port with output port"):
|
|
Direction.Input & Direction.Output
|
|
with self.assertRaises(TypeError):
|
|
Direction.Bidir & 3
|
|
|
|
|
|
class SingleEndedPortTestCase(FHDLTestCase):
|
|
def test_construct(self):
|
|
io = IOPort(4)
|
|
port = SingleEndedPort(io)
|
|
self.assertIs(port.io, io)
|
|
self.assertEqual(port.invert, (False, False, False, False))
|
|
self.assertEqual(port.direction, Direction.Bidir)
|
|
self.assertEqual(len(port), 4)
|
|
self.assertRepr(port, "SingleEndedPort((io-port io), invert=False, direction=Direction.Bidir)")
|
|
port = SingleEndedPort(io, invert=True, direction='i')
|
|
self.assertEqual(port.invert, (True, True, True, True))
|
|
self.assertRepr(port, "SingleEndedPort((io-port io), invert=True, direction=Direction.Input)")
|
|
port = SingleEndedPort(io, invert=[True, False, True, False], direction=Direction.Output)
|
|
self.assertIsInstance(port.invert, tuple)
|
|
self.assertEqual(port.invert, (True, False, True, False))
|
|
self.assertRepr(port, "SingleEndedPort((io-port io), invert=(True, False, True, False), direction=Direction.Output)")
|
|
|
|
def test_construct_wrong(self):
|
|
io = IOPort(4)
|
|
sig = Signal(4)
|
|
with self.assertRaisesRegex(TypeError,
|
|
r"^Object \(sig sig\) cannot be converted to an IO value$"):
|
|
SingleEndedPort(sig)
|
|
with self.assertRaisesRegex(TypeError,
|
|
r"^'invert' must be a bool or iterable of bool, not 3$"):
|
|
SingleEndedPort(io, invert=3)
|
|
with self.assertRaisesRegex(TypeError,
|
|
r"^'invert' must be a bool or iterable of bool, not \[1, 2, 3, 4\]$"):
|
|
SingleEndedPort(io, invert=[1, 2, 3, 4])
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Length of 'invert' \(5\) doesn't match length of 'io' \(4\)$"):
|
|
SingleEndedPort(io, invert=[False, False, False, False, False])
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^'bidir' is not a valid Direction$"):
|
|
SingleEndedPort(io, direction="bidir")
|
|
|
|
def test_slice(self):
|
|
io = IOPort(8)
|
|
port = SingleEndedPort(io, invert=(True, False, False, True, True, False, False, True), direction="o")
|
|
self.assertRepr(port[2:5], "SingleEndedPort((io-slice (io-port io) 2:5), invert=(False, True, True), direction=Direction.Output)")
|
|
self.assertRepr(port[7], "SingleEndedPort((io-slice (io-port io) 7:8), invert=True, direction=Direction.Output)")
|
|
|
|
def test_cat(self):
|
|
ioa = IOPort(3)
|
|
iob = IOPort(2)
|
|
porta = SingleEndedPort(ioa, direction=Direction.Input)
|
|
portb = SingleEndedPort(iob, invert=True, direction=Direction.Input)
|
|
cport = porta + portb
|
|
self.assertRepr(cport, "SingleEndedPort((io-cat (io-port ioa) (io-port iob)), invert=(False, False, False, True, True), direction=Direction.Input)")
|
|
with self.assertRaises(TypeError):
|
|
porta + iob
|
|
|
|
def test_invert(self):
|
|
io = IOPort(4)
|
|
port = SingleEndedPort(io, invert=[True, False, True, False], direction=Direction.Output)
|
|
iport = ~port
|
|
self.assertRepr(iport, "SingleEndedPort((io-port io), invert=(False, True, False, True), direction=Direction.Output)")
|
|
|
|
|
|
class DifferentialPortTestCase(FHDLTestCase):
|
|
def test_construct(self):
|
|
iop = IOPort(4)
|
|
ion = IOPort(4)
|
|
port = DifferentialPort(iop, ion)
|
|
self.assertIs(port.p, iop)
|
|
self.assertIs(port.n, ion)
|
|
self.assertEqual(port.invert, (False, False, False, False))
|
|
self.assertEqual(port.direction, Direction.Bidir)
|
|
self.assertEqual(len(port), 4)
|
|
self.assertRepr(port, "DifferentialPort((io-port iop), (io-port ion), invert=False, direction=Direction.Bidir)")
|
|
port = DifferentialPort(iop, ion, invert=True, direction='i')
|
|
self.assertEqual(port.invert, (True, True, True, True))
|
|
self.assertRepr(port, "DifferentialPort((io-port iop), (io-port ion), invert=True, direction=Direction.Input)")
|
|
port = DifferentialPort(iop, ion, invert=[True, False, True, False], direction=Direction.Output)
|
|
self.assertIsInstance(port.invert, tuple)
|
|
self.assertEqual(port.invert, (True, False, True, False))
|
|
self.assertRepr(port, "DifferentialPort((io-port iop), (io-port ion), invert=(True, False, True, False), direction=Direction.Output)")
|
|
|
|
def test_construct_wrong(self):
|
|
iop = IOPort(4)
|
|
ion = IOPort(4)
|
|
sig = Signal(4)
|
|
with self.assertRaisesRegex(TypeError,
|
|
r"^Object \(sig sig\) cannot be converted to an IO value$"):
|
|
DifferentialPort(iop, sig)
|
|
with self.assertRaisesRegex(TypeError,
|
|
r"^Object \(sig sig\) cannot be converted to an IO value$"):
|
|
DifferentialPort(sig, ion)
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Length of 'p' \(4\) doesn't match length of 'n' \(3\)$"):
|
|
DifferentialPort(iop, ion[:3])
|
|
with self.assertRaisesRegex(TypeError,
|
|
r"^'invert' must be a bool or iterable of bool, not 3$"):
|
|
DifferentialPort(iop, ion, invert=3)
|
|
with self.assertRaisesRegex(TypeError,
|
|
r"^'invert' must be a bool or iterable of bool, not \[1, 2, 3, 4\]$"):
|
|
DifferentialPort(iop, ion, invert=[1, 2, 3, 4])
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Length of 'invert' \(5\) doesn't match length of 'p' \(4\)$"):
|
|
DifferentialPort(iop, ion, invert=[False, False, False, False, False])
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^'bidir' is not a valid Direction$"):
|
|
DifferentialPort(iop, ion, direction="bidir")
|
|
|
|
def test_slice(self):
|
|
iop = IOPort(8)
|
|
ion = IOPort(8)
|
|
port = DifferentialPort(iop, ion, invert=(True, False, False, True, True, False, False, True), direction="o")
|
|
self.assertRepr(port[2:5], "DifferentialPort((io-slice (io-port iop) 2:5), (io-slice (io-port ion) 2:5), invert=(False, True, True), direction=Direction.Output)")
|
|
self.assertRepr(port[7], "DifferentialPort((io-slice (io-port iop) 7:8), (io-slice (io-port ion) 7:8), invert=True, direction=Direction.Output)")
|
|
|
|
def test_cat(self):
|
|
ioap = IOPort(3)
|
|
ioan = IOPort(3)
|
|
iobp = IOPort(2)
|
|
iobn = IOPort(2)
|
|
porta = DifferentialPort(ioap, ioan, direction=Direction.Input)
|
|
portb = DifferentialPort(iobp, iobn, invert=True, direction=Direction.Input)
|
|
cport = porta + portb
|
|
self.assertRepr(cport, "DifferentialPort((io-cat (io-port ioap) (io-port iobp)), (io-cat (io-port ioan) (io-port iobn)), invert=(False, False, False, True, True), direction=Direction.Input)")
|
|
with self.assertRaises(TypeError):
|
|
porta + SingleEndedPort(ioap)
|
|
|
|
def test_invert(self):
|
|
iop = IOPort(4)
|
|
ion = IOPort(4)
|
|
port = DifferentialPort(iop, ion, invert=[True, False, True, False], direction=Direction.Output)
|
|
iport = ~port
|
|
self.assertRepr(iport, "DifferentialPort((io-port iop), (io-port ion), invert=(False, True, False, True), direction=Direction.Output)")
|
|
|
|
|
|
class BufferTestCase(FHDLTestCase):
|
|
def test_signature(self):
|
|
sig_i = Buffer.Signature("i", 4)
|
|
self.assertEqual(sig_i.direction, Direction.Input)
|
|
self.assertEqual(sig_i.width, 4)
|
|
self.assertEqual(sig_i.members, wiring.SignatureMembers({
|
|
"i": wiring.In(4),
|
|
}))
|
|
sig_o = Buffer.Signature("o", 4)
|
|
self.assertEqual(sig_o.direction, Direction.Output)
|
|
self.assertEqual(sig_o.width, 4)
|
|
self.assertEqual(sig_o.members, wiring.SignatureMembers({
|
|
"o": wiring.Out(4),
|
|
"oe": wiring.Out(1, init=1),
|
|
}))
|
|
sig_io = Buffer.Signature("io", 4)
|
|
self.assertEqual(sig_io.direction, Direction.Bidir)
|
|
self.assertEqual(sig_io.width, 4)
|
|
self.assertEqual(sig_io.members, wiring.SignatureMembers({
|
|
"i": wiring.In(4),
|
|
"o": wiring.Out(4),
|
|
"oe": wiring.Out(1, init=0),
|
|
}))
|
|
self.assertNotEqual(sig_i, sig_io)
|
|
self.assertEqual(sig_i, sig_i)
|
|
self.assertRepr(sig_io, "Buffer.Signature(Direction.Bidir, 4)")
|
|
|
|
def test_construct(self):
|
|
io = IOPort(4)
|
|
port = SingleEndedPort(io)
|
|
buf = Buffer("i", port)
|
|
self.assertEqual(buf.direction, Direction.Input)
|
|
self.assertIs(buf.port, port)
|
|
self.assertRepr(buf.signature, "Buffer.Signature(Direction.Input, 4).flip()")
|
|
|
|
def test_construct_wrong(self):
|
|
io = IOPort(4)
|
|
port_i = SingleEndedPort(io, direction="i")
|
|
port_o = SingleEndedPort(io, direction="o")
|
|
with self.assertRaisesRegex(TypeError,
|
|
r"^'port' must be a 'PortLike', not \(io-port io\)$"):
|
|
Buffer("io", io)
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Input port cannot be used with Bidir buffer$"):
|
|
Buffer("io", port_i)
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Output port cannot be used with Input buffer$"):
|
|
Buffer("i", port_o)
|
|
|
|
def test_elaborate(self):
|
|
io = IOPort(4)
|
|
|
|
port = SingleEndedPort(io)
|
|
buf = Buffer("io", port)
|
|
nl = build_netlist(Fragment.get(buf, None), [buf.i, buf.o, buf.oe])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(input 'o' 0.2:6)
|
|
(input 'oe' 0.6)
|
|
(output 'i' 1.0:4)
|
|
(io inout 'io' 0.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(input 'o' 2:6)
|
|
(input 'oe' 6:7)
|
|
(output 'i' 1.0:4)
|
|
))
|
|
(cell 1 0 (iob inout 0.0:4 0.2:6 0.6))
|
|
)
|
|
""")
|
|
|
|
port = SingleEndedPort(io, invert=[False, True, False, True])
|
|
buf = Buffer("io", port)
|
|
nl = build_netlist(Fragment.get(buf, None), [buf.i, buf.o, buf.oe])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(input 'o' 0.2:6)
|
|
(input 'oe' 0.6)
|
|
(output 'i' 2.0:4)
|
|
(io inout 'io' 0.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(input 'o' 2:6)
|
|
(input 'oe' 6:7)
|
|
(output 'i' 2.0:4)
|
|
))
|
|
(cell 1 0 (^ 0.2:6 4'd10))
|
|
(cell 2 0 (^ 3.0:4 4'd10))
|
|
(cell 3 0 (iob inout 0.0:4 1.0:4 0.6))
|
|
)
|
|
""")
|
|
|
|
buf = Buffer("i", port)
|
|
nl = build_netlist(Fragment.get(buf, None), [buf.i])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(output 'i' 1.0:4)
|
|
(io input 'io' 0.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(output 'i' 1.0:4)
|
|
))
|
|
(cell 1 0 (^ 2.0:4 4'd10))
|
|
(cell 2 0 (iob input 0.0:4))
|
|
)
|
|
""")
|
|
|
|
buf = Buffer("o", port)
|
|
nl = build_netlist(Fragment.get(buf, None), [buf.o, buf.oe])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(input 'o' 0.2:6)
|
|
(input 'oe' 0.6)
|
|
(io output 'io' 0.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(input 'o' 2:6)
|
|
(input 'oe' 6:7)
|
|
))
|
|
(cell 1 0 (^ 0.2:6 4'd10))
|
|
(cell 2 0 (iob output 0.0:4 1.0:4 0.6))
|
|
)
|
|
""")
|
|
|
|
def test_elaborate_diff(self):
|
|
iop = IOPort(4)
|
|
ion = IOPort(4)
|
|
|
|
port = DifferentialPort(iop, ion)
|
|
buf = Buffer("io", port)
|
|
nl = build_netlist(Fragment.get(buf, None), [buf.i, buf.o, buf.oe])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(input 'o' 0.2:6)
|
|
(input 'oe' 0.6)
|
|
(output 'i' 1.0:4)
|
|
(io inout 'iop' 0.0:4)
|
|
(io output 'ion' 1.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(input 'o' 2:6)
|
|
(input 'oe' 6:7)
|
|
(output 'i' 1.0:4)
|
|
))
|
|
(cell 1 0 (iob inout 0.0:4 0.2:6 0.6))
|
|
(cell 2 0 (~ 0.2:6))
|
|
(cell 3 0 (iob output 1.0:4 2.0:4 0.6))
|
|
)
|
|
""")
|
|
|
|
port = DifferentialPort(iop, ion, invert=[False, True, False, True])
|
|
buf = Buffer("io", port)
|
|
nl = build_netlist(Fragment.get(buf, None), [buf.i, buf.o, buf.oe])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(input 'o' 0.2:6)
|
|
(input 'oe' 0.6)
|
|
(output 'i' 2.0:4)
|
|
(io inout 'iop' 0.0:4)
|
|
(io output 'ion' 1.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(input 'o' 2:6)
|
|
(input 'oe' 6:7)
|
|
(output 'i' 2.0:4)
|
|
))
|
|
(cell 1 0 (^ 0.2:6 4'd10))
|
|
(cell 2 0 (^ 3.0:4 4'd10))
|
|
(cell 3 0 (iob inout 0.0:4 1.0:4 0.6))
|
|
(cell 4 0 (~ 1.0:4))
|
|
(cell 5 0 (iob output 1.0:4 4.0:4 0.6))
|
|
)
|
|
""")
|
|
|
|
buf = Buffer("i", port)
|
|
nl = build_netlist(Fragment.get(buf, None), [buf.i])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(output 'i' 1.0:4)
|
|
(io input 'iop' 0.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(output 'i' 1.0:4)
|
|
))
|
|
(cell 1 0 (^ 2.0:4 4'd10))
|
|
(cell 2 0 (iob input 0.0:4))
|
|
)
|
|
""")
|
|
|
|
buf = Buffer("o", port)
|
|
nl = build_netlist(Fragment.get(buf, None), [buf.o, buf.oe])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(input 'o' 0.2:6)
|
|
(input 'oe' 0.6)
|
|
(io output 'iop' 0.0:4)
|
|
(io output 'ion' 1.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(input 'o' 2:6)
|
|
(input 'oe' 6:7)
|
|
))
|
|
(cell 1 0 (^ 0.2:6 4'd10))
|
|
(cell 2 0 (iob output 0.0:4 1.0:4 0.6))
|
|
(cell 3 0 (~ 1.0:4))
|
|
(cell 4 0 (iob output 1.0:4 3.0:4 0.6))
|
|
)
|
|
""")
|
|
|
|
|
|
class FFBufferTestCase(FHDLTestCase):
|
|
def test_signature(self):
|
|
sig_i = FFBuffer.Signature("i", 4)
|
|
self.assertEqual(sig_i.direction, Direction.Input)
|
|
self.assertEqual(sig_i.width, 4)
|
|
self.assertEqual(sig_i.members, wiring.SignatureMembers({
|
|
"i": wiring.In(4),
|
|
}))
|
|
sig_o = FFBuffer.Signature("o", 4)
|
|
self.assertEqual(sig_o.direction, Direction.Output)
|
|
self.assertEqual(sig_o.width, 4)
|
|
self.assertEqual(sig_o.members, wiring.SignatureMembers({
|
|
"o": wiring.Out(4),
|
|
"oe": wiring.Out(1, init=1),
|
|
}))
|
|
sig_io = FFBuffer.Signature("io", 4)
|
|
self.assertEqual(sig_io.direction, Direction.Bidir)
|
|
self.assertEqual(sig_io.width, 4)
|
|
self.assertEqual(sig_io.members, wiring.SignatureMembers({
|
|
"i": wiring.In(4),
|
|
"o": wiring.Out(4),
|
|
"oe": wiring.Out(1, init=0),
|
|
}))
|
|
self.assertNotEqual(sig_i, sig_io)
|
|
self.assertEqual(sig_i, sig_i)
|
|
self.assertRepr(sig_io, "FFBuffer.Signature(Direction.Bidir, 4)")
|
|
|
|
def test_construct(self):
|
|
io = IOPort(4)
|
|
port = SingleEndedPort(io)
|
|
buf = FFBuffer("i", port)
|
|
self.assertEqual(buf.direction, Direction.Input)
|
|
self.assertIs(buf.port, port)
|
|
self.assertRepr(buf.signature, "FFBuffer.Signature(Direction.Input, 4).flip()")
|
|
self.assertEqual(buf.i_domain, "sync")
|
|
with self.assertRaisesRegex(AttributeError,
|
|
r"^Input buffer doesn't have an output domain$"):
|
|
buf.o_domain
|
|
buf = FFBuffer("i", port, i_domain="inp")
|
|
self.assertEqual(buf.i_domain, "inp")
|
|
buf = FFBuffer("o", port)
|
|
self.assertEqual(buf.direction, Direction.Output)
|
|
self.assertIs(buf.port, port)
|
|
self.assertRepr(buf.signature, "FFBuffer.Signature(Direction.Output, 4).flip()")
|
|
self.assertEqual(buf.o_domain, "sync")
|
|
with self.assertRaisesRegex(AttributeError,
|
|
r"^Output buffer doesn't have an input domain$"):
|
|
buf.i_domain
|
|
buf = FFBuffer("o", port, o_domain="out")
|
|
self.assertEqual(buf.o_domain, "out")
|
|
buf = FFBuffer("io", port)
|
|
self.assertEqual(buf.direction, Direction.Bidir)
|
|
self.assertIs(buf.port, port)
|
|
self.assertRepr(buf.signature, "FFBuffer.Signature(Direction.Bidir, 4).flip()")
|
|
self.assertEqual(buf.i_domain, "sync")
|
|
self.assertEqual(buf.o_domain, "sync")
|
|
buf = FFBuffer("io", port, i_domain="input", o_domain="output")
|
|
self.assertEqual(buf.i_domain, "input")
|
|
self.assertEqual(buf.o_domain, "output")
|
|
|
|
def test_construct_wrong(self):
|
|
io = IOPort(4)
|
|
port = SingleEndedPort(io)
|
|
port_i = SingleEndedPort(io, direction="i")
|
|
port_o = SingleEndedPort(io, direction="o")
|
|
with self.assertRaisesRegex(TypeError,
|
|
r"^'port' must be a 'PortLike', not \(io-port io\)$"):
|
|
FFBuffer("io", io)
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Input port cannot be used with Bidir buffer$"):
|
|
FFBuffer("io", port_i)
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Output port cannot be used with Input buffer$"):
|
|
FFBuffer("i", port_o)
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Input buffer doesn't have an output domain$"):
|
|
FFBuffer("i", port, o_domain="output")
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Output buffer doesn't have an input domain$"):
|
|
FFBuffer("o", port, i_domain="input")
|
|
|
|
def test_elaborate(self):
|
|
io = IOPort(4)
|
|
|
|
port = SingleEndedPort(io)
|
|
m = Module()
|
|
m.domains.inp = ClockDomain()
|
|
m.domains.outp = ClockDomain()
|
|
m.submodules.buf = buf = FFBuffer("io", port, i_domain="inp", o_domain="outp")
|
|
nl = build_netlist(Fragment.get(m, None), [
|
|
buf.i, buf.o, buf.oe,
|
|
ClockSignal("inp"), ResetSignal("inp"),
|
|
ClockSignal("outp"), ResetSignal("outp"),
|
|
])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(input 'o' 0.2:6)
|
|
(input 'oe' 0.6)
|
|
(input 'inp_clk' 0.7)
|
|
(input 'inp_rst' 0.8)
|
|
(input 'outp_clk' 0.9)
|
|
(input 'outp_rst' 0.10)
|
|
(output 'i' 2.0:4)
|
|
(io inout 'io' 0.0:4)
|
|
)
|
|
(module 1 0 ('top' 'buf')
|
|
(input 'o$11' 0.2:6)
|
|
(input 'oe$12' 0.6)
|
|
(input 'inp_clk' 0.7)
|
|
(input 'inp_rst' 0.8)
|
|
(input 'outp_clk' 0.9)
|
|
(input 'outp_rst' 0.10)
|
|
(output 'i_ff' 2.0:4)
|
|
(io inout 'io' 0.0:4)
|
|
)
|
|
(module 2 1 ('top' 'buf' 'io_buffer')
|
|
(output 'i' 1.0:4)
|
|
(input 'o' 3.0:4)
|
|
(input 'oe' 4.0)
|
|
(io inout 'io' 0.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(input 'o' 2:6)
|
|
(input 'oe' 6:7)
|
|
(input 'inp_clk' 7:8)
|
|
(input 'inp_rst' 8:9)
|
|
(input 'outp_clk' 9:10)
|
|
(input 'outp_rst' 10:11)
|
|
(output 'i' 2.0:4)
|
|
))
|
|
(cell 1 2 (iob inout 0.0:4 3.0:4 4.0))
|
|
(cell 2 1 (flipflop 1.0:4 0 pos 0.7 0))
|
|
(cell 3 1 (flipflop 0.2:6 0 pos 0.9 0))
|
|
(cell 4 1 (flipflop 0.6 0 pos 0.9 0))
|
|
)
|
|
""")
|
|
|
|
port = SingleEndedPort(io, invert=[False, True, False, True])
|
|
m = Module()
|
|
m.domains.inp = ClockDomain(reset_less=True)
|
|
m.domains.outp = ClockDomain(reset_less=True)
|
|
m.submodules.buf = buf = FFBuffer("io", port, i_domain="inp", o_domain="outp")
|
|
nl = build_netlist(Fragment.get(m, None), [
|
|
buf.i, buf.o, buf.oe,
|
|
ClockSignal("inp"), ClockSignal("outp"),
|
|
])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(input 'o' 0.2:6)
|
|
(input 'oe' 0.6)
|
|
(input 'inp_clk' 0.7)
|
|
(input 'outp_clk' 0.8)
|
|
(output 'i' 4.0:4)
|
|
(io inout 'io' 0.0:4)
|
|
)
|
|
(module 1 0 ('top' 'buf')
|
|
(input 'o$9' 0.2:6)
|
|
(input 'oe$10' 0.6)
|
|
(input 'inp_clk' 0.7)
|
|
(input 'outp_clk' 0.8)
|
|
(output 'i_ff' 4.0:4)
|
|
(io inout 'io' 0.0:4)
|
|
)
|
|
(module 2 1 ('top' 'buf' 'io_buffer')
|
|
(output 'i' 2.0:4)
|
|
(input 'o' 5.0:4)
|
|
(input 'oe' 6.0)
|
|
(io inout 'io' 0.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(input 'o' 2:6)
|
|
(input 'oe' 6:7)
|
|
(input 'inp_clk' 7:8)
|
|
(input 'outp_clk' 8:9)
|
|
(output 'i' 4.0:4)
|
|
))
|
|
(cell 1 2 (^ 5.0:4 4'd10))
|
|
(cell 2 2 (^ 3.0:4 4'd10))
|
|
(cell 3 2 (iob inout 0.0:4 1.0:4 6.0))
|
|
(cell 4 1 (flipflop 2.0:4 0 pos 0.7 0))
|
|
(cell 5 1 (flipflop 0.2:6 0 pos 0.8 0))
|
|
(cell 6 1 (flipflop 0.6 0 pos 0.8 0))
|
|
)
|
|
""")
|
|
|
|
buf = FFBuffer("i", port)
|
|
nl = build_netlist(Fragment.get(buf, None), [buf.i])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(input 'clk' 0.2)
|
|
(input 'rst' 0.3)
|
|
(output 'i' 3.0:4)
|
|
(io input 'io' 0.0:4)
|
|
)
|
|
(module 1 0 ('top' 'io_buffer')
|
|
(output 'i' 1.0:4)
|
|
(io input 'io' 0.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(input 'clk' 2:3)
|
|
(input 'rst' 3:4)
|
|
(output 'i' 3.0:4)
|
|
))
|
|
(cell 1 1 (^ 2.0:4 4'd10))
|
|
(cell 2 1 (iob input 0.0:4))
|
|
(cell 3 0 (flipflop 1.0:4 0 pos 0.2 0))
|
|
)
|
|
""")
|
|
|
|
buf = FFBuffer("o", port)
|
|
nl = build_netlist(Fragment.get(buf, None), [buf.o, buf.oe])
|
|
self.assertRepr(nl, """
|
|
(
|
|
(module 0 None ('top')
|
|
(input 'o' 0.2:6)
|
|
(input 'oe' 0.6)
|
|
(input 'clk' 0.7)
|
|
(input 'rst' 0.8)
|
|
(io output 'io' 0.0:4)
|
|
)
|
|
(module 1 0 ('top' 'io_buffer')
|
|
(input 'o' 3.0:4)
|
|
(input 'oe' 4.0)
|
|
(io output 'io' 0.0:4)
|
|
)
|
|
(cell 0 0 (top
|
|
(input 'o' 2:6)
|
|
(input 'oe' 6:7)
|
|
(input 'clk' 7:8)
|
|
(input 'rst' 8:9)
|
|
))
|
|
(cell 1 1 (^ 3.0:4 4'd10))
|
|
(cell 2 1 (iob output 0.0:4 1.0:4 4.0))
|
|
(cell 3 0 (flipflop 0.2:6 0 pos 0.7 0))
|
|
(cell 4 0 (flipflop 0.6 0 pos 0.7 0))
|
|
)
|
|
""")
|
|
|
|
|
|
class DDRBufferTestCase(FHDLTestCase):
|
|
def test_signature(self):
|
|
sig_i = DDRBuffer.Signature("i", 4)
|
|
self.assertEqual(sig_i.direction, Direction.Input)
|
|
self.assertEqual(sig_i.width, 4)
|
|
self.assertEqual(sig_i.members, wiring.SignatureMembers({
|
|
"i": wiring.In(data.ArrayLayout(4, 2)),
|
|
}))
|
|
sig_o = DDRBuffer.Signature("o", 4)
|
|
self.assertEqual(sig_o.direction, Direction.Output)
|
|
self.assertEqual(sig_o.width, 4)
|
|
self.assertEqual(sig_o.members, wiring.SignatureMembers({
|
|
"o": wiring.Out(data.ArrayLayout(4, 2)),
|
|
"oe": wiring.Out(1, init=1),
|
|
}))
|
|
sig_io = DDRBuffer.Signature("io", 4)
|
|
self.assertEqual(sig_io.direction, Direction.Bidir)
|
|
self.assertEqual(sig_io.width, 4)
|
|
self.assertEqual(sig_io.members, wiring.SignatureMembers({
|
|
"i": wiring.In(data.ArrayLayout(4, 2)),
|
|
"o": wiring.Out(data.ArrayLayout(4, 2)),
|
|
"oe": wiring.Out(1, init=0),
|
|
}))
|
|
self.assertNotEqual(sig_i, sig_io)
|
|
self.assertEqual(sig_i, sig_i)
|
|
self.assertRepr(sig_io, "DDRBuffer.Signature(Direction.Bidir, 4)")
|
|
|
|
def test_construct(self):
|
|
io = IOPort(4)
|
|
port = SingleEndedPort(io)
|
|
buf = DDRBuffer("i", port)
|
|
self.assertEqual(buf.direction, Direction.Input)
|
|
self.assertIs(buf.port, port)
|
|
self.assertRepr(buf.signature, "DDRBuffer.Signature(Direction.Input, 4).flip()")
|
|
self.assertEqual(buf.i_domain, "sync")
|
|
with self.assertRaisesRegex(AttributeError,
|
|
r"^Input buffer doesn't have an output domain$"):
|
|
buf.o_domain
|
|
buf = DDRBuffer("i", port, i_domain="inp")
|
|
self.assertEqual(buf.i_domain, "inp")
|
|
buf = DDRBuffer("o", port)
|
|
self.assertEqual(buf.direction, Direction.Output)
|
|
self.assertIs(buf.port, port)
|
|
self.assertRepr(buf.signature, "DDRBuffer.Signature(Direction.Output, 4).flip()")
|
|
self.assertEqual(buf.o_domain, "sync")
|
|
with self.assertRaisesRegex(AttributeError,
|
|
r"^Output buffer doesn't have an input domain$"):
|
|
buf.i_domain
|
|
buf = DDRBuffer("o", port, o_domain="out")
|
|
self.assertEqual(buf.o_domain, "out")
|
|
buf = DDRBuffer("io", port)
|
|
self.assertEqual(buf.direction, Direction.Bidir)
|
|
self.assertIs(buf.port, port)
|
|
self.assertRepr(buf.signature, "DDRBuffer.Signature(Direction.Bidir, 4).flip()")
|
|
self.assertEqual(buf.i_domain, "sync")
|
|
self.assertEqual(buf.o_domain, "sync")
|
|
buf = DDRBuffer("io", port, i_domain="input", o_domain="output")
|
|
self.assertEqual(buf.i_domain, "input")
|
|
self.assertEqual(buf.o_domain, "output")
|
|
|
|
def test_construct_wrong(self):
|
|
io = IOPort(4)
|
|
port = SingleEndedPort(io)
|
|
port_i = SingleEndedPort(io, direction="i")
|
|
port_o = SingleEndedPort(io, direction="o")
|
|
with self.assertRaisesRegex(TypeError,
|
|
r"^'port' must be a 'PortLike', not \(io-port io\)$"):
|
|
DDRBuffer("io", io)
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Input port cannot be used with Bidir buffer$"):
|
|
DDRBuffer("io", port_i)
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Output port cannot be used with Input buffer$"):
|
|
DDRBuffer("i", port_o)
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Input buffer doesn't have an output domain$"):
|
|
DDRBuffer("i", port, o_domain="output")
|
|
with self.assertRaisesRegex(ValueError,
|
|
r"^Output buffer doesn't have an input domain$"):
|
|
DDRBuffer("o", port, i_domain="input")
|
|
|
|
|
|
class PinSignatureTestCase(FHDLTestCase):
|
|
def assertSignatureEqual(self, signature, expected):
|
|
self.assertEqual(signature.members, Signature(expected).members)
|
|
|
|
|
|
class PinSignatureCombTestCase(PinSignatureTestCase):
|
|
def test_signature_i(self):
|
|
sig_1 = Pin.Signature(1, dir="i")
|
|
self.assertSignatureEqual(sig_1, {
|
|
"i": In(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="i")
|
|
self.assertSignatureEqual(sig_2, {
|
|
"i": In(2),
|
|
})
|
|
|
|
def test_signature_o(self):
|
|
sig_1 = Pin.Signature(1, dir="o")
|
|
self.assertSignatureEqual(sig_1, {
|
|
"o": Out(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="o")
|
|
self.assertSignatureEqual(sig_2, {
|
|
"o": Out(2),
|
|
})
|
|
|
|
def test_signature_oe(self):
|
|
sig_1 = Pin.Signature(1, dir="oe")
|
|
self.assertSignatureEqual(sig_1, {
|
|
"o": Out(1),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="oe")
|
|
self.assertSignatureEqual(sig_2, {
|
|
"o": Out(2),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
def test_signature_io(self):
|
|
sig_1 = Pin.Signature(1, dir="io")
|
|
self.assertSignatureEqual(sig_1, {
|
|
"i": In(1),
|
|
"o": Out(1),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="io")
|
|
self.assertSignatureEqual(sig_2, {
|
|
"i": In(2),
|
|
"o": Out(2),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
|
|
class PinSignatureSDRTestCase(PinSignatureTestCase):
|
|
def test_signature_i(self):
|
|
sig_1 = Pin.Signature(1, dir="i", xdr=1)
|
|
self.assertSignatureEqual(sig_1, {
|
|
"i_clk": Out(1),
|
|
"i": In(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="i", xdr=1)
|
|
self.assertSignatureEqual(sig_2, {
|
|
"i_clk": Out(1),
|
|
"i": In(2),
|
|
})
|
|
|
|
def test_signature_o(self):
|
|
sig_1 = Pin.Signature(1, dir="o", xdr=1)
|
|
self.assertSignatureEqual(sig_1, {
|
|
"o_clk": Out(1),
|
|
"o": Out(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="o", xdr=1)
|
|
self.assertSignatureEqual(sig_2, {
|
|
"o_clk": Out(1),
|
|
"o": Out(2),
|
|
})
|
|
|
|
def test_signature_oe(self):
|
|
sig_1 = Pin.Signature(1, dir="oe", xdr=1)
|
|
self.assertSignatureEqual(sig_1, {
|
|
"o_clk": Out(1),
|
|
"o": Out(1),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="oe", xdr=1)
|
|
self.assertSignatureEqual(sig_2, {
|
|
"o_clk": Out(1),
|
|
"o": Out(2),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
def test_signature_io(self):
|
|
sig_1 = Pin.Signature(1, dir="io", xdr=1)
|
|
self.assertSignatureEqual(sig_1, {
|
|
"i_clk": Out(1),
|
|
"i": In(1),
|
|
"o_clk": Out(1),
|
|
"o": Out(1),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="io", xdr=1)
|
|
self.assertSignatureEqual(sig_2, {
|
|
"i_clk": Out(1),
|
|
"i": In(2),
|
|
"o_clk": Out(1),
|
|
"o": Out(2),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
|
|
class PinSignatureDDRTestCase(PinSignatureTestCase):
|
|
def test_signature_i(self):
|
|
sig_1 = Pin.Signature(1, dir="i", xdr=2)
|
|
self.assertSignatureEqual(sig_1, {
|
|
"i_clk": Out(1),
|
|
"i0": In(1),
|
|
"i1": In(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="i", xdr=2)
|
|
self.assertSignatureEqual(sig_2, {
|
|
"i_clk": Out(1),
|
|
"i0": In(2),
|
|
"i1": In(2),
|
|
})
|
|
|
|
def test_signature_o(self):
|
|
sig_1 = Pin.Signature(1, dir="o", xdr=2)
|
|
self.assertSignatureEqual(sig_1, {
|
|
"o_clk": Out(1),
|
|
"o0": Out(1),
|
|
"o1": Out(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="o", xdr=2)
|
|
self.assertSignatureEqual(sig_2, {
|
|
"o_clk": Out(1),
|
|
"o0": Out(2),
|
|
"o1": Out(2),
|
|
})
|
|
|
|
def test_signature_oe(self):
|
|
sig_1 = Pin.Signature(1, dir="oe", xdr=2)
|
|
self.assertSignatureEqual(sig_1, {
|
|
"o_clk": Out(1),
|
|
"o0": Out(1),
|
|
"o1": Out(1),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="oe", xdr=2)
|
|
self.assertSignatureEqual(sig_2, {
|
|
"o_clk": Out(1),
|
|
"o0": Out(2),
|
|
"o1": Out(2),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
def test_signature_io(self):
|
|
sig_1 = Pin.Signature(1, dir="io", xdr=2)
|
|
self.assertSignatureEqual(sig_1, {
|
|
"i_clk": Out(1),
|
|
"i0": In(1),
|
|
"i1": In(1),
|
|
"o_clk": Out(1),
|
|
"o0": Out(1),
|
|
"o1": Out(1),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
sig_2 = Pin.Signature(2, dir="io", xdr=2)
|
|
self.assertSignatureEqual(sig_2, {
|
|
"i_clk": Out(1),
|
|
"i0": In(2),
|
|
"i1": In(2),
|
|
"o_clk": Out(1),
|
|
"o0": Out(2),
|
|
"o1": Out(2),
|
|
"oe": Out(1),
|
|
})
|
|
|
|
|
|
class PinSignatureReprCase(FHDLTestCase):
|
|
def test_repr(self):
|
|
sig_0 = Pin.Signature(1, dir="i")
|
|
self.assertRepr(sig_0, "Pin.Signature(1, dir='i')")
|
|
sig_0 = Pin.Signature(2, dir="o", xdr=1)
|
|
self.assertRepr(sig_0, "Pin.Signature(2, dir='o', xdr=1)")
|
|
sig_0 = Pin.Signature(3, dir="io", xdr=2)
|
|
self.assertRepr(sig_0, "Pin.Signature(3, dir='io', xdr=2)")
|
|
|
|
|
|
class PinTestCase(FHDLTestCase):
|
|
def test_attributes(self):
|
|
pin = Pin(2, dir="io", xdr=2)
|
|
self.assertEqual(pin.width, 2)
|
|
self.assertEqual(pin.dir, "io")
|
|
self.assertEqual(pin.xdr, 2)
|
|
self.assertEqual(pin.signature.width, 2)
|
|
self.assertEqual(pin.signature.dir, "io")
|
|
self.assertEqual(pin.signature.xdr, 2)
|
|
self.assertEqual(pin.name, "pin")
|
|
self.assertEqual(pin.path, ("pin",))
|
|
self.assertEqual(pin.i0.name, "pin__i0")
|
|
pin = Pin(2, dir="io", xdr=2, name="testpin")
|
|
self.assertEqual(pin.name, "testpin")
|
|
self.assertEqual(pin.path, ("testpin",))
|
|
self.assertEqual(pin.i0.name, "testpin__i0")
|
|
pin = Pin(2, dir="io", xdr=2, path=["a", "b"])
|
|
self.assertEqual(pin.name, "a__b")
|
|
self.assertEqual(pin.path, ("a", "b"))
|
|
self.assertEqual(pin.i0.name, "a__b__i0")
|