+ rdmask = get_rdflags(m, self.ireg.e, fu)
+ comb += fu.rdmaskn.eq(~rdmask)
+
+ # sigh - need a NOP counter
+ counter = Signal(2)
+ with m.If(counter != 0):
+ sync += counter.eq(counter - 1)
+ comb += busy_o.eq(1)
+
+ # default to reading from incoming instruction: may be overridden
+ # by copy from latch when "waiting"
+ comb += self.ireg.eq(self.i)
+ # always say "ready" except if overridden
+ comb += self.p.o_ready.eq(1)
+
+ with m.FSM():
+ with m.State("READY"):
+ with m.If(self.p.i_valid): # run only when valid
+ with m.Switch(self.ireg.e.do.insn_type):
+ # check for ATTN: halt if true
+ with m.Case(MicrOp.OP_ATTN):
+ m.d.sync += self.o.core_terminate_o.eq(1)
+
+ # fake NOP - this isn't really used (Issuer detects NOP)
+ with m.Case(MicrOp.OP_NOP):
+ sync += counter.eq(2)
+ comb += busy_o.eq(1)
+
+ with m.Default():
+ comb += self.instr_active.eq(1)
+ comb += self.p.o_ready.eq(0)
+ # connect instructions. only one enabled at a time
+ for funame, fu in fus.items():
+ do = self.des[funame]
+ enable = fu_bitdict[funame]
+
+ # run this FunctionUnit if enabled route op,
+ # issue, busy, read flags and mask to FU
+ with m.If(enable):
+ # operand comes from the *local* decoder
+ # do not actually issue, though, if there
+ # is a waw hazard. decoder has to still
+ # be asserted in order to detect that, tho
+ comb += fu.oper_i.eq_from(do)
+ if funame == 'mmu0':
+ # URRR this is truly dreadful.
+ # OP_FETCH_FAILED is a "fake" op.
+ # no instruction creates it. OP_TRAP
+ # uses the *main* decoder: this is
+ # a *Satellite* decoder that reacts
+ # on *insn_in*... not fake ops. gaah.
+ main_op = self.ireg.e.do
+ with m.If(main_op.insn_type ==
+ MicrOp.OP_FETCH_FAILED):
+ comb += fu.oper_i.insn_type.eq(
+ MicrOp.OP_FETCH_FAILED)
+ comb += fu.oper_i.fn_unit.eq(
+ Function.MMU)
+ # issue when valid (and no write-hazard)
+ comb += fu.issue_i.eq(~self.waw_hazard)
+ # instruction ok, indicate ready
+ comb += self.p.o_ready.eq(1)
+
+ if self.allow_overlap:
+ with m.If(~fu_found | self.waw_hazard):
+ # latch copy of instruction
+ sync += ilatch.eq(self.i)
+ comb += self.p.o_ready.eq(1) # accept
+ comb += busy_o.eq(1)
+ m.next = "WAITING"
+
+ with m.State("WAITING"):
+ comb += self.instr_active.eq(1)
+ comb += self.p.o_ready.eq(0)
+ comb += busy_o.eq(1)
+ # using copy of instruction, keep waiting until an FU is free
+ comb += self.ireg.eq(ilatch)
+ with m.If(fu_found): # wait for conflict to clear
+ # connect instructions. only one enabled at a time
+ for funame, fu in fus.items():
+ do = self.des[funame]
+ enable = fu_bitdict[funame]
+
+ # run this FunctionUnit if enabled route op,
+ # issue, busy, read flags and mask to FU
+ with m.If(enable):
+ # operand comes from the *local* decoder,
+ # which is asserted even if not issued,
+ # so that WaW-detection can check for hazards.
+ # only if the waw hazard is clear does the
+ # instruction actually get issued
+ comb += fu.oper_i.eq_from(do)
+ # issue when valid
+ comb += fu.issue_i.eq(~self.waw_hazard)
+ with m.If(~self.waw_hazard):
+ comb += self.p.o_ready.eq(1)
+ comb += busy_o.eq(0)
+ m.next = "READY"
+
+ print ("core: overlap allowed", self.allow_overlap)
+ # true when any FU is busy (including the cycle where it is perhaps
+ # to be issued - because that's what fu_busy is)
+ comb += any_busy_o.eq(fu_busy.bool())
+ if not self.allow_overlap:
+ # for simple non-overlap, if any instruction is busy, set
+ # busy output for core.
+ comb += busy_o.eq(any_busy_o)
+ else:
+ # sigh deal with a fun situation that needs to be investigated
+ # and resolved
+ with m.If(self.issue_conflict):
+ comb += busy_o.eq(1)
+ # make sure that LDST, SPR, MMU, Branch and Trap all say "busy"
+ # and do not allow overlap. these are all the ones that
+ # are non-forward-progressing: exceptions etc. that otherwise
+ # change CoreState for some reason (MSR, PC, SVSTATE)
+ for funame, fu in fus.items():
+ if (funame.lower().startswith('ldst') or
+ funame.lower().startswith('branch') or
+ funame.lower().startswith('mmu') or
+ funame.lower().startswith('spr') or
+ funame.lower().startswith('trap')):
+ with m.If(fu.busy_o):
+ comb += busy_o.eq(1)
+ # for SPR pipeline pause dec/tb FSM to avoid race condition
+ # TODO: really this should be much more sophisticated,
+ # spot MTSPR, spot that DEC/TB is what is to be updated.
+ # a job for PowerDecoder2, there
+ if funame.lower().startswith('spr'):
+ with m.If(fu.busy_o #& fu.oper_i.insn_type == OP_MTSPR
+ ):
+ comb += self.pause_dec_tb.eq(1)
+
+ # return both the function unit "enable" dict as well as the "busy".
+ # the "busy-or-issued" can be passed in to the Read/Write port
+ # connecters to give them permission to request access to regfiles
+ return fu_bitdict, fu_selected
+
+ def connect_rdport(self, m, fu_bitdict, fu_selected,
+ rdpickers, regfile, regname, fspec):
+ comb, sync = m.d.comb, m.d.sync
+ fus = self.fus.fus
+ regs = self.regs
+
+ rpidx = regname
+
+ # select the required read port. these are pre-defined sizes
+ rfile = regs.rf[regfile.lower()]
+ rport = rfile.r_ports[rpidx]
+ print("read regfile", rpidx, regfile, regs.rf.keys(),
+ rfile, rfile.unary)
+
+ # for checking if the read port has an outstanding write
+ if self.make_hazard_vecs:
+ wv = regs.wv[regfile.lower()]
+ wvchk = wv.q_int # write-vec bit-level hazard check
+
+ # if a hazard is detected on this read port, simply blithely block
+ # every FU from reading on it. this is complete overkill but very
+ # simple for now.
+ hazard_detected = Signal(name="raw_%s_%s" % (regfile, rpidx))
+
+ fspecs = fspec
+ if not isinstance(fspecs, list):
+ fspecs = [fspecs]
+
+ rdflags = []
+ pplen = 0
+ ppoffs = []
+ for i, fspec in enumerate(fspecs):
+ # get the regfile specs for this regfile port
+ print ("fpsec", i, fspec, len(fspec.specs))
+ name = "%s_%s_%d" % (regfile, regname, i)
+ ppoffs.append(pplen) # record offset for picker
+ pplen += len(fspec.specs)
+ rdflag = Signal(name="rdflag_"+name, reset_less=True)
+ comb += rdflag.eq(fspec.okflag)
+ rdflags.append(rdflag)
+
+ print ("pplen", pplen)
+
+ # create a priority picker to manage this port
+ rdpickers[regfile][rpidx] = rdpick = PriorityPicker(pplen)
+ m.submodules["rdpick_%s_%s" % (regfile, rpidx)] = rdpick
+
+ rens = []
+ addrs = []
+ wvens = []
+
+ for i, fspec in enumerate(fspecs):
+ (rf, _read, wid, fuspecs) = \
+ (fspec.okflag, fspec.regport, fspec.wid, fspec.specs)
+ # connect up the FU req/go signals, and the reg-read to the FU
+ # and create a Read Broadcast Bus
+ for pi, fuspec in enumerate(fspec.specs):
+ (funame, fu, idx) = (fuspec.funame, fuspec.fu, fuspec.idx)
+ pi += ppoffs[i]
+ name = "%s_%s_%s_%i" % (regfile, rpidx, funame, pi)
+ fu_active = fu_selected[funame]
+ fu_issued = fu_bitdict[funame]
+
+ # get (or set up) a latched copy of read register number
+ # and (sigh) also the read-ok flag
+ # TODO: use nmutil latchregister
+ rhname = "%s_%s_%d" % (regfile, regname, i)
+ rdflag = Signal(name="rdflag_%s_%s" % (funame, rhname),
+ reset_less=True)
+ if rhname not in fu.rf_latches:
+ rfl = Signal(name="rdflag_latch_%s_%s" % (funame, rhname))
+ fu.rf_latches[rhname] = rfl
+ with m.If(fu.issue_i):
+ sync += rfl.eq(rdflags[i])
+ else:
+ rfl = fu.rf_latches[rhname]
+
+ # now the register port
+ rname = "%s_%s_%s_%d" % (funame, regfile, regname, pi)
+ read = Signal.like(_read, name="read_"+rname)
+ if rname not in fu.rd_latches:
+ rdl = Signal.like(_read, name="rdlatch_"+rname)
+ fu.rd_latches[rname] = rdl
+ with m.If(fu.issue_i):
+ sync += rdl.eq(_read)
+ else:
+ rdl = fu.rd_latches[rname]
+
+ # make the read immediately available on issue cycle
+ # after the read cycle, otherwies use the latched copy.
+ # this captures the regport and okflag on issue
+ with m.If(fu.issue_i):
+ comb += read.eq(_read)
+ comb += rdflag.eq(rdflags[i])
+ with m.Else():
+ comb += read.eq(rdl)
+ comb += rdflag.eq(rfl)
+
+ # connect request-read to picker input, and output to go-rd
+ addr_en = Signal.like(read, name="addr_en_"+name)
+ pick = Signal(name="pick_"+name) # picker input
+ rp = Signal(name="rp_"+name) # picker output
+ delay_pick = Signal(name="dp_"+name) # read-enable "underway"
+ rhazard = Signal(name="rhaz_"+name)
+
+ # exclude any currently-enabled read-request (mask out active)
+ # entirely block anything hazarded from being picked
+ comb += pick.eq(fu.rd_rel_o[idx] & fu_active & rdflag &
+ ~delay_pick & ~rhazard)
+ comb += rdpick.i[pi].eq(pick)
+ comb += fu.go_rd_i[idx].eq(delay_pick) # pass in *delayed* pick
+
+ # if picked, select read-port "reg select" number to port
+ comb += rp.eq(rdpick.o[pi] & rdpick.en_o)
+ sync += delay_pick.eq(rp) # delayed "pick"
+ comb += addr_en.eq(Mux(rp, read, 0))
+
+ # the read-enable happens combinatorially (see mux-bus below)
+ # but it results in the data coming out on a one-cycle delay.
+ if rfile.unary:
+ rens.append(addr_en)
+ else:
+ addrs.append(addr_en)
+ rens.append(rp)
+
+ # use the *delayed* pick signal to put requested data onto bus
+ with m.If(delay_pick):
+ # connect regfile port to input, creating fan-out Bus
+ src = fu.src_i[idx]
+ print("reg connect widths",
+ regfile, regname, pi, funame,
+ src.shape(), rport.o_data.shape())
+ # all FUs connect to same port
+ comb += src.eq(rport.o_data)
+
+ if not self.make_hazard_vecs:
+ continue
+
+ # read the write-hazard bitvector (wv) for any bit that is
+ wvchk_en = Signal(len(wvchk), name="wv_chk_addr_en_"+name)
+ issue_active = Signal(name="rd_iactive_"+name)
+ # XXX combinatorial loop here
+ comb += issue_active.eq(fu_active & rdflag)
+ with m.If(issue_active):
+ if rfile.unary:
+ comb += wvchk_en.eq(read)
+ else:
+ comb += wvchk_en.eq(1<<read)
+ # if FU is busy (which doesn't get set at the same time as
+ # issue) and no hazard was detected, clear wvchk_en (i.e.
+ # stop checking for hazards). there is a loop here, but it's
+ # via a DFF, so is ok. some linters may complain, but hey.
+ with m.If(fu.busy_o & ~rhazard):
+ comb += wvchk_en.eq(0)
+
+ # read-hazard is ANDed with (filtered by) what is actually
+ # being requested.
+ comb += rhazard.eq((wvchk & wvchk_en).bool())
+
+ wvens.append(wvchk_en)
+
+ # or-reduce the muxed read signals
+ if rfile.unary:
+ # for unary-addressed
+ comb += rport.ren.eq(ortreereduce_sig(rens))
+ else:
+ # for binary-addressed
+ comb += rport.addr.eq(ortreereduce_sig(addrs))
+ comb += rport.ren.eq(Cat(*rens).bool())
+ print ("binary", regfile, rpidx, rport, rport.ren, rens, addrs)
+
+ if not self.make_hazard_vecs:
+ return Const(0) # declare "no hazards"
+
+ # enable the read bitvectors for this issued instruction
+ # and return whether any write-hazard bit is set
+ wvchk_and = Signal(len(wvchk), name="wv_chk_"+name)
+ comb += wvchk_and.eq(wvchk & ortreereduce_sig(wvens))
+ comb += hazard_detected.eq(wvchk_and.bool())
+ return hazard_detected
+
+ def connect_rdports(self, m, fu_bitdict, fu_selected):