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