Fix indentation of regfile/formal/proof_regfile.py
[soc.git] / src / soc / regfile / virtual_port.py
index e05b9f67a2ecdb3d9bdcd1f55cfc514d5863bafa..322efe8e73588a7d6dd4f2761572957086781111 100644 (file)
@@ -21,84 +21,185 @@ class VirtualRegPort(RegFileArray):
     def __init__(self, bitwidth, n_regs):
         self.bitwidth = bitwidth
         self.nregs = n_regs
-        self.regwidth = bitwidth // n_regs
+        self.regwidth = 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"))
-
+        # "full" depth variant of the "external" port
+        self.full_wr = RecordObject([("wen", n_regs),
+                                     ("data_i", bitwidth)], # *full* wid
+                                    name="full_wr")
+        self.full_rd = RecordObject([("ren", n_regs),
+                                     ("data_o", bitwidth)], # *full* wid
+                                    name="full_rd")
     def elaborate(self, platform):
-        m = Module()
-        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)
+        m = super().elaborate(platform)
+        comb = m.d.comb
+
+        # for internal use only.
+        wr_regs = self.write_reg_port(f"w")
+        rd_regs = self.read_reg_port(f"r")
+
+        # connect up full read port
+        rfull = self.full_rd
+
+        # wire up the enable signals and chain-accumulate the data
+        l = map(lambda port: port.data_o, rd_regs) # get port data(s)
+        le = map(lambda port: port.ren, rd_regs) # get port ren(s)
+
+        comb += rfull.data_o.eq(Cat(*l)) # we like Cat on lists
+        comb += Cat(*le).eq(rfull.ren)
+
+        # connect up full write port
+        wfull = self.full_wr
+
+        # wire up the enable signals from the large (full) port
+        l = map(lambda port: port.data_i, wr_regs)
+        le = map(lambda port: port.wen, wr_regs) # get port wen(s)
+
+        # get list of all data_i (and wens) and assign to them via Cat
+        comb += Cat(*l).eq(wfull.data_i)
+        comb += Cat(*le).eq(wfull.wen)
+
+        return m
+
+    def __iter__(self):
+        yield from super().__iter__()
+        yield from self.full_wr
+        yield from self.full_rd
+
+
+def regfile_array_sim(dut, rp1, rp2, rp3, wp):
+    # part-port write
+    yield wp.data_i.eq(2)
+    yield wp.wen.eq(1<<1)
+    yield
+    yield wp.wen.eq(0)
+    # part-port read
+    yield rp1.ren.eq(1<<1)
+    yield
+    data = yield rp1.data_o
+    print (data)
+    assert data == 2
+
+    # simultaneous read/write - should be pass-thru
+    yield rp1.ren.eq(1<<5)
+    yield rp2.ren.eq(1<<1)
+    yield wp.wen.eq(1<<5)
+    yield wp.data_i.eq(6)
+    yield
+    yield wp.wen.eq(0)
+    yield rp1.ren.eq(0)
+    yield rp2.ren.eq(0)
+    data1 = yield rp1.data_o
+    print (data1)
+    assert data1 == 6, data1
+    data2 = yield rp2.data_o
+    print (data2)
+    assert data2 == 2, data2
+    yield
+    data = yield rp1.data_o
+    print (data)
+
+    # full port read (whole reg)
+    yield dut.full_rd.ren.eq(0xff)
+    yield
+    yield dut.full_rd.ren.eq(0)
+    data = yield dut.full_rd.data_o
+    print (hex(data))
+
+    # full port read (part reg)
+    yield dut.full_rd.ren.eq(0x1<<5)
+    yield
+    yield dut.full_rd.ren.eq(0)
+    data = yield dut.full_rd.data_o
+    print (hex(data))
+
+    # full port part-write (part masked reg)
+    yield dut.full_wr.wen.eq(0x1<<1)
+    yield dut.full_wr.data_i.eq(0xe0)
+    yield
+    yield dut.full_wr.wen.eq(0x0)
+
+    # full port read (whole reg)
+    yield dut.full_rd.ren.eq(0xff)
+    yield
+    yield dut.full_rd.ren.eq(0)
+    data = yield dut.full_rd.data_o
+    print (hex(data))
+
+    # full port write
+    yield dut.full_wr.wen.eq(0xff)
+    yield dut.full_wr.data_i.eq(0xcafeface)
+    yield
+    yield dut.full_wr.wen.eq(0x0)
+
+    # full port read (whole reg)
+    yield dut.full_rd.ren.eq(0xff)
+    yield
+    yield dut.full_rd.ren.eq(0)
+    data = yield dut.full_rd.data_o
+    print (hex(data))
+
+    # part write
+    yield wp.data_i.eq(2)
+    yield wp.wen.eq(1<<1)
+    yield
+    yield wp.wen.eq(0)
+    yield rp1.ren.eq(1<<1)
+    yield
+    data = yield rp1.data_o
+    print (hex(data))
+    assert data == 2
+
+    # full port read (whole reg)
+    yield dut.full_rd.ren.eq(0xff)
+    yield
+    yield dut.full_rd.ren.eq(0)
+    data = yield dut.full_rd.data_o
+    print (hex(data))
+
+    # simultaneous read/write: full-write, part-write, 3x part-read
+    yield rp1.ren.eq(1<<5)
+    yield rp2.ren.eq(1<<1)
+    yield rp3.ren.eq(1<<3)
+    yield wp.wen.eq(1<<3)
+    yield wp.data_i.eq(6)
+    yield dut.full_wr.wen.eq((1<<1) | (1<<5))
+    yield dut.full_wr.data_i.eq((0xa<<(1*4)) | (0x3<<(5*4)))
+    yield
+    yield dut.full_wr.wen.eq(0)
+    yield wp.wen.eq(0)
+    yield rp1.ren.eq(0)
+    yield rp2.ren.eq(0)
+    yield rp3.ren.eq(0)
+    data1 = yield rp1.data_o
+    print (hex(data1))
+    assert data1 == 0x3
+    data2 = yield rp2.data_o
+    print (hex(data2))
+    assert data2 == 0xa
+    data3 = yield rp3.data_o
+    print (hex(data3))
+    assert data3 == 0x6
 
 
 def test_regfile():
     dut = VirtualRegPort(32, 8)
-    vl = rtlil.convert(dut, ports=dut.ports() + dut.w_ports + dut.r_ports)
+    rp1 = dut.read_port("read1")
+    rp2 = dut.read_port("read2")
+    rp3 = dut.read_port("read3")
+    wp = dut.write_port("write")
+
+    ports=dut.ports()
+    print ("ports", ports)
+    vl = rtlil.convert(dut, ports=ports)
     with open("test_virtualregfile.il", "w") as f:
         f.write(vl)
 
+    run_simulation(dut, regfile_array_sim(dut, rp1, rp2, rp3, wp),
+                   vcd_name='test_regfile_array.vcd')
+
 if __name__ == '__main__':
     test_regfile()
 
+