hdl.mem: add tests for all error conditions.
authorwhitequark <whitequark@whitequark.org>
Fri, 21 Dec 2018 06:07:16 +0000 (06:07 +0000)
committerwhitequark <whitequark@whitequark.org>
Fri, 21 Dec 2018 06:07:16 +0000 (06:07 +0000)
nmigen/hdl/mem.py
nmigen/test/test_hdl_cd.py
nmigen/test/test_hdl_mem.py [new file with mode: 0644]

index b6aacb7c29d393fa5628125ffe24d66609e5f6c8..c8518dbca806904d35848bfe8f4ee4b8b80b8a51 100644 (file)
@@ -28,16 +28,27 @@ class Memory:
         self.depth = depth
         self.init  = None if init is None else list(init)
 
-    def read_port(self, domain="sync", synchronous=False, transparent=True):
+        if self.init is not None and len(self.init) > self.depth:
+            raise ValueError("Memory initialization value count exceed memory depth ({} > {})"
+                             .format(len(self.init), self.depth))
+
+    def read_port(self, domain="sync", synchronous=True, transparent=True):
+        if not synchronous and not transparent:
+            raise ValueError("Read port cannot be simultaneously asynchronous and non-transparent")
         return ReadPort(self, domain, synchronous, transparent)
 
     def write_port(self, domain="sync", priority=0, granularity=None):
         if granularity is None:
             granularity = self.width
-        if not isinstance(granularity, int) or granularity < 0 or granularity > self.width:
-            raise TypeError("Write port granularity must be a non-negative integer not greater "
-                            "than memory width, not '{!r}'"
+        if not isinstance(granularity, int) or granularity < 0:
+            raise TypeError("Write port granularity must be a non-negative integer, not '{!r}'"
                             .format(granularity))
+        if granularity > self.width:
+            raise ValueError("Write port granularity must not be greater than memory width "
+                             "({} > {})"
+                             .format(granularity, self.width))
+        if self.width // granularity * granularity != self.width:
+            raise ValueError("Write port granularity must divide memory width evenly")
         return WritePort(self, domain, priority, granularity)
 
 
@@ -50,7 +61,7 @@ class ReadPort:
 
         self.addr = Signal(max=memory.depth)
         self.data = Signal(memory.width)
-        if synchronous and transparent:
+        if synchronous and not transparent:
             self.en = Signal()
         else:
             self.en = Const(1)
@@ -63,7 +74,7 @@ class ReadPort:
             p_CLK_ENABLE=self.synchronous,
             p_CLK_POLARITY=1,
             p_TRANSPARENT=self.transparent,
-            i_CLK=ClockSignal(self.domain),
+            i_CLK=ClockSignal(self.domain) if self.synchronous else Const(0),
             i_EN=self.en,
             i_ADDR=self.addr,
             o_DATA=self.data,
index 8e7dcdf248879726ced50c6367ba29010914074e..43619f8d171e0e89389254a382aad525a46f18c7 100644 (file)
@@ -2,7 +2,7 @@ from ..hdl.cd import *
 from .tools import *
 
 
-class ClockDomainCase(FHDLTestCase):
+class ClockDomainTestCase(FHDLTestCase):
     def test_name(self):
         sync = ClockDomain()
         self.assertEqual(sync.name, "sync")
diff --git a/nmigen/test/test_hdl_mem.py b/nmigen/test/test_hdl_mem.py
new file mode 100644 (file)
index 0000000..22ad5e3
--- /dev/null
@@ -0,0 +1,109 @@
+from ..hdl.ast import *
+from ..hdl.mem import *
+from .tools 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")
+
+    def test_geometry(self):
+        m = Memory(width=8, depth=4)
+        self.assertEqual(m.width, 8)
+        self.assertEqual(m.depth, 4)
+
+    def test_geometry_wrong(self):
+        with self.assertRaises(TypeError,
+                msg="Memory width must be a non-negative integer, not '-1'"):
+            m = Memory(width=-1, depth=4)
+        with self.assertRaises(TypeError,
+                msg="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])
+
+    def test_init_wrong(self):
+        with self.assertRaises(ValueError,
+                msg="Memory initialization value count exceed memory depth (8 > 4)"):
+            m = Memory(width=8, depth=4, init=range(8))
+
+    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.synchronous, True)
+        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, Const)
+        self.assertEqual(rdport.en.value, 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.synchronous, True)
+        self.assertEqual(rdport.transparent, False)
+        self.assertEqual(len(rdport.en), 1)
+        self.assertIsInstance(rdport.en, Signal)
+
+    def test_read_port_asynchronous(self):
+        mem    = Memory(width=8, depth=4)
+        rdport = mem.read_port(synchronous=False)
+        self.assertEqual(rdport.memory, mem)
+        self.assertEqual(rdport.domain, "sync")
+        self.assertEqual(rdport.synchronous, False)
+        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.assertRaises(ValueError,
+                msg="Read port cannot be simultaneously asynchronous and non-transparent"):
+            mem.read_port(synchronous=False, 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.priority, 0)
+        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.priority, 0)
+        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 self.assertRaises(TypeError,
+                msg="Write port granularity must be a non-negative integer, not '-1'"):
+            mem.write_port(granularity=-1)
+        with self.assertRaises(ValueError,
+                msg="Write port granularity must not be greater than memory width (10 > 8)"):
+            mem.write_port(granularity=10)
+        with self.assertRaises(ValueError,
+                msg="Write port granularity must divide memory width evenly"):
+            mem.write_port(granularity=3)