lib.io: Implement *Buffer from RFC 55.

This commit is contained in:
Wanda 2024-03-19 03:42:21 +01:00 committed by Catherine
parent 81eae1dd35
commit 456dcaeb7b
3 changed files with 965 additions and 25 deletions

View file

@ -1,7 +1,11 @@
# 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 *
@ -169,6 +173,548 @@ class DifferentialPortTestCase(FHDLTestCase):
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)