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
48 self
.issue_i
= Signal(reset_less
=True)
49 self
.busy_o
= Signal(reset_less
=True)
51 def elaborate(self
, platform
):
55 m
.submodules
.pdecode2
= dec2
= self
.pdecode2
56 m
.submodules
.fus
= self
.fus
57 self
.regs
.elaborate_into(m
, platform
)
61 # enable-signals for each FU, get one bit for each FU (by name)
62 fu_enable
= Signal(len(fus
), reset_less
=True)
64 for i
, funame
in enumerate(fus
.keys()):
65 fu_bitdict
[funame
] = fu_enable
[i
]
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())
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
)
80 # dictionary of lists of regfile read ports
81 byregfiles_rd
, byregfiles_rdspec
= self
.get_byregfiles(True)
83 # okaay, now we need a PriorityPicker per regfile per regfile port
84 # loootta pickers... peter piper picked a pack of pickled peppers...
86 for regfile
, spec
in byregfiles_rd
.items():
87 fuspecs
= byregfiles_rdspec
[regfile
]
88 rdpickers
[regfile
] = {}
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)
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
103 rpidx
+= 1 # start indexing port 0 from 1
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
]
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
)
113 # connect the regspec "reg select" number to this port
114 with m
.If(rdpick
.en_o
):
115 comb
+= rport
.ren
.eq(read
)
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
):
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
])
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
134 # dictionary of lists of regfile write ports
135 byregfiles_wr
, byregfiles_wrspec
= self
.get_byregfiles(False)
137 # same for write ports.
138 # BLECH! complex code-duplication! BLECH!
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
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
152 rpidx
+= 1 # start indexing port 0 from 1
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
]
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
)
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
)
168 # connect up the FU req/go signals and the reg-read to the FU
169 # these are arbitrated by Data.ok signals
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
)
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())
189 # here is where we create the Write Broadcast Bus. simple, eh?
190 comb
+= wport
.data_i
.eq(ortreereduce(wsigs
, "data"))
194 def get_byregfiles(self
, readmode
):
196 mode
= "read" if readmode
else "write"
201 # dictionary of lists of regfile ports
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
):
208 (regfile
, regname
, wid
) = fu
.get_in_spec(idx
)
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
)
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
)
239 return byregfiles
, byregfiles_spec
242 yield from self
.fus
.ports()
243 yield from self
.pdecode2
.ports()
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
: