continue virtual regfile port
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 26 May 2020 11:44:18 +0000 (12:44 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Tue, 26 May 2020 11:44:18 +0000 (12:44 +0100)
src/soc/regfile/virtual_port.py

index 7f02ecc2540b8fac99e2a497fad4f8f8659160d3..e05b9f67a2ecdb3d9bdcd1f55cfc514d5863bafa 100644 (file)
+"""VirtualRegPort - terrible name for a complex register class
+
+This Register file has a "virtual" port on it which is effectively
+the ability to read and write to absolutely every bit in the regfile
+at once.  This is achieved by having N actual read and write ports
+if there are N registers.  That results in a staggeringly high gate count
+with full crossbars, so attempting to do use this for anything other
+than really small registers (XER, CR) is a seriously bad idea.
+"""
+
 from nmigen.compat.sim import run_simulation
 from nmigen.cli import verilog, rtlil
 
 from nmigen import Cat, Const, Array, Signal, Elaboratable, Module
 from nmutil.iocontrol import RecordObject
 
-from soc.regfile import RegFileArray
+from soc.regfile.regfile import RegFileArray
 
 
-class VirtualPort(RegFileArray):
+class VirtualRegPort(RegFileArray):
     def __init__(self, bitwidth, n_regs):
         self.bitwidth = bitwidth
         self.nregs = n_regs
-        self.regwidth = bitwidth / n_regs
-        self.w_ports = [ self.write_port(f"{i}") for i in range(n_regs) ]
-        self.r_ports = [ self.read_port(f"{i}") for i in range(n_regs) ]
-        self.extra_wr.append(RecordObject([("ren", nregs), ("data_o", bitwidth, name="extra")]))
-        self.extra_rd.append(RecordObject([("ren", nregs), ("data_o", bitwidth, name="extra")]))
+        self.regwidth = bitwidth // n_regs
+        super().__init__(self.regwidth, n_regs)
+
+        # create suite of 8 write ports
+        self.w_ports = [ self.write_port(f"extw_{i}") for i in range(n_regs) ]
+        self.r_ports = [ self.read_port(f"extr_{i}") for i in range(n_regs) ]
+        # now start again: one set will be internal, the other external
+        self._rdports = []
+        self._wrports = []
+        for i in range(n_regs):
+            self.write_port(f"intw_{i}")
+            self.read_port(f"intr_{i}")
+        # and append the "full" depth variant to the "external" ports
+        self.w_ports.append(RecordObject([("wen", n_regs),
+                                          ("data_i", bitwidth)], # *full* wid
+                                         name="full"))
+        self.r_ports.append(RecordObject([("ren", n_regs),
+                                          ("data_o", bitwidth)], # *full* wid
+                                         name="full"))
 
     def elaborate(self, platform):
         m = Module()
-        with m.If(self._get_en_sig(extra, "ren") == 0)
-            pass
-        with m.Else()
-            "send data through the corresponding lower indexed ports"
+        comb, sync = m.d.comb, m.d.sync
+
+        # connect up: detect if read is requested on large (full) port
+        # nothing fancy needed because reads are same-cycle
+        rlast = self.r_ports[-1]
+        print (rlast)
+        with m.If(self._get_en_sig([rlast], "ren") != 0):
+            # wire up the enable signals and accumulate the data
+            l = []
+            print (self._rdports)
+            for i, port in enumerate(self._rdports[:-1]):
+                print (port)
+                comb += port.ren.eq(1<<i) # port indices are *unary*-indexed
+                l.append(port.data_o)
+            comb += rlast.data_o.eq(Cat(*l)) # we like Cat on lists
+        with m.Else():
+            # allow request through the corresponding lower indexed ports
+            for i, port in enumerate(self._rdports[:-1]):
+                comb += port.eq(self.r_ports[i])
+
+        # connect up: detect if write is requested on large (full) port
+        # however due to the delay (1 clock) on write, we also need to
+        # delay the test.  enable is not-delayed, but data is.
+        en_sig = Signal(reset_less=True)   # combinatorial
+        data_sig = Signal(reset_less=True) # sync (one clock delay)
+
+        wlast = self.w_ports[-1]
+        comb += en_sig.eq(self._get_en_sig(wlast, "wen") != 0)
+        sync += data_sig.eq(en_sig)
+
+        with m.If(en_sig):
+            # wire up the enable signals
+            for i, port in enumerate(self._wrports[:-1]):
+                comb += port.wen.eq(1<<i) # port indices are *unary*-indexed
+        with m.Else():
+            # allow request through the corresponding lower indexed ports
+            for i, port in enumerate(self._wrports[:-1]):
+                comb += port.wen.eq(self.w_ports[i].wen)
+
+        # and (sigh) data is on one clock-delay, connect that too
+        with m.If(data_sig):
+            # get list of all data_i and assign to them via Cat
+            l = map(lambda port: port.data_i, self._wrports[:-1])
+            comb += Cat(*l).eq(wlast.data_i)
+        with m.Else():
+            # allow data through the corresponding lower indexed ports
+            for i, port in enumerate(self._wrports[:-1]):
+                comb += self.w_ports[i].data_i.eq(port.data_i)
+
+
+def test_regfile():
+    dut = VirtualRegPort(32, 8)
+    vl = rtlil.convert(dut, ports=dut.ports() + dut.w_ports + dut.r_ports)
+    with open("test_virtualregfile.il", "w") as f:
+        f.write(vl)
+
+if __name__ == '__main__':
+    test_regfile()
+