add rdmask and issue/busy setting
[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 self.issue_i = Signal(reset_less=True)
49 self.busy_o = Signal(reset_less=True)
50
51 def elaborate(self, platform):
52 m = Module()
53 comb = m.d.comb
54
55 m.submodules.pdecode2 = dec2 = self.pdecode2
56 m.submodules.fus = self.fus
57 self.regs.elaborate_into(m, platform)
58 regs = self.regs
59 fus = self.fus.fus
60
61 # enable-signals for each FU, get one bit for each FU (by name)
62 fu_enable = Signal(len(fus), reset_less=True)
63 fu_bitdict = {}
64 for i, funame in enumerate(fus.keys()):
65 fu_bitdict[funame] = fu_enable[i]
66
67 # connect up instructions
68 for funame, fu in fus.items():
69 fnunit = fu.fnunit.value
70 enable = Signal(name="en_%s" % funame, reset_less=True)
71 comb += enable.eq(self.ivalid_i & (dec2.e.fn_unit & fnunit).bool())
72 with m.If(enable):
73 comb += fu.oper_i.eq_from_execute1(dec2.e)
74 comb += fu.issue_i.eq(self.issue_i)
75 comb += self.busy_o.eq(fu.busy_o)
76 rdmask = dec2.rdflags(fu)
77 comb += fu.rdmaskn.eq(~rdmask)
78 comb += fu_bitdict[funame].eq(enable)
79
80 # dictionary of lists of regfile read ports
81 byregfiles_rd, byregfiles_rdspec = self.get_byregfiles(True)
82
83 # okaay, now we need a PriorityPicker per regfile per regfile port
84 # loootta pickers... peter piper picked a pack of pickled peppers...
85 rdpickers = {}
86 for regfile, spec in byregfiles_rd.items():
87 fuspecs = byregfiles_rdspec[regfile]
88 rdpickers[regfile] = {}
89
90 # for each named regfile port, connect up all FUs to that port
91 for rpidx, (regname, fspec) in enumerate(fuspecs.items()):
92 # get the regfile specs for this regfile port
93 (rf, read, write, wid, fuspec) = fspec
94 name = "rdflag_%s_%s" % (regfile, regname)
95 rdflag = Signal(name=name, reset_less=True)
96 comb += rdflag.eq(rf)
97
98 # "munge" the regfile port index, due to full-port access
99 if regfile in ['XER', 'CA']:
100 if regname.startswith('full'):
101 rpidx = 0 # by convention, first port
102 else:
103 rpidx += 1 # start indexing port 0 from 1
104
105 # select the required read port. these are pre-defined sizes
106 print (regfile, regs.rf.keys())
107 rport = regs.rf[regfile.lower()].r_ports[rpidx]
108
109 # create a priority picker to manage this port
110 rdpickers[regfile][rpidx] = rdpick = PriorityPicker(len(fuspec))
111 setattr(m.submodules, "rdpick_%s_%d" % (regfile, rpidx), rdpick)
112
113 # connect the regspec "reg select" number to this port
114 with m.If(rdpick.en_o):
115 comb += rport.ren.eq(read)
116
117 # connect up the FU req/go signals, and the reg-read to the FU
118 # and create a Read Broadcast Bus
119 for pi, (funame, fu, idx) in enumerate(fuspec):
120 src = fu.src_i[idx]
121
122 # connect request-read to picker input, and output to go-rd
123 fu_active = fu_bitdict[funame]
124 pick = fu.rd_rel_o[idx] & fu_active & rdflag
125 comb += rdpick.i[pi].eq(pick)
126 comb += fu.go_rd_i[idx].eq(rdpick.o[pi])
127
128 # connect regfile port to input, creating a Broadcast Bus
129 print ("reg connect widths",
130 regfile, regname, pi, funame,
131 src.shape(), rport.data_o.shape())
132 comb += src.eq(rport.data_o) # all FUs connect to same port
133
134 # dictionary of lists of regfile write ports
135 byregfiles_wr, byregfiles_wrspec = self.get_byregfiles(False)
136
137 # same for write ports.
138 # BLECH! complex code-duplication! BLECH!
139 wrpickers = {}
140 for regfile, spec in byregfiles_wr.items():
141 fuspecs = byregfiles_wrspec[regfile]
142 wrpickers[regfile] = {}
143 for rpidx, (regname, fspec) in enumerate(fuspecs.items()):
144 # get the regfile specs for this regfile port
145 (rf, read, write, wid, fuspec) = fspec
146
147 # "munge" the regfile port index, due to full-port access
148 if regfile in ['XER', 'CA']:
149 if regname.startswith('full'):
150 rpidx = 0 # by convention, first port
151 else:
152 rpidx += 1 # start indexing port 0 from 1
153
154 # select the required write port. these are pre-defined sizes
155 print (regfile, regs.rf.keys())
156 wport = regs.rf[regfile.lower()].w_ports[rpidx]
157
158 # create a priority picker to manage this port
159 wrpickers[regfile][rpidx] = wrpick = PriorityPicker(len(fuspec))
160 setattr(m.submodules, "wrpick_%s_%d" % (regfile, rpidx), wrpick)
161
162 # connect the regspec write "reg select" number to this port
163 # only if one FU actually requests (and is granted) the port
164 # will the write-enable be activated
165 with m.If(wrpick.en_o):
166 comb += wport.wen.eq(write)
167
168 # connect up the FU req/go signals and the reg-read to the FU
169 # these are arbitrated by Data.ok signals
170 wsigs = []
171 for pi, (funame, fu, idx) in enumerate(fuspec):
172 # write-request comes from dest.ok
173 dest = fu.get_out(idx)
174 name = "wrflag_%s_%s_%d" % (funame, regname, idx)
175 wrflag = Signal(name=name, reset_less=True)
176 comb += wrflag.eq(dest.ok)
177
178 # connect request-read to picker input, and output to go-wr
179 fu_active = fu_bitdict[funame]
180 pick = fu.wr.rel[idx] & fu_active & wrflag
181 comb += wrpick.i[pi].eq(pick)
182 comb += fu.go_wr_i[idx].eq(wrpick.o[pi])
183 # connect regfile port to input
184 print ("reg connect widths",
185 regfile, regname, pi, funame,
186 dest.shape(), wport.data_i.shape())
187 wsigs.append(dest)
188
189 # here is where we create the Write Broadcast Bus. simple, eh?
190 comb += wport.data_i.eq(ortreereduce(wsigs, "data"))
191
192 return m
193
194 def get_byregfiles(self, readmode):
195
196 mode = "read" if readmode else "write"
197 dec2 = self.pdecode2
198 regs = self.regs
199 fus = self.fus.fus
200
201 # dictionary of lists of regfile ports
202 byregfiles = {}
203 byregfiles_spec = {}
204 for (funame, fu) in fus.items():
205 print ("%s ports for %s" % (mode, funame))
206 for idx in range(fu.n_src if readmode else fu.n_dst):
207 if readmode:
208 (regfile, regname, wid) = fu.get_in_spec(idx)
209 else:
210 (regfile, regname, wid) = fu.get_out_spec(idx)
211 print (" %d %s %s %s" % (idx, regfile, regname, str(wid)))
212 rdflag, read, write = dec2.regspecmap(regfile, regname)
213 if regfile not in byregfiles:
214 byregfiles[regfile] = {}
215 byregfiles_spec[regfile] = {}
216 if regname not in byregfiles_spec[regfile]:
217 byregfiles_spec[regfile][regname] = \
218 [rdflag, read, write, wid, []]
219 # here we start to create "lanes"
220 if idx not in byregfiles[regfile]:
221 byregfiles[regfile][idx] = []
222 fuspec = (funame, fu, idx)
223 byregfiles[regfile][idx].append(fuspec)
224 byregfiles_spec[regfile][regname][4].append(fuspec)
225
226 # ok just print that out, for convenience
227 for regfile, spec in byregfiles.items():
228 print ("regfile %s ports:" % mode, regfile)
229 fuspecs = byregfiles_spec[regfile]
230 for regname, fspec in fuspecs.items():
231 [rdflag, read, write, wid, fuspec] = fspec
232 print (" rf %s port %s lane: %s" % (mode, regfile, regname))
233 print (" %s" % regname, wid, read, write, rdflag)
234 for (funame, fu, idx) in fuspec:
235 fusig = fu.src_i[idx] if readmode else fu.dest[idx]
236 print (" ", funame, fu, idx, fusig)
237 print ()
238
239 return byregfiles, byregfiles_spec
240
241 def __iter__(self):
242 yield from self.fus.ports()
243 yield from self.pdecode2.ports()
244 # TODO: regs
245
246 def ports(self):
247 return list(self)
248
249
250 if __name__ == '__main__':
251 dut = NonProductionCore()
252 vl = rtlil.convert(dut, ports=dut.ports())
253 with open("non_production_core.il", "w") as f:
254 f.write(vl)