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.
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.
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.
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.
22 from nmigen
import Elaboratable
, Module
, Signal
23 from nmigen
.cli
import rtlil
25 from nmutil
.picker
import PriorityPicker
26 from nmutil
.util
import treereduce
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
35 # helper function for reducing a list of signals down to a parallel
37 def ortreereduce(tree
, attr
="data_o"):
38 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
41 class NonProductionCore(Elaboratable
):
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
49 def elaborate(self
, platform
):
53 m
.submodules
.pdecode2
= dec2
= self
.pdecode2
54 m
.submodules
.fus
= self
.fus
55 self
.regs
.elaborate_into(m
, platform
)
59 # enable-signals for each FU, get one bit for each FU (by name)
60 fu_enable
= Signal(len(fus
), reset_less
=True)
62 for i
, funame
in enumerate(fus
.keys()):
63 fu_bitdict
[funame
] = fu_enable
[i
]
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())
71 comb
+= fu
.oper_i
.eq_from_execute1(dec2
.e
)
72 comb
+= fu_bitdict
[funame
].eq(enable
)
74 # dictionary of lists of regfile read ports
75 byregfiles_rd
, byregfiles_rdspec
= self
.get_byregfiles(True)
77 # okaay, now we need a PriorityPicker per regfile per regfile port
78 # loootta pickers... peter piper picked a pack of pickled peppers...
80 for regfile
, spec
in byregfiles_rd
.items():
81 fuspecs
= byregfiles_rdspec
[regfile
]
82 rdpickers
[regfile
] = {}
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)
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
97 rpidx
+= 1 # start indexing port 0 from 1
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
]
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
)
107 # connect the regspec "reg select" number to this port
108 with m
.If(rdpick
.en_o
):
109 comb
+= rport
.ren
.eq(read
)
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
):
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
])
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
128 # dictionary of lists of regfile write ports
129 byregfiles_wr
, byregfiles_wrspec
= self
.get_byregfiles(False)
131 # same for write ports.
132 # BLECH! complex code-duplication! BLECH!
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
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
146 rpidx
+= 1 # start indexing port 0 from 1
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
]
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
)
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
)
162 # connect up the FU req/go signals and the reg-read to the FU
163 # these are arbitrated by Data.ok signals
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
)
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())
183 # here is where we create the Write Broadcast Bus. simple, eh?
184 comb
+= wport
.data_i
.eq(ortreereduce(wsigs
, "data"))
188 def get_byregfiles(self
, readmode
):
190 mode
= "read" if readmode
else "write"
195 # dictionary of lists of regfile ports
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
):
202 (regfile
, regname
, wid
) = fu
.get_in_spec(idx
)
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
)
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
)
233 return byregfiles
, byregfiles_spec
236 yield from self
.fus
.ports()
237 yield from self
.pdecode2
.ports()
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
: