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