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
32 from soc
.decoder
.decode2execute1
import Data
33 from soc
.experiment
.l0_cache
import TstL0CacheBuffer
# test only
37 # helper function for reducing a list of signals down to a parallel
39 def ortreereduce(tree
, attr
="data_o"):
40 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
43 # helper function to place full regs declarations first
44 def sort_fuspecs(fuspecs
):
46 for (regname
, fspec
) in fuspecs
.items():
47 if regname
.startswith("full"):
48 res
.append((regname
, fspec
))
49 for (regname
, fspec
) in fuspecs
.items():
50 if not regname
.startswith("full"):
51 res
.append((regname
, fspec
))
52 return res
# enumerate(res)
55 class NonProductionCore(Elaboratable
):
56 def __init__(self
, addrwid
=6, idepth
=16):
57 # single LD/ST funnel for memory access
58 self
.l0
= TstL0CacheBuffer(n_units
=1, regwid
=64, addrwid
=addrwid
)
59 pi
= self
.l0
.l0
.dports
[0]
61 # function units (only one each)
62 self
.fus
= AllFunctionUnits(pilist
=[pi
], addrwid
=addrwid
)
64 # register files (yes plural)
65 self
.regs
= RegFiles()
68 pdecode
= create_pdecode()
69 self
.pdecode2
= PowerDecode2(pdecode
) # instruction decoder
71 # issue/valid/busy signalling
72 self
.ivalid_i
= self
.pdecode2
.e
.valid
# instruction is valid
73 self
.issue_i
= Signal(reset_less
=True)
74 self
.busy_o
= Signal(name
="corebusy_o", reset_less
=True)
77 self
.bigendian_i
= self
.pdecode2
.dec
.bigendian
78 self
.raw_opcode_i
= self
.pdecode2
.dec
.raw_opcode_in
80 def elaborate(self
, platform
):
83 m
.submodules
.pdecode2
= dec2
= self
.pdecode2
84 m
.submodules
.fus
= self
.fus
85 m
.submodules
.l0
= l0
= self
.l0
86 self
.regs
.elaborate_into(m
, platform
)
90 fu_bitdict
= self
.connect_instruction(m
)
91 self
.connect_rdports(m
, fu_bitdict
)
92 self
.connect_wrports(m
, fu_bitdict
)
96 def connect_instruction(self
, m
):
97 comb
, sync
= m
.d
.comb
, m
.d
.sync
101 # enable-signals for each FU, get one bit for each FU (by name)
102 fu_enable
= Signal(len(fus
), reset_less
=True)
104 for i
, funame
in enumerate(fus
.keys()):
105 fu_bitdict
[funame
] = fu_enable
[i
]
107 # connect up instructions. only one is enabled at any given time
108 for funame
, fu
in fus
.items():
109 fnunit
= fu
.fnunit
.value
110 enable
= Signal(name
="en_%s" % funame
, reset_less
=True)
111 comb
+= enable
.eq(self
.ivalid_i
& (dec2
.e
.fn_unit
& fnunit
).bool())
113 comb
+= fu
.oper_i
.eq_from_execute1(dec2
.e
)
114 comb
+= fu
.issue_i
.eq(self
.issue_i
)
115 comb
+= self
.busy_o
.eq(fu
.busy_o
)
116 rdmask
= dec2
.rdflags(fu
)
117 comb
+= fu
.rdmaskn
.eq(~rdmask
)
118 comb
+= fu_bitdict
[funame
].eq(enable
)
122 def connect_rdports(self
, m
, fu_bitdict
):
123 """connect read ports
125 orders the read regspecs into a dict-of-dicts, by regfile, by
126 regport name, then connects all FUs that want that regport by
127 way of a PriorityPicker.
129 comb
, sync
= m
.d
.comb
, m
.d
.sync
133 # dictionary of lists of regfile read ports
134 byregfiles_rd
, byregfiles_rdspec
= self
.get_byregfiles(True)
136 # okaay, now we need a PriorityPicker per regfile per regfile port
137 # loootta pickers... peter piper picked a pack of pickled peppers...
139 for regfile
, spec
in byregfiles_rd
.items():
140 fuspecs
= byregfiles_rdspec
[regfile
]
141 rdpickers
[regfile
] = {}
143 # for each named regfile port, connect up all FUs to that port
144 for (regname
, fspec
) in sort_fuspecs(fuspecs
):
145 print ("connect rd", regname
, fspec
)
147 # get the regfile specs for this regfile port
148 (rf
, read
, write
, wid
, fuspec
) = fspec
149 name
= "rdflag_%s_%s" % (regfile
, regname
)
150 rdflag
= Signal(name
=name
, reset_less
=True)
151 comb
+= rdflag
.eq(rf
)
153 # select the required read port. these are pre-defined sizes
154 print (rpidx
, regfile
, regs
.rf
.keys())
155 rport
= regs
.rf
[regfile
.lower()].r_ports
[rpidx
]
157 # create a priority picker to manage this port
158 rdpickers
[regfile
][rpidx
] = rdpick
= PriorityPicker(len(fuspec
))
159 setattr(m
.submodules
, "rdpick_%s_%s" % (regfile
, rpidx
), rdpick
)
161 # connect the regspec "reg select" number to this port
162 with m
.If(rdpick
.en_o
):
163 comb
+= rport
.ren
.eq(read
)
165 # connect up the FU req/go signals, and the reg-read to the FU
166 # and create a Read Broadcast Bus
167 for pi
, (funame
, fu
, idx
) in enumerate(fuspec
):
170 # connect request-read to picker input, and output to go-rd
171 fu_active
= fu_bitdict
[funame
]
172 pick
= fu
.rd_rel_o
[idx
] & fu_active
& rdflag
173 comb
+= rdpick
.i
[pi
].eq(pick
)
174 comb
+= fu
.go_rd_i
[idx
].eq(rdpick
.o
[pi
])
176 # connect regfile port to input, creating a Broadcast Bus
177 print ("reg connect widths",
178 regfile
, regname
, pi
, funame
,
179 src
.shape(), rport
.data_o
.shape())
180 comb
+= src
.eq(rport
.data_o
) # all FUs connect to same port
182 def connect_wrports(self
, m
, fu_bitdict
):
183 """connect write ports
185 orders the write regspecs into a dict-of-dicts, by regfile,
186 by regport name, then connects all FUs that want that regport
187 by way of a PriorityPicker.
189 note that the write-port wen, write-port data, and go_wr_i all need to
190 be on the exact same clock cycle. as there is a combinatorial loop bug
191 at the moment, these all use sync.
193 comb
, sync
= m
.d
.comb
, m
.d
.sync
196 # dictionary of lists of regfile write ports
197 byregfiles_wr
, byregfiles_wrspec
= self
.get_byregfiles(False)
199 # same for write ports.
200 # BLECH! complex code-duplication! BLECH!
202 for regfile
, spec
in byregfiles_wr
.items():
203 fuspecs
= byregfiles_wrspec
[regfile
]
204 wrpickers
[regfile
] = {}
205 for (regname
, fspec
) in sort_fuspecs(fuspecs
):
206 print ("connect wr", regname
, fspec
)
208 # get the regfile specs for this regfile port
209 (rf
, read
, write
, wid
, fuspec
) = fspec
211 # select the required write port. these are pre-defined sizes
212 print (regfile
, regs
.rf
.keys())
213 wport
= regs
.rf
[regfile
.lower()].w_ports
[rpidx
]
215 # create a priority picker to manage this port
216 wrpickers
[regfile
][rpidx
] = wrpick
= PriorityPicker(len(fuspec
))
217 setattr(m
.submodules
, "wrpick_%s_%s" % (regfile
, rpidx
), wrpick
)
219 # connect the regspec write "reg select" number to this port
220 # only if one FU actually requests (and is granted) the port
221 # will the write-enable be activated
222 with m
.If(wrpick
.en_o
):
223 sync
+= wport
.wen
.eq(write
)
225 sync
+= wport
.wen
.eq(0)
227 # connect up the FU req/go signals and the reg-read to the FU
228 # these are arbitrated by Data.ok signals
230 for pi
, (funame
, fu
, idx
) in enumerate(fuspec
):
231 # write-request comes from dest.ok
232 dest
= fu
.get_out(idx
)
233 name
= "wrflag_%s_%s_%d" % (funame
, regname
, idx
)
234 wrflag
= Signal(name
=name
, reset_less
=True)
235 comb
+= wrflag
.eq(dest
.ok
)
237 # connect request-read to picker input, and output to go-wr
238 fu_active
= fu_bitdict
[funame
]
239 pick
= fu
.wr
.rel
[idx
] & fu_active
#& wrflag
240 comb
+= wrpick
.i
[pi
].eq(pick
)
241 sync
+= fu
.go_wr_i
[idx
].eq(wrpick
.o
[pi
] & wrpick
.en_o
)
242 # connect regfile port to input
243 print ("reg connect widths",
244 regfile
, regname
, pi
, funame
,
245 dest
.shape(), wport
.data_i
.shape())
248 # here is where we create the Write Broadcast Bus. simple, eh?
249 sync
+= wport
.data_i
.eq(ortreereduce(wsigs
, "data"))
251 def get_byregfiles(self
, readmode
):
253 mode
= "read" if readmode
else "write"
258 # dictionary of lists of regfile ports
261 for (funame
, fu
) in fus
.items():
262 print ("%s ports for %s" % (mode
, funame
))
263 for idx
in range(fu
.n_src
if readmode
else fu
.n_dst
):
265 (regfile
, regname
, wid
) = fu
.get_in_spec(idx
)
267 (regfile
, regname
, wid
) = fu
.get_out_spec(idx
)
268 print (" %d %s %s %s" % (idx
, regfile
, regname
, str(wid
)))
270 rdflag
, read
= dec2
.regspecmap_read(regfile
, regname
)
273 rdflag
, read
= None, None
274 wrport
, write
= dec2
.regspecmap_write(regfile
, regname
)
275 if regfile
not in byregfiles
:
276 byregfiles
[regfile
] = {}
277 byregfiles_spec
[regfile
] = {}
278 if regname
not in byregfiles_spec
[regfile
]:
279 byregfiles_spec
[regfile
][regname
] = \
280 [rdflag
, read
, write
, wid
, []]
281 # here we start to create "lanes"
282 if idx
not in byregfiles
[regfile
]:
283 byregfiles
[regfile
][idx
] = []
284 fuspec
= (funame
, fu
, idx
)
285 byregfiles
[regfile
][idx
].append(fuspec
)
286 byregfiles_spec
[regfile
][regname
][4].append(fuspec
)
288 # ok just print that out, for convenience
289 for regfile
, spec
in byregfiles
.items():
290 print ("regfile %s ports:" % mode
, regfile
)
291 fuspecs
= byregfiles_spec
[regfile
]
292 for regname
, fspec
in fuspecs
.items():
293 [rdflag
, read
, write
, wid
, fuspec
] = fspec
294 print (" rf %s port %s lane: %s" % (mode
, regfile
, regname
))
295 print (" %s" % regname
, wid
, read
, write
, rdflag
)
296 for (funame
, fu
, idx
) in fuspec
:
297 fusig
= fu
.src_i
[idx
] if readmode
else fu
.dest
[idx
]
298 print (" ", funame
, fu
, idx
, fusig
)
301 return byregfiles
, byregfiles_spec
304 yield from self
.fus
.ports()
305 yield from self
.pdecode2
.ports()
312 if __name__
== '__main__':
313 dut
= NonProductionCore()
314 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
315 with
open("test_core.il", "w") as f
: