Merge branch 'master' of ssh://git.libre-riscv.org:922/soc
[soc.git] / src / soc / scoreboard / group_picker.py
1 """ Group Picker: to select an instruction that is permitted to read (or write)
2 based on the Function Unit expressing a *desire* to read (or write).
3
4 The job of the Group Picker is extremely simple yet extremely important.
5 It sits in front of a register file port (read or write) and stops it from
6 being corrupted. It's a "port contention selector", basically.
7
8 The way it works is:
9
10 * Function Units need to read from (or write to) the register file,
11 in order to get (or store) their operands, so they each have a signal,
12 readable (or writable), which "expresses" this need. This is an
13 *unary* encoding.
14
15 * The Function Units also have a signal which indicates that they
16 are requesting "release" of the register file port (this because
17 in the scoreboard, readable/writable can be permanently HI even
18 if the FU is idle, whereas the "release" signal is very specifically
19 only HI if the read (or write) latch is still active)
20
21 * The Group Picker takes this unary encoding of the desire to read
22 (or write) and, on a priority basis, activates one *and only* one
23 of those signals, again as an unary output.
24
25 * Due to the way that the Computation Unit works, that signal (Go_Read
26 or Go_Write) will fire for one (and only one) cycle, and can be used
27 to enable the register file port read (or write) lines. The Go_Read/Wr
28 signal basically loops back to the Computation Unit and resets the
29 "desire-to-read/write-expressing" latch.
30
31 In theory (and in practice!) the following is possible:
32
33 * Separate src1 and src2 Group Pickers. This would allow instructions
34 with only one operand to read to not block up other instructions,
35 and it would also allow 3-operand instructions to be interleaved
36 with 1 and 2 operand instructions.
37
38 * *Multiple* Group Pickers (multi-issue). This would require
39 a corresponding increase in the number of register file ports,
40 either 4R2W (or more) or by "striping" the register file into
41 split banks (a strategy best deployed on Vector Processors)
42
43 """
44
45 from nmigen.compat.sim import run_simulation
46 from nmigen.cli import verilog, rtlil
47 from nmigen import Module, Signal, Elaboratable, Array
48
49 from nmutil.picker import MultiPriorityPicker as MPP
50
51
52 class GroupPicker(Elaboratable):
53 """ implements 10.5 mitch alsup group picker, p27
54 """
55 def __init__(self, wid, n_src, n_dst):
56 self.n_src, self.n_dst = n_src, n_dst
57 self.gp_wid = wid
58
59 # arrays
60 rdr = []
61 rd = []
62 ri = []
63 for i in range(n_src):
64 rdr.append(Signal(wid, name="rdrel%d_i" % i, reset_less=True))
65 rd.append(Signal(wid, name="gordl%d_i" % i, reset_less=True))
66 ri.append(Signal(wid, name="readable%d_i" % i, reset_less=True))
67 wrr = []
68 wr = []
69 wi = []
70 for i in range(n_dst):
71 wrr.append(Signal(wid, name="reqrel%d_i" % i, reset_less=True))
72 wr.append(Signal(wid, name="gowr%d_i" % i, reset_less=True))
73 wi.append(Signal(wid, name="writable%d_i" % i, reset_less=True))
74
75 # inputs
76 self.rd_rel_i = Array(rdr) # go read in (top)
77 self.req_rel_i = Array(wrr) # release request in (top)
78 self.readable_i = Array(ri) # readable in (top)
79 self.writable_i = Array(wi) # writable in (top)
80
81 # outputs
82 self.go_rd_o = Array(rd) # go read (bottom)
83 self.go_wr_o = Array(wr) # go write (bottom)
84
85 def elaborate(self, platform):
86 m = Module()
87
88 m.submodules.rpick = rpick = MPP(self.gp_wid, self.n_src, False, True)
89 m.submodules.wpick = wpick = MPP(self.gp_wid, self.n_dst, False, True)
90
91 # combine release (output ready signal) with writeable
92 for i in range(self.n_dst):
93 m.d.comb += wpick.i[i].eq(self.writable_i[i] & self.req_rel_i[i])
94 m.d.comb += self.go_wr_o[i].eq(wpick.o[i])
95
96 for i in range(self.n_src):
97 m.d.comb += rpick.i[i].eq(self.readable_i[i] & self.rd_rel_i[i])
98 m.d.comb += self.go_rd_o[i].eq(rpick.o[i])
99
100 return m
101
102 def __iter__(self):
103 yield from self.readable_i
104 yield from self.writable_i
105 yield from self.req_rel_i
106 yield from self.rd_rel_i
107 yield from self.go_rd_o
108 yield from self.go_wr_o
109
110 def ports(self):
111 return list(self)
112
113
114 def grp_pick_sim(dut):
115 yield dut.dest_i.eq(1)
116 yield dut.issue_i.eq(1)
117 yield
118 yield dut.issue_i.eq(0)
119 yield
120 yield dut.src1_i.eq(1)
121 yield dut.issue_i.eq(1)
122 yield
123 yield
124 yield
125 yield dut.issue_i.eq(0)
126 yield
127 yield dut.rd_rel_i.eq(1)
128 yield
129 yield dut.rd_rel_i.eq(0)
130 yield
131 yield dut.go_wr_i.eq(1)
132 yield
133 yield dut.go_wr_i.eq(0)
134 yield
135
136
137 def test_grp_pick():
138 dut = GroupPicker(4, 2, 2)
139 vl = rtlil.convert(dut, ports=dut.ports())
140 with open("test_grp_pick.il", "w") as f:
141 f.write(vl)
142
143 run_simulation(dut, grp_pick_sim(dut), vcd_name='test_grp_pick.vcd')
144
145 if __name__ == '__main__':
146 test_grp_pick()