096cda34f4a5974ecdd9cc93c454e595765a3192
[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 soc.decoder.power_enums import Function
50 from soc.config.test.test_loadstore import TestMemPspec
51
52 # pipeline / spec imports
53
54 from soc.fu.alu.pipeline import ALUBasePipe
55 from soc.fu.alu.pipe_data import ALUPipeSpec
56
57 from soc.fu.logical.pipeline import LogicalBasePipe
58 from soc.fu.logical.pipe_data import LogicalPipeSpec
59
60 from soc.fu.cr.pipeline import CRBasePipe
61 from soc.fu.cr.pipe_data import CRPipeSpec
62
63 from soc.fu.branch.pipeline import BranchBasePipe
64 from soc.fu.branch.pipe_data import BranchPipeSpec
65
66 from soc.fu.shift_rot.pipeline import ShiftRotBasePipe
67 from soc.fu.shift_rot.pipe_data import ShiftRotPipeSpec
68
69 from soc.fu.spr.pipeline import SPRBasePipe
70 from soc.fu.spr.pipe_data import SPRPipeSpec
71
72 from soc.fu.trap.pipeline import TrapBasePipe
73 from soc.fu.trap.pipe_data import TrapPipeSpec
74
75 from soc.fu.div.pipeline import DivBasePipe
76 from soc.fu.div.pipe_data import DivPipeSpecFSMDivCore
77 from soc.fu.div.pipe_data import DivPipeSpecDivPipeCore
78
79 from soc.fu.mmu.fsm import FSMMMUStage
80 from soc.fu.mmu.pipe_data import MMUPipeSpec
81
82 from soc.fu.mul.pipeline import MulBasePipe
83 from soc.fu.mul.pipe_data import MulPipeSpec
84
85 from soc.fu.ldst.pipe_data import LDSTPipeSpec
86 from soc.experiment.compldst_multi import LDSTCompUnit # special-case
87
88
89 ###################################################################
90 ###### FunctionUnitBaseSingle - use to make single-stge pipes #####
91
92 class FunctionUnitBaseSingle(MultiCompUnit):
93 """FunctionUnitBaseSingle
94
95 main "glue" class that brings everything together.
96 ONLY use this class for single-stage pipelines.
97
98 * :speckls: - the specification. contains regspec and op subset info,
99 and contains common "stuff" like the pipeline ctx,
100 what type of nmutil pipeline base is to be used (etc)
101 * :pipekls: - the type of pipeline. actually connects things together
102
103 note that it is through MultiCompUnit.get_in/out that we *actually*
104 connect up the association between regspec variable names (defined
105 in the pipe_data).
106
107 note that the rdflags function obtains (dynamically, from instruction
108 decoding) which read-register ports are to be requested. this is not
109 ideal (it could be a lot neater) but works for now.
110 """
111
112 def __init__(self, speckls, pipekls, idx):
113 alu_name = "alu_%s%d" % (self.fnunit.name.lower(), idx)
114 pspec = speckls(id_wid=2) # spec (NNNPipeSpec instance)
115 opsubset = pspec.opsubsetkls # get the operand subset class
116 regspec = pspec.regspec # get the regspec
117 alu = pipekls(pspec) # create actual NNNBasePipe
118 self.pspec = pspec
119 super().__init__(regspec, alu, opsubset, name=alu_name) # MultiCompUnit
120
121
122 ##############################################################
123 # TODO: ReservationStations-based (FunctionUnitBaseConcurrent)
124
125 class FunctionUnitBaseMulti:
126 pass
127
128
129 ######################################################################
130 ###### actual Function Units: these are "single" stage pipelines #####
131
132 class ALUFunctionUnit(FunctionUnitBaseSingle):
133 fnunit = Function.ALU
134
135 def __init__(self, idx):
136 super().__init__(ALUPipeSpec, ALUBasePipe, idx)
137
138
139 class LogicalFunctionUnit(FunctionUnitBaseSingle):
140 fnunit = Function.LOGICAL
141
142 def __init__(self, idx):
143 super().__init__(LogicalPipeSpec, LogicalBasePipe, idx)
144
145
146 class CRFunctionUnit(FunctionUnitBaseSingle):
147 fnunit = Function.CR
148
149 def __init__(self, idx):
150 super().__init__(CRPipeSpec, CRBasePipe, idx)
151
152
153 class BranchFunctionUnit(FunctionUnitBaseSingle):
154 fnunit = Function.BRANCH
155
156 def __init__(self, idx):
157 super().__init__(BranchPipeSpec, BranchBasePipe, idx)
158
159
160 class ShiftRotFunctionUnit(FunctionUnitBaseSingle):
161 fnunit = Function.SHIFT_ROT
162
163 def __init__(self, idx):
164 super().__init__(ShiftRotPipeSpec, ShiftRotBasePipe, idx)
165
166
167 class DivFSMFunctionUnit(FunctionUnitBaseSingle):
168 fnunit = Function.DIV
169
170 def __init__(self, idx):
171 super().__init__(DivPipeSpecFSMDivCore, DivBasePipe, idx)
172
173
174 class MMUFSMFunctionUnit(FunctionUnitBaseSingle):
175 fnunit = Function.MMU
176
177 def __init__(self, idx):
178 super().__init__(MMUPipeSpec, FSMMMUStage, idx)
179
180
181 class DivPipeFunctionUnit(FunctionUnitBaseSingle):
182 fnunit = Function.DIV
183
184 def __init__(self, idx):
185 super().__init__(DivPipeSpecDivPipeCore, DivBasePipe, idx)
186
187
188 class MulFunctionUnit(FunctionUnitBaseSingle):
189 fnunit = Function.MUL
190
191 def __init__(self, idx):
192 super().__init__(MulPipeSpec, MulBasePipe, idx)
193
194
195 class TrapFunctionUnit(FunctionUnitBaseSingle):
196 fnunit = Function.TRAP
197
198 def __init__(self, idx):
199 super().__init__(TrapPipeSpec, TrapBasePipe, idx)
200
201
202 class SPRFunctionUnit(FunctionUnitBaseSingle):
203 fnunit = Function.SPR
204
205 def __init__(self, idx):
206 super().__init__(SPRPipeSpec, SPRBasePipe, idx)
207
208
209 # special-case: LD/ST conforms to the CompUnit API but is not a pipeline
210
211 class LDSTFunctionUnit(LDSTCompUnit):
212 fnunit = Function.LDST
213
214 def __init__(self, pi, awid, idx):
215 alu_name = "ldst_%s%d" % (self.fnunit.name.lower(), idx)
216 pspec = LDSTPipeSpec(id_wid=2) # spec (NNNPipeSpec instance)
217 opsubset = pspec.opsubsetkls # get the operand subset class
218 regspec = pspec.regspec # get the regspec
219 self.opsubsetkls = opsubset
220 super().__init__(pi, regspec, awid, opsubset, name=alu_name)
221
222
223 #####################################################################
224 ###### actual Function Units: these are "multi" stage pipelines #####
225
226 # TODO: ReservationStations-based.
227
228
229 # simple one-only function unit class, for test purposes
230 class AllFunctionUnits(Elaboratable):
231 """AllFunctionUnits
232
233 creates a dictionary of Function Units according to required spec.
234 tuple is of:
235
236 * name of ALU,
237 * quantity of FUs required
238 * type of FU required
239
240 """
241
242 def __init__(self, pspec, pilist=None, div_fsm=True):
243 addrwid = pspec.addr_wid
244 units = pspec.units
245 if not isinstance(units, dict):
246 units = {'alu': 1, 'cr': 1, 'branch': 1, 'trap': 1,
247 'spr': 1,
248 'logical': 1,
249 'mul': 1,
250 #'mmu': 1,
251 'div': 1, 'shiftrot': 1}
252 alus = {'alu': ALUFunctionUnit,
253 'cr': CRFunctionUnit,
254 'branch': BranchFunctionUnit,
255 'trap': TrapFunctionUnit,
256 'spr': SPRFunctionUnit,
257 'mul': MulFunctionUnit,
258 'mmu': MMUFSMFunctionUnit,
259 'logical': LogicalFunctionUnit,
260 'shiftrot': ShiftRotFunctionUnit,
261 }
262 if div_fsm:
263 alus['div'] = DivFSMFunctionUnit
264 else:
265 alus['div'] = DivPipeFunctionUnit
266
267 self.fus = {}
268 for name, qty in units.items():
269 kls = alus[name]
270 for i in range(qty):
271 self.fus["%s%d" % (name, i)] = kls(i)
272 if pilist is None:
273 return
274 for i, pi in enumerate(pilist):
275 self.fus["ldst%d" % (i)] = LDSTFunctionUnit(pi, addrwid, i)
276
277 def elaborate(self, platform):
278 m = Module()
279 for (name, fu) in self.fus.items():
280 setattr(m.submodules, name, fu)
281 return m
282
283 def __iter__(self):
284 for (name, fu) in self.fus.items():
285 yield from fu.ports()
286
287 def ports(self):
288 return list(self)
289
290
291 def tst_single_fus_il():
292 for (name, kls) in (('alu', ALUFunctionUnit),
293 ('cr', CRFunctionUnit),
294 ('branch', BranchFunctionUnit),
295 ('trap', TrapFunctionUnit),
296 ('spr', SPRFunctionUnit),
297 ('mul', MulFunctionUnit),
298 ('logical', LogicalFunctionUnit),
299 ('shiftrot', ShiftRotFunctionUnit)):
300 fu = kls(0)
301 vl = rtlil.convert(fu, ports=fu.ports())
302 with open("fu_%s.il" % name, "w") as f:
303 f.write(vl)
304
305
306 def tst_all_fus():
307 pspec = TestMemPspec(ldst_ifacetype='testpi',
308 imem_ifacetype='',
309 addr_wid=48,
310 mask_wid=8,
311 reg_wid=64)
312 dut = AllFunctionUnits(pspec)
313 vl = rtlil.convert(dut, ports=dut.ports())
314 with open("all_fus.il", "w") as f:
315 f.write(vl)
316
317
318 if __name__ == '__main__':
319 tst_single_fus_il()
320 tst_all_fus()