add to docstring
authorLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Mon, 3 Jun 2019 09:36:27 +0000 (10:36 +0100)
committerLuke Kenneth Casson Leighton <lkcl@lkcl.net>
Mon, 3 Jun 2019 09:36:27 +0000 (10:36 +0100)
src/scoreboard/addr_match.py [new file with mode: 0644]

diff --git a/src/scoreboard/addr_match.py b/src/scoreboard/addr_match.py
new file mode 100644 (file)
index 0000000..d7692b3
--- /dev/null
@@ -0,0 +1,102 @@
+""" Load / Store partial address matcher
+
+Loads and Stores do not need a full match (CAM), they need "good enough"
+avoidance.  Around 11 bits on a 64-bit address is "good enough".
+
+The simplest way to use this module is to ignore not only the top bits,
+but also the bottom bits as well: in this case (this RV64 processor),
+enough to cover a DWORD (64-bit).  that means ignore the bottom 4 bits,
+due to the possibility of 64-bit LD/ST being misaligned.
+
+To reiterate: the use of this module is an *optimisation*.  All it has
+to do is cover the cases that are *definitely* matches (by checking 11
+bits or so), and if a few opportunities for parallel LD/STs are missed
+because the top (or bottom) bits weren't checked, so what: all that
+happens is: the mis-matched addresses are LD/STd on single-cycles. Big Deal.
+"""
+
+from nmigen.compat.sim import run_simulation
+from nmigen.cli import verilog, rtlil
+from nmigen import Module, Signal, Const, Array, Cat, Elaboratable
+
+
+class PartialAddrMatch(Elaboratable):
+    """A partial address matcher
+    """
+    def __init__(self, n_adr, bitwid):
+        self.n_adr = n_adr
+        self.bitwid = bitwid
+        # inputs
+        self.addrs_i = Array(Signal(bitwid, name="addr") for i in range(n_adr))
+        self.addr_we_i = Signal(n_adr) # write-enable for incoming address
+        self.addr_en_i = Signal(n_adr) # address activated (0 == ignore)
+
+        # output
+        self.addr_match_o = Signal(n_adr)
+
+    def elaborate(self, platform):
+        m = Module()
+        comb = m.d.comb
+        sync = m.d.sync
+
+        addrs_r = Array(Signal(self.bitwid, "a_r") for i in range(self.n_adr))
+        addr_en_r = Signal(self.n_adr)
+
+        # copy in addresses (and "enable" signals)
+        for i in range(self.n_adr):
+            with m.If(self.addr_we_i[i]):
+                sync += addrs_r[i].eq(self.addrs_i[i])
+                sync += addr_en_r[i].eq(self.addr_en_i[i])
+
+        # is there a clash, yes/no
+        for i in range(self.n_adr):
+            match = []
+            for j in range(self.n_adr):
+                if i == j:
+                    match.append(Const(0)) # don't match against self!
+                else:
+                    match.append(addrs_r[i] == addrs_r[j])
+            comb += self.addr_match_o.eq(Cat(*match).bool() & addr_en_r)
+            
+        return m
+
+    def __iter__(self):
+        yield from self.addrs_i
+        yield self.addr_we_i
+        yield self.addr_en_i
+        yield self.addr_match_o
+
+    def ports(self):
+        return list(self)
+
+
+def part_addr_sim(dut):
+    yield dut.dest_i.eq(1)
+    yield dut.issue_i.eq(1)
+    yield
+    yield dut.issue_i.eq(0)
+    yield
+    yield dut.src1_i.eq(1)
+    yield dut.issue_i.eq(1)
+    yield
+    yield dut.issue_i.eq(0)
+    yield
+    yield dut.go_rd_i.eq(1)
+    yield
+    yield dut.go_rd_i.eq(0)
+    yield
+    yield dut.go_wr_i.eq(1)
+    yield
+    yield dut.go_wr_i.eq(0)
+    yield
+
+def test_part_addr():
+    dut = PartialAddrMatch(3, 10)
+    vl = rtlil.convert(dut, ports=dut.ports())
+    with open("test_part_addr.il", "w") as f:
+        f.write(vl)
+
+    run_simulation(dut, part_addr_sim(dut), vcd_name='test_part_addr.vcd')
+
+if __name__ == '__main__':
+    test_part_addr()