switch over to single-entry (num_rows=1) ReservationStation2 based
[soc.git] / src / soc / fu / compunits / compunits.py
1 ###################################################################
2 """Function Units Construction
3
4 This module pulls all of the pipelines together (soc.fu.*) and, using
5 the regspec and Computation Unit APIs, constructs Scoreboard-aware
6 Function Units that may systematically and automatically be wired up
7 to appropriate Register Files.
8
9 Two types exist:
10
11 * Single-cycle Function Units. these are FUs that will only block for
12 one cycle. it is expected that multiple of these be instantiated,
13 because they are simple and trivial, and not many gates.
14
15 - ALU, Logical: definitely several
16 - CR: not so many needed (perhaps)
17 - Branch: one or two of these (depending on speculation run-ahead)
18 - Trap: yeah really only one of these
19 - SPR: again, only one.
20 - ShiftRot (perhaps not too many of these)
21
22 * Multi-cycle (and FSM) Function Units. these are FUs that can only
23 handle a limited number of values, and take several cycles to complete.
24 Given that under Scoreboard Management, start and completion must be
25 fully managed, a "Reservation Station" style approach is required:
26 *one* multiple-stage (N stage) pipelines need a minimum of N (plural)
27 "CompUnit" front-ends. this includes:
28
29 - MUL (all versions including MAC)
30 - DIV (including modulo)
31
32 In either case, there will be multiple MultiCompUnits: it's just that
33 single-cycle ones are instantiated individually (one single-cycle pipeline
34 per MultiCompUnit, and multi-cycle ones need to be instantiated en-masse,
35 where *only one* actual pipeline (or FSM) has *multiple* Reservation
36 Stations.
37
38 see:
39
40 * https://libre-soc.org/3d_gpu/architecture/regfile/ section on regspecs
41
42 """
43
44 # imports
45
46 from nmigen import Elaboratable, Module
47 from nmigen.cli import rtlil
48 from soc.experiment.compalu_multi import MultiCompUnit
49 from openpower.decoder.power_enums import Function
50 from soc.config.test.test_loadstore import TestMemPspec
51 from nmutil.concurrentunit import ReservationStations2
52
53 # pipeline / spec imports
54
55 from soc.fu.alu.pipeline import ALUBasePipe
56 from soc.fu.alu.pipe_data import ALUPipeSpec
57
58 from soc.fu.logical.pipeline import LogicalBasePipe
59 from soc.fu.logical.pipe_data import LogicalPipeSpec
60
61 from soc.fu.cr.pipeline import CRBasePipe
62 from soc.fu.cr.pipe_data import CRPipeSpec
63
64 from soc.fu.branch.pipeline import BranchBasePipe
65 from soc.fu.branch.pipe_data import BranchPipeSpec
66
67 from soc.fu.shift_rot.pipeline import ShiftRotBasePipe
68 from soc.fu.shift_rot.pipe_data import ShiftRotPipeSpec
69
70 from soc.fu.spr.pipeline import SPRBasePipe
71 from soc.fu.spr.pipe_data import SPRPipeSpec
72
73 from soc.fu.trap.pipeline import TrapBasePipe
74 from soc.fu.trap.pipe_data import TrapPipeSpec
75
76 from soc.fu.div.pipeline import DivBasePipe
77 from soc.fu.div.pipe_data import DivPipeSpecFSMDivCore
78 from soc.fu.div.pipe_data import DivPipeSpecDivPipeCore
79
80 from soc.fu.mmu.fsm import FSMMMUStage
81 from soc.fu.mmu.pipe_data import MMUPipeSpec
82
83 from soc.fu.mul.pipeline import MulBasePipe
84 from soc.fu.mul.pipe_data import MulPipeSpec
85
86 from soc.fu.ldst.pipe_data import LDSTPipeSpec
87 from soc.experiment.compldst_multi import LDSTCompUnit # special-case
88
89
90 ###################################################################
91 ###### FunctionUnitBaseSingle - use to make single-stge pipes #####
92
93 class FunctionUnitBaseSingle(MultiCompUnit):
94 """FunctionUnitBaseSingle
95
96 main "glue" class that brings everything together.
97 ONLY use this class for single-stage pipelines.
98
99 * :speckls: - the specification. contains regspec and op subset info,
100 and contains common "stuff" like the pipeline ctx,
101 what type of nmutil pipeline base is to be used (etc)
102 * :pipekls: - the type of pipeline. actually connects things together
103
104 note that it is through MultiCompUnit.get_in/out that we *actually*
105 connect up the association between regspec variable names (defined
106 in the pipe_data).
107
108 note that the rdflags function obtains (dynamically, from instruction
109 decoding) which read-register ports are to be requested. this is not
110 ideal (it could be a lot neater) but works for now.
111 """
112
113 def __init__(self, speckls, pipekls, idx):
114 alu_name = "alu_%s%d" % (self.fnunit.name.lower(), idx)
115 pspec = speckls(id_wid=2) # spec (NNNPipeSpec instance)
116 opsubset = pspec.opsubsetkls # get the operand subset class
117 regspec = pspec.regspec # get the regspec
118 alu = pipekls(pspec) # create actual NNNBasePipe
119 self.pspec = pspec
120 super().__init__(regspec, alu, opsubset, name=alu_name) # MultiCompUnit
121
122
123 ##############################################################
124 # TODO: ReservationStations-based (FunctionUnitBaseConcurrent)
125
126 class FunctionUnitBaseMulti(ReservationStations2):
127 """FunctionUnitBaseMulti
128
129 similar to FunctionUnitBaseSingle except it creates a list
130 of MultiCompUnit instances all using the same ALU instance.
131
132 * :speckls: - the specification. contains regspec and op subset info,
133 and contains common "stuff" like the pipeline ctx,
134 what type of nmutil pipeline base is to be used (etc)
135 * :pipekls: - the type of pipeline. actually connects things together
136
137 * :num_rows: - number of ReservationStations wrapped around the FU
138
139 note that it is through MultiCompUnit.get_in/out that we *actually*
140 connect up the association between regspec variable names (defined
141 in the pipe_data).
142
143 note that the rdflags function obtains (dynamically, from instruction
144 decoding) which read-register ports are to be requested. this is not
145 ideal (it could be a lot neater) but works for now.
146 """
147
148 def __init__(self, speckls, pipekls, num_rows):
149 id_wid = num_rows.bit_length()
150 pspec = speckls(id_wid=id_wid) # spec (NNNPipeSpec instance)
151 opsubset = pspec.opsubsetkls # get the operand subset class
152 regspec = pspec.regspec # get the regspec
153 alu = pipekls(pspec) # create actual NNNBasePipe
154 self.pspec = pspec
155 alu_name = self.fnunit.name.lower()
156 super().__init__(alu, num_rows, alu_name) # initialise fan-in/fan-out
157 self.cu = []
158 for idx in range(num_rows):
159 alu_name = "alu_%s%d" % (alu_name, idx)
160 palu = self.pseudoalus[idx]
161 cu = MultiCompUnit(regspec, palu, opsubset, name=alu_name)
162 cu.fnunit = self.fnunit
163 self.cu.append(cu)
164
165
166 ######################################################################
167 ###### actual Function Units: these are "single" stage pipelines #####
168
169 #class ALUFunctionUnit(FunctionUnitBaseSingle):
170 class ALUFunctionUnit(FunctionUnitBaseMulti):
171 fnunit = Function.ALU
172
173 def __init__(self, idx):
174 super().__init__(ALUPipeSpec, ALUBasePipe, 1)
175
176
177 #class LogicalFunctionUnit(FunctionUnitBaseSingle):
178 class LogicalFunctionUnit(FunctionUnitBaseMulti):
179 fnunit = Function.LOGICAL
180
181 def __init__(self, idx):
182 super().__init__(LogicalPipeSpec, LogicalBasePipe, idx)
183
184
185 #class CRFunctionUnit(FunctionUnitBaseSingle):
186 class CRFunctionUnit(FunctionUnitBaseMulti):
187 fnunit = Function.CR
188
189 def __init__(self, idx):
190 super().__init__(CRPipeSpec, CRBasePipe, idx)
191
192
193 #class BranchFunctionUnit(FunctionUnitBaseSingle):
194 class BranchFunctionUnit(FunctionUnitBaseMulti):
195 fnunit = Function.BRANCH
196
197 def __init__(self, idx):
198 super().__init__(BranchPipeSpec, BranchBasePipe, idx)
199
200
201 #class ShiftRotFunctionUnit(FunctionUnitBaseSingle):
202 class ShiftRotFunctionUnit(FunctionUnitBaseMulti):
203 fnunit = Function.SHIFT_ROT
204
205 def __init__(self, idx):
206 super().__init__(ShiftRotPipeSpec, ShiftRotBasePipe, idx)
207
208
209 class DivFSMFunctionUnit(FunctionUnitBaseSingle):
210 fnunit = Function.DIV
211
212 def __init__(self, idx):
213 super().__init__(DivPipeSpecFSMDivCore, DivBasePipe, idx)
214
215
216 class MMUFSMFunctionUnit(FunctionUnitBaseSingle):
217 fnunit = Function.MMU
218
219 def __init__(self, idx):
220 super().__init__(MMUPipeSpec, FSMMMUStage, idx)
221
222
223 class DivPipeFunctionUnit(FunctionUnitBaseSingle):
224 fnunit = Function.DIV
225
226 def __init__(self, idx):
227 super().__init__(DivPipeSpecDivPipeCore, DivBasePipe, idx)
228
229
230 #class MulFunctionUnit(FunctionUnitBaseSingle):
231 class MulFunctionUnit(FunctionUnitBaseMulti):
232 fnunit = Function.MUL
233
234 def __init__(self, idx):
235 super().__init__(MulPipeSpec, MulBasePipe, idx)
236
237
238 class TrapFunctionUnit(FunctionUnitBaseSingle):
239 fnunit = Function.TRAP
240
241 def __init__(self, idx):
242 super().__init__(TrapPipeSpec, TrapBasePipe, idx)
243
244
245 class SPRFunctionUnit(FunctionUnitBaseSingle):
246 fnunit = Function.SPR
247
248 def __init__(self, idx):
249 super().__init__(SPRPipeSpec, SPRBasePipe, idx)
250
251
252 # special-case: LD/ST conforms to the CompUnit API but is not a pipeline
253
254 class LDSTFunctionUnit(LDSTCompUnit):
255 fnunit = Function.LDST
256
257 def __init__(self, pi, awid, idx):
258 alu_name = "ldst_%s%d" % (self.fnunit.name.lower(), idx)
259 pspec = LDSTPipeSpec(id_wid=2) # spec (NNNPipeSpec instance)
260 opsubset = pspec.opsubsetkls # get the operand subset class
261 regspec = pspec.regspec # get the regspec
262 self.opsubsetkls = opsubset
263 super().__init__(pi, regspec, awid, opsubset, name=alu_name)
264
265
266 #####################################################################
267 ###### actual Function Units: these are "multi" stage pipelines #####
268
269 # TODO: ReservationStations-based.
270
271 # simple one-only function unit class, for test purposes
272 class AllFunctionUnits(Elaboratable):
273 """AllFunctionUnits
274
275 creates a dictionary of Function Units according to required spec.
276 tuple is of:
277
278 * name of ALU,
279 * quantity of FUs required
280 * type of FU required
281
282 """
283
284 def __init__(self, pspec, pilist=None, div_fsm=True):
285 addrwid = pspec.addr_wid
286 units = pspec.units
287 microwatt_mmu = hasattr(pspec, "mmu") and pspec.mmu == True
288 print("AllFunctionUnits.microwatt_mmu="+str(microwatt_mmu))
289 if not isinstance(units, dict):
290 units = {'alu': 1, 'cr': 1, 'branch': 1, 'trap': 1,
291 'spr': 1,
292 'logical': 1,
293 'mul': 1,
294 'div': 1, 'shiftrot': 1}
295 if microwatt_mmu:
296 units['mmu'] = 1
297 alus = {'alu': ALUFunctionUnit,
298 'cr': CRFunctionUnit,
299 'branch': BranchFunctionUnit,
300 'trap': TrapFunctionUnit,
301 'spr': SPRFunctionUnit,
302 'mul': MulFunctionUnit,
303 'mmu': MMUFSMFunctionUnit,
304 'logical': LogicalFunctionUnit,
305 'shiftrot': ShiftRotFunctionUnit,
306 }
307 if div_fsm:
308 alus['div'] = DivFSMFunctionUnit
309 else:
310 alus['div'] = DivPipeFunctionUnit
311
312 # create dictionary of Function Units
313 self.fus = {}
314 self.actual_alus = {}
315 for name, qty in units.items():
316 kls = alus[name]
317 if issubclass(kls, FunctionUnitBaseMulti):
318 fu = kls(qty) # create just the one ALU but many "fronts"
319 self.actual_alus[name] = fu # to be made a module of AllFUs
320 for i in range(qty):
321 self.fus["%s%d" % (name, i)] = fu.cu[i]
322 else:
323 for i in range(qty):
324 self.fus["%s%d" % (name, i)] = kls(i)
325
326 # debug print for MMU ALU
327 if microwatt_mmu:
328 alu = self.fus["mmu0"].alu
329 print("MMU alu", alu)
330
331 # if any PortInterfaces, we want LDST Units.
332 if pilist is None:
333 return
334 print ("pilist", pilist)
335 for i, pi in enumerate(pilist):
336 self.fus["ldst%d" % (i)] = LDSTFunctionUnit(pi, addrwid, i)
337
338 # extract exceptions from any FunctionUnits for easy access
339 self.excs = {}
340 for name, alu in self.fus.items():
341 if hasattr(alu, "exc_o"):
342 print ("FU exceptions", name, type(alu.exc_o), alu.exc_o)
343 self.excs[name] = alu.exc_o
344
345 def get_exc(self, name):
346 return self.excs.get(name)
347
348 def get_fu(self, name):
349 return self.fus.get(name)
350
351 def elaborate(self, platform):
352 m = Module()
353 # add MultiCompUnit modules (Single CompUnits add their own ALU)
354 for (name, fu) in self.fus.items():
355 setattr(m.submodules, name, fu)
356 # if any ReservationStations, there is only one ALU per RS so add that
357 for (name, alu) in self.actual_alus.items():
358 setattr(m.submodules, name, alu)
359 return m
360
361 def __iter__(self):
362 for (name, fu) in self.fus.items():
363 yield from fu.ports()
364
365 def ports(self):
366 return list(self)
367
368
369 def tst_single_fus_il():
370 for (name, kls) in (('alu', ALUFunctionUnit),
371 ('cr', CRFunctionUnit),
372 ('branch', BranchFunctionUnit),
373 ('trap', TrapFunctionUnit),
374 ('spr', SPRFunctionUnit),
375 ('mul', MulFunctionUnit),
376 ('logical', LogicalFunctionUnit),
377 ('shiftrot', ShiftRotFunctionUnit)):
378 fu = kls(0)
379 vl = rtlil.convert(fu, ports=fu.ports())
380 with open("fu_%s.il" % name, "w") as f:
381 f.write(vl)
382
383
384 def tst_all_fus():
385 pspec = TestMemPspec(ldst_ifacetype='testpi',
386 imem_ifacetype='',
387 addr_wid=48,
388 mask_wid=8,
389 reg_wid=64)
390 dut = AllFunctionUnits(pspec)
391 vl = rtlil.convert(dut, ports=dut.ports())
392 with open("all_fus.il", "w") as f:
393 f.write(vl)
394
395
396 if __name__ == '__main__':
397 tst_single_fus_il()
398 tst_all_fus()