connect up write-ports from Regfiles to FUs
[soc.git] / src / soc / simple / core.py
1 """simple core
2
3 not in any way intended for production use. connects up FunctionUnits to
4 Register Files in a brain-dead fashion that only permits one and only one
5 Function Unit to be operational.
6
7 the principle here is to take the Function Units, analyse their regspecs,
8 and turn their requirements for access to register file read/write ports
9 into groupings by Register File and Register File Port name.
10
11 under each grouping - by regfile/port - a list of Function Units that
12 need to connect to that port is created. as these are a contended
13 resource a "Broadcast Bus" per read/write port is then also created,
14 with access to it managed by a PriorityPicker.
15
16 the brain-dead part of this module is that even though there is no
17 conflict of access, regfile read/write hazards are *not* analysed,
18 and consequently it is safer to wait for the Function Unit to complete
19 before allowing a new instruction to proceed.
20 """
21
22 from nmigen import Elaboratable, Module, Signal
23 from nmigen.cli import rtlil
24
25 from nmutil.picker import PriorityPicker
26 from nmutil.util import treereduce
27
28 from soc.fu.compunits.compunits import AllFunctionUnits
29 from soc.regfile.regfiles import RegFiles
30 from soc.decoder.power_decoder import create_pdecode
31 from soc.decoder.power_decoder2 import PowerDecode2
32 import operator
33
34
35 # helper function for reducing a list of signals down to a parallel
36 # ORed single signal.
37 def ortreereduce(tree, attr="data_o"):
38 return treereduce(tree, operator.or_, lambda x: getattr(x, attr))
39
40
41 class NonProductionCore(Elaboratable):
42 def __init__(self):
43 self.fus = AllFunctionUnits()
44 self.regs = RegFiles()
45 self.pdecode = pdecode = create_pdecode()
46 self.pdecode2 = PowerDecode2(pdecode) # instruction decoder
47 self.ivalid_i = self.pdecode2.e.valid # instruction is valid
48
49 def elaborate(self, platform):
50 m = Module()
51 comb = m.d.comb
52
53 m.submodules.pdecode2 = dec2 = self.pdecode2
54 m.submodules.fus = self.fus
55 self.regs.elaborate_into(m, platform)
56 regs = self.regs
57 fus = self.fus.fus
58
59 # enable-signals for each FU, get one bit for each FU (by name)
60 fu_enable = Signal(len(fus), reset_less=True)
61 fu_bitdict = {}
62 for i, funame in enumerate(fus.keys()):
63 fu_bitdict[funame] = fu_enable[i]
64
65 # connect up instructions
66 for funame, fu in fus.items():
67 fnunit = fu.fnunit.value
68 enable = Signal(name="en_%s" % funame, reset_less=True)
69 comb += enable.eq(self.ivalid_i & (dec2.e.fn_unit & fnunit).bool())
70 with m.If(enable):
71 comb += fu.oper_i.eq_from_execute1(dec2.e)
72 comb += fu_bitdict[funame].eq(enable)
73
74 # dictionary of lists of regfile read ports
75 byregfiles_rd, byregfiles_rdspec = self.get_byregfiles(True)
76
77 # okaay, now we need a PriorityPicker per regfile per regfile port
78 # loootta pickers... peter piper picked a pack of pickled peppers...
79 rdpickers = {}
80 for regfile, spec in byregfiles_rd.items():
81 fuspecs = byregfiles_rdspec[regfile]
82 rdpickers[regfile] = {}
83
84 # for each named regfile port, connect up all FUs to that port
85 for rpidx, (regname, fspec) in enumerate(fuspecs.items()):
86 # get the regfile specs for this regfile port
87 (rf, read, write, wid, fuspec) = fspec
88 name = "rdflag_%s_%s" % (regfile, regname)
89 rdflag = Signal(name=name, reset_less=True)
90 comb += rdflag.eq(rf)
91
92 # "munge" the regfile port index, due to full-port access
93 if regfile in ['XER', 'CA']:
94 if regname.startswith('full'):
95 rpidx = 0 # by convention, first port
96 else:
97 rpidx += 1 # start indexing port 0 from 1
98
99 # select the required read port. these are pre-defined sizes
100 print (regfile, regs.rf.keys())
101 rport = regs.rf[regfile.lower()].r_ports[rpidx]
102
103 # create a priority picker to manage this port
104 rdpickers[regfile][rpidx] = rdpick = PriorityPicker(len(fuspec))
105 setattr(m.submodules, "rdpick_%s_%d" % (regfile, rpidx), rdpick)
106
107 # connect the regspec "reg select" number to this port
108 with m.If(rdpick.en_o):
109 comb += rport.ren.eq(read)
110
111 # connect up the FU req/go signals, and the reg-read to the FU
112 # and create a Read Broadcast Bus
113 for pi, (funame, fu, idx) in enumerate(fuspec):
114 src = fu.src_i[idx]
115
116 # connect request-read to picker input, and output to go-rd
117 fu_active = fu_bitdict[funame]
118 pick = fu.rd_rel_o[idx] & fu_active & rdflag
119 comb += rdpick.i[pi].eq(pick)
120 comb += fu.go_rd_i[idx].eq(rdpick.o[pi])
121
122 # connect regfile port to input, creating a Broadcast Bus
123 print ("reg connect widths",
124 regfile, regname, pi, funame,
125 src.shape(), rport.data_o.shape())
126 comb += src.eq(rport.data_o) # all FUs connect to same port
127
128 # dictionary of lists of regfile write ports
129 byregfiles_wr, byregfiles_wrspec = self.get_byregfiles(False)
130
131 # same for write ports.
132 # BLECH! complex code-duplication! BLECH!
133 wrpickers = {}
134 for regfile, spec in byregfiles_wr.items():
135 fuspecs = byregfiles_wrspec[regfile]
136 wrpickers[regfile] = {}
137 for rpidx, (regname, fspec) in enumerate(fuspecs.items()):
138 # get the regfile specs for this regfile port
139 (rf, read, write, wid, fuspec) = fspec
140
141 # "munge" the regfile port index, due to full-port access
142 if regfile in ['XER', 'CA']:
143 if regname.startswith('full'):
144 rpidx = 0 # by convention, first port
145 else:
146 rpidx += 1 # start indexing port 0 from 1
147
148 # select the required write port. these are pre-defined sizes
149 print (regfile, regs.rf.keys())
150 wport = regs.rf[regfile.lower()].w_ports[rpidx]
151
152 # create a priority picker to manage this port
153 wrpickers[regfile][rpidx] = wrpick = PriorityPicker(len(fuspec))
154 setattr(m.submodules, "wrpick_%s_%d" % (regfile, rpidx), wrpick)
155
156 # connect the regspec write "reg select" number to this port
157 # only if one FU actually requests (and is granted) the port
158 # will the write-enable be activated
159 with m.If(wrpick.en_o):
160 comb += wport.wen.eq(write)
161
162 # connect up the FU req/go signals and the reg-read to the FU
163 # these are arbitrated by Data.ok signals
164 wsigs = []
165 for pi, (funame, fu, idx) in enumerate(fuspec):
166 # write-request comes from dest.ok
167 dest = fu.get_out(idx)
168 name = "wrflag_%s_%s_%d" % (funame, regname, idx)
169 wrflag = Signal(name=name, reset_less=True)
170 comb += wrflag.eq(dest.ok)
171
172 # connect request-read to picker input, and output to go-wr
173 fu_active = fu_bitdict[funame]
174 pick = fu.wr.rel[idx] & fu_active & wrflag
175 comb += wrpick.i[pi].eq(pick)
176 comb += fu.go_wr_i[idx].eq(wrpick.o[pi])
177 # connect regfile port to input
178 print ("reg connect widths",
179 regfile, regname, pi, funame,
180 dest.shape(), wport.data_i.shape())
181 wsigs.append(dest)
182
183 # here is where we create the Write Broadcast Bus. simple, eh?
184 comb += wport.data_i.eq(ortreereduce(wsigs, "data"))
185
186 return m
187
188 def get_byregfiles(self, readmode):
189
190 mode = "read" if readmode else "write"
191 dec2 = self.pdecode2
192 regs = self.regs
193 fus = self.fus.fus
194
195 # dictionary of lists of regfile ports
196 byregfiles = {}
197 byregfiles_spec = {}
198 for (funame, fu) in fus.items():
199 print ("%s ports for %s" % (mode, funame))
200 for idx in range(fu.n_src if readmode else fu.n_dst):
201 if readmode:
202 (regfile, regname, wid) = fu.get_in_spec(idx)
203 else:
204 (regfile, regname, wid) = fu.get_out_spec(idx)
205 print (" %d %s %s %s" % (idx, regfile, regname, str(wid)))
206 rdflag, read, write = dec2.regspecmap(regfile, regname)
207 if regfile not in byregfiles:
208 byregfiles[regfile] = {}
209 byregfiles_spec[regfile] = {}
210 if regname not in byregfiles_spec[regfile]:
211 byregfiles_spec[regfile][regname] = \
212 [rdflag, read, write, wid, []]
213 # here we start to create "lanes"
214 if idx not in byregfiles[regfile]:
215 byregfiles[regfile][idx] = []
216 fuspec = (funame, fu, idx)
217 byregfiles[regfile][idx].append(fuspec)
218 byregfiles_spec[regfile][regname][4].append(fuspec)
219
220 # ok just print that out, for convenience
221 for regfile, spec in byregfiles.items():
222 print ("regfile %s ports:" % mode, regfile)
223 fuspecs = byregfiles_spec[regfile]
224 for regname, fspec in fuspecs.items():
225 [rdflag, read, write, wid, fuspec] = fspec
226 print (" rf %s port %s lane: %s" % (mode, regfile, regname))
227 print (" %s" % regname, wid, read, write, rdflag)
228 for (funame, fu, idx) in fuspec:
229 fusig = fu.src_i[idx] if readmode else fu.dest[idx]
230 print (" ", funame, fu, idx, fusig)
231 print ()
232
233 return byregfiles, byregfiles_spec
234
235 def __iter__(self):
236 yield from self.fus.ports()
237 yield from self.pdecode2.ports()
238 # TODO: regs
239
240 def ports(self):
241 return list(self)
242
243
244 if __name__ == '__main__':
245 dut = NonProductionCore()
246 vl = rtlil.convert(dut, ports=dut.ports())
247 with open("non_production_core.il", "w") as f:
248 f.write(vl)