f9dc2dc16b232001d3a0ad3c0d029715ee1c8654
[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. what is actually
274 # required is one PriorityPicker per group of matching fnunits,
275 # and for only one actual FU to be "picked". this basically means
276 # when ReservationStations are enabled it will be possible to
277 # monitor multiple outstanding processing properly.
278 for funame, fu in fus.items():
279 fnunit = fu.fnunit.value
280 enable = Signal(name="en_%s" % funame, reset_less=True)
281 comb += enable.eq((self.i.e.do.fn_unit & fnunit).bool())
282 comb += fu_bitdict[funame].eq(enable)
283
284 # sigh - need a NOP counter
285 counter = Signal(2)
286 with m.If(counter != 0):
287 sync += counter.eq(counter - 1)
288 comb += busy_o.eq(1)
289
290 with m.If(self.p.i_valid): # run only when valid
291 with m.Switch(self.i.e.do.insn_type):
292 # check for ATTN: halt if true
293 with m.Case(MicrOp.OP_ATTN):
294 m.d.sync += self.o.core_terminate_o.eq(1)
295
296 # fake NOP - this isn't really used (Issuer detects NOP)
297 with m.Case(MicrOp.OP_NOP):
298 sync += counter.eq(2)
299 comb += busy_o.eq(1)
300
301 with m.Default():
302 # connect up instructions. only one enabled at a time
303 for funame, fu in fus.items():
304 do = self.des[funame]
305 enable = fu_bitdict[funame]
306
307 # run this FunctionUnit if enabled
308 # route op, issue, busy, read flags and mask to FU
309 with m.If(enable):
310 # operand comes from the *local* decoder
311 comb += fu.oper_i.eq_from(do)
312 comb += fu.issue_i.eq(1) # issue when input valid
313 comb += busy_o.eq(fu.busy_o)
314 # rdmask, which is for registers, needs to come
315 # from the *main* decoder
316 rdmask = get_rdflags(self.i.e, fu)
317 comb += fu.rdmaskn.eq(~rdmask)
318
319 # if instruction is busy, set busy output for core. also
320 # continue to hold each fu rdmask
321 for funame, fu in fus.items():
322 with m.If(fu.busy_o):
323 comb += busy_o.eq(fu.busy_o)
324 # rdmask, which is for registers, needs to come
325 # from the *main* decoder
326 rdmask = get_rdflags(self.i.e, fu)
327 comb += fu.rdmaskn.eq(~rdmask)
328
329 # set ready/valid signalling. if busy, means refuse incoming issue
330 # XXX note: for an in-order core this is far too simple. busy must
331 # be gated with the *availability* of the incoming (requested)
332 # instruction, where Core must be prepared to store-and-hold
333 # an instruction if no FU is available.
334 comb += self.p.o_ready.eq(~busy_o)
335
336 return fu_bitdict
337
338 def connect_rdport(self, m, fu_bitdict, rdpickers, regfile, regname, fspec):
339 comb, sync = m.d.comb, m.d.sync
340 fus = self.fus.fus
341 regs = self.regs
342
343 rpidx = regname
344
345 # select the required read port. these are pre-defined sizes
346 rfile = regs.rf[regfile.lower()]
347 rport = rfile.r_ports[rpidx]
348 print("read regfile", rpidx, regfile, regs.rf.keys(),
349 rfile, rfile.unary)
350
351 fspecs = fspec
352 if not isinstance(fspecs, list):
353 fspecs = [fspecs]
354
355 rdflags = []
356 pplen = 0
357 reads = []
358 ppoffs = []
359 for i, fspec in enumerate(fspecs):
360 # get the regfile specs for this regfile port
361 (rf, read, write, wid, fuspec) = fspec
362 print ("fpsec", i, fspec, len(fuspec))
363 ppoffs.append(pplen) # record offset for picker
364 pplen += len(fuspec)
365 name = "rdflag_%s_%s_%d" % (regfile, regname, i)
366 rdflag = Signal(name=name, reset_less=True)
367 comb += rdflag.eq(rf)
368 rdflags.append(rdflag)
369 reads.append(read)
370
371 print ("pplen", pplen)
372
373 # create a priority picker to manage this port
374 rdpickers[regfile][rpidx] = rdpick = PriorityPicker(pplen)
375 setattr(m.submodules, "rdpick_%s_%s" % (regfile, rpidx), rdpick)
376
377 rens = []
378 addrs = []
379 for i, fspec in enumerate(fspecs):
380 (rf, read, write, wid, fuspec) = fspec
381 # connect up the FU req/go signals, and the reg-read to the FU
382 # and create a Read Broadcast Bus
383 for pi, (funame, fu, idx) in enumerate(fuspec):
384 pi += ppoffs[i]
385
386 # connect request-read to picker input, and output to go-rd
387 fu_active = fu_bitdict[funame]
388 name = "%s_%s_%s_%i" % (regfile, rpidx, funame, pi)
389 addr_en = Signal.like(reads[i], name="addr_en_"+name)
390 pick = Signal(name="pick_"+name) # picker input
391 rp = Signal(name="rp_"+name) # picker output
392 delay_pick = Signal(name="dp_"+name) # read-enable "underway"
393
394 # exclude any currently-enabled read-request (mask out active)
395 comb += pick.eq(fu.rd_rel_o[idx] & fu_active & rdflags[i] &
396 ~delay_pick)
397 comb += rdpick.i[pi].eq(pick)
398 comb += fu.go_rd_i[idx].eq(delay_pick) # pass in *delayed* pick
399
400 # if picked, select read-port "reg select" number to port
401 comb += rp.eq(rdpick.o[pi] & rdpick.en_o)
402 sync += delay_pick.eq(rp) # delayed "pick"
403 comb += addr_en.eq(Mux(rp, reads[i], 0))
404
405 # the read-enable happens combinatorially (see mux-bus below)
406 # but it results in the data coming out on a one-cycle delay.
407 if rfile.unary:
408 rens.append(addr_en)
409 else:
410 addrs.append(addr_en)
411 rens.append(rp)
412
413 # use the *delayed* pick signal to put requested data onto bus
414 with m.If(delay_pick):
415 # connect regfile port to input, creating fan-out Bus
416 src = fu.src_i[idx]
417 print("reg connect widths",
418 regfile, regname, pi, funame,
419 src.shape(), rport.o_data.shape())
420 # all FUs connect to same port
421 comb += src.eq(rport.o_data)
422
423 # or-reduce the muxed read signals
424 if rfile.unary:
425 # for unary-addressed
426 comb += rport.ren.eq(ortreereduce_sig(rens))
427 else:
428 # for binary-addressed
429 comb += rport.addr.eq(ortreereduce_sig(addrs))
430 comb += rport.ren.eq(Cat(*rens).bool())
431 print ("binary", regfile, rpidx, rport, rport.ren, rens, addrs)
432
433 def connect_rdports(self, m, fu_bitdict):
434 """connect read ports
435
436 orders the read regspecs into a dict-of-dicts, by regfile, by
437 regport name, then connects all FUs that want that regport by
438 way of a PriorityPicker.
439 """
440 comb, sync = m.d.comb, m.d.sync
441 fus = self.fus.fus
442 regs = self.regs
443
444 # dictionary of lists of regfile read ports
445 byregfiles_rd, byregfiles_rdspec = self.get_byregfiles(True)
446
447 # okaay, now we need a PriorityPicker per regfile per regfile port
448 # loootta pickers... peter piper picked a pack of pickled peppers...
449 rdpickers = {}
450 for regfile, spec in byregfiles_rd.items():
451 fuspecs = byregfiles_rdspec[regfile]
452 rdpickers[regfile] = {}
453
454 # argh. an experiment to merge RA and RB in the INT regfile
455 # (we have too many read/write ports)
456 if self.regreduce_en:
457 if regfile == 'INT':
458 fuspecs['rabc'] = [fuspecs.pop('rb')]
459 fuspecs['rabc'].append(fuspecs.pop('rc'))
460 fuspecs['rabc'].append(fuspecs.pop('ra'))
461 if regfile == 'FAST':
462 fuspecs['fast1'] = [fuspecs.pop('fast1')]
463 if 'fast2' in fuspecs:
464 fuspecs['fast1'].append(fuspecs.pop('fast2'))
465 if 'fast3' in fuspecs:
466 fuspecs['fast1'].append(fuspecs.pop('fast3'))
467
468 # for each named regfile port, connect up all FUs to that port
469 for (regname, fspec) in sort_fuspecs(fuspecs):
470 print("connect rd", regname, fspec)
471 self.connect_rdport(m, fu_bitdict, rdpickers, regfile,
472 regname, fspec)
473
474 def connect_wrport(self, m, fu_bitdict, wrpickers, regfile, regname, fspec):
475 comb, sync = m.d.comb, m.d.sync
476 fus = self.fus.fus
477 regs = self.regs
478
479 print("connect wr", regname, fspec)
480 rpidx = regname
481
482 # select the required write port. these are pre-defined sizes
483 print(regfile, regs.rf.keys())
484 rfile = regs.rf[regfile.lower()]
485 wport = rfile.w_ports[rpidx]
486
487 fspecs = fspec
488 if not isinstance(fspecs, list):
489 fspecs = [fspecs]
490
491 pplen = 0
492 writes = []
493 ppoffs = []
494 for i, fspec in enumerate(fspecs):
495 # get the regfile specs for this regfile port
496 (rf, read, write, wid, fuspec) = fspec
497 print ("fpsec", i, fspec, len(fuspec))
498 ppoffs.append(pplen) # record offset for picker
499 pplen += len(fuspec)
500
501 # create a priority picker to manage this port
502 wrpickers[regfile][rpidx] = wrpick = PriorityPicker(pplen)
503 setattr(m.submodules, "wrpick_%s_%s" % (regfile, rpidx), wrpick)
504
505 wsigs = []
506 wens = []
507 addrs = []
508 for i, fspec in enumerate(fspecs):
509 # connect up the FU req/go signals and the reg-read to the FU
510 # these are arbitrated by Data.ok signals
511 (rf, read, write, wid, fuspec) = fspec
512 for pi, (funame, fu, idx) in enumerate(fuspec):
513 pi += ppoffs[i]
514
515 # write-request comes from dest.ok
516 dest = fu.get_out(idx)
517 fu_dest_latch = fu.get_fu_out(idx) # latched output
518 name = "wrflag_%s_%s_%d" % (funame, regname, idx)
519 wrflag = Signal(name=name, reset_less=True)
520 comb += wrflag.eq(dest.ok & fu.busy_o)
521
522 # connect request-write to picker input, and output to go-wr
523 fu_active = fu_bitdict[funame]
524 pick = fu.wr.rel_o[idx] & fu_active # & wrflag
525 comb += wrpick.i[pi].eq(pick)
526 # create a single-pulse go write from the picker output
527 wr_pick = Signal(name="wpick_%s_%s_%d" % (funame, regname, idx))
528 comb += wr_pick.eq(wrpick.o[pi] & wrpick.en_o)
529 comb += fu.go_wr_i[idx].eq(rising_edge(m, wr_pick))
530
531 # connect the regspec write "reg select" number to this port
532 # only if one FU actually requests (and is granted) the port
533 # will the write-enable be activated
534 addr_en = Signal.like(write)
535 wp = Signal()
536 comb += wp.eq(wr_pick & wrpick.en_o)
537 comb += addr_en.eq(Mux(wp, write, 0))
538 if rfile.unary:
539 wens.append(addr_en)
540 else:
541 addrs.append(addr_en)
542 wens.append(wp)
543
544 # connect regfile port to input
545 print("reg connect widths",
546 regfile, regname, pi, funame,
547 dest.shape(), wport.i_data.shape())
548 wsigs.append(fu_dest_latch)
549
550 # here is where we create the Write Broadcast Bus. simple, eh?
551 comb += wport.i_data.eq(ortreereduce_sig(wsigs))
552 if rfile.unary:
553 # for unary-addressed
554 comb += wport.wen.eq(ortreereduce_sig(wens))
555 else:
556 # for binary-addressed
557 comb += wport.addr.eq(ortreereduce_sig(addrs))
558 comb += wport.wen.eq(ortreereduce_sig(wens))
559
560 def connect_wrports(self, m, fu_bitdict):
561 """connect write ports
562
563 orders the write regspecs into a dict-of-dicts, by regfile,
564 by regport name, then connects all FUs that want that regport
565 by way of a PriorityPicker.
566
567 note that the write-port wen, write-port data, and go_wr_i all need to
568 be on the exact same clock cycle. as there is a combinatorial loop bug
569 at the moment, these all use sync.
570 """
571 comb, sync = m.d.comb, m.d.sync
572 fus = self.fus.fus
573 regs = self.regs
574 # dictionary of lists of regfile write ports
575 byregfiles_wr, byregfiles_wrspec = self.get_byregfiles(False)
576
577 # same for write ports.
578 # BLECH! complex code-duplication! BLECH!
579 wrpickers = {}
580 for regfile, spec in byregfiles_wr.items():
581 fuspecs = byregfiles_wrspec[regfile]
582 wrpickers[regfile] = {}
583
584 if self.regreduce_en:
585 # argh, more port-merging
586 if regfile == 'INT':
587 fuspecs['o'] = [fuspecs.pop('o')]
588 fuspecs['o'].append(fuspecs.pop('o1'))
589 if regfile == 'FAST':
590 fuspecs['fast1'] = [fuspecs.pop('fast1')]
591 if 'fast2' in fuspecs:
592 fuspecs['fast1'].append(fuspecs.pop('fast2'))
593 if 'fast3' in fuspecs:
594 fuspecs['fast1'].append(fuspecs.pop('fast3'))
595
596 for (regname, fspec) in sort_fuspecs(fuspecs):
597 self.connect_wrport(m, fu_bitdict, wrpickers,
598 regfile, regname, fspec)
599
600 def get_byregfiles(self, readmode):
601
602 mode = "read" if readmode else "write"
603 regs = self.regs
604 fus = self.fus.fus
605 e = self.i.e # decoded instruction to execute
606
607 # dictionary of lists of regfile ports
608 byregfiles = {}
609 byregfiles_spec = {}
610 for (funame, fu) in fus.items():
611 print("%s ports for %s" % (mode, funame))
612 for idx in range(fu.n_src if readmode else fu.n_dst):
613 if readmode:
614 (regfile, regname, wid) = fu.get_in_spec(idx)
615 else:
616 (regfile, regname, wid) = fu.get_out_spec(idx)
617 print(" %d %s %s %s" % (idx, regfile, regname, str(wid)))
618 if readmode:
619 rdflag, read = regspec_decode_read(e, regfile, regname)
620 write = None
621 else:
622 rdflag, read = None, None
623 wrport, write = regspec_decode_write(e, regfile, regname)
624 if regfile not in byregfiles:
625 byregfiles[regfile] = {}
626 byregfiles_spec[regfile] = {}
627 if regname not in byregfiles_spec[regfile]:
628 byregfiles_spec[regfile][regname] = \
629 (rdflag, read, write, wid, [])
630 # here we start to create "lanes"
631 if idx not in byregfiles[regfile]:
632 byregfiles[regfile][idx] = []
633 fuspec = (funame, fu, idx)
634 byregfiles[regfile][idx].append(fuspec)
635 byregfiles_spec[regfile][regname][4].append(fuspec)
636
637 # ok just print that out, for convenience
638 for regfile, spec in byregfiles.items():
639 print("regfile %s ports:" % mode, regfile)
640 fuspecs = byregfiles_spec[regfile]
641 for regname, fspec in fuspecs.items():
642 [rdflag, read, write, wid, fuspec] = fspec
643 print(" rf %s port %s lane: %s" % (mode, regfile, regname))
644 print(" %s" % regname, wid, read, write, rdflag)
645 for (funame, fu, idx) in fuspec:
646 fusig = fu.src_i[idx] if readmode else fu.dest[idx]
647 print(" ", funame, fu, idx, fusig)
648 print()
649
650 return byregfiles, byregfiles_spec
651
652 def __iter__(self):
653 yield from self.fus.ports()
654 yield from self.i.e.ports()
655 yield from self.l0.ports()
656 # TODO: regs
657
658 def ports(self):
659 return list(self)
660
661
662 if __name__ == '__main__':
663 pspec = TestMemPspec(ldst_ifacetype='testpi',
664 imem_ifacetype='',
665 addr_wid=48,
666 mask_wid=8,
667 reg_wid=64)
668 dut = NonProductionCore(pspec)
669 vl = rtlil.convert(dut, ports=dut.ports())
670 with open("test_core.il", "w") as f:
671 f.write(vl)