convert from public static functions/properties for regspecs
[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 also note: additional members, fu.rd_latches and fu.wr_latches
113 are replaced, here, by core.py. those contain the latched
114 read/write register information which the FU needs in order
115 to actually read (and write) the correct register number
116 """
117
118 def __init__(self, speckls, pipekls, idx, parent_pspec):
119 alu_name = "alu_%s%d" % (self.fnunit.name.lower(), idx)
120 # spec (NNNPipeSpec instance)
121 pspec = speckls(id_wid=2, parent_pspec=parent_pspec)
122 opsubset = pspec.opsubsetkls # get the operand subset class
123 rsk = pspec.regspecklses # get the regspec classes
124 regspec = []
125 for kls in rsk:
126 regspec.append(kls(pspec).regspec)
127 print ("regspecs", regspec)
128 alu = pipekls(pspec) # create actual NNNBasePipe
129 self.pspec = pspec
130 super().__init__(regspec, alu, opsubset, name=alu_name) # MultiCompUnit
131 # these are set to None for now: core get_byregfiles fills them in
132 # (for now)
133 self.fu_rdlatches = None
134 self.fu_wrlatches = None
135
136
137 ##############################################################
138 # TODO: ReservationStations-based (FunctionUnitBaseConcurrent)
139
140 class FunctionUnitBaseMulti(ReservationStations2):
141 """FunctionUnitBaseMulti
142
143 similar to FunctionUnitBaseSingle except it creates a list
144 of MultiCompUnit instances all using the same ALU instance.
145
146 * :speckls: - the specification. contains regspec and op subset info,
147 and contains common "stuff" like the pipeline ctx,
148 what type of nmutil pipeline base is to be used (etc)
149 * :pipekls: - the type of pipeline. actually connects things together
150
151 * :num_rows: - number of ReservationStations wrapped around the FU
152
153 note that it is through MultiCompUnit.get_in/out that we *actually*
154 connect up the association between regspec variable names (defined
155 in the pipe_data).
156
157 note that the rdflags function obtains (dynamically, from instruction
158 decoding) which read-register ports are to be requested. this is not
159 ideal (it could be a lot neater) but works for now.
160 """
161
162 def __init__(self, speckls, pipekls, num_rows, parent_pspec):
163 id_wid = num_rows.bit_length()
164
165 # spec (NNNPipeSpec instance)
166 pspec = speckls(id_wid=id_wid, parent_pspec=parent_pspec)
167 self.pspec = pspec
168 opsubset = pspec.opsubsetkls # get the operand subset class
169 rsk = pspec.regspecklses # get the regspec classes
170 regspec = []
171 for kls in rsk:
172 regspec.append(kls(pspec).regspec)
173 print ("regspecs", regspec)
174 alu = pipekls(pspec) # create actual NNNBasePipe
175 alu_name = self.fnunit.name.lower()
176 super().__init__(alu, num_rows, alu_name) # initialise fan-in/fan-out
177 self.cu = []
178 for idx in range(num_rows):
179 alu_name = "alu_%s%d" % (alu_name, idx)
180 palu = self.pseudoalus[idx]
181 cu = MultiCompUnit(regspec, palu, opsubset, name=alu_name,
182 sync_rw=False)
183 cu.fnunit = self.fnunit
184 cu.fu_muxidx = idx
185 self.cu.append(cu)
186
187 def elaborate(self, platform):
188 m = super().elaborate(platform)
189 # set the muxids so that ReservationStations2 can direct data
190 # without this the incoming data gets routed to the wrong place!
191 # NOTE: for Mask Cancellation this has to be done slightly differently
192 for i, p in enumerate(self.p):
193 m.d.comb += p.i_data.muxid.eq(i)
194 return m
195
196 ######################################################################
197 ###### actual Function Units: these are "single" stage pipelines #####
198
199 # class ALUFunctionUnit(FunctionUnitBaseSingle):
200
201
202 class ALUFunctionUnit(FunctionUnitBaseMulti):
203 fnunit = Function.ALU
204
205 def __init__(self, num_rses, parent_pspec):
206 super().__init__(ALUPipeSpec, ALUBasePipe, num_rses, parent_pspec)
207
208
209 # class LogicalFunctionUnit(FunctionUnitBaseSingle):
210 class LogicalFunctionUnit(FunctionUnitBaseMulti):
211 fnunit = Function.LOGICAL
212
213 def __init__(self, idx, parent_pspec):
214 super().__init__(LogicalPipeSpec, LogicalBasePipe, idx, parent_pspec)
215
216
217 # class CRFunctionUnit(FunctionUnitBaseSingle):
218 class CRFunctionUnit(FunctionUnitBaseMulti):
219 fnunit = Function.CR
220
221 def __init__(self, idx, parent_pspec):
222 super().__init__(CRPipeSpec, CRBasePipe, idx, parent_pspec)
223
224
225 # class BranchFunctionUnit(FunctionUnitBaseSingle):
226 class BranchFunctionUnit(FunctionUnitBaseMulti):
227 fnunit = Function.BRANCH
228
229 def __init__(self, idx, parent_pspec):
230 super().__init__(BranchPipeSpec, BranchBasePipe, idx, parent_pspec)
231
232
233 # class ShiftRotFunctionUnit(FunctionUnitBaseSingle):
234 class ShiftRotFunctionUnit(FunctionUnitBaseMulti):
235 fnunit = Function.SHIFT_ROT
236
237 def __init__(self, idx, parent_pspec):
238 super().__init__(ShiftRotPipeSpec, ShiftRotBasePipe, idx, parent_pspec)
239
240
241 class DivFSMFunctionUnit(FunctionUnitBaseSingle):
242 fnunit = Function.DIV
243
244 def __init__(self, idx, parent_pspec):
245 super().__init__(DivPipeSpecFSMDivCore, DivBasePipe, idx, parent_pspec)
246
247
248 class MMUFSMFunctionUnit(FunctionUnitBaseSingle):
249 fnunit = Function.MMU
250
251 def __init__(self, idx, parent_pspec):
252 super().__init__(MMUPipeSpec, FSMMMUStage, idx, parent_pspec)
253 self.exc_o = self.alu.exc_o # get at MMU exception
254
255
256 class DivPipeFunctionUnit(FunctionUnitBaseSingle):
257 fnunit = Function.DIV
258
259 def __init__(self, idx, parent_pspec):
260 super().__init__(DivPipeSpecDivPipeCore, DivBasePipe, idx, parent_pspec)
261
262
263 # class MulFunctionUnit(FunctionUnitBaseSingle):
264 class MulFunctionUnit(FunctionUnitBaseMulti):
265 fnunit = Function.MUL
266
267 def __init__(self, idx, parent_pspec):
268 super().__init__(MulPipeSpec, MulBasePipe, idx, parent_pspec)
269
270
271 class TrapFunctionUnit(FunctionUnitBaseSingle):
272 fnunit = Function.TRAP
273
274 def __init__(self, idx, parent_pspec):
275 super().__init__(TrapPipeSpec, TrapBasePipe, idx, parent_pspec)
276
277
278 class SPRFunctionUnit(FunctionUnitBaseSingle):
279 fnunit = Function.SPR
280
281 def __init__(self, idx, parent_pspec):
282 super().__init__(SPRPipeSpec, SPRBasePipe, idx, parent_pspec)
283
284
285 # special-case: LD/ST conforms to the CompUnit API but is not a pipeline
286
287 class LDSTFunctionUnit(LDSTCompUnit):
288 fnunit = Function.LDST
289
290 def __init__(self, pi, awid, idx, parent_pspec):
291 alu_name = "ldst_%s%d" % (self.fnunit.name.lower(), idx)
292 # spec (NNNPipeSpec instance)
293 pspec = LDSTPipeSpec(id_wid=2, parent_pspec=parent_pspec)
294 opsubset = pspec.opsubsetkls # get the operand subset class
295 rsk = pspec.regspecklses # get the regspec classes
296 regspec = []
297 for kls in rsk:
298 regspec.append(kls(pspec).regspec)
299 print ("regspecs", regspec)
300 self.opsubsetkls = opsubset
301 super().__init__(pi, regspec, awid, opsubset, name=alu_name)
302
303
304 #####################################################################
305 ###### actual Function Units: these are "multi" stage pipelines #####
306
307 # TODO: ReservationStations-based.
308
309 # simple one-only function unit class, for test purposes
310 class AllFunctionUnits(Elaboratable):
311 """AllFunctionUnits
312
313 creates a dictionary of Function Units according to required spec.
314 tuple is of:
315
316 * name of ALU,
317 * quantity of FUs required
318 * type of FU required
319
320 """
321
322 def __init__(self, pspec, pilist=None, div_fsm=True):
323 addrwid = pspec.addr_wid
324 units = pspec.units
325 microwatt_mmu = hasattr(pspec, "mmu") and pspec.mmu == True
326 print("AllFunctionUnits.microwatt_mmu="+str(microwatt_mmu))
327 if not isinstance(units, dict):
328 units = {'alu': 1, 'cr': 1, 'branch': 1, 'trap': 1,
329 'spr': 1,
330 'logical': 1,
331 'mul': 1,
332 'div': 1, 'shiftrot': 1}
333 if microwatt_mmu:
334 units['mmu'] = 1
335 alus = {'alu': ALUFunctionUnit,
336 'cr': CRFunctionUnit,
337 'branch': BranchFunctionUnit,
338 'trap': TrapFunctionUnit,
339 'spr': SPRFunctionUnit,
340 'mul': MulFunctionUnit,
341 'mmu': MMUFSMFunctionUnit,
342 'logical': LogicalFunctionUnit,
343 'shiftrot': ShiftRotFunctionUnit,
344 }
345 if div_fsm:
346 alus['div'] = DivFSMFunctionUnit
347 else:
348 alus['div'] = DivPipeFunctionUnit
349
350 # create dictionary of Function Units
351 self.fus = {}
352 self.actual_alus = {}
353 for name, qty in units.items():
354 kls = alus[name]
355 if issubclass(kls, FunctionUnitBaseMulti):
356 # create just the one ALU but many "fronts"
357 fu = kls(qty, parent_pspec=pspec)
358 self.actual_alus[name] = fu # to be made a module of AllFUs
359 for i in range(qty):
360 self.fus["%s%d" % (name, i)] = fu.cu[i]
361 else:
362 for i in range(qty):
363 self.fus["%s%d" % (name, i)] = kls(i, parent_pspec=pspec)
364
365 # debug print for MMU ALU
366 if microwatt_mmu:
367 alu = self.fus["mmu0"].alu
368 print("MMU alu", alu)
369
370 # if any PortInterfaces, we want LDST Units.
371 if pilist is None:
372 return
373 print("pilist", pilist)
374 for i, pi in enumerate(pilist):
375 self.fus["ldst%d" % (i)] = LDSTFunctionUnit(pi, addrwid, i, pspec)
376
377 # extract exceptions from any FunctionUnits for easy access
378 self.excs = {}
379 for name, alu in self.fus.items():
380 if hasattr(alu, "exc_o"):
381 print("FU exceptions", name, type(alu.exc_o), alu.exc_o)
382 self.excs[name] = alu.exc_o
383
384 def get_exc(self, name):
385 return self.excs.get(name)
386
387 def get_fu(self, name):
388 return self.fus.get(name)
389
390 def elaborate(self, platform):
391 m = Module()
392 # add MultiCompUnit modules (Single CompUnits add their own ALU)
393 for (name, fu) in self.fus.items():
394 m.submodules[name] = fu
395 # if any ReservationStations, there is only one ALU per RS so add that
396 for (name, alu) in self.actual_alus.items():
397 m.submodules[name] = alu
398 return m
399
400 def __iter__(self):
401 for (name, fu) in self.fus.items():
402 yield from fu.ports()
403
404 def ports(self):
405 return list(self)
406
407
408 def tst_single_fus_il():
409 for (name, kls) in (('alu', ALUFunctionUnit),
410 ('cr', CRFunctionUnit),
411 ('branch', BranchFunctionUnit),
412 ('trap', TrapFunctionUnit),
413 ('spr', SPRFunctionUnit),
414 ('mul', MulFunctionUnit),
415 ('logical', LogicalFunctionUnit),
416 ('shiftrot', ShiftRotFunctionUnit)):
417 fu = kls(0)
418 vl = rtlil.convert(fu, ports=fu.ports())
419 with open("fu_%s.il" % name, "w") as f:
420 f.write(vl)
421
422
423 def tst_all_fus():
424 pspec = TestMemPspec(ldst_ifacetype='testpi',
425 imem_ifacetype='',
426 addr_wid=64,
427 mask_wid=8,
428 reg_wid=64)
429 dut = AllFunctionUnits(pspec)
430 vl = rtlil.convert(dut, ports=dut.ports())
431 with open("all_fus.il", "w") as f:
432 f.write(vl)
433
434
435 if __name__ == '__main__':
436 tst_single_fus_il()
437 tst_all_fus()