3 not in any way intended for production use. connects up FunctionUnits to
4 Register Files in a brain-dead fashion that only permits one and only one
5 Function Unit to be operational.
7 the principle here is to take the Function Units, analyse their regspecs,
8 and turn their requirements for access to register file read/write ports
9 into groupings by Register File and Register File Port name.
11 under each grouping - by regfile/port - a list of Function Units that
12 need to connect to that port is created. as these are a contended
13 resource a "Broadcast Bus" per read/write port is then also created,
14 with access to it managed by a PriorityPicker.
16 the brain-dead part of this module is that even though there is no
17 conflict of access, regfile read/write hazards are *not* analysed,
18 and consequently it is safer to wait for the Function Unit to complete
19 before allowing a new instruction to proceed.
22 from nmigen
import Elaboratable
, Module
, Signal
, ResetSignal
, Cat
, Mux
23 from nmigen
.cli
import rtlil
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
30 from nmutil
.picker
import PriorityPicker
31 from nmutil
.util
import treereduce
32 from nmutil
.singlepipe
import ControlBase
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
42 from collections
import defaultdict
45 from nmutil
.util
import rising_edge
48 # helper function for reducing a list of signals down to a parallel
50 def ortreereduce(tree
, attr
="o_data"):
51 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
54 def ortreereduce_sig(tree
):
55 return treereduce(tree
, operator
.or_
, lambda x
: x
)
58 # helper function to place full regs declarations first
59 def sort_fuspecs(fuspecs
):
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)
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
):
76 # test is SVP64 is to be enabled
77 self
.svp64_en
= hasattr(pspec
, "svp64") and (pspec
.svp64
== True)
79 # test to see if regfile ports should be reduced
80 self
.regreduce_en
= (hasattr(pspec
, "regreduce") and
81 (pspec
.regreduce
== True))
84 self
.core_type
= "fsm"
85 if hasattr(pspec
, "core_type"):
86 self
.core_type
= pspec
.core_type
88 super().__init
__(stage
=self
)
90 # single LD/ST funnel for memory access
91 self
.l0
= l0
= TstL0CacheBuffer(pspec
, n_units
=1)
94 # function units (only one each)
95 # only include mmu if enabled in pspec
96 self
.fus
= AllFunctionUnits(pspec
, pilist
=[pi
])
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
)
104 mmu
.alu
.set_ldst_interface(l0
.cmpi
.lsmem
.lsi
)
106 # register files (yes plural)
107 self
.regs
= RegFiles(pspec
, make_hazard_vecs
=True)
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
115 # create per-FU instruction decoders (subsetted)
119 for funame
, fu
in self
.fus
.fus
.items():
120 f_name
= fu
.fnunit
.name
121 fnunit
= fu
.fnunit
.value
122 opkls
= fu
.opsubsetkls
124 # TRAP decoder is the *main* decoder
125 self
.trapunit
= funame
127 self
.decoders
[funame
] = PowerDecodeSubset(None, opkls
, f_name
,
130 svp64_en
=self
.svp64_en
,
131 regreduce_en
=self
.regreduce_en
)
132 self
.des
[funame
] = self
.decoders
[funame
].do
134 if "mmu0" in self
.decoders
:
135 self
.decoders
["mmu0"].mmu0_spr_dec
= self
.decoders
["spr0"]
137 def setup(self
, m
, i
):
141 return CoreInput(self
.pspec
, self
.svp64_en
, self
.regreduce_en
)
146 def elaborate(self
, platform
):
147 m
= super().elaborate(platform
)
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
156 m
.submodules
.fus
= self
.fus
157 m
.submodules
.l0
= l0
= self
.l0
158 self
.regs
.elaborate_into(m
, platform
)
163 self
.connect_satellite_decoders(m
)
165 # ssh, cheat: trap uses the main decoder because of the rewriting
166 self
.des
[self
.trapunit
] = self
.i
.e
.do
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
)
173 # note if an exception happened. in a pipelined or OoO design
174 # this needs to be accompanied by "shadowing" (or stalling)
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())
183 def connect_satellite_decoders(self
, m
):
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
)
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
203 if k
.lower().startswith("ldst"):
204 comb
+= v
.use_svp64_ldst_dec
.eq(
205 self
.i
.use_svp64_ldst_dec
)
207 def connect_instruction(self
, m
):
208 """connect_instruction
210 uses decoded (from PowerOp) function unit information from CSV files
211 to ascertain which Function Unit should deal with the current
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).
219 comb
, sync
= m
.d
.comb
, m
.d
.sync
222 # indicate if core is busy
223 busy_o
= self
.o
.busy_o
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)
230 for i
, funame
in enumerate(fus
.keys()):
231 fu_bitdict
[funame
] = fu_enable
[i
]
232 fu_selected
[funame
] = fu_busy
[i
]
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
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
)
248 # now create a PriorityPicker per FU-type such that only one
249 # non-busy FU will be picked
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
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
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
))
280 # sigh - need a NOP counter
282 with m
.If(counter
!= 0):
283 sync
+= counter
.eq(counter
- 1)
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)
292 # fake NOP - this isn't really used (Issuer detects NOP)
293 with m
.Case(MicrOp
.OP_NOP
):
294 sync
+= counter
.eq(2)
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
]
303 # run this FunctionUnit if enabled
304 # route op, issue, busy, read flags and mask to FU
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
)
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())
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
)
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
332 def connect_rdport(self
, m
, fu_bitdict
, rdpickers
, regfile
, regname
, fspec
):
333 comb
, sync
= m
.d
.comb
, m
.d
.sync
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(),
346 if not isinstance(fspecs
, list):
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
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
)
365 print ("pplen", pplen
)
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
)
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
):
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"
388 # exclude any currently-enabled read-request (mask out active)
389 comb
+= pick
.eq(fu
.rd_rel_o
[idx
] & fu_active
& rdflags
[i
] &
391 comb
+= rdpick
.i
[pi
].eq(pick
)
392 comb
+= fu
.go_rd_i
[idx
].eq(delay_pick
) # pass in *delayed* pick
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))
399 # the read-enable happens combinatorially (see mux-bus below)
400 # but it results in the data coming out on a one-cycle delay.
404 addrs
.append(addr_en
)
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
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
)
417 # or-reduce the muxed read signals
419 # for unary-addressed
420 comb
+= rport
.ren
.eq(ortreereduce_sig(rens
))
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
)
427 def connect_rdports(self
, m
, fu_bitdict
):
428 """connect read ports
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.
434 comb
, sync
= m
.d
.comb
, m
.d
.sync
438 # dictionary of lists of regfile read ports
439 byregfiles_rd
, byregfiles_rdspec
= self
.get_byregfiles(True)
441 # okaay, now we need a PriorityPicker per regfile per regfile port
442 # loootta pickers... peter piper picked a pack of pickled peppers...
444 for regfile
, spec
in byregfiles_rd
.items():
445 fuspecs
= byregfiles_rdspec
[regfile
]
446 rdpickers
[regfile
] = {}
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
:
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'))
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
,
468 def connect_wrport(self
, m
, fu_bitdict
, wrpickers
, regfile
, regname
, fspec
):
469 comb
, sync
= m
.d
.comb
, m
.d
.sync
473 print("connect wr", regname
, fspec
)
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
]
482 if not isinstance(fspecs
, list):
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
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
)
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
):
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
)
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
))
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
)
530 comb
+= wp
.eq(wr_pick
& wrpick
.en_o
)
531 comb
+= addr_en
.eq(Mux(wp
, write
, 0))
535 addrs
.append(addr_en
)
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
)
544 # here is where we create the Write Broadcast Bus. simple, eh?
545 comb
+= wport
.i_data
.eq(ortreereduce_sig(wsigs
))
547 # for unary-addressed
548 comb
+= wport
.wen
.eq(ortreereduce_sig(wens
))
550 # for binary-addressed
551 comb
+= wport
.addr
.eq(ortreereduce_sig(addrs
))
552 comb
+= wport
.wen
.eq(ortreereduce_sig(wens
))
554 def connect_wrports(self
, m
, fu_bitdict
):
555 """connect write ports
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.
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.
565 comb
, sync
= m
.d
.comb
, m
.d
.sync
568 # dictionary of lists of regfile write ports
569 byregfiles_wr
, byregfiles_wrspec
= self
.get_byregfiles(False)
571 # same for write ports.
572 # BLECH! complex code-duplication! BLECH!
574 for regfile
, spec
in byregfiles_wr
.items():
575 fuspecs
= byregfiles_wrspec
[regfile
]
576 wrpickers
[regfile
] = {}
578 if self
.regreduce_en
:
579 # argh, more port-merging
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'))
590 for (regname
, fspec
) in sort_fuspecs(fuspecs
):
591 self
.connect_wrport(m
, fu_bitdict
, wrpickers
,
592 regfile
, regname
, fspec
)
594 def get_byregfiles(self
, readmode
):
596 mode
= "read" if readmode
else "write"
599 e
= self
.i
.e
# decoded instruction to execute
601 # dictionary of lists of regfile ports
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
):
608 (regfile
, regname
, wid
) = fu
.get_in_spec(idx
)
610 (regfile
, regname
, wid
) = fu
.get_out_spec(idx
)
611 print(" %d %s %s %s" % (idx
, regfile
, regname
, str(wid
)))
613 rdflag
, read
= regspec_decode_read(e
, regfile
, regname
)
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
)
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
)
644 return byregfiles
, byregfiles_spec
647 yield from self
.fus
.ports()
648 yield from self
.i
.e
.ports()
649 yield from self
.l0
.ports()
656 if __name__
== '__main__':
657 pspec
= TestMemPspec(ldst_ifacetype
='testpi',
662 dut
= NonProductionCore(pspec
)
663 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
664 with
open("test_core.il", "w") as f
: