unit test for DataMerger
[soc.git] / src / soc / experiment / l0_cache.py
index 7dc1c260bb029c1d30af4b53ff3d6fafcbfc8e1e..3e2c453c5a936a1e8b51ae914e14d1d3559f1da7 100644 (file)
@@ -25,6 +25,8 @@ from nmigen.hdl.rec import Record, Layout
 from nmutil.latch import SRLatch, latchregister
 from soc.decoder.power_decoder2 import Data
 from soc.decoder.power_enums import InternalOp
+from soc.regfile.regfile import ortreereduce
+from nmutil.util import treereduce
 
 from soc.experiment.compldst import CompLDSTOpSubset
 from soc.decoder.power_decoder2 import Data
@@ -34,7 +36,6 @@ from nmigen.lib.coding import PriorityEncoder
 # for testing purposes
 from soc.experiment.testmem import TestMemory
 
-
 class PortInterface(RecordObject):
     """PortInterface
 
@@ -143,20 +144,36 @@ class DataMergerRecord(Record):
 
         Record.__init__(self, Layout(layout), name=name)
 
-# TODO:
+        #FIXME: make resetless
 
+# TODO: formal verification
 
 class DataMerger(Elaboratable):
     """DataMerger
 
-    Merges data based on an address-match matrix
-
+    Merges data based on an address-match matrix.
+    Identifies (picks) one (any) row, then uses that row,
+    based on matching address bits, to merge (OR) all data
+    rows into the output.
+
+    Basically, by the time DataMerger is used, all of its incoming data is
+    determined not to conflict.  The last step before actually submitting
+    the request to the Memory Subsystem is to work out which requests,
+    on the same 128-bit cache line, can be "merged" due to them being:
+    (A) on the same address (bits 4 and above) (B) having byte-enable
+    lines that (as previously mentioned) do not conflict.
+
+    Therefore, put simply, this module will:
+    (1) pick a row (any row) and identify it by an index labelled "idx"
+    (2) merge all byte-enable lines which are on that same address, as
+        indicated by addr_match_i[idx], onto the output
     """
 
     def __init__(self, array_size):
         """
-        :addr_array_i: an NxN Array of
-                       Signals with bits set indicating address match
+        :addr_array_i: an NxN Array of Signals with bits set indicating address
+                       match.  bits across the diagonal (addr_array_i[x][x])
+                       will always be set, to indicate "active".
         :data_i: an Nx Array of Records {data: 128 bit, byte_enable: 16 bit}
         :data_o: an Output Record of same type
                  {data: 128 bit, byte_enable: 16 bit}
@@ -175,6 +192,29 @@ class DataMerger(Elaboratable):
         self.data_i = Array(ul)
         self.data_o = DataMergerRecord()
 
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        #(1) pick a row
+        m.submodules.pick = pick = PriorityEncoder(self.array_size)
+        for j in range(self.array_size):
+            comb += pick.i[j].eq(self.addr_array_i[j].bool())
+        valid = ~pick.n
+        idx = pick.o
+        #(2) merge
+        with m.If(valid):
+            l = []
+            for j in range(self.array_size):
+                select = self.addr_array_i[idx][j]
+                r = DataMergerRecord()
+                with m.If(select):
+                    comb += r.eq(self.data_i[j])
+                l.append(r)
+            comb += self.data_o.data.eq(ortreereduce(l,"data"))
+            comb += self.data_o.en.eq(ortreereduce(l,"en"))
+
+        return m
+
 
 class LDSTPort(Elaboratable):
     def __init__(self, idx, regwid=64, addrwid=48):
@@ -489,6 +529,24 @@ def l0_cache_ldst(dut):
     assert data == result, "data %x != %x" % (result, data)
     assert data2 == result2, "data2 %x != %x" % (result2, data2)
 
+def data_merger_merge(dut):
+    print("data_merger")
+    #starting with all inputs zero
+    en = yield dut.data_o.en
+    data = yield dut.data_o.data
+    assert en == 0, "en must be zero"
+    assert data == 0, "data must be zero"
+    yield
+    yield dut.addr_array_i[0].eq(0xFF)
+    for j in range(dut.array_size):
+        yield dut.data_i[j].en.eq(1 << j)
+        yield dut.data_i[j].data.eq(0xFF << (16*j))
+    yield
+    en = yield dut.data_o.en
+    data = yield dut.data_o.data
+    assert data == 0xff00ff00ff00ff00ff00ff00ff00ff
+    assert en == 0xff
+    yield
 
 def test_l0_cache():
 
@@ -500,6 +558,17 @@ def test_l0_cache():
     run_simulation(dut, l0_cache_ldst(dut),
                    vcd_name='test_l0_cache_basic.vcd')
 
+def test_data_merger():
+
+    dut = DataMerger(8)
+    #vl = rtlil.convert(dut, ports=dut.ports())
+    #with open("test_data_merger.il", "w") as f:
+    #    f.write(vl)
+
+    run_simulation(dut, data_merger_merge(dut),
+                   vcd_name='test_data_merger.vcd')
+
 
 if __name__ == '__main__':
     test_l0_cache()
+    test_data_merger()