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
.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
47 from nmutil
.util
import rising_edge
50 # helper function for reducing a list of signals down to a parallel
52 def ortreereduce(tree
, attr
="o_data"):
53 return treereduce(tree
, operator
.or_
, lambda x
: getattr(x
, attr
))
56 def ortreereduce_sig(tree
):
57 return treereduce(tree
, operator
.or_
, lambda x
: x
)
60 # helper function to place full regs declarations first
61 def sort_fuspecs(fuspecs
):
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)
73 def __init__(self
, pspec
, svp64_en
, regreduce_en
):
75 self
.svp64_en
= svp64_en
76 self
.e
= Decode2ToExecute1Type("core", opkls
=IssuerDecode2ToOperand
,
77 regreduce_en
=regreduce_en
)
79 # SVP64 RA_OR_ZERO needs to know if the relevant EXTRA2/3 field is zero
80 self
.sv_a_nz
= Signal()
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
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
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
)
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
)
110 # start/stop and terminated signalling
111 self
.core_terminate_o
= Signal(reset
=0) # indicates stopped
112 self
.exc_happened
= Signal() # exception happened
115 self
.core_terminate_o
.eq(i
.core_terminate_o
)
116 self
.exc_happened
.eq(i
.exc_happened
)
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
):
125 # test is SVP64 is to be enabled
126 self
.svp64_en
= hasattr(pspec
, "svp64") and (pspec
.svp64
== True)
128 # test to see if regfile ports should be reduced
129 self
.regreduce_en
= (hasattr(pspec
, "regreduce") and
130 (pspec
.regreduce
== True))
132 super().__init
__(stage
=self
)
134 # single LD/ST funnel for memory access
135 self
.l0
= l0
= TstL0CacheBuffer(pspec
, n_units
=1)
138 # function units (only one each)
139 # only include mmu if enabled in pspec
140 self
.fus
= AllFunctionUnits(pspec
, pilist
=[pi
])
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
)
148 mmu
.alu
.set_ldst_interface(l0
.cmpi
.lsmem
.lsi
)
150 # register files (yes plural)
151 self
.regs
= RegFiles(pspec
)
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
159 # create per-FU instruction decoders (subsetted)
163 for funame
, fu
in self
.fus
.fus
.items():
164 f_name
= fu
.fnunit
.name
165 fnunit
= fu
.fnunit
.value
166 opkls
= fu
.opsubsetkls
168 # TRAP decoder is the *main* decoder
169 self
.trapunit
= funame
171 self
.decoders
[funame
] = PowerDecodeSubset(None, opkls
, f_name
,
174 svp64_en
=self
.svp64_en
,
175 regreduce_en
=self
.regreduce_en
)
176 self
.des
[funame
] = self
.decoders
[funame
].do
178 if "mmu0" in self
.decoders
:
179 self
.decoders
["mmu0"].mmu0_spr_dec
= self
.decoders
["spr0"]
181 def setup(self
, m
, i
):
185 return CoreInput(self
.pspec
, self
.svp64_en
, self
.regreduce_en
)
190 def elaborate(self
, platform
):
191 m
= super().elaborate(platform
)
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
200 m
.submodules
.fus
= self
.fus
201 m
.submodules
.l0
= l0
= self
.l0
202 self
.regs
.elaborate_into(m
, platform
)
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
)
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
225 if k
.lower().startswith("ldst"):
226 comb
+= v
.use_svp64_ldst_dec
.eq(
227 self
.i
.use_svp64_ldst_dec
)
229 # ssh, cheat: trap uses the main decoder because of the rewriting
230 self
.des
[self
.trapunit
] = self
.i
.e
.do
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
)
237 # note if an exception happened. in a pipelined or OoO design
238 # this needs to be accompanied by "shadowing" (or stalling)
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())
247 def connect_instruction(self
, m
):
248 """connect_instruction
250 uses decoded (from PowerOp) function unit information from CSV files
251 to ascertain which Function Unit should deal with the current
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).
259 comb
, sync
= m
.d
.comb
, m
.d
.sync
262 # indicate if core is busy
263 busy_o
= Signal(name
="corebusy_o", reset_less
=True)
265 # enable-signals for each FU, get one bit for each FU (by name)
266 fu_enable
= Signal(len(fus
), reset_less
=True)
268 for i
, funame
in enumerate(fus
.keys()):
269 fu_bitdict
[funame
] = fu_enable
[i
]
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
)
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 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
)
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
)
325 # set ready/valid signalling. if busy, means refuse incoming issue
326 comb
+= self
.p
.o_ready
.eq(~busy_o
)
330 def connect_rdport(self
, m
, fu_bitdict
, rdpickers
, regfile
, regname
, fspec
):
331 comb
, sync
= m
.d
.comb
, m
.d
.sync
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(),
344 if not isinstance(fspecs
, list):
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
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
)
363 print ("pplen", pplen
)
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
)
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
):
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"
386 # exclude any currently-enabled read-request (mask out active)
387 comb
+= pick
.eq(fu
.rd_rel_o
[idx
] & fu_active
& rdflags
[i
] &
389 comb
+= rdpick
.i
[pi
].eq(pick
)
390 comb
+= fu
.go_rd_i
[idx
].eq(delay_pick
) # pass in *delayed* pick
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))
397 # the read-enable happens combinatorially (see mux-bus below)
398 # but it results in the data coming out on a one-cycle delay.
402 addrs
.append(addr_en
)
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
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
)
415 # or-reduce the muxed read signals
417 # for unary-addressed
418 comb
+= rport
.ren
.eq(ortreereduce_sig(rens
))
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
)
425 def connect_rdports(self
, m
, fu_bitdict
):
426 """connect read ports
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.
432 comb
, sync
= m
.d
.comb
, m
.d
.sync
436 # dictionary of lists of regfile read ports
437 byregfiles_rd
, byregfiles_rdspec
= self
.get_byregfiles(True)
439 # okaay, now we need a PriorityPicker per regfile per regfile port
440 # loootta pickers... peter piper picked a pack of pickled peppers...
442 for regfile
, spec
in byregfiles_rd
.items():
443 fuspecs
= byregfiles_rdspec
[regfile
]
444 rdpickers
[regfile
] = {}
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
:
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'))
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
,
466 def connect_wrport(self
, m
, fu_bitdict
, wrpickers
, regfile
, regname
, fspec
):
467 comb
, sync
= m
.d
.comb
, m
.d
.sync
471 print("connect wr", regname
, fspec
)
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
]
480 if not isinstance(fspecs
, list):
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
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
)
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
):
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
)
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
))
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
)
528 comb
+= wp
.eq(wr_pick
& wrpick
.en_o
)
529 comb
+= addr_en
.eq(Mux(wp
, write
, 0))
533 addrs
.append(addr_en
)
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
)
542 # here is where we create the Write Broadcast Bus. simple, eh?
543 comb
+= wport
.i_data
.eq(ortreereduce_sig(wsigs
))
545 # for unary-addressed
546 comb
+= wport
.wen
.eq(ortreereduce_sig(wens
))
548 # for binary-addressed
549 comb
+= wport
.addr
.eq(ortreereduce_sig(addrs
))
550 comb
+= wport
.wen
.eq(ortreereduce_sig(wens
))
552 def connect_wrports(self
, m
, fu_bitdict
):
553 """connect write ports
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.
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.
563 comb
, sync
= m
.d
.comb
, m
.d
.sync
566 # dictionary of lists of regfile write ports
567 byregfiles_wr
, byregfiles_wrspec
= self
.get_byregfiles(False)
569 # same for write ports.
570 # BLECH! complex code-duplication! BLECH!
572 for regfile
, spec
in byregfiles_wr
.items():
573 fuspecs
= byregfiles_wrspec
[regfile
]
574 wrpickers
[regfile
] = {}
576 if self
.regreduce_en
:
577 # argh, more port-merging
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'))
588 for (regname
, fspec
) in sort_fuspecs(fuspecs
):
589 self
.connect_wrport(m
, fu_bitdict
, wrpickers
,
590 regfile
, regname
, fspec
)
592 def get_byregfiles(self
, readmode
):
594 mode
= "read" if readmode
else "write"
597 e
= self
.i
.e
# decoded instruction to execute
599 # dictionary of lists of regfile ports
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
):
606 (regfile
, regname
, wid
) = fu
.get_in_spec(idx
)
608 (regfile
, regname
, wid
) = fu
.get_out_spec(idx
)
609 print(" %d %s %s %s" % (idx
, regfile
, regname
, str(wid
)))
611 rdflag
, read
= regspec_decode_read(e
, regfile
, regname
)
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
)
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
)
642 return byregfiles
, byregfiles_spec
645 yield from self
.fus
.ports()
646 yield from self
.i
.e
.ports()
647 yield from self
.l0
.ports()
654 if __name__
== '__main__':
655 pspec
= TestMemPspec(ldst_ifacetype
='testpi',
660 dut
= NonProductionCore(pspec
)
661 vl
= rtlil
.convert(dut
, ports
=dut
.ports())
662 with
open("test_core.il", "w") as f
: