hdl.mem: implement memories.
authorwhitequark <cz@m-labs.hk>
Fri, 21 Dec 2018 01:53:32 +0000 (01:53 +0000)
committerwhitequark <cz@m-labs.hk>
Fri, 21 Dec 2018 01:53:32 +0000 (01:53 +0000)
doc/COMPAT_SUMMARY.md
nmigen/__init__.py
nmigen/hdl/ast.py
nmigen/hdl/mem.py [new file with mode: 0644]

index 86ba8f919d850435cba34f2836af203afc49c1cb..f5746a491684a4209a1d5e10395317cd013af270 100644 (file)
@@ -54,9 +54,14 @@ Compatibility summary
       - (−) `Tristate` ?
       - (+) `TSTriple` → `.lib.io.TSTriple`, `bits_sign=`→`shape=`
       - (−) `Instance` ?
-      - (−) `READ_FIRST`/`WRITE_FIRST`/`NO_CHANGE` ?
-      - (−) `_MemoryPort` ?
-      - (−) `Memory` ?
+      - (−) `Memory` id
+        - (−) `.get_port` **obs** → `.read_port()` + `.write_port()`
+      - (−) `_MemoryPort` **obs**
+        <br>Note: nMigen separates read and write ports.
+      - (−) `READ_FIRST`/`WRITE_FIRST` **obs**
+        <br>Note: `READ_FIRST` corresponds to `mem.read_port(transparent=False)`, and `WRITE_FIRST` to `mem.read_port(transparent=True)`.
+      - (-) `NO_CHANGE` **brk**
+        <br>Note: in designs using `NO_CHANGE`, repalce it with an asynchronous read port and logic implementing required semantics explicitly.
     - (−) `structure` → `.hdl.ast`
       - (+) `DUID` id
       - (+) `_Value` → `Value`
index ce1afeba4a9092601469510758ee5eef8a279ac5..a9ed14de77e33fde6e87bf632b16ddfbbc009fd1 100644 (file)
@@ -2,6 +2,7 @@ from .hdl.ast import Value, Const, C, Mux, Cat, Repl, Array, Signal, ClockSignal
 from .hdl.dsl import Module
 from .hdl.cd import ClockDomain
 from .hdl.ir import Fragment, Instance
+from .hdl.mem import Memory
 from .hdl.xfrm import ResetInserter, CEInserter
 
 from .lib.cdc import MultiReg
index e890b4aa14b9223ff2736cf14fe2941b0cbea56f..52e7dd38899688ad408a64cecf4c3212a8eed813 100644 (file)
@@ -236,7 +236,7 @@ class Const(Value):
             shape = shape, self.value < 0
         self.nbits, self.signed = shape
         if not isinstance(self.nbits, int) or self.nbits < 0:
-            raise TypeError("Width must be a non-negative integer")
+            raise TypeError("Width must be a non-negative integer, not '{!r}'", self.nbits)
         self.value = self.normalize(self.value, shape)
 
     def shape(self):
@@ -1067,7 +1067,7 @@ class ValueSet(_MappedKeySet):
 class SignalKey:
     def __init__(self, signal):
         if type(signal) is not Signal:
-            raise TypeError("Object '{!r}' is not an nMigen signal")
+            raise TypeError("Object '{!r}' is not an nMigen signal".format(signal))
         self.signal = signal
 
     def __hash__(self):
@@ -1080,7 +1080,7 @@ class SignalKey:
 
     def __lt__(self, other):
         if type(other) is not SignalKey:
-            raise TypeError("Object '{!r}' cannot be compared to a SignalKey")
+            raise TypeError("Object '{!r}' cannot be compared to a SignalKey".format(signal))
         return self.signal.duid < other.signal.duid
 
     def __repr__(self):
diff --git a/nmigen/hdl/mem.py b/nmigen/hdl/mem.py
new file mode 100644 (file)
index 0000000..3091890
--- /dev/null
@@ -0,0 +1,92 @@
+import traceback
+
+from .. import tracer
+from .ast import *
+from .ir import Instance
+
+
+class Memory:
+    def __init__(self, width, depth, init=None, name=None):
+        if not isinstance(width, int) or width < 0:
+            raise TypeError("Memory width must be a non-negative integer, not '{!r}'"
+                            .format(width))
+        if not isinstance(depth, int) or depth < 0:
+            raise TypeError("Memory depth must be a non-negative integer, not '{!r}'"
+                            .format(depth))
+
+        tb = traceback.extract_stack(limit=2)
+        self.src_loc = (tb[0].filename, tb[0].lineno)
+
+        if name is None:
+            try:
+                name = tracer.get_var_name(depth=2)
+            except tracer.NameNotFound:
+                name = "$memory"
+        self.name  = name
+
+        self.width = width
+        self.depth = depth
+        self.init  = None if init is None else list(init)
+
+    def read_port(self, domain="sync", asynchronous=False, transparent=True):
+        return ReadPort(self, domain, asynchronous, 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}'"
+                            .format(granularity))
+        return WritePort(self, domain, priority, granularity)
+
+
+class ReadPort:
+    def __init__(self, memory, domain, asynchronous, transparent):
+        self.memory       = memory
+        self.domain       = domain
+        self.asynchronous = asynchronous
+        self.transparent  = transparent
+
+        self.addr = Signal(max=memory.depth)
+        self.data = Signal(memory.width)
+        self.en   = Signal()
+
+    def get_fragment(self, platform):
+        return Instance("$memrd",
+            p_MEMID=self.memory,
+            p_ABITS=self.addr.nbits,
+            p_WIDTH=self.data.nbits,
+            p_CLK_ENABLE=not self.asynchronous,
+            p_CLK_POLARITY=1,
+            p_TRANSPARENT=self.transparent,
+            i_CLK=ClockSignal(self.domain),
+            i_EN=self.en,
+            i_ADDR=self.addr,
+            o_DATA=self.data,
+        )
+
+class WritePort:
+    def __init__(self, memory, domain, priority, granularity):
+        self.memory       = memory
+        self.domain       = domain
+        self.priority     = priority
+        self.granularity  = granularity
+
+        self.addr = Signal(max=memory.depth)
+        self.data = Signal(memory.width)
+        self.en   = Signal(memory.width // granularity)
+
+    def get_fragment(self, platform):
+        return Instance("$memwr",
+            p_MEMID=self.memory,
+            p_ABITS=self.addr.nbits,
+            p_WIDTH=self.data.nbits,
+            p_CLK_ENABLE=1,
+            p_CLK_POLARITY=1,
+            p_PRIORITY=self.priority,
+            i_CLK=ClockSignal(self.domain),
+            i_EN=Cat(Repl(en_bit, self.granularity) for en_bit in self.en),
+            i_ADDR=self.addr,
+            i_DATA=self.data,
+        )