split out core input/output into separate file core_data.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, ResetSignal, Cat, Mux
23 from nmigen.cli import rtlil
24
25 from openpower.decoder.power_decoder2 import PowerDecodeSubset
26 from openpower.decoder.power_regspec_map import regspec_decode_read
27 from openpower.decoder.power_regspec_map import regspec_decode_write
28 from openpower.sv.svp64 import SVP64Rec
29
30 from nmutil.picker import PriorityPicker
31 from nmutil.util import treereduce
32 from nmutil.singlepipe import ControlBase
33
34 from soc.fu.compunits.compunits import AllFunctionUnits
35 from soc.regfile.regfiles import RegFiles
36 from openpower.decoder.power_decoder2 import get_rdflags
37 from soc.experiment.l0_cache import TstL0CacheBuffer # test only
38 from soc.config.test.test_loadstore import TestMemPspec
39 from openpower.decoder.power_enums import MicrOp, Function
40 from soc.simple.core_data import CoreInput, CoreOutput
41
42 from collections import defaultdict
43 import operator
44
45 from nmutil.util import rising_edge
46
47
48 # helper function for reducing a list of signals down to a parallel
49 # ORed single signal.
50 def ortreereduce(tree, attr="o_data"):
51 return treereduce(tree, operator.or_, lambda x: getattr(x, attr))
52
53
54 def ortreereduce_sig(tree):
55 return treereduce(tree, operator.or_, lambda x: x)
56
57
58 # helper function to place full regs declarations first
59 def sort_fuspecs(fuspecs):
60 res = []
61 for (regname, fspec) in fuspecs.items():
62 if regname.startswith("full"):
63 res.append((regname, fspec))
64 for (regname, fspec) in fuspecs.items():
65 if not regname.startswith("full"):
66 res.append((regname, fspec))
67 return res # enumerate(res)
68
69
70 # derive from ControlBase rather than have a separate Stage instance,
71 # this is simpler to do
72 class NonProductionCore(ControlBase):
73 def __init__(self, pspec):
74 self.pspec = pspec
75
76 # test is SVP64 is to be enabled
77 self.svp64_en = hasattr(pspec, "svp64") and (pspec.svp64 == True)
78
79 # test to see if regfile ports should be reduced
80 self.regreduce_en = (hasattr(pspec, "regreduce") and
81 (pspec.regreduce == True))
82
83 # test core type
84 self.core_type = "fsm"
85 if hasattr(pspec, "core_type"):
86 self.core_type = pspec.core_type
87
88 super().__init__(stage=self)
89
90 # single LD/ST funnel for memory access
91 self.l0 = l0 = TstL0CacheBuffer(pspec, n_units=1)
92 pi = l0.l0.dports[0]
93
94 # function units (only one each)
95 # only include mmu if enabled in pspec
96 self.fus = AllFunctionUnits(pspec, pilist=[pi])
97
98 # link LoadStore1 into MMU
99 mmu = self.fus.get_fu('mmu0')
100 print ("core pspec", pspec.ldst_ifacetype)
101 print ("core mmu", mmu)
102 print ("core lsmem.lsi", l0.cmpi.lsmem.lsi)
103 if mmu is not None:
104 mmu.alu.set_ldst_interface(l0.cmpi.lsmem.lsi)
105
106 # register files (yes plural)
107 self.regs = RegFiles(pspec, make_hazard_vecs=True)
108
109 # set up input and output: unusual requirement to set data directly
110 # (due to the way that the core is set up in a different domain,
111 # see TestIssuer.setup_peripherals
112 self.i, self.o = self.new_specs(None)
113 self.i, self.o = self.p.i_data, self.n.o_data
114
115 # create per-FU instruction decoders (subsetted)
116 self.decoders = {}
117 self.des = {}
118
119 for funame, fu in self.fus.fus.items():
120 f_name = fu.fnunit.name
121 fnunit = fu.fnunit.value
122 opkls = fu.opsubsetkls
123 if f_name == 'TRAP':
124 # TRAP decoder is the *main* decoder
125 self.trapunit = funame
126 continue
127 self.decoders[funame] = PowerDecodeSubset(None, opkls, f_name,
128 final=True,
129 state=self.i.state,
130 svp64_en=self.svp64_en,
131 regreduce_en=self.regreduce_en)
132 self.des[funame] = self.decoders[funame].do
133
134 if "mmu0" in self.decoders:
135 self.decoders["mmu0"].mmu0_spr_dec = self.decoders["spr0"]
136
137 def setup(self, m, i):
138 pass
139
140 def ispec(self):
141 return CoreInput(self.pspec, self.svp64_en, self.regreduce_en)
142
143 def ospec(self):
144 return CoreOutput()
145
146 def elaborate(self, platform):
147 m = super().elaborate(platform)
148
149 # for testing purposes, to cut down on build time in coriolis2
150 if hasattr(self.pspec, "nocore") and self.pspec.nocore == True:
151 x = Signal() # dummy signal
152 m.d.sync += x.eq(~x)
153 return m
154 comb = m.d.comb
155
156 m.submodules.fus = self.fus
157 m.submodules.l0 = l0 = self.l0
158 self.regs.elaborate_into(m, platform)
159 regs = self.regs
160 fus = self.fus.fus
161
162 # connect decoders
163 self.connect_satellite_decoders(m)
164
165 # ssh, cheat: trap uses the main decoder because of the rewriting
166 self.des[self.trapunit] = self.i.e.do
167
168 # connect up Function Units, then read/write ports
169 fu_bitdict, fu_selected = self.connect_instruction(m)
170 self.connect_rdports(m, fu_selected)
171 self.connect_wrports(m, fu_selected)
172
173 # note if an exception happened. in a pipelined or OoO design
174 # this needs to be accompanied by "shadowing" (or stalling)
175 el = []
176 for exc in self.fus.excs.values():
177 el.append(exc.happened)
178 if len(el) > 0: # at least one exception
179 comb += self.o.exc_happened.eq(Cat(*el).bool())
180
181 return m
182
183 def connect_satellite_decoders(self, m):
184 comb = m.d.comb
185 for k, v in self.decoders.items():
186 # connect each satellite decoder and give it the instruction.
187 # as subset decoders this massively reduces wire fanout given
188 # the large number of ALUs
189 setattr(m.submodules, "dec_%s" % v.fn_name, v)
190 comb += v.dec.raw_opcode_in.eq(self.i.raw_insn_i)
191 comb += v.dec.bigendian.eq(self.i.bigendian_i)
192 # sigh due to SVP64 RA_OR_ZERO detection connect these too
193 comb += v.sv_a_nz.eq(self.i.sv_a_nz)
194 if self.svp64_en:
195 comb += v.pred_sm.eq(self.i.sv_pred_sm)
196 comb += v.pred_dm.eq(self.i.sv_pred_dm)
197 if k != self.trapunit:
198 comb += v.sv_rm.eq(self.i.sv_rm) # pass through SVP64 ReMap
199 comb += v.is_svp64_mode.eq(self.i.is_svp64_mode)
200 # only the LDST PowerDecodeSubset *actually* needs to
201 # know to use the alternative decoder. this is all
202 # a terrible hack
203 if k.lower().startswith("ldst"):
204 comb += v.use_svp64_ldst_dec.eq(
205 self.i.use_svp64_ldst_dec)
206
207 def connect_instruction(self, m):
208 """connect_instruction
209
210 uses decoded (from PowerOp) function unit information from CSV files
211 to ascertain which Function Unit should deal with the current
212 instruction.
213
214 some (such as OP_ATTN, OP_NOP) are dealt with here, including
215 ignoring it and halting the processor. OP_NOP is a bit annoying
216 because the issuer expects busy flag still to be raised then lowered.
217 (this requires a fake counter to be set).
218 """
219 comb, sync = m.d.comb, m.d.sync
220 fus = self.fus.fus
221
222 # indicate if core is busy
223 busy_o = self.o.busy_o
224
225 # enable/busy-signals for each FU, get one bit for each FU (by name)
226 fu_enable = Signal(len(fus), reset_less=True)
227 fu_busy = Signal(len(fus), reset_less=True)
228 fu_bitdict = {}
229 fu_selected = {}
230 for i, funame in enumerate(fus.keys()):
231 fu_bitdict[funame] = fu_enable[i]
232 fu_selected[funame] = fu_busy[i]
233
234 # identify function units and create a list by fnunit so that
235 # PriorityPickers can be created for selecting one of them that
236 # isn't busy at the time the incoming instruction needs passing on
237 by_fnunit = defaultdict(list)
238 for fname, member in Function.__members__.items():
239 for funame, fu in fus.items():
240 fnunit = fu.fnunit.value
241 if member.value & fnunit: # this FU handles this type of op
242 by_fnunit[fname].append((funame, fu)) # add by Function
243
244 # ok now just print out the list of FUs by Function, because we can
245 for fname, fu_list in by_fnunit.items():
246 print ("FUs by type", fname, fu_list)
247
248 # now create a PriorityPicker per FU-type such that only one
249 # non-busy FU will be picked
250 issue_pps = {}
251 fu_found = Signal() # take a note if no Function Unit was available
252 for fname, fu_list in by_fnunit.items():
253 i_pp = PriorityPicker(len(fu_list))
254 m.submodules['i_pp_%s' % fname] = i_pp
255 i_l = []
256 for i, (funame, fu) in enumerate(fu_list):
257 # match the decoded instruction (e.do.fn_unit) against the
258 # "capability" of this FU, gate that by whether that FU is
259 # busy, and drop that into the PriorityPicker.
260 # this will give us an output of the first available *non-busy*
261 # Function Unit (Reservation Statio) capable of handling this
262 # instruction.
263 fnunit = fu.fnunit.value
264 en_req = Signal(name="issue_en_%s" % funame, reset_less=True)
265 fnmatch = (self.i.e.do.fn_unit & fnunit).bool()
266 comb += en_req.eq(fnmatch & ~fu.busy_o & self.p.i_valid)
267 i_l.append(en_req) # store in list for doing the Cat-trick
268 # picker output, gated by enable: store in fu_bitdict
269 po = Signal(name="o_issue_pick_"+funame) # picker output
270 comb += po.eq(i_pp.o[i] & i_pp.en_o)
271 comb += fu_bitdict[funame].eq(po)
272 comb += fu_selected[funame].eq(fu.busy_o | po)
273 # if we don't do this, then when there are no FUs available,
274 # the "p.o_ready" signal will go back "ok we accepted this
275 # instruction" which of course isn't true.
276 comb += fu_found.eq(~fnmatch | i_pp.en_o)
277 # for each input, Cat them together and drop them into the picker
278 comb += i_pp.i.eq(Cat(*i_l))
279
280 # sigh - need a NOP counter
281 counter = Signal(2)
282 with m.If(counter != 0):
283 sync += counter.eq(counter - 1)
284 comb += busy_o.eq(1)
285
286 with m.If(self.p.i_valid): # run only when valid
287 with m.Switch(self.i.e.do.insn_type):
288 # check for ATTN: halt if true
289 with m.Case(MicrOp.OP_ATTN):
290 m.d.sync += self.o.core_terminate_o.eq(1)
291
292 # fake NOP - this isn't really used (Issuer detects NOP)
293 with m.Case(MicrOp.OP_NOP):
294 sync += counter.eq(2)
295 comb += busy_o.eq(1)
296
297 with m.Default():
298 # connect up instructions. only one enabled at a time
299 for funame, fu in fus.items():
300 do = self.des[funame]
301 enable = fu_bitdict[funame]
302
303 # run this FunctionUnit if enabled
304 # route op, issue, busy, read flags and mask to FU
305 with m.If(enable):
306 # operand comes from the *local* decoder
307 comb += fu.oper_i.eq_from(do)
308 comb += fu.issue_i.eq(1) # issue when input valid
309 # rdmask, which is for registers, needs to come
310 # from the *main* decoder
311 rdmask = get_rdflags(self.i.e, fu)
312 comb += fu.rdmaskn.eq(~rdmask)
313
314 # if instruction is busy, set busy output for core.
315 busys = map(lambda fu: fu.busy_o, fus.values())
316 comb += busy_o.eq(Cat(*busys).bool())
317
318 # ready/valid signalling. if busy, means refuse incoming issue.
319 # (this is a global signal, TODO, change to one which allows
320 # overlapping instructions)
321 # also, if there was no fu found we must not send back a valid
322 # indicator. BUT, of course, when there is no instruction
323 # we must ignore the fu_found flag, otherwise o_ready will never
324 # be set when everything is idle
325 comb += self.p.o_ready.eq(fu_found | ~self.p.i_valid)
326
327 # return both the function unit "enable" dict as well as the "busy".
328 # the "busy-or-issued" can be passed in to the Read/Write port
329 # connecters to give them permission to request access to regfiles
330 return fu_bitdict, fu_selected
331
332 def connect_rdport(self, m, fu_bitdict, rdpickers, regfile, regname, fspec):
333 comb, sync = m.d.comb, m.d.sync
334 fus = self.fus.fus
335 regs = self.regs
336
337 rpidx = regname
338
339 # select the required read port. these are pre-defined sizes
340 rfile = regs.rf[regfile.lower()]
341 rport = rfile.r_ports[rpidx]
342 print("read regfile", rpidx, regfile, regs.rf.keys(),
343 rfile, rfile.unary)
344
345 fspecs = fspec
346 if not isinstance(fspecs, list):
347 fspecs = [fspecs]
348
349 rdflags = []
350 pplen = 0
351 reads = []
352 ppoffs = []
353 for i, fspec in enumerate(fspecs):
354 # get the regfile specs for this regfile port
355 (rf, read, write, wid, fuspec) = fspec
356 print ("fpsec", i, fspec, len(fuspec))
357 ppoffs.append(pplen) # record offset for picker
358 pplen += len(fuspec)
359 name = "rdflag_%s_%s_%d" % (regfile, regname, i)
360 rdflag = Signal(name=name, reset_less=True)
361 comb += rdflag.eq(rf)
362 rdflags.append(rdflag)
363 reads.append(read)
364
365 print ("pplen", pplen)
366
367 # create a priority picker to manage this port
368 rdpickers[regfile][rpidx] = rdpick = PriorityPicker(pplen)
369 setattr(m.submodules, "rdpick_%s_%s" % (regfile, rpidx), rdpick)
370
371 rens = []
372 addrs = []
373 for i, fspec in enumerate(fspecs):
374 (rf, read, write, wid, fuspec) = fspec
375 # connect up the FU req/go signals, and the reg-read to the FU
376 # and create a Read Broadcast Bus
377 for pi, (funame, fu, idx) in enumerate(fuspec):
378 pi += ppoffs[i]
379
380 # connect request-read to picker input, and output to go-rd
381 fu_active = fu_bitdict[funame]
382 name = "%s_%s_%s_%i" % (regfile, rpidx, funame, pi)
383 addr_en = Signal.like(reads[i], name="addr_en_"+name)
384 pick = Signal(name="pick_"+name) # picker input
385 rp = Signal(name="rp_"+name) # picker output
386 delay_pick = Signal(name="dp_"+name) # read-enable "underway"
387
388 # exclude any currently-enabled read-request (mask out active)
389 comb += pick.eq(fu.rd_rel_o[idx] & fu_active & rdflags[i] &
390 ~delay_pick)
391 comb += rdpick.i[pi].eq(pick)
392 comb += fu.go_rd_i[idx].eq(delay_pick) # pass in *delayed* pick
393
394 # if picked, select read-port "reg select" number to port
395 comb += rp.eq(rdpick.o[pi] & rdpick.en_o)
396 sync += delay_pick.eq(rp) # delayed "pick"
397 comb += addr_en.eq(Mux(rp, reads[i], 0))
398
399 # the read-enable happens combinatorially (see mux-bus below)
400 # but it results in the data coming out on a one-cycle delay.
401 if rfile.unary:
402 rens.append(addr_en)
403 else:
404 addrs.append(addr_en)
405 rens.append(rp)
406
407 # use the *delayed* pick signal to put requested data onto bus
408 with m.If(delay_pick):
409 # connect regfile port to input, creating fan-out Bus
410 src = fu.src_i[idx]
411 print("reg connect widths",
412 regfile, regname, pi, funame,
413 src.shape(), rport.o_data.shape())
414 # all FUs connect to same port
415 comb += src.eq(rport.o_data)
416
417 # or-reduce the muxed read signals
418 if rfile.unary:
419 # for unary-addressed
420 comb += rport.ren.eq(ortreereduce_sig(rens))
421 else:
422 # for binary-addressed
423 comb += rport.addr.eq(ortreereduce_sig(addrs))
424 comb += rport.ren.eq(Cat(*rens).bool())
425 print ("binary", regfile, rpidx, rport, rport.ren, rens, addrs)
426
427 def connect_rdports(self, m, fu_bitdict):
428 """connect read ports
429
430 orders the read regspecs into a dict-of-dicts, by regfile, by
431 regport name, then connects all FUs that want that regport by
432 way of a PriorityPicker.
433 """
434 comb, sync = m.d.comb, m.d.sync
435 fus = self.fus.fus
436 regs = self.regs
437
438 # dictionary of lists of regfile read ports
439 byregfiles_rd, byregfiles_rdspec = self.get_byregfiles(True)
440
441 # okaay, now we need a PriorityPicker per regfile per regfile port
442 # loootta pickers... peter piper picked a pack of pickled peppers...
443 rdpickers = {}
444 for regfile, spec in byregfiles_rd.items():
445 fuspecs = byregfiles_rdspec[regfile]
446 rdpickers[regfile] = {}
447
448 # argh. an experiment to merge RA and RB in the INT regfile
449 # (we have too many read/write ports)
450 if self.regreduce_en:
451 if regfile == 'INT':
452 fuspecs['rabc'] = [fuspecs.pop('rb')]
453 fuspecs['rabc'].append(fuspecs.pop('rc'))
454 fuspecs['rabc'].append(fuspecs.pop('ra'))
455 if regfile == 'FAST':
456 fuspecs['fast1'] = [fuspecs.pop('fast1')]
457 if 'fast2' in fuspecs:
458 fuspecs['fast1'].append(fuspecs.pop('fast2'))
459 if 'fast3' in fuspecs:
460 fuspecs['fast1'].append(fuspecs.pop('fast3'))
461
462 # for each named regfile port, connect up all FUs to that port
463 for (regname, fspec) in sort_fuspecs(fuspecs):
464 print("connect rd", regname, fspec)
465 self.connect_rdport(m, fu_bitdict, rdpickers, regfile,
466 regname, fspec)
467
468 def connect_wrport(self, m, fu_bitdict, wrpickers, regfile, regname, fspec):
469 comb, sync = m.d.comb, m.d.sync
470 fus = self.fus.fus
471 regs = self.regs
472
473 print("connect wr", regname, fspec)
474 rpidx = regname
475
476 # select the required write port. these are pre-defined sizes
477 print(regfile, regs.rf.keys())
478 rfile = regs.rf[regfile.lower()]
479 wport = rfile.w_ports[rpidx]
480
481 fspecs = fspec
482 if not isinstance(fspecs, list):
483 fspecs = [fspecs]
484
485 pplen = 0
486 writes = []
487 ppoffs = []
488 for i, fspec in enumerate(fspecs):
489 # get the regfile specs for this regfile port
490 (rf, read, write, wid, fuspec) = fspec
491 print ("fpsec", i, fspec, len(fuspec))
492 ppoffs.append(pplen) # record offset for picker
493 pplen += len(fuspec)
494
495 # create a priority picker to manage this port
496 wrpickers[regfile][rpidx] = wrpick = PriorityPicker(pplen)
497 setattr(m.submodules, "wrpick_%s_%s" % (regfile, rpidx), wrpick)
498
499 wsigs = []
500 wens = []
501 addrs = []
502 for i, fspec in enumerate(fspecs):
503 # connect up the FU req/go signals and the reg-read to the FU
504 # these are arbitrated by Data.ok signals
505 (rf, read, write, wid, fuspec) = fspec
506 for pi, (funame, fu, idx) in enumerate(fuspec):
507 pi += ppoffs[i]
508
509 # write-request comes from dest.ok
510 dest = fu.get_out(idx)
511 fu_dest_latch = fu.get_fu_out(idx) # latched output
512 name = "wrflag_%s_%s_%d" % (funame, regname, idx)
513 wrflag = Signal(name=name, reset_less=True)
514 comb += wrflag.eq(dest.ok & fu.busy_o)
515
516 # connect request-write to picker input, and output to go-wr
517 fu_active = fu_bitdict[funame]
518 pick = fu.wr.rel_o[idx] & fu_active # & wrflag
519 comb += wrpick.i[pi].eq(pick)
520 # create a single-pulse go write from the picker output
521 wr_pick = Signal(name="wpick_%s_%s_%d" % (funame, regname, idx))
522 comb += wr_pick.eq(wrpick.o[pi] & wrpick.en_o)
523 comb += fu.go_wr_i[idx].eq(rising_edge(m, wr_pick))
524
525 # connect the regspec write "reg select" number to this port
526 # only if one FU actually requests (and is granted) the port
527 # will the write-enable be activated
528 addr_en = Signal.like(write)
529 wp = Signal()
530 comb += wp.eq(wr_pick & wrpick.en_o)
531 comb += addr_en.eq(Mux(wp, write, 0))
532 if rfile.unary:
533 wens.append(addr_en)
534 else:
535 addrs.append(addr_en)
536 wens.append(wp)
537
538 # connect regfile port to input
539 print("reg connect widths",
540 regfile, regname, pi, funame,
541 dest.shape(), wport.i_data.shape())
542 wsigs.append(fu_dest_latch)
543
544 # here is where we create the Write Broadcast Bus. simple, eh?
545 comb += wport.i_data.eq(ortreereduce_sig(wsigs))
546 if rfile.unary:
547 # for unary-addressed
548 comb += wport.wen.eq(ortreereduce_sig(wens))
549 else:
550 # for binary-addressed
551 comb += wport.addr.eq(ortreereduce_sig(addrs))
552 comb += wport.wen.eq(ortreereduce_sig(wens))
553
554 def connect_wrports(self, m, fu_bitdict):
555 """connect write ports
556
557 orders the write regspecs into a dict-of-dicts, by regfile,
558 by regport name, then connects all FUs that want that regport
559 by way of a PriorityPicker.
560
561 note that the write-port wen, write-port data, and go_wr_i all need to
562 be on the exact same clock cycle. as there is a combinatorial loop bug
563 at the moment, these all use sync.
564 """
565 comb, sync = m.d.comb, m.d.sync
566 fus = self.fus.fus
567 regs = self.regs
568 # dictionary of lists of regfile write ports
569 byregfiles_wr, byregfiles_wrspec = self.get_byregfiles(False)
570
571 # same for write ports.
572 # BLECH! complex code-duplication! BLECH!
573 wrpickers = {}
574 for regfile, spec in byregfiles_wr.items():
575 fuspecs = byregfiles_wrspec[regfile]
576 wrpickers[regfile] = {}
577
578 if self.regreduce_en:
579 # argh, more port-merging
580 if regfile == 'INT':
581 fuspecs['o'] = [fuspecs.pop('o')]
582 fuspecs['o'].append(fuspecs.pop('o1'))
583 if regfile == 'FAST':
584 fuspecs['fast1'] = [fuspecs.pop('fast1')]
585 if 'fast2' in fuspecs:
586 fuspecs['fast1'].append(fuspecs.pop('fast2'))
587 if 'fast3' in fuspecs:
588 fuspecs['fast1'].append(fuspecs.pop('fast3'))
589
590 for (regname, fspec) in sort_fuspecs(fuspecs):
591 self.connect_wrport(m, fu_bitdict, wrpickers,
592 regfile, regname, fspec)
593
594 def get_byregfiles(self, readmode):
595
596 mode = "read" if readmode else "write"
597 regs = self.regs
598 fus = self.fus.fus
599 e = self.i.e # decoded instruction to execute
600
601 # dictionary of lists of regfile ports
602 byregfiles = {}
603 byregfiles_spec = {}
604 for (funame, fu) in fus.items():
605 print("%s ports for %s" % (mode, funame))
606 for idx in range(fu.n_src if readmode else fu.n_dst):
607 if readmode:
608 (regfile, regname, wid) = fu.get_in_spec(idx)
609 else:
610 (regfile, regname, wid) = fu.get_out_spec(idx)
611 print(" %d %s %s %s" % (idx, regfile, regname, str(wid)))
612 if readmode:
613 rdflag, read = regspec_decode_read(e, regfile, regname)
614 write = None
615 else:
616 rdflag, read = None, None
617 wrport, write = regspec_decode_write(e, regfile, regname)
618 if regfile not in byregfiles:
619 byregfiles[regfile] = {}
620 byregfiles_spec[regfile] = {}
621 if regname not in byregfiles_spec[regfile]:
622 byregfiles_spec[regfile][regname] = \
623 (rdflag, read, write, wid, [])
624 # here we start to create "lanes"
625 if idx not in byregfiles[regfile]:
626 byregfiles[regfile][idx] = []
627 fuspec = (funame, fu, idx)
628 byregfiles[regfile][idx].append(fuspec)
629 byregfiles_spec[regfile][regname][4].append(fuspec)
630
631 # ok just print that out, for convenience
632 for regfile, spec in byregfiles.items():
633 print("regfile %s ports:" % mode, regfile)
634 fuspecs = byregfiles_spec[regfile]
635 for regname, fspec in fuspecs.items():
636 [rdflag, read, write, wid, fuspec] = fspec
637 print(" rf %s port %s lane: %s" % (mode, regfile, regname))
638 print(" %s" % regname, wid, read, write, rdflag)
639 for (funame, fu, idx) in fuspec:
640 fusig = fu.src_i[idx] if readmode else fu.dest[idx]
641 print(" ", funame, fu, idx, fusig)
642 print()
643
644 return byregfiles, byregfiles_spec
645
646 def __iter__(self):
647 yield from self.fus.ports()
648 yield from self.i.e.ports()
649 yield from self.l0.ports()
650 # TODO: regs
651
652 def ports(self):
653 return list(self)
654
655
656 if __name__ == '__main__':
657 pspec = TestMemPspec(ldst_ifacetype='testpi',
658 imem_ifacetype='',
659 addr_wid=48,
660 mask_wid=8,
661 reg_wid=64)
662 dut = NonProductionCore(pspec)
663 vl = rtlil.convert(dut, ports=dut.ports())
664 with open("test_core.il", "w") as f:
665 f.write(vl)