add start of new ReservationStations2 class
[nmutil.git] / src / nmutil / concurrentunit.py
1 """ concurrent unit from mitch alsup augmentations to 6600 scoreboard
2
3 This work is funded through NLnet under Grant 2019-02-012
4
5 License: LGPLv3+
6
7
8 * data fans in
9 * data goes through a pipeline
10 * results fan back out.
11
12 the output data format has to have a member "muxid", which is used
13 as the array index on fan-out
14
15 Associated bugreports:
16
17 * https://bugs.libre-soc.org/show_bug.cgi?id=538
18 """
19
20 from math import log
21 from nmigen import Module, Elaboratable, Signal
22 from nmigen.asserts import Assert
23 from nmigen.cli import main, verilog
24
25 from nmutil.singlepipe import PassThroughStage
26 from nmutil.multipipe import CombMuxOutPipe
27 from nmutil.multipipe import PriorityCombMuxInPipe
28 from nmutil.multipipe import InputPriorityArbiter
29 from nmutil.iocontrol import NextControl, PrevControl
30
31
32 def num_bits(n):
33 return int(log(n) / log(2))
34
35
36 class PipeContext:
37
38 def __init__(self, pspec):
39 """ creates a pipeline context. currently: operator (op) and muxid
40
41 opkls (within pspec) - the class to create that will be the
42 "operator". instance must have an "eq"
43 function.
44 """
45 self.id_wid = pspec.id_wid
46 self.op_wid = pspec.op_wid
47 self.muxid = Signal(self.id_wid, reset_less=True) # RS multiplex ID
48 opkls = pspec.opkls
49 if opkls is None:
50 self.op = Signal(self.op_wid, reset_less=True)
51 else:
52 self.op = opkls(pspec)
53
54 def eq(self, i):
55 ret = [self.muxid.eq(i.muxid)]
56 ret.append(self.op.eq(i.op))
57 # don't forget to update matches if you add fields later.
58 return ret
59
60 def matches(self, another):
61 """
62 Returns a list of Assert()s validating that this context
63 matches the other context.
64 """
65 # I couldn't figure a clean way of overloading the == operator.
66 return [
67 Assert(self.muxid == another.muxid),
68 Assert(self.op == another.op),
69 ]
70
71 def __iter__(self):
72 yield self.muxid
73 yield self.op
74
75 def ports(self):
76 if hasattr(self.op, "ports"):
77 return [self.muxid] + self.op.ports()
78 else:
79 return list(self)
80
81
82 class InMuxPipe(PriorityCombMuxInPipe):
83 def __init__(self, num_rows, iospecfn, maskwid=0):
84 self.num_rows = num_rows
85 stage = PassThroughStage(iospecfn)
86 PriorityCombMuxInPipe.__init__(self, stage, p_len=self.num_rows,
87 maskwid=maskwid)
88
89
90 class MuxOutPipe(CombMuxOutPipe):
91 def __init__(self, num_rows, iospecfn, maskwid=0):
92 self.num_rows = num_rows
93 stage = PassThroughStage(iospecfn)
94 CombMuxOutPipe.__init__(self, stage, n_len=self.num_rows,
95 maskwid=maskwid)
96
97
98 class ALUProxy:
99 """ALUProxy: create a series of ALUs that look like the ALU being
100 sandwiched in between the fan-in and fan-out. One ALU looks like
101 it is multiple concurrent ALUs
102 """
103 def __init__(self, alu, p, n):
104 self.alu = alu
105 self.p = p
106 self.n = n
107
108
109 class ReservationStations(Elaboratable):
110 """ Reservation-Station pipeline
111
112 Input: num_rows - number of input and output Reservation Stations
113
114 Requires: the addition of an "alu" object, from which ispec and ospec
115 are taken, and inpipe and outpipe are connected to it
116
117 * fan-in on inputs (an array of BaseData: a,b,mid)
118 * ALU pipeline
119 * fan-out on outputs (an array of FPPackData: z,mid)
120
121 Fan-in and Fan-out are combinatorial.
122 """
123 def __init__(self, num_rows, maskwid=0, feedback_width=None):
124 self.num_rows = nr = num_rows
125 self.feedback_width = feedback_width
126 self.inpipe = InMuxPipe(nr, self.i_specfn, maskwid) # fan-in
127 self.outpipe = MuxOutPipe(nr, self.o_specfn, maskwid) # fan-out
128
129 self.p = self.inpipe.p # kinda annoying,
130 self.n = self.outpipe.n # use pipe in/out as this class in/out
131 self._ports = self.inpipe.ports() + self.outpipe.ports()
132
133 def setup_pseudoalus(self):
134 """setup_pseudoalus: establishes a suite of pseudo-alus
135 that look to all pipeline-intents-and-purposes just like the original
136 """
137 self.pseudoalus = []
138 for i in range(self.num_rows):
139 self.pseudoalus.append(ALUProxy(self.alu, self.p[i], self.n[i]))
140
141 def elaborate(self, platform):
142 m = Module()
143 m.submodules.inpipe = self.inpipe
144 m.submodules.alu = self.alu
145 m.submodules.outpipe = self.outpipe
146
147 m.d.comb += self.inpipe.n.connect_to_next(self.alu.p)
148 m.d.comb += self.alu.connect_to_next(self.outpipe)
149
150 if self.feedback_width is None:
151 return m
152
153 # connect all outputs above the feedback width back to their inputs
154 # (hence, feedback). pipeline stages are then expected to *modify*
155 # the muxid (with care) in order to use the "upper numbered" RSes
156 # for storing partially-completed results. micro-coding, basically
157
158 for i in range(self.feedback_width, self.num_rows):
159 self.outpipe.n[i].connect_to_next(self.inpipe.p[i])
160
161 return m
162
163 def ports(self):
164 return self._ports
165
166 def i_specfn(self):
167 return self.alu.ispec()
168
169 def o_specfn(self):
170 return self.alu.ospec()
171
172
173 class ReservationStations2(Elaboratable):
174 """ Reservation-Station pipeline
175
176 Input: num_rows - number of input and output Reservation Stations
177
178 Requires: the addition of an "alu" object, from which ispec and ospec
179 are taken, and inpipe and outpipe are connected to it
180
181 * fan-in on inputs (an array of BaseData: a,b,mid)
182 * ALU pipeline
183 * fan-out on outputs (an array of FPPackData: z,mid)
184
185 Fan-in and Fan-out are combinatorial.
186 """
187 def __init__(self, num_rows):
188 self.num_rows = nr = num_rows
189 id_wid = num_rows.bit_length()
190 self.p = []
191 self.n = []
192 for i in range(p_len):
193 self.p.append(PrevControl(maskwid=i_wid))
194 self.n.append(NextControl(maskwid=i_wid))
195
196 self.p = self.inpipe.p # kinda annoying,
197 self.n = self.outpipe.n # use pipe in/out as this class in/out
198 self.pipe = self # for Arbiter to select the incoming prevcontrols
199 self.p_mux = InputPriorityArbiter(self, num_rows)
200
201 def __iter__(self):
202 for p in self.p:
203 yield from p
204 for n in self.n:
205 yield from n
206
207 def ports(self):
208 return list(self)
209
210 def setup_pseudoalus(self):
211 """setup_pseudoalus: establishes a suite of pseudo-alus
212 that look to all pipeline-intents-and-purposes just like the original
213 """
214 self.pseudoalus = []
215 for i in range(self.num_rows):
216 self.pseudoalus.append(ALUProxy(self.alu, self.p[i], self.n[i]))
217
218 def elaborate(self, platform):
219 m = Module()
220 m.submodules.alu = self.alu
221 m.submodules.p_mux = self.p_mux
222
223 mid = self.p_mux.m_id
224 print ("CombMuxIn mid", self, mid, p_len)
225
226 for i in range(self.num_rows):
227 r_busy = Signal(name="busy%d" % i)
228 result = _spec(self.stage.ospec, "r_tmp%d" % i)
229
230 # establish some combinatorial temporaries
231 n_i_ready = Signal(reset_less=True, name="n_i_rdy_data%d" % i)
232 p_i_valid_p_o_ready = Signal(reset_less=True, name="pivpor%d" % i)
233 p_i_valid = Signal(reset_less=True, name="p_i_valid%d" % i)
234 m.d.comb += [p_i_valid.eq(self.p.i_valid_test),
235 n_i_ready.eq(self.n.i_ready_test),
236 p_i_valid_p_o_ready.eq(p_i_valid & self.p.o_ready),
237 ]
238
239 # store result of processing in combinatorial temporary
240 m.d.comb += nmoperator.eq(result, self.data_r)
241
242 # previous valid and ready
243 with m.If(p_i_valid_p_o_ready):
244 o_data = self._postprocess(result) # XXX does nothing now
245 m.d.sync += [r_busy.eq(1), # output valid
246 nmoperator.eq(self.n.o_data, o_data), # output
247 ]
248 # previous invalid or not ready, however next is accepting
249 with m.Elif(n_i_ready):
250 o_data = self._postprocess(result) # XXX does nothing now
251 m.d.sync += [nmoperator.eq(self.n.o_data, o_data)]
252 m.d.sync += r_busy.eq(0) # ...so set output invalid
253
254 m.d.comb += self.n.o_valid.eq(r_busy)
255 # if next is ready, so is previous
256 m.d.comb += self.p._o_ready.eq(n_i_ready)
257
258 return self.m
259
260 return m
261
262 def ports(self):
263 return self._ports
264
265 def i_specfn(self):
266 return self.alu.ispec()
267
268 def o_specfn(self):
269 return self.alu.ospec()