update docstring on simple/core.py
[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
33
34
35 def ortreereduce(tree, attr="data_o"):
36 return treereduce(tree, operator.or_, lambda x: getattr(x, attr))
37
38
39 class NonProductionCore(Elaboratable):
40 def __init__(self):
41 self.fus = AllFunctionUnits()
42 self.regs = RegFiles()
43 self.pdecode = pdecode = create_pdecode()
44 self.pdecode2 = PowerDecode2(pdecode) # instruction decoder
45 self.ivalid_i = self.pdecode2.e.valid # instruction is valid
46
47 def elaborate(self, platform):
48 m = Module()
49 comb = m.d.comb
50
51 m.submodules.pdecode2 = dec2 = self.pdecode2
52 m.submodules.fus = self.fus
53 self.regs.elaborate_into(m, platform)
54 regs = self.regs
55 fus = self.fus.fus
56
57 # enable-signals for each FU, get one bit for each FU (by name)
58 fu_enable = Signal(len(fus), reset_less=True)
59 fu_bitdict = {}
60 for i, funame in enumerate(fus.keys()):
61 fu_bitdict[funame] = fu_enable[i]
62
63 # connect up instructions
64 for funame, fu in fus.items():
65 fnunit = fu.fnunit.value
66 enable = Signal(name="en_%s" % funame, reset_less=True)
67 comb += enable.eq(self.ivalid_i & (dec2.e.fn_unit & fnunit).bool())
68 with m.If(enable):
69 comb += fu.oper_i.eq_from_execute1(dec2.e)
70 comb += fu_bitdict[funame].eq(enable)
71
72 # dictionary of lists of regfile read ports
73 byregfiles_rd, byregfiles_rdspec = self.get_byregfiles(True)
74
75 # okaay, now we need a PriorityPicker per regfile per regfile port
76 # loootta pickers... peter piper picked a pack of pickled peppers...
77 rdpickers = {}
78 for regfile, spec in byregfiles_rd.items():
79 fuspecs = byregfiles_rdspec[regfile]
80 rdpickers[regfile] = {}
81 for rpidx, (regname, fspec) in enumerate(fuspecs.items()):
82 # get the regfile specs for this regfile port
83 (rf, read, write, wid, fuspec) = fspec
84 name = "rdflag_%s_%s" % (regfile, regname)
85 rdflag = Signal(name=name, reset_less=True)
86 comb += rdflag.eq(rf)
87
88 # "munge" the regfile port index, due to full-port access
89 if regfile in ['XER', 'CA']:
90 if regname.startswith('full'):
91 rpidx = 0 # by convention, first port
92 else:
93 rpidx += 1 # start indexing port 0 from 1
94
95 # select the required read port. these are pre-defined sizes
96 print (regfile, regs.rf.keys())
97 rport = regs.rf[regfile.lower()].r_ports[rpidx]
98
99 # create a priority picker to manage this port
100 rdpickers[regfile][rpidx] = rdpick = PriorityPicker(len(fuspec))
101 setattr(m.submodules, "rdpick_%s_%d" % (regfile, rpidx), rdpick)
102
103 # connect the regspec "reg select" number to this port
104 with m.If(rdpick.en_o):
105 comb += rport.ren.eq(read)
106
107 # connect up the FU req/go signals and the reg-read to the FU
108 for pi, (funame, fu, idx) in enumerate(fuspec):
109 # connect request-read to picker input, and output to go-rd
110 fu_active = fu_bitdict[funame]
111 pick = fu.rd_rel_o[idx] & fu_active & rdflag
112 comb += rdpick.i[pi].eq(pick)
113 comb += fu.go_rd_i[idx].eq(rdpick.o[pi])
114 # connect regfile port to input
115 print ("reg connect widths",
116 regfile, regname, pi, funame,
117 fu.src_i[idx].shape(), rport.data_o.shape())
118 comb += fu.src_i[idx].eq(rport.data_o)
119
120 # dictionary of lists of regfile write ports
121 byregfiles_rd, byregfiles_rdspec = self.get_byregfiles(False)
122
123 return m
124
125 def get_byregfiles(self, readmode):
126
127 mode = "read" if readmode else "write"
128 dec2 = self.pdecode2
129 regs = self.regs
130 fus = self.fus.fus
131
132 # dictionary of lists of regfile ports
133 byregfiles = {}
134 byregfiles_spec = {}
135 for (funame, fu) in fus.items():
136 print ("%s ports for %s" % (mode, funame))
137 for idx in range(fu.n_src if readmode else fu.n_dst):
138 if readmode:
139 (regfile, regname, wid) = fu.get_in_spec(idx)
140 else:
141 (regfile, regname, wid) = fu.get_out_spec(idx)
142 print (" %d %s %s %s" % (idx, regfile, regname, str(wid)))
143 rdflag, read, write = dec2.regspecmap(regfile, regname)
144 if regfile not in byregfiles:
145 byregfiles[regfile] = {}
146 byregfiles_spec[regfile] = {}
147 if regname not in byregfiles_spec[regfile]:
148 byregfiles_spec[regfile][regname] = \
149 [rdflag, read, write, wid, []]
150 # here we start to create "lanes"
151 if idx not in byregfiles[regfile]:
152 byregfiles[regfile][idx] = []
153 fuspec = (funame, fu, idx)
154 byregfiles[regfile][idx].append(fuspec)
155 byregfiles_spec[regfile][regname][4].append(fuspec)
156
157 # ok just print that out, for convenience
158 for regfile, spec in byregfiles.items():
159 print ("regfile %s ports:" % mode, regfile)
160 fuspecs = byregfiles_spec[regfile]
161 for regname, fspec in fuspecs.items():
162 [rdflag, read, write, wid, fuspec] = fspec
163 print (" rf %s port %s lane: %s" % (mode, regfile, regname))
164 print (" %s" % regname, wid, read, write, rdflag)
165 for (funame, fu, idx) in fuspec:
166 fusig = fu.src_i[idx] if readmode else fu.dest[idx]
167 print (" ", funame, fu, idx, fusig)
168 print ()
169
170 return byregfiles, byregfiles_spec
171
172 def __iter__(self):
173 yield from self.fus.ports()
174 yield from self.pdecode2.ports()
175 # TODO: regs
176
177 def ports(self):
178 return list(self)
179
180
181 if __name__ == '__main__':
182 dut = NonProductionCore()
183 vl = rtlil.convert(dut, ports=dut.ports())
184 with open("non_production_core.il", "w") as f:
185 f.write(vl)