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