add regfile.py
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 7 May 2019 07:35:20 +0000 (08:35 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 7 May 2019 07:35:20 +0000 (08:35 +0100)
src/regfile/regfile.py [new file with mode: 0644]

diff --git a/src/regfile/regfile.py b/src/regfile/regfile.py
new file mode 100644 (file)
index 0000000..cb3608a
--- /dev/null
@@ -0,0 +1,112 @@
+from nmigen.compat.sim import run_simulation
+from nmigen.cli import verilog, rtlil
+
+from nmigen import Const, Array, Signal, Elaboratable, Module
+from nmutil.iocontrol import RecordObject
+
+from math import log
+
+
+class RegFile(Elaboratable):
+    def __init__(self, width, depth):
+        self.width = width
+        self.depth = depth
+        self._rdports = []
+        self._wrports = []
+
+    def read_port(self):
+        bsz = int(log(self.width) / log(2))
+        port = RecordObject([("raddr", bsz),
+                             ("ren", 1),
+                             ("data_o", self.width)])
+        self._rdports.append(port)
+        return port
+
+    def write_port(self):
+        bsz = int(log(self.width) / log(2))
+        port = RecordObject([("waddr", bsz),
+                             ("wen", 1),
+                             ("data_i", self.width)])
+        self._wrports.append(port)
+        return port
+
+    def elaborate(self, platform):
+        m = Module()
+        bsz = int(log(self.width) / log(2))
+        regs = Array(Signal(self.width, name="reg") for _ in range(self.depth))
+
+        # read ports. has write-through detection (returns data written)
+        for rp in self._rdports:
+            wr_detect = Signal(reset_less=False)
+            with m.If(rp.ren):
+                m.d.comb += wr_detect.eq(0)
+                for wp in self._wrports:
+                    addrmatch = Signal(reset_less=False)
+                    m.d.comb += addrmatch.eq(wp.waddr == rp.raddr)
+                    with m.If(wp.wen & addrmatch):
+                        m.d.comb += rp.data_o.eq(wp.data_i)
+                        m.d.comb += wr_detect.eq(1)
+                with m.If(~wr_detect):
+                    m.d.comb += rp.data_o.eq(regs[rp.raddr])
+
+        # write ports, don't allow write to address 0 (ignore it)
+        for wp in self._wrports:
+            with m.If(wp.wen & (wp.waddr != Const(0, bsz))):
+                m.d.sync += regs[wp.waddr].eq(wp.data_i)
+
+        return m
+
+    def __iter__(self):
+        yield from self._rdports
+        yield from self._wrports
+
+    def ports(self):
+        res = list(self)
+        for r in res:
+            if isinstance(r, RecordObject):
+                yield from r
+            else:
+                yield r
+
+def regfile_sim(dut, rp, wp):
+    yield wp.waddr.eq(1)
+    yield wp.data_i.eq(2)
+    yield wp.wen.eq(1)
+    yield
+    yield wp.wen.eq(0)
+    yield rp.ren.eq(1)
+    yield rp.raddr.eq(1)
+    yield
+    data = yield rp.data_o
+    print (data)
+    assert data == 2
+
+    yield wp.waddr.eq(5)
+    yield rp.raddr.eq(5)
+    yield rp.ren.eq(1)
+    yield wp.wen.eq(1)
+    yield wp.data_i.eq(6)
+    data = yield rp.data_o
+    print (data)
+    yield
+    yield wp.wen.eq(0)
+    yield rp.ren.eq(0)
+    data = yield rp.data_o
+    print (data)
+    assert data == 6
+    yield
+    data = yield rp.data_o
+    print (data)
+
+def test_regfile():
+    dut = RegFile(32, 8)
+    rp = dut.read_port()
+    wp = dut.write_port()
+    vl = rtlil.convert(dut, ports=dut.ports())
+    with open("test_regfile.il", "w") as f:
+        f.write(vl)
+
+    run_simulation(dut, regfile_sim(dut, rp, wp), vcd_name='test_regfile.vcd')
+
+if __name__ == '__main__':
+    test_regfile()