add to docstring
[soc.git] / src / scoreboard / addr_match.py
1 """ Load / Store partial address matcher
2
3 Loads and Stores do not need a full match (CAM), they need "good enough"
4 avoidance. Around 11 bits on a 64-bit address is "good enough".
5
6 The simplest way to use this module is to ignore not only the top bits,
7 but also the bottom bits as well: in this case (this RV64 processor),
8 enough to cover a DWORD (64-bit). that means ignore the bottom 4 bits,
9 due to the possibility of 64-bit LD/ST being misaligned.
10
11 To reiterate: the use of this module is an *optimisation*. All it has
12 to do is cover the cases that are *definitely* matches (by checking 11
13 bits or so), and if a few opportunities for parallel LD/STs are missed
14 because the top (or bottom) bits weren't checked, so what: all that
15 happens is: the mis-matched addresses are LD/STd on single-cycles. Big Deal.
16 """
17
18 from nmigen.compat.sim import run_simulation
19 from nmigen.cli import verilog, rtlil
20 from nmigen import Module, Signal, Const, Array, Cat, Elaboratable
21
22
23 class PartialAddrMatch(Elaboratable):
24 """A partial address matcher
25 """
26 def __init__(self, n_adr, bitwid):
27 self.n_adr = n_adr
28 self.bitwid = bitwid
29 # inputs
30 self.addrs_i = Array(Signal(bitwid, name="addr") for i in range(n_adr))
31 self.addr_we_i = Signal(n_adr) # write-enable for incoming address
32 self.addr_en_i = Signal(n_adr) # address activated (0 == ignore)
33
34 # output
35 self.addr_match_o = Signal(n_adr)
36
37 def elaborate(self, platform):
38 m = Module()
39 comb = m.d.comb
40 sync = m.d.sync
41
42 addrs_r = Array(Signal(self.bitwid, "a_r") for i in range(self.n_adr))
43 addr_en_r = Signal(self.n_adr)
44
45 # copy in addresses (and "enable" signals)
46 for i in range(self.n_adr):
47 with m.If(self.addr_we_i[i]):
48 sync += addrs_r[i].eq(self.addrs_i[i])
49 sync += addr_en_r[i].eq(self.addr_en_i[i])
50
51 # is there a clash, yes/no
52 for i in range(self.n_adr):
53 match = []
54 for j in range(self.n_adr):
55 if i == j:
56 match.append(Const(0)) # don't match against self!
57 else:
58 match.append(addrs_r[i] == addrs_r[j])
59 comb += self.addr_match_o.eq(Cat(*match).bool() & addr_en_r)
60
61 return m
62
63 def __iter__(self):
64 yield from self.addrs_i
65 yield self.addr_we_i
66 yield self.addr_en_i
67 yield self.addr_match_o
68
69 def ports(self):
70 return list(self)
71
72
73 def part_addr_sim(dut):
74 yield dut.dest_i.eq(1)
75 yield dut.issue_i.eq(1)
76 yield
77 yield dut.issue_i.eq(0)
78 yield
79 yield dut.src1_i.eq(1)
80 yield dut.issue_i.eq(1)
81 yield
82 yield dut.issue_i.eq(0)
83 yield
84 yield dut.go_rd_i.eq(1)
85 yield
86 yield dut.go_rd_i.eq(0)
87 yield
88 yield dut.go_wr_i.eq(1)
89 yield
90 yield dut.go_wr_i.eq(0)
91 yield
92
93 def test_part_addr():
94 dut = PartialAddrMatch(3, 10)
95 vl = rtlil.convert(dut, ports=dut.ports())
96 with open("test_part_addr.il", "w") as f:
97 f.write(vl)
98
99 run_simulation(dut, part_addr_sim(dut), vcd_name='test_part_addr.vcd')
100
101 if __name__ == '__main__':
102 test_part_addr()