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