Implement RFC 45: Move hdl.Memory to lib.Memory.
This commit is contained in:
parent
6d65dc1366
commit
890e099ec3
16 changed files with 983 additions and 141 deletions
|
|
@ -2,113 +2,128 @@
|
|||
|
||||
from amaranth.hdl._ast import *
|
||||
from amaranth.hdl._mem import *
|
||||
from amaranth._utils import _ignore_deprecated
|
||||
|
||||
from .utils import *
|
||||
|
||||
|
||||
class MemoryTestCase(FHDLTestCase):
|
||||
def test_name(self):
|
||||
m1 = Memory(width=8, depth=4)
|
||||
self.assertEqual(m1.name, "m1")
|
||||
m2 = [Memory(width=8, depth=4)][0]
|
||||
self.assertEqual(m2.name, "$memory")
|
||||
m3 = Memory(width=8, depth=4, name="foo")
|
||||
self.assertEqual(m3.name, "foo")
|
||||
with _ignore_deprecated():
|
||||
m1 = Memory(width=8, depth=4)
|
||||
self.assertEqual(m1.name, "m1")
|
||||
m2 = [Memory(width=8, depth=4)][0]
|
||||
self.assertEqual(m2.name, "$memory")
|
||||
m3 = Memory(width=8, depth=4, name="foo")
|
||||
self.assertEqual(m3.name, "foo")
|
||||
|
||||
def test_geometry(self):
|
||||
m = Memory(width=8, depth=4)
|
||||
self.assertEqual(m.width, 8)
|
||||
self.assertEqual(m.depth, 4)
|
||||
with _ignore_deprecated():
|
||||
m = Memory(width=8, depth=4)
|
||||
self.assertEqual(m.width, 8)
|
||||
self.assertEqual(m.depth, 4)
|
||||
|
||||
def test_geometry_wrong(self):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Memory width must be a non-negative integer, not -1$"):
|
||||
m = Memory(width=-1, depth=4)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Memory depth must be a non-negative integer, not -1$"):
|
||||
m = Memory(width=8, depth=-1)
|
||||
with _ignore_deprecated():
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Memory width must be a non-negative integer, not -1$"):
|
||||
m = Memory(width=-1, depth=4)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Memory depth must be a non-negative integer, not -1$"):
|
||||
m = Memory(width=8, depth=-1)
|
||||
|
||||
def test_init(self):
|
||||
m = Memory(width=8, depth=4, init=range(4))
|
||||
self.assertEqual(m.init, [0, 1, 2, 3])
|
||||
with _ignore_deprecated():
|
||||
m = Memory(width=8, depth=4, init=range(4))
|
||||
self.assertEqual(m.init, [0, 1, 2, 3])
|
||||
|
||||
def test_init_wrong_count(self):
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Memory initialization value count exceed memory depth \(8 > 4\)$"):
|
||||
m = Memory(width=8, depth=4, init=range(8))
|
||||
with _ignore_deprecated():
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Memory initialization value count exceed memory depth \(8 > 4\)$"):
|
||||
m = Memory(width=8, depth=4, init=range(8))
|
||||
|
||||
def test_init_wrong_type(self):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
(r"^Memory initialization value at address 1: "
|
||||
r"'str' object cannot be interpreted as an integer$")):
|
||||
m = Memory(width=8, depth=4, init=[1, "0"])
|
||||
with _ignore_deprecated():
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
(r"^Memory initialization value at address 1: "
|
||||
r"'str' object cannot be interpreted as an integer$")):
|
||||
m = Memory(width=8, depth=4, init=[1, "0"])
|
||||
|
||||
def test_attrs(self):
|
||||
m1 = Memory(width=8, depth=4)
|
||||
self.assertEqual(m1.attrs, {})
|
||||
m2 = Memory(width=8, depth=4, attrs={"ram_block": True})
|
||||
self.assertEqual(m2.attrs, {"ram_block": True})
|
||||
with _ignore_deprecated():
|
||||
m1 = Memory(width=8, depth=4)
|
||||
self.assertEqual(m1.attrs, {})
|
||||
m2 = Memory(width=8, depth=4, attrs={"ram_block": True})
|
||||
self.assertEqual(m2.attrs, {"ram_block": True})
|
||||
|
||||
def test_read_port_transparent(self):
|
||||
mem = Memory(width=8, depth=4)
|
||||
rdport = mem.read_port()
|
||||
self.assertEqual(rdport.memory, mem)
|
||||
self.assertEqual(rdport.domain, "sync")
|
||||
self.assertEqual(rdport.transparent, True)
|
||||
self.assertEqual(len(rdport.addr), 2)
|
||||
self.assertEqual(len(rdport.data), 8)
|
||||
self.assertEqual(len(rdport.en), 1)
|
||||
self.assertIsInstance(rdport.en, Signal)
|
||||
self.assertEqual(rdport.en.init, 1)
|
||||
with _ignore_deprecated():
|
||||
mem = Memory(width=8, depth=4)
|
||||
rdport = mem.read_port()
|
||||
self.assertEqual(rdport.memory, mem)
|
||||
self.assertEqual(rdport.domain, "sync")
|
||||
self.assertEqual(rdport.transparent, True)
|
||||
self.assertEqual(len(rdport.addr), 2)
|
||||
self.assertEqual(len(rdport.data), 8)
|
||||
self.assertEqual(len(rdport.en), 1)
|
||||
self.assertIsInstance(rdport.en, Signal)
|
||||
self.assertEqual(rdport.en.init, 1)
|
||||
|
||||
def test_read_port_non_transparent(self):
|
||||
mem = Memory(width=8, depth=4)
|
||||
rdport = mem.read_port(transparent=False)
|
||||
self.assertEqual(rdport.memory, mem)
|
||||
self.assertEqual(rdport.domain, "sync")
|
||||
self.assertEqual(rdport.transparent, False)
|
||||
self.assertEqual(len(rdport.en), 1)
|
||||
self.assertIsInstance(rdport.en, Signal)
|
||||
self.assertEqual(rdport.en.init, 1)
|
||||
with _ignore_deprecated():
|
||||
mem = Memory(width=8, depth=4)
|
||||
rdport = mem.read_port(transparent=False)
|
||||
self.assertEqual(rdport.memory, mem)
|
||||
self.assertEqual(rdport.domain, "sync")
|
||||
self.assertEqual(rdport.transparent, False)
|
||||
self.assertEqual(len(rdport.en), 1)
|
||||
self.assertIsInstance(rdport.en, Signal)
|
||||
self.assertEqual(rdport.en.init, 1)
|
||||
|
||||
def test_read_port_asynchronous(self):
|
||||
mem = Memory(width=8, depth=4)
|
||||
rdport = mem.read_port(domain="comb")
|
||||
self.assertEqual(rdport.memory, mem)
|
||||
self.assertEqual(rdport.domain, "comb")
|
||||
self.assertEqual(rdport.transparent, True)
|
||||
self.assertEqual(len(rdport.en), 1)
|
||||
self.assertIsInstance(rdport.en, Const)
|
||||
self.assertEqual(rdport.en.value, 1)
|
||||
with _ignore_deprecated():
|
||||
mem = Memory(width=8, depth=4)
|
||||
rdport = mem.read_port(domain="comb")
|
||||
self.assertEqual(rdport.memory, mem)
|
||||
self.assertEqual(rdport.domain, "comb")
|
||||
self.assertEqual(rdport.transparent, True)
|
||||
self.assertEqual(len(rdport.en), 1)
|
||||
self.assertIsInstance(rdport.en, Const)
|
||||
self.assertEqual(rdport.en.value, 1)
|
||||
|
||||
def test_read_port_wrong(self):
|
||||
mem = Memory(width=8, depth=4)
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Read port cannot be simultaneously asynchronous and non-transparent$"):
|
||||
mem.read_port(domain="comb", transparent=False)
|
||||
with _ignore_deprecated():
|
||||
mem = Memory(width=8, depth=4)
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Read port cannot be simultaneously asynchronous and non-transparent$"):
|
||||
mem.read_port(domain="comb", transparent=False)
|
||||
|
||||
def test_write_port(self):
|
||||
mem = Memory(width=8, depth=4)
|
||||
wrport = mem.write_port()
|
||||
self.assertEqual(wrport.memory, mem)
|
||||
self.assertEqual(wrport.domain, "sync")
|
||||
self.assertEqual(wrport.granularity, 8)
|
||||
self.assertEqual(len(wrport.addr), 2)
|
||||
self.assertEqual(len(wrport.data), 8)
|
||||
self.assertEqual(len(wrport.en), 1)
|
||||
with _ignore_deprecated():
|
||||
mem = Memory(width=8, depth=4)
|
||||
wrport = mem.write_port()
|
||||
self.assertEqual(wrport.memory, mem)
|
||||
self.assertEqual(wrport.domain, "sync")
|
||||
self.assertEqual(wrport.granularity, 8)
|
||||
self.assertEqual(len(wrport.addr), 2)
|
||||
self.assertEqual(len(wrport.data), 8)
|
||||
self.assertEqual(len(wrport.en), 1)
|
||||
|
||||
def test_write_port_granularity(self):
|
||||
mem = Memory(width=8, depth=4)
|
||||
wrport = mem.write_port(granularity=2)
|
||||
self.assertEqual(wrport.memory, mem)
|
||||
self.assertEqual(wrport.domain, "sync")
|
||||
self.assertEqual(wrport.granularity, 2)
|
||||
self.assertEqual(len(wrport.addr), 2)
|
||||
self.assertEqual(len(wrport.data), 8)
|
||||
self.assertEqual(len(wrport.en), 4)
|
||||
with _ignore_deprecated():
|
||||
mem = Memory(width=8, depth=4)
|
||||
wrport = mem.write_port(granularity=2)
|
||||
self.assertEqual(wrport.memory, mem)
|
||||
self.assertEqual(wrport.domain, "sync")
|
||||
self.assertEqual(wrport.granularity, 2)
|
||||
self.assertEqual(len(wrport.addr), 2)
|
||||
self.assertEqual(len(wrport.data), 8)
|
||||
self.assertEqual(len(wrport.en), 4)
|
||||
|
||||
def test_write_port_granularity_wrong(self):
|
||||
mem = Memory(width=8, depth=4)
|
||||
with _ignore_deprecated():
|
||||
mem = Memory(width=8, depth=4)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Write port granularity must be a non-negative integer, not -1$"):
|
||||
mem.write_port(granularity=-1)
|
||||
|
|
@ -119,20 +134,32 @@ class MemoryTestCase(FHDLTestCase):
|
|||
r"^Write port granularity must divide memory width evenly$"):
|
||||
mem.write_port(granularity=3)
|
||||
|
||||
def test_deprecated(self):
|
||||
with self.assertWarnsRegex(DeprecationWarning,
|
||||
r"^`amaranth.hdl.Memory` is deprecated.*$"):
|
||||
mem = Memory(width=8, depth=4)
|
||||
|
||||
|
||||
class DummyPortTestCase(FHDLTestCase):
|
||||
def test_name(self):
|
||||
p1 = DummyPort(data_width=8, addr_width=2)
|
||||
self.assertEqual(p1.addr.name, "p1_addr")
|
||||
p2 = [DummyPort(data_width=8, addr_width=2)][0]
|
||||
self.assertEqual(p2.addr.name, "dummy_addr")
|
||||
p3 = DummyPort(data_width=8, addr_width=2, name="foo")
|
||||
self.assertEqual(p3.addr.name, "foo_addr")
|
||||
with _ignore_deprecated():
|
||||
p1 = DummyPort(data_width=8, addr_width=2)
|
||||
self.assertEqual(p1.addr.name, "p1_addr")
|
||||
p2 = [DummyPort(data_width=8, addr_width=2)][0]
|
||||
self.assertEqual(p2.addr.name, "dummy_addr")
|
||||
p3 = DummyPort(data_width=8, addr_width=2, name="foo")
|
||||
self.assertEqual(p3.addr.name, "foo_addr")
|
||||
|
||||
def test_sizes(self):
|
||||
p1 = DummyPort(data_width=8, addr_width=2)
|
||||
self.assertEqual(p1.addr.width, 2)
|
||||
self.assertEqual(p1.data.width, 8)
|
||||
self.assertEqual(p1.en.width, 1)
|
||||
p2 = DummyPort(data_width=8, addr_width=2, granularity=2)
|
||||
self.assertEqual(p2.en.width, 4)
|
||||
with _ignore_deprecated():
|
||||
p1 = DummyPort(data_width=8, addr_width=2)
|
||||
self.assertEqual(p1.addr.width, 2)
|
||||
self.assertEqual(p1.data.width, 8)
|
||||
self.assertEqual(p1.en.width, 1)
|
||||
p2 = DummyPort(data_width=8, addr_width=2, granularity=2)
|
||||
self.assertEqual(p2.en.width, 4)
|
||||
|
||||
def test_deprecated(self):
|
||||
with self.assertWarnsRegex(DeprecationWarning,
|
||||
r"^`DummyPort` is deprecated.*$"):
|
||||
DummyPort(data_width=8, addr_width=2)
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@ class DomainRenamerTestCase(FHDLTestCase):
|
|||
|
||||
def test_rename_mem_ports(self):
|
||||
m = Module()
|
||||
mem = Memory(depth=4, width=16)
|
||||
with _ignore_deprecated():
|
||||
mem = Memory(depth=4, width=16)
|
||||
m.submodules.mem = mem
|
||||
mem.read_port(domain="a")
|
||||
mem.read_port(domain="b")
|
||||
|
|
@ -397,7 +398,8 @@ class EnableInserterTestCase(FHDLTestCase):
|
|||
""")
|
||||
|
||||
def test_enable_read_port(self):
|
||||
mem = Memory(width=8, depth=4)
|
||||
with _ignore_deprecated():
|
||||
mem = Memory(width=8, depth=4)
|
||||
mem.read_port(transparent=False)
|
||||
f = EnableInserter(self.c1)(mem).elaborate(platform=None)
|
||||
self.assertRepr(f._read_ports[0]._en, """
|
||||
|
|
@ -405,7 +407,8 @@ class EnableInserterTestCase(FHDLTestCase):
|
|||
""")
|
||||
|
||||
def test_enable_write_port(self):
|
||||
mem = Memory(width=8, depth=4)
|
||||
with _ignore_deprecated():
|
||||
mem = Memory(width=8, depth=4)
|
||||
mem.write_port(granularity=2)
|
||||
f = EnableInserter(self.c1)(mem).elaborate(platform=None)
|
||||
self.assertRepr(f._write_ports[0]._en, """
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from amaranth.hdl import *
|
|||
from amaranth.asserts import *
|
||||
from amaranth.sim import *
|
||||
from amaranth.lib.fifo import *
|
||||
from amaranth.lib.memory import *
|
||||
|
||||
from .utils import *
|
||||
from amaranth._utils import _ignore_deprecated
|
||||
|
|
@ -81,9 +82,9 @@ class FIFOModel(Elaboratable, FIFOInterface):
|
|||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
storage = Memory(width=self.width, depth=self.depth)
|
||||
w_port = m.submodules.w_port = storage.write_port(domain=self.w_domain)
|
||||
r_port = m.submodules.r_port = storage.read_port (domain="comb")
|
||||
storage = m.submodules.storage = Memory(shape=self.width, depth=self.depth, init=[])
|
||||
w_port = storage.write_port(domain=self.w_domain)
|
||||
r_port = storage.read_port (domain="comb")
|
||||
|
||||
produce = Signal(range(self.depth))
|
||||
consume = Signal(range(self.depth))
|
||||
|
|
|
|||
369
tests/test_lib_memory.py
Normal file
369
tests/test_lib_memory.py
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
# amaranth: UnusedElaboratable=no
|
||||
|
||||
from amaranth.hdl._ast import *
|
||||
from amaranth.hdl._mem import MemoryInstance
|
||||
from amaranth.lib.memory import *
|
||||
from amaranth.lib.data import *
|
||||
from amaranth.lib.wiring import In, Out, SignatureMembers
|
||||
|
||||
from .utils import *
|
||||
|
||||
class MyStruct(Struct):
|
||||
a: unsigned(3)
|
||||
b: signed(2)
|
||||
|
||||
|
||||
class WritePortTestCase(FHDLTestCase):
|
||||
def test_signature(self):
|
||||
sig = WritePort.Signature(addr_width=2, shape=signed(4))
|
||||
self.assertEqual(sig.addr_width, 2)
|
||||
self.assertEqual(sig.shape, signed(4))
|
||||
self.assertEqual(sig.granularity, None)
|
||||
self.assertEqual(sig.members, SignatureMembers({
|
||||
"addr": In(2),
|
||||
"data": In(signed(4)),
|
||||
"en": In(1),
|
||||
}))
|
||||
sig = WritePort.Signature(addr_width=2, shape=8, granularity=2)
|
||||
self.assertEqual(sig.addr_width, 2)
|
||||
self.assertEqual(sig.shape, 8)
|
||||
self.assertEqual(sig.members, SignatureMembers({
|
||||
"addr": In(2),
|
||||
"data": In(8),
|
||||
"en": In(4),
|
||||
}))
|
||||
sig = WritePort.Signature(addr_width=2, shape=ArrayLayout(9, 8), granularity=2)
|
||||
self.assertEqual(sig.addr_width, 2)
|
||||
self.assertEqual(sig.shape, ArrayLayout(9, 8))
|
||||
self.assertEqual(sig.members, SignatureMembers({
|
||||
"addr": In(2),
|
||||
"data": In(ArrayLayout(9, 8)),
|
||||
"en": In(4),
|
||||
}))
|
||||
sig = WritePort.Signature(addr_width=2, shape=0, granularity=0)
|
||||
self.assertEqual(sig.addr_width, 2)
|
||||
self.assertEqual(sig.shape, 0)
|
||||
self.assertEqual(sig.members, SignatureMembers({
|
||||
"addr": In(2),
|
||||
"data": In(0),
|
||||
"en": In(0),
|
||||
}))
|
||||
sig = WritePort.Signature(addr_width=2, shape=ArrayLayout(9, 0), granularity=0)
|
||||
self.assertEqual(sig.addr_width, 2)
|
||||
self.assertEqual(sig.shape, ArrayLayout(9, 0))
|
||||
self.assertEqual(sig.members, SignatureMembers({
|
||||
"addr": In(2),
|
||||
"data": In(ArrayLayout(9, 0)),
|
||||
"en": In(0),
|
||||
}))
|
||||
|
||||
def test_signature_wrong(self):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
"^`addr_width` must be a non-negative int, not -2$"):
|
||||
WritePort.Signature(addr_width=-2, shape=8)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
"^Granularity must be a non-negative int or None, not -2$"):
|
||||
WritePort.Signature(addr_width=4, shape=8, granularity=-2)
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
"^Granularity cannot be specified with signed shape$"):
|
||||
WritePort.Signature(addr_width=2, shape=signed(8), granularity=2)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
"^Granularity can only be specified for plain unsigned `Shape` or `ArrayLayout`$"):
|
||||
WritePort.Signature(addr_width=2, shape=MyStruct, granularity=2)
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
"^Granularity must be positive$"):
|
||||
WritePort.Signature(addr_width=2, shape=8, granularity=0)
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
"^Granularity must be positive$"):
|
||||
WritePort.Signature(addr_width=2, shape=ArrayLayout(8, 8), granularity=0)
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
"^Granularity must divide data width$"):
|
||||
WritePort.Signature(addr_width=2, shape=8, granularity=3)
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
"^Granularity must divide data array length$"):
|
||||
WritePort.Signature(addr_width=2, shape=ArrayLayout(8, 8), granularity=3)
|
||||
|
||||
def test_constructor(self):
|
||||
signature = WritePort.Signature(shape=MyStruct, addr_width=4)
|
||||
port = WritePort(signature, memory=None, domain="sync")
|
||||
self.assertEqual(port.signature, signature)
|
||||
self.assertIsNone(port.memory)
|
||||
self.assertEqual(port.domain, "sync")
|
||||
self.assertIsInstance(port.addr, Signal)
|
||||
self.assertEqual(port.addr.shape(), unsigned(4))
|
||||
self.assertIsInstance(port.data, View)
|
||||
self.assertEqual(port.data.shape(), MyStruct)
|
||||
self.assertIsInstance(port.en, Signal)
|
||||
self.assertEqual(port.en.shape(), unsigned(1))
|
||||
|
||||
signature = WritePort.Signature(shape=8, addr_width=4, granularity=2)
|
||||
port = WritePort(signature, memory=None, domain="sync")
|
||||
self.assertEqual(port.signature, signature)
|
||||
self.assertIsNone(port.memory)
|
||||
self.assertEqual(port.domain, "sync")
|
||||
self.assertIsInstance(port.addr, Signal)
|
||||
self.assertEqual(port.addr.shape(), unsigned(4))
|
||||
self.assertIsInstance(port.data, Signal)
|
||||
self.assertEqual(port.data.shape(), unsigned(8))
|
||||
self.assertIsInstance(port.en, Signal)
|
||||
self.assertEqual(port.en.shape(), unsigned(4))
|
||||
|
||||
m = Memory(depth=16, shape=8, init=[])
|
||||
port = WritePort(signature, memory=m, domain="sync")
|
||||
self.assertIs(port.memory, m)
|
||||
self.assertEqual(m.w_ports, (port,))
|
||||
|
||||
def test_constructor_wrong(self):
|
||||
signature = ReadPort.Signature(shape=8, addr_width=4)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Expected `WritePort.Signature`, not ReadPort.Signature\(.*\)$"):
|
||||
WritePort(signature, memory=None, domain="sync")
|
||||
signature = WritePort.Signature(shape=8, addr_width=4, granularity=2)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Domain has to be a string, not None$"):
|
||||
WritePort(signature, memory=None, domain=None)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Expected `Memory` or `None`, not 'a'$"):
|
||||
WritePort(signature, memory="a", domain="sync")
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Write port domain cannot be \"comb\"$"):
|
||||
WritePort(signature, memory=None, domain="comb")
|
||||
signature = WritePort.Signature(shape=8, addr_width=4)
|
||||
m = Memory(depth=8, shape=8, init=[])
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Memory address width 3 doesn't match port address width 4$"):
|
||||
WritePort(signature, memory=m, domain="sync")
|
||||
m = Memory(depth=16, shape=signed(8), init=[])
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Memory shape signed\(8\) doesn't match port shape 8$"):
|
||||
WritePort(signature, memory=m, domain="sync")
|
||||
|
||||
|
||||
class ReadPortTestCase(FHDLTestCase):
|
||||
def test_signature(self):
|
||||
sig = ReadPort.Signature(addr_width=2, shape=signed(4))
|
||||
self.assertEqual(sig.addr_width, 2)
|
||||
self.assertEqual(sig.shape, signed(4))
|
||||
self.assertEqual(sig.members, SignatureMembers({
|
||||
"addr": In(2),
|
||||
"data": Out(signed(4)),
|
||||
"en": In(1, init=1),
|
||||
}))
|
||||
sig = ReadPort.Signature(addr_width=2, shape=8)
|
||||
self.assertEqual(sig.addr_width, 2)
|
||||
self.assertEqual(sig.shape, 8)
|
||||
self.assertEqual(sig.members, SignatureMembers({
|
||||
"addr": In(2),
|
||||
"data": Out(8),
|
||||
"en": In(1, init=1),
|
||||
}))
|
||||
sig = ReadPort.Signature(addr_width=2, shape=MyStruct)
|
||||
self.assertEqual(sig.addr_width, 2)
|
||||
self.assertEqual(sig.shape, MyStruct)
|
||||
self.assertEqual(sig.members, SignatureMembers({
|
||||
"addr": In(2),
|
||||
"data": Out(MyStruct),
|
||||
"en": In(1, init=1),
|
||||
}))
|
||||
|
||||
def test_signature_wrong(self):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
"^`addr_width` must be a non-negative int, not -2$"):
|
||||
ReadPort.Signature(addr_width=-2, shape=8)
|
||||
|
||||
def test_constructor(self):
|
||||
signature = ReadPort.Signature(shape=MyStruct, addr_width=4)
|
||||
port = ReadPort(signature, memory=None, domain="sync")
|
||||
self.assertEqual(port.signature, signature)
|
||||
self.assertIsNone(port.memory)
|
||||
self.assertEqual(port.domain, "sync")
|
||||
self.assertIsInstance(port.addr, Signal)
|
||||
self.assertEqual(port.addr.shape(), unsigned(4))
|
||||
self.assertIsInstance(port.data, View)
|
||||
self.assertEqual(port.data.shape(), MyStruct)
|
||||
self.assertIsInstance(port.en, Signal)
|
||||
self.assertEqual(port.en.shape(), unsigned(1))
|
||||
self.assertEqual(port.transparent_for, ())
|
||||
|
||||
signature = ReadPort.Signature(shape=8, addr_width=4)
|
||||
port = ReadPort(signature, memory=None, domain="comb")
|
||||
self.assertEqual(port.signature, signature)
|
||||
self.assertIsNone(port.memory)
|
||||
self.assertEqual(port.domain, "comb")
|
||||
self.assertIsInstance(port.addr, Signal)
|
||||
self.assertEqual(port.addr.shape(), unsigned(4))
|
||||
self.assertIsInstance(port.data, Signal)
|
||||
self.assertEqual(port.data.shape(), unsigned(8))
|
||||
self.assertIsInstance(port.en, Const)
|
||||
self.assertEqual(port.en.shape(), unsigned(1))
|
||||
self.assertEqual(port.en.value, 1)
|
||||
self.assertEqual(port.transparent_for, ())
|
||||
|
||||
m = Memory(depth=16, shape=8, init=[])
|
||||
port = ReadPort(signature, memory=m, domain="sync")
|
||||
self.assertIs(port.memory, m)
|
||||
self.assertEqual(m.r_ports, (port,))
|
||||
write_port = m.write_port()
|
||||
port = ReadPort(signature, memory=m, domain="sync", transparent_for=[write_port])
|
||||
self.assertIs(port.memory, m)
|
||||
self.assertEqual(port.transparent_for, (write_port,))
|
||||
|
||||
def test_constructor_wrong(self):
|
||||
signature = WritePort.Signature(shape=8, addr_width=4)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Expected `ReadPort.Signature`, not WritePort.Signature\(.*\)$"):
|
||||
ReadPort(signature, memory=None, domain="sync")
|
||||
signature = ReadPort.Signature(shape=8, addr_width=4)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Domain has to be a string, not None$"):
|
||||
ReadPort(signature, memory=None, domain=None)
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Expected `Memory` or `None`, not 'a'$"):
|
||||
ReadPort(signature, memory="a", domain="sync")
|
||||
signature = ReadPort.Signature(shape=8, addr_width=4)
|
||||
m = Memory(depth=8, shape=8, init=[])
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Memory address width 3 doesn't match port address width 4$"):
|
||||
ReadPort(signature, memory=m, domain="sync")
|
||||
m = Memory(depth=16, shape=signed(8), init=[])
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Memory shape signed\(8\) doesn't match port shape 8$"):
|
||||
ReadPort(signature, memory=m, domain="sync")
|
||||
m = Memory(depth=16, shape=8, init=[])
|
||||
port = m.read_port()
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^`transparent_for` must contain only `WritePort` instances$"):
|
||||
ReadPort(signature, memory=m, domain="sync", transparent_for=[port])
|
||||
write_port = m.write_port()
|
||||
m2 = Memory(depth=16, shape=8, init=[])
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Transparent write ports must belong to the same memory$"):
|
||||
ReadPort(signature, memory=m2, domain="sync", transparent_for=[write_port])
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Transparent write ports must belong to the same domain$"):
|
||||
ReadPort(signature, memory=m, domain="other", transparent_for=[write_port])
|
||||
|
||||
|
||||
class MemoryTestCase(FHDLTestCase):
|
||||
def test_constructor(self):
|
||||
m = Memory(shape=8, depth=4, init=[1, 2, 3])
|
||||
self.assertEqual(m.shape, 8)
|
||||
self.assertEqual(m.depth, 4)
|
||||
self.assertEqual(m.init.shape, 8)
|
||||
self.assertEqual(m.init.depth, 4)
|
||||
self.assertEqual(m.attrs, {})
|
||||
self.assertIsInstance(m.init, Memory.Init)
|
||||
self.assertEqual(list(m.init), [1, 2, 3, 0])
|
||||
self.assertEqual(m.init._raw, [1, 2, 3, 0])
|
||||
self.assertRepr(m.init, "Memory.Init([1, 2, 3, 0])")
|
||||
self.assertEqual(m.r_ports, ())
|
||||
self.assertEqual(m.w_ports, ())
|
||||
|
||||
def test_constructor_shapecastable(self):
|
||||
init = [
|
||||
{"a": 0, "b": 1},
|
||||
{"a": 2, "b": 3},
|
||||
]
|
||||
m = Memory(shape=MyStruct, depth=4, init=init, attrs={"ram_style": "block"})
|
||||
self.assertEqual(m.shape, MyStruct)
|
||||
self.assertEqual(m.depth, 4)
|
||||
self.assertEqual(m.attrs, {"ram_style": "block"})
|
||||
self.assertIsInstance(m.init, Memory.Init)
|
||||
self.assertEqual(list(m.init), [{"a": 0, "b": 1}, {"a": 2, "b": 3}, None, None])
|
||||
self.assertEqual(m.init._raw, [8, 0x1a, 0, 0])
|
||||
|
||||
def test_constructor_wrong(self):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Memory depth must be a non-negative integer, not 'a'$"):
|
||||
Memory(shape=8, depth="a", init=[])
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Memory depth must be a non-negative integer, not -1$"):
|
||||
Memory(shape=8, depth=-1, init=[])
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Object 'a' cannot be converted to an Amaranth shape$"):
|
||||
Memory(shape="a", depth=3, init=[])
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
(r"^Memory initialization value at address 1: "
|
||||
r"'str' object cannot be interpreted as an integer$")):
|
||||
Memory(shape=8, depth=4, init=[1, "0"])
|
||||
|
||||
def test_init_set(self):
|
||||
m = Memory(shape=8, depth=4, init=[])
|
||||
m.init[1] = 2
|
||||
self.assertEqual(list(m.init), [0, 2, 0, 0])
|
||||
self.assertEqual(m.init._raw, [0, 2, 0, 0])
|
||||
m.init[2:] = [4, 5]
|
||||
self.assertEqual(list(m.init), [0, 2, 4, 5])
|
||||
|
||||
def test_init_set_shapecastable(self):
|
||||
m = Memory(shape=MyStruct, depth=4, init=[])
|
||||
m.init[1] = {"a": 1, "b": 2}
|
||||
self.assertEqual(list(m.init), [None, {"a": 1, "b": 2}, None, None])
|
||||
self.assertEqual(m.init._raw, [0, 0x11, 0, 0])
|
||||
|
||||
def test_init_set_wrong(self):
|
||||
m = Memory(shape=8, depth=4, init=[])
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^'str' object cannot be interpreted as an integer$"):
|
||||
m.init[0] = "a"
|
||||
m = Memory(shape=MyStruct, depth=4, init=[])
|
||||
# underlying TypeError message differs between PyPy and CPython
|
||||
with self.assertRaises(TypeError):
|
||||
m.init[0] = 1
|
||||
|
||||
def test_init_set_slice_wrong(self):
|
||||
m = Memory(shape=8, depth=4, init=[])
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r"^Changing length of Memory.init is not allowed$"):
|
||||
m.init[1:] = [1, 2]
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Deleting items from Memory.init is not allowed$"):
|
||||
del m.init[1:2]
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"^Inserting items into Memory.init is not allowed$"):
|
||||
m.init.insert(1, 3)
|
||||
|
||||
def test_port(self):
|
||||
for depth, addr_width in [
|
||||
(0, 0),
|
||||
(1, 0),
|
||||
(3, 2),
|
||||
(4, 2),
|
||||
(5, 3),
|
||||
]:
|
||||
m = Memory(shape=8, depth=depth, init=[])
|
||||
rp = m.read_port()
|
||||
self.assertEqual(rp.signature.addr_width, addr_width)
|
||||
self.assertEqual(rp.signature.shape, 8)
|
||||
wp = m.write_port()
|
||||
self.assertEqual(wp.signature.addr_width, addr_width)
|
||||
self.assertEqual(wp.signature.shape, 8)
|
||||
self.assertEqual(m.r_ports, (rp,))
|
||||
self.assertEqual(m.w_ports, (wp,))
|
||||
|
||||
def test_elaborate(self):
|
||||
m = Memory(shape=MyStruct, depth=4, init=[{"a": 1, "b": 2}])
|
||||
wp = m.write_port()
|
||||
rp0 = m.read_port(domain="sync", transparent_for=[wp])
|
||||
rp1 = m.read_port(domain="comb")
|
||||
f = m.elaborate(None)
|
||||
self.assertIsInstance(f, MemoryInstance)
|
||||
self.assertIs(f._identity, m._identity)
|
||||
self.assertEqual(f._depth, 4)
|
||||
self.assertEqual(f._width, 5)
|
||||
self.assertEqual(f._init, (0x11, 0, 0, 0))
|
||||
self.assertEqual(f._write_ports[0]._domain, "sync")
|
||||
self.assertEqual(f._write_ports[0]._granularity, 5)
|
||||
self.assertIs(f._write_ports[0]._addr, wp.addr)
|
||||
self.assertIs(f._write_ports[0]._data, wp.data.as_value())
|
||||
self.assertIs(f._write_ports[0]._en, wp.en)
|
||||
self.assertEqual(f._read_ports[0]._domain, "sync")
|
||||
self.assertEqual(f._read_ports[0]._transparent_for, (0,))
|
||||
self.assertIs(f._read_ports[0]._addr, rp0.addr)
|
||||
self.assertIs(f._read_ports[0]._data, rp0.data.as_value())
|
||||
self.assertIs(f._read_ports[0]._en, rp0.en)
|
||||
self.assertEqual(f._read_ports[1]._domain, "comb")
|
||||
self.assertEqual(f._read_ports[1]._transparent_for, ())
|
||||
self.assertIs(f._read_ports[1]._addr, rp1.addr)
|
||||
self.assertIs(f._read_ports[1]._data, rp1.data.as_value())
|
||||
self.assertIs(f._read_ports[1]._en, rp1.en)
|
||||
|
|
@ -5,13 +5,13 @@ from contextlib import contextmanager
|
|||
from amaranth._utils import flatten
|
||||
from amaranth.hdl._ast import *
|
||||
from amaranth.hdl._cd import *
|
||||
from amaranth.hdl._mem import *
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings(action="ignore", category=DeprecationWarning)
|
||||
from amaranth.hdl.rec import *
|
||||
from amaranth.hdl._dsl import *
|
||||
from amaranth.hdl._ir import *
|
||||
from amaranth.sim import *
|
||||
from amaranth.lib.memory import Memory
|
||||
|
||||
from .utils import *
|
||||
from amaranth._utils import _ignore_deprecated
|
||||
|
|
@ -752,14 +752,12 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
|||
sim.add_testbench(process)
|
||||
self.assertTrue(survived)
|
||||
|
||||
def setUp_memory(self, rd_synchronous=True, rd_transparent=True, wr_granularity=None):
|
||||
def setUp_memory(self, rd_synchronous=True, rd_transparent=False, wr_granularity=None):
|
||||
self.m = Module()
|
||||
self.memory = Memory(width=8, depth=4, init=[0xaa, 0x55])
|
||||
self.m.submodules.rdport = self.rdport = \
|
||||
self.memory.read_port(domain="sync" if rd_synchronous else "comb",
|
||||
transparent=rd_transparent)
|
||||
self.m.submodules.wrport = self.wrport = \
|
||||
self.memory.write_port(granularity=wr_granularity)
|
||||
self.memory = self.m.submodules.memory = Memory(shape=8, depth=4, init=[0xaa, 0x55])
|
||||
self.wrport = self.memory.write_port(granularity=wr_granularity)
|
||||
self.rdport = self.memory.read_port(domain="sync" if rd_synchronous else "comb",
|
||||
transparent_for=[self.wrport] if rd_transparent else [])
|
||||
|
||||
def test_memory_init(self):
|
||||
self.setUp_memory()
|
||||
|
|
@ -862,8 +860,8 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
|||
|
||||
def test_memory_read_only(self):
|
||||
self.m = Module()
|
||||
self.memory = Memory(width=8, depth=4, init=[0xaa, 0x55])
|
||||
self.m.submodules.rdport = self.rdport = self.memory.read_port()
|
||||
self.m.submodules.memory = self.memory = Memory(shape=8, depth=4, init=[0xaa, 0x55])
|
||||
self.rdport = self.memory.read_port()
|
||||
with self.assertSimulation(self.m) as sim:
|
||||
def process():
|
||||
yield Tick()
|
||||
|
|
@ -920,9 +918,9 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
|||
def test_memory_transparency_simple(self):
|
||||
m = Module()
|
||||
init = [0x11, 0x22, 0x33, 0x44]
|
||||
m.submodules.memory = memory = Memory(width=8, depth=4, init=init)
|
||||
rdport = memory.read_port()
|
||||
m.submodules.memory = memory = Memory(shape=8, depth=4, init=init)
|
||||
wrport = memory.write_port(granularity=8)
|
||||
rdport = memory.read_port(transparent_for=[wrport])
|
||||
with self.assertSimulation(m) as sim:
|
||||
def process():
|
||||
yield rdport.addr.eq(0)
|
||||
|
|
@ -959,9 +957,9 @@ class SimulatorIntegrationTestCase(FHDLTestCase):
|
|||
def test_memory_transparency_multibit(self):
|
||||
m = Module()
|
||||
init = [0x11111111, 0x22222222, 0x33333333, 0x44444444]
|
||||
m.submodules.memory = memory = Memory(width=32, depth=4, init=init)
|
||||
rdport = memory.read_port()
|
||||
m.submodules.memory = memory = Memory(shape=32, depth=4, init=init)
|
||||
wrport = memory.write_port(granularity=8)
|
||||
rdport = memory.read_port(transparent_for=[wrport])
|
||||
with self.assertSimulation(m) as sim:
|
||||
def process():
|
||||
yield rdport.addr.eq(0)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue