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