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