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