from nmigen.cli import main
import sys
+from nmutil.singlepipe import ControlBase
+from soc.simple.core_data import FetchOutput, FetchInput
+
from nmigen.lib.coding import PriorityEncoder
from openpower.decoder.power_decoder import create_pdecode
from openpower.decoder.decode2execute1 import IssuerDecode2ToOperand
from openpower.decoder.decode2execute1 import Data
from openpower.decoder.power_enums import (MicrOp, SVP64PredInt, SVP64PredCR,
- SVP64PredMode)
+ SVP64PredMode)
from openpower.state import CoreState
from openpower.consts import (CR, SVP64CROffs)
-from soc.experiment.testmem import TestMemory # test only for instructions
+from soc.experiment.testmem import TestMemory # test only for instructions
from soc.regfile.regfiles import StateRegs, FastRegs
from soc.simple.core import NonProductionCore
from soc.config.test.test_loadstore import TestMemPspec
from nmutil.util import rising_edge
+
def get_insn(f_instr_o, pc):
if f_instr_o.width == 32:
return f_instr_o
return f_instr_o.word_select(pc[2], 32)
# gets state input or reads from state regfile
+
+
def state_get(m, core_rst, state_i, name, regfile, regnum):
comb = m.d.comb
sync = m.d.sync
comb += res.eq(state_i.data)
with m.Else():
# otherwise read StateRegs regfile for PC...
- comb += regfile.ren.eq(1<<regnum)
+ comb += regfile.ren.eq(1 << regnum)
# ... but on a 1-clock delay
with m.If(res_ok_delay):
comb += res.eq(regfile.o_data)
return res
+
def get_predint(m, mask, name):
"""decode SVP64 predicate integer mask field to reg number and invert
this is identical to the equivalent function in ISACaller except that
comb += invert.eq(1)
return regread, invert, unary, all1s
+
def get_predcr(m, mask, name):
"""decode SVP64 predicate CR to reg number field and invert status
this is identical to _get_predcr in ISACaller
return idx, invert
+# Fetch Finite State Machine.
+# WARNING: there are currently DriverConflicts but it's actually working.
+# TODO, here: everything that is global in nature, information from the
+# main TestIssuerInternal, needs to move to either ispec() or ospec().
+# not only that: TestIssuerInternal.imem can entirely move into here
+# because imem is only ever accessed inside the FetchFSM.
+class FetchFSM(ControlBase):
+ def __init__(self, allow_overlap, svp64_en, imem, core_rst,
+ pdecode2, cur_state,
+ dbg, core, svstate, nia, is_svp64_mode):
+ self.allow_overlap = allow_overlap
+ self.svp64_en = svp64_en
+ self.imem = imem
+ self.core_rst = core_rst
+ self.pdecode2 = pdecode2
+ self.cur_state = cur_state
+ self.dbg = dbg
+ self.core = core
+ self.svstate = svstate
+ self.nia = nia
+ self.is_svp64_mode = is_svp64_mode
+
+ # set up pipeline ControlBase and allocate i/o specs
+ # (unusual: normally done by the Pipeline API)
+ super().__init__(stage=self)
+ self.p.i_data, self.n.o_data = self.new_specs(None)
+ self.i, self.o = self.p.i_data, self.n.o_data
+
+ # next 3 functions are Stage API Compliance
+ def setup(self, m, i):
+ pass
+
+ def ispec(self):
+ return FetchInput()
+
+ def ospec(self):
+ return FetchOutput()
+
+ def elaborate(self, platform):
+ """fetch FSM
+
+ this FSM performs fetch of raw instruction data, partial-decodes
+ it 32-bit at a time to detect SVP64 prefixes, and will optionally
+ read a 2nd 32-bit quantity if that occurs.
+ """
+ m = super().elaborate(platform)
+
+ dbg = self.dbg
+ core = self.core,
+ pc = self.i.pc
+ svstate = self.svstate
+ nia = self.nia
+ is_svp64_mode = self.is_svp64_mode
+ fetch_pc_o_ready = self.p.o_ready
+ fetch_pc_i_valid = self.p.i_valid
+ fetch_insn_o_valid = self.n.o_valid
+ fetch_insn_i_ready = self.n.i_ready
+
+ comb = m.d.comb
+ sync = m.d.sync
+ pdecode2 = self.pdecode2
+ cur_state = self.cur_state
+ dec_opcode_o = pdecode2.dec.raw_opcode_in # raw opcode
+
+ msr_read = Signal(reset=1)
+
+ # don't read msr every cycle
+ staterf = self.core.regs.rf['state']
+ state_r_msr = staterf.r_ports['msr'] # MSR rd
+
+ comb += state_r_msr.ren.eq(0)
+
+ with m.FSM(name='fetch_fsm'):
+
+ # waiting (zzz)
+ with m.State("IDLE"):
+ with m.If(~dbg.stopping_o):
+ comb += fetch_pc_o_ready.eq(1)
+ with m.If(fetch_pc_i_valid):
+ # instruction allowed to go: start by reading the PC
+ # capture the PC and also drop it into Insn Memory
+ # we have joined a pair of combinatorial memory
+ # lookups together. this is Generally Bad.
+ comb += self.imem.a_pc_i.eq(pc)
+ comb += self.imem.a_i_valid.eq(1)
+ comb += self.imem.f_i_valid.eq(1)
+ sync += cur_state.pc.eq(pc)
+ sync += cur_state.svstate.eq(svstate) # and svstate
+
+ # initiate read of MSR. arrives one clock later
+ comb += state_r_msr.ren.eq(1 << StateRegs.MSR)
+ sync += msr_read.eq(0)
+
+ m.next = "INSN_READ" # move to "wait for bus" phase
+
+ # dummy pause to find out why simulation is not keeping up
+ with m.State("INSN_READ"):
+ if self.allow_overlap:
+ stopping = dbg.stopping_o
+ else:
+ stopping = Const(0)
+ with m.If(stopping):
+ # stopping: jump back to idle
+ m.next = "IDLE"
+ with m.Else():
+ # one cycle later, msr/sv read arrives. valid only once.
+ with m.If(~msr_read):
+ sync += msr_read.eq(1) # yeah don't read it again
+ sync += cur_state.msr.eq(state_r_msr.o_data)
+ with m.If(self.imem.f_busy_o): # zzz...
+ # busy: stay in wait-read
+ comb += self.imem.a_i_valid.eq(1)
+ comb += self.imem.f_i_valid.eq(1)
+ with m.Else():
+ # not busy: instruction fetched
+ insn = get_insn(self.imem.f_instr_o, cur_state.pc)
+ if self.svp64_en:
+ svp64 = self.svp64
+ # decode the SVP64 prefix, if any
+ comb += svp64.raw_opcode_in.eq(insn)
+ comb += svp64.bigendian.eq(self.core_bigendian_i)
+ # pass the decoded prefix (if any) to PowerDecoder2
+ sync += pdecode2.sv_rm.eq(svp64.svp64_rm)
+ sync += pdecode2.is_svp64_mode.eq(is_svp64_mode)
+ # remember whether this is a prefixed instruction,
+ # so the FSM can readily loop when VL==0
+ sync += is_svp64_mode.eq(svp64.is_svp64_mode)
+ # calculate the address of the following instruction
+ insn_size = Mux(svp64.is_svp64_mode, 8, 4)
+ sync += nia.eq(cur_state.pc + insn_size)
+ with m.If(~svp64.is_svp64_mode):
+ # with no prefix, store the instruction
+ # and hand it directly to the next FSM
+ sync += dec_opcode_o.eq(insn)
+ m.next = "INSN_READY"
+ with m.Else():
+ # fetch the rest of the instruction from memory
+ comb += self.imem.a_pc_i.eq(cur_state.pc + 4)
+ comb += self.imem.a_i_valid.eq(1)
+ comb += self.imem.f_i_valid.eq(1)
+ m.next = "INSN_READ2"
+ else:
+ # not SVP64 - 32-bit only
+ sync += nia.eq(cur_state.pc + 4)
+ sync += dec_opcode_o.eq(insn)
+ m.next = "INSN_READY"
+
+ with m.State("INSN_READ2"):
+ with m.If(self.imem.f_busy_o): # zzz...
+ # busy: stay in wait-read
+ comb += self.imem.a_i_valid.eq(1)
+ comb += self.imem.f_i_valid.eq(1)
+ with m.Else():
+ # not busy: instruction fetched
+ insn = get_insn(self.imem.f_instr_o, cur_state.pc+4)
+ sync += dec_opcode_o.eq(insn)
+ m.next = "INSN_READY"
+ # TODO: probably can start looking at pdecode2.rm_dec
+ # here or maybe even in INSN_READ state, if svp64_mode
+ # detected, in order to trigger - and wait for - the
+ # predicate reading.
+ if self.svp64_en:
+ pmode = pdecode2.rm_dec.predmode
+ """
+ if pmode != SVP64PredMode.ALWAYS.value:
+ fire predicate loading FSM and wait before
+ moving to INSN_READY
+ else:
+ sync += self.srcmask.eq(-1) # set to all 1s
+ sync += self.dstmask.eq(-1) # set to all 1s
+ m.next = "INSN_READY"
+ """
+
+ with m.State("INSN_READY"):
+ # hand over the instruction, to be decoded
+ comb += fetch_insn_o_valid.eq(1)
+ with m.If(fetch_insn_i_ready):
+ m.next = "IDLE"
+
+ # whatever was done above, over-ride it if core reset is held
+ with m.If(self.core_rst):
+ sync += nia.eq(0)
+
+ return m
+
+
class TestIssuerInternal(Elaboratable):
"""TestIssuer - reads instructions from TestMemory and issues them
and code clarity is. optimisations (which almost 100% interfere with
easy understanding) come later.
"""
+
def __init__(self, pspec):
# test is SVP64 is to be enabled
# and if regfiles are reduced
self.regreduce_en = (hasattr(pspec, "regreduce") and
- (pspec.regreduce == True))
+ (pspec.regreduce == True))
+
+ # and if overlap requested
+ self.allow_overlap = (hasattr(pspec, "allow_overlap") and
+ (pspec.allow_overlap == True))
# JTAG interface. add this right at the start because if it's
# added it *modifies* the pspec, by adding enable/disable signals
# for parts of the rest of the core
self.jtag_en = hasattr(pspec, "debug") and pspec.debug == 'jtag'
- self.dbg_domain = "sync" # sigh "dbgsunc" too problematic
- #self.dbg_domain = "dbgsync" # domain for DMI/JTAG clock
+ self.dbg_domain = "sync" # sigh "dbgsunc" too problematic
+ # self.dbg_domain = "dbgsync" # domain for DMI/JTAG clock
if self.jtag_en:
# XXX MUST keep this up-to-date with litex, and
# soc-cocotb-sim, and err.. all needs sorting out, argh
'eint', 'gpio', 'mspi0',
# 'mspi1', - disabled for now
# 'pwm', 'sd0', - disabled for now
- 'sdr']
+ 'sdr']
self.jtag = JTAG(get_pinspecs(subset=subset),
domain=self.dbg_domain)
# add signals to pspec to enable/disable icache and dcache
self.sram4k = []
for i in range(4):
self.sram4k.append(SPBlock512W64B8W(name="sram4k_%d" % i,
- #features={'err'}
+ # features={'err'}
))
# add interrupt controller?
# instruction decoder. goes into Trap Record
#pdecode = create_pdecode()
- self.cur_state = CoreState("cur") # current state (MSR/PC/SVSTATE)
+ self.cur_state = CoreState("cur") # current state (MSR/PC/SVSTATE)
self.pdecode2 = PowerDecode2(None, state=self.cur_state,
opkls=IssuerDecode2ToOperand,
svp64_en=self.svp64_en,
pdecode = self.pdecode2.dec
if self.svp64_en:
- self.svp64 = SVP64PrefixDecoder() # for decoding SVP64 prefix
+ self.svp64 = SVP64PrefixDecoder() # for decoding SVP64 prefix
# Test Instruction memory
self.imem = ConfigFetchUnit(pspec).fu
# instruction go/monitor
self.pc_o = Signal(64, reset_less=True)
- self.pc_i = Data(64, "pc_i") # set "ok" to indicate "please change me"
- self.svstate_i = Data(64, "svstate_i") # ditto
- self.core_bigendian_i = Signal() # TODO: set based on MSR.LE
+ self.pc_i = Data(64, "pc_i") # set "ok" to indicate "please change me"
+ self.svstate_i = Data(64, "svstate_i") # ditto
+ self.core_bigendian_i = Signal() # TODO: set based on MSR.LE
self.busy_o = Signal(reset_less=True)
self.memerr_o = Signal(reset_less=True)
# STATE regfile read /write ports for PC, MSR, SVSTATE
staterf = self.core.regs.rf['state']
- self.state_r_pc = staterf.r_ports['cia'] # PC rd
- self.state_w_pc = staterf.w_ports['d_wr1'] # PC wr
- self.state_r_msr = staterf.r_ports['msr'] # MSR rd
- self.state_r_sv = staterf.r_ports['sv'] # SVSTATE rd
- self.state_w_sv = staterf.w_ports['sv'] # SVSTATE wr
+ self.state_r_pc = staterf.r_ports['cia'] # PC rd
+ self.state_w_pc = staterf.w_ports['d_wr1'] # PC wr
+ self.state_r_sv = staterf.r_ports['sv'] # SVSTATE rd
+ self.state_w_sv = staterf.w_ports['sv'] # SVSTATE wr
# DMI interface access
intrf = self.core.regs.rf['int']
crrf = self.core.regs.rf['cr']
xerrf = self.core.regs.rf['xer']
- self.int_r = intrf.r_ports['dmi'] # INT read
- self.cr_r = crrf.r_ports['full_cr_dbg'] # CR read
- self.xer_r = xerrf.r_ports['full_xer'] # XER read
+ self.int_r = intrf.r_ports['dmi'] # INT read
+ self.cr_r = crrf.r_ports['full_cr_dbg'] # CR read
+ self.xer_r = xerrf.r_ports['full_xer'] # XER read
if self.svp64_en:
# for predication
- self.int_pred = intrf.r_ports['pred'] # INT predicate read
- self.cr_pred = crrf.r_ports['cr_pred'] # CR predicate read
+ self.int_pred = intrf.r_ports['pred'] # INT predicate read
+ self.cr_pred = crrf.r_ports['cr_pred'] # CR predicate read
# hack method of keeping an eye on whether branch/trap set the PC
self.state_nia = self.core.regs.rf['state'].w_ports['nia']
# pulse to synchronize the simulator at instruction end
self.insn_done = Signal()
+ # indicate any instruction still outstanding, in execution
+ self.any_busy = Signal()
+
if self.svp64_en:
# store copies of predicate masks
self.srcmask = Signal(64)
self.dstmask = Signal(64)
- def fetch_fsm(self, m, core, pc, svstate, nia, is_svp64_mode,
- fetch_pc_o_ready, fetch_pc_i_valid,
- fetch_insn_o_valid, fetch_insn_i_ready):
- """fetch FSM
-
- this FSM performs fetch of raw instruction data, partial-decodes
- it 32-bit at a time to detect SVP64 prefixes, and will optionally
- read a 2nd 32-bit quantity if that occurs.
- """
- comb = m.d.comb
- sync = m.d.sync
- pdecode2 = self.pdecode2
- cur_state = self.cur_state
- dec_opcode_i = pdecode2.dec.raw_opcode_in # raw opcode
-
- msr_read = Signal(reset=1)
-
- with m.FSM(name='fetch_fsm'):
-
- # waiting (zzz)
- with m.State("IDLE"):
- comb += fetch_pc_o_ready.eq(1)
- with m.If(fetch_pc_i_valid):
- # instruction allowed to go: start by reading the PC
- # capture the PC and also drop it into Insn Memory
- # we have joined a pair of combinatorial memory
- # lookups together. this is Generally Bad.
- comb += self.imem.a_pc_i.eq(pc)
- comb += self.imem.a_i_valid.eq(1)
- comb += self.imem.f_i_valid.eq(1)
- sync += cur_state.pc.eq(pc)
- sync += cur_state.svstate.eq(svstate) # and svstate
-
- # initiate read of MSR. arrives one clock later
- comb += self.state_r_msr.ren.eq(1 << StateRegs.MSR)
- sync += msr_read.eq(0)
-
- m.next = "INSN_READ" # move to "wait for bus" phase
-
- # dummy pause to find out why simulation is not keeping up
- with m.State("INSN_READ"):
- # one cycle later, msr/sv read arrives. valid only once.
- with m.If(~msr_read):
- sync += msr_read.eq(1) # yeah don't read it again
- sync += cur_state.msr.eq(self.state_r_msr.o_data)
- with m.If(self.imem.f_busy_o): # zzz...
- # busy: stay in wait-read
- comb += self.imem.a_i_valid.eq(1)
- comb += self.imem.f_i_valid.eq(1)
- with m.Else():
- # not busy: instruction fetched
- insn = get_insn(self.imem.f_instr_o, cur_state.pc)
- if self.svp64_en:
- svp64 = self.svp64
- # decode the SVP64 prefix, if any
- comb += svp64.raw_opcode_in.eq(insn)
- comb += svp64.bigendian.eq(self.core_bigendian_i)
- # pass the decoded prefix (if any) to PowerDecoder2
- sync += pdecode2.sv_rm.eq(svp64.svp64_rm)
- sync += pdecode2.is_svp64_mode.eq(is_svp64_mode)
- # remember whether this is a prefixed instruction, so
- # the FSM can readily loop when VL==0
- sync += is_svp64_mode.eq(svp64.is_svp64_mode)
- # calculate the address of the following instruction
- insn_size = Mux(svp64.is_svp64_mode, 8, 4)
- sync += nia.eq(cur_state.pc + insn_size)
- with m.If(~svp64.is_svp64_mode):
- # with no prefix, store the instruction
- # and hand it directly to the next FSM
- sync += dec_opcode_i.eq(insn)
- m.next = "INSN_READY"
- with m.Else():
- # fetch the rest of the instruction from memory
- comb += self.imem.a_pc_i.eq(cur_state.pc + 4)
- comb += self.imem.a_i_valid.eq(1)
- comb += self.imem.f_i_valid.eq(1)
- m.next = "INSN_READ2"
- else:
- # not SVP64 - 32-bit only
- sync += nia.eq(cur_state.pc + 4)
- sync += dec_opcode_i.eq(insn)
- m.next = "INSN_READY"
-
- with m.State("INSN_READ2"):
- with m.If(self.imem.f_busy_o): # zzz...
- # busy: stay in wait-read
- comb += self.imem.a_i_valid.eq(1)
- comb += self.imem.f_i_valid.eq(1)
- with m.Else():
- # not busy: instruction fetched
- insn = get_insn(self.imem.f_instr_o, cur_state.pc+4)
- sync += dec_opcode_i.eq(insn)
- m.next = "INSN_READY"
- # TODO: probably can start looking at pdecode2.rm_dec
- # here or maybe even in INSN_READ state, if svp64_mode
- # detected, in order to trigger - and wait for - the
- # predicate reading.
- if self.svp64_en:
- pmode = pdecode2.rm_dec.predmode
- """
- if pmode != SVP64PredMode.ALWAYS.value:
- fire predicate loading FSM and wait before
- moving to INSN_READY
- else:
- sync += self.srcmask.eq(-1) # set to all 1s
- sync += self.dstmask.eq(-1) # set to all 1s
- m.next = "INSN_READY"
- """
-
- with m.State("INSN_READY"):
- # hand over the instruction, to be decoded
- comb += fetch_insn_o_valid.eq(1)
- with m.If(fetch_insn_i_ready):
- m.next = "IDLE"
-
def fetch_predicate_fsm(self, m,
pred_insn_i_valid, pred_insn_o_ready,
pred_mask_o_valid, pred_mask_i_ready):
comb = m.d.comb
sync = m.d.sync
pdecode2 = self.pdecode2
- rm_dec = pdecode2.rm_dec # SVP64RMModeDecode
+ rm_dec = pdecode2.rm_dec # SVP64RMModeDecode
predmode = rm_dec.predmode
srcpred, dstpred = rm_dec.srcpred, rm_dec.dstpred
cr_pred, int_pred = self.cr_pred, self.int_pred # read regfiles
scr_bit = Signal()
dcr_bit = Signal()
comb += cr_field.eq(cr_pred.o_data)
- comb += scr_bit.eq(cr_field.bit_select(sidx, 1) ^ scrinvert)
- comb += dcr_bit.eq(cr_field.bit_select(didx, 1) ^ dcrinvert)
+ comb += scr_bit.eq(cr_field.bit_select(sidx, 1)
+ ^ scrinvert)
+ comb += dcr_bit.eq(cr_field.bit_select(didx, 1)
+ ^ dcrinvert)
# set the corresponding mask bit
bit_to_set = Signal.like(self.srcmask)
comb += bit_to_set.eq(1 << cur_cr_idx)
cur_state = self.cur_state
# temporaries
- dec_opcode_i = pdecode2.dec.raw_opcode_in # raw opcode
+ dec_opcode_i = pdecode2.dec.raw_opcode_in # raw opcode
# for updating svstate (things like srcstep etc.)
- update_svstate = Signal() # set this (below) if updating
+ update_svstate = Signal() # set this (below) if updating
new_svstate = SVSTATERec("new_svstate")
comb += new_svstate.eq(cur_state.svstate)
# note if an exception happened. in a pipelined or OoO design
# this needs to be accompanied by "shadowing" (or stalling)
- el = []
- for exc in core.fus.excs.values():
- el.append(exc.happened)
- exc_happened = Signal()
- if len(el) > 0: # at least one exception
- comb += exc_happened.eq(Cat(*el).bool())
+ exc_happened = self.core.o.exc_happened
+ # also note instruction fetch failed
+ if hasattr(core, "icache"):
+ fetch_failed = core.icache.i_out.fetch_failed
+ else:
+ fetch_failed = Const(0, 1)
+ # set to zero initially
+ sync += pdecode2.instr_fault.eq(0)
with m.FSM(name="issue_fsm"):
# wait on "core stop" release, before next fetch
# need to do this here, in case we are in a VL==0 loop
with m.If(~dbg.core_stop_o & ~core_rst):
- comb += fetch_pc_i_valid.eq(1) # tell fetch to start
+ comb += fetch_pc_i_valid.eq(1) # tell fetch to start
with m.If(fetch_pc_o_ready): # fetch acknowledged us
m.next = "INSN_WAIT"
with m.Else():
# wait for an instruction to arrive from Fetch
with m.State("INSN_WAIT"):
- comb += fetch_insn_i_ready.eq(1)
- with m.If(fetch_insn_o_valid):
- # loop into ISSUE_START if it's a SVP64 instruction
- # and VL == 0. this because VL==0 is a for-loop
- # from 0 to 0 i.e. always, always a NOP.
- cur_vl = cur_state.svstate.vl
- with m.If(is_svp64_mode & (cur_vl == 0)):
- # update the PC before fetching the next instruction
- # since we are in a VL==0 loop, no instruction was
- # executed that we could be overwriting
- comb += self.state_w_pc.wen.eq(1 << StateRegs.PC)
- comb += self.state_w_pc.i_data.eq(nia)
- comb += self.insn_done.eq(1)
- m.next = "ISSUE_START"
- with m.Else():
- if self.svp64_en:
- m.next = "PRED_START" # start fetching predicate
- else:
- m.next = "DECODE_SV" # skip predication
+ if self.allow_overlap:
+ stopping = dbg.stopping_o
+ else:
+ stopping = Const(0)
+ with m.If(stopping):
+ # stopping: jump back to idle
+ m.next = "ISSUE_START"
+ with m.Else():
+ comb += fetch_insn_i_ready.eq(1)
+ with m.If(fetch_insn_o_valid):
+ # loop into ISSUE_START if it's a SVP64 instruction
+ # and VL == 0. this because VL==0 is a for-loop
+ # from 0 to 0 i.e. always, always a NOP.
+ cur_vl = cur_state.svstate.vl
+ with m.If(is_svp64_mode & (cur_vl == 0)):
+ # update the PC before fetching the next instruction
+ # since we are in a VL==0 loop, no instruction was
+ # executed that we could be overwriting
+ comb += self.state_w_pc.wen.eq(1 << StateRegs.PC)
+ comb += self.state_w_pc.i_data.eq(nia)
+ comb += self.insn_done.eq(1)
+ m.next = "ISSUE_START"
+ with m.Else():
+ if self.svp64_en:
+ m.next = "PRED_START" # fetching predicate
+ else:
+ m.next = "DECODE_SV" # skip predication
with m.State("PRED_START"):
comb += pred_insn_i_valid.eq(1) # tell fetch_pred to start
m.next = "MASK_WAIT"
with m.State("MASK_WAIT"):
- comb += pred_mask_i_ready.eq(1) # ready to receive the masks
- with m.If(pred_mask_o_valid): # predication masks are ready
+ comb += pred_mask_i_ready.eq(1) # ready to receive the masks
+ with m.If(pred_mask_o_valid): # predication masks are ready
m.next = "PRED_SKIP"
# skip zeros in predicate
# pass predicate mask bits through to satellite decoders
# TODO: for SIMD this will be *multiple* bits
- sync += core.sv_pred_sm.eq(self.srcmask[0])
- sync += core.sv_pred_dm.eq(self.dstmask[0])
+ sync += core.i.sv_pred_sm.eq(self.srcmask[0])
+ sync += core.i.sv_pred_dm.eq(self.dstmask[0])
# after src/dst step have been updated, we are ready
# to decode the instruction
with m.State("DECODE_SV"):
# decode the instruction
- sync += core.e.eq(pdecode2.e)
- sync += core.state.eq(cur_state)
- sync += core.raw_insn_i.eq(dec_opcode_i)
- sync += core.bigendian_i.eq(self.core_bigendian_i)
+ sync += core.i.e.eq(pdecode2.e)
+ sync += core.i.state.eq(cur_state)
+ sync += core.i.raw_insn_i.eq(dec_opcode_i)
+ sync += core.i.bigendian_i.eq(self.core_bigendian_i)
if self.svp64_en:
- sync += core.sv_rm.eq(pdecode2.sv_rm)
+ sync += core.i.sv_rm.eq(pdecode2.sv_rm)
# set RA_OR_ZERO detection in satellite decoders
- sync += core.sv_a_nz.eq(pdecode2.sv_a_nz)
+ sync += core.i.sv_a_nz.eq(pdecode2.sv_a_nz)
# and svp64 detection
- sync += core.is_svp64_mode.eq(is_svp64_mode)
+ sync += core.i.is_svp64_mode.eq(is_svp64_mode)
# and svp64 bit-rev'd ldst mode
ldst_dec = pdecode2.use_svp64_ldst_dec
- sync += core.use_svp64_ldst_dec.eq(ldst_dec)
+ sync += core.i.use_svp64_ldst_dec.eq(ldst_dec)
+ # after decoding, reset any previous exception condition,
+ # allowing it to be set again during the next execution
+ sync += pdecode2.ldst_exc.eq(0)
+
+ # update (highest priority) instruction fault
+ sync += pdecode2.instr_fault.eq(fetch_failed)
m.next = "INSN_EXECUTE" # move to "execute"
# handshake with execution FSM, move to "wait" once acknowledged
with m.State("INSN_EXECUTE"):
- comb += exec_insn_i_valid.eq(1) # trigger execute
+ comb += exec_insn_i_valid.eq(1) # trigger execute
with m.If(exec_insn_o_ready): # execute acknowledged us
m.next = "EXECUTE_WAIT"
with m.If(~dbg.core_stop_o & ~core_rst):
comb += exec_pc_i_ready.eq(1)
# see https://bugs.libre-soc.org/show_bug.cgi?id=636
- #with m.If(exec_pc_o_valid & exc_happened):
- # probably something like this:
- # sync += pdecode2.ldst_exc.eq(core.fus.get_exc("ldst0")
- # TODO: the exception info needs to be blatted
- # into pdecode.ldst_exc, and the instruction "re-run".
+ # the exception info needs to be blatted into
+ # pdecode.ldst_exc, and the instruction "re-run".
# when ldst_exc.happened is set, the PowerDecoder2
# reacts very differently: it re-writes the instruction
# with a "trap" (calls PowerDecoder2.trap()) which
# PC to the exception address, as well as alter MSR.
# nothing else needs to be done other than to note
# the change of PC and MSR (and, later, SVSTATE)
- #with m.Elif(exec_pc_o_valid):
- with m.If(exec_pc_o_valid): # replace with Elif (above)
+ with m.If(exc_happened):
+ sync += pdecode2.ldst_exc.eq(core.fus.get_exc("ldst0"))
+
+ with m.If(exec_pc_o_valid):
# was this the last loop iteration?
is_last = Signal()
cur_vl = cur_state.svstate.vl
comb += is_last.eq(next_srcstep == cur_vl)
+ # return directly to Decode if Execute generated an
+ # exception.
+ with m.If(pdecode2.ldst_exc.happened):
+ m.next = "DECODE_SV"
+
# if either PC or SVSTATE were changed by the previous
# instruction, go directly back to Fetch, without
# updating either PC or SVSTATE
- with m.If(pc_changed | sv_changed):
+ with m.Elif(pc_changed | sv_changed):
m.next = "ISSUE_START"
# also return to Fetch, when no output was a vector
# check if svstate needs updating: if so, write it to State Regfile
with m.If(update_svstate):
- comb += self.state_w_sv.wen.eq(1<<StateRegs.SVSTATE)
+ comb += self.state_w_sv.wen.eq(1 << StateRegs.SVSTATE)
comb += self.state_w_sv.i_data.eq(new_svstate)
- sync += cur_state.svstate.eq(new_svstate) # for next clock
+ sync += cur_state.svstate.eq(new_svstate) # for next clock
def execute_fsm(self, m, core, pc_changed, sv_changed,
exec_insn_i_valid, exec_insn_o_ready,
pdecode2 = self.pdecode2
# temporaries
- core_busy_o = core.busy_o # core is busy
- core_ivalid_i = core.ivalid_i # instruction is valid
- core_issue_i = core.issue_i # instruction is issued
- insn_type = core.e.do.insn_type # instruction MicroOp type
+ core_busy_o = core.n.o_data.busy_o # core is busy
+ core_ivalid_i = core.p.i_valid # instruction is valid
with m.FSM(name="exec_fsm"):
with m.State("INSN_START"):
comb += exec_insn_o_ready.eq(1)
with m.If(exec_insn_i_valid):
- comb += core_ivalid_i.eq(1) # instruction is valid
- comb += core_issue_i.eq(1) # and issued
+ comb += core_ivalid_i.eq(1) # instruction is valid/issued
sync += sv_changed.eq(0)
sync += pc_changed.eq(0)
- m.next = "INSN_ACTIVE" # move to "wait completion"
+ with m.If(core.p.o_ready): # only move if accepted
+ m.next = "INSN_ACTIVE" # move to "wait completion"
# instruction started: must wait till it finishes
with m.State("INSN_ACTIVE"):
- with m.If(insn_type != MicrOp.OP_NOP):
- comb += core_ivalid_i.eq(1) # instruction is valid
# note changes to PC and SVSTATE
- with m.If(self.state_nia.wen & (1<<StateRegs.SVSTATE)):
+ with m.If(self.state_nia.wen & (1 << StateRegs.SVSTATE)):
sync += sv_changed.eq(1)
- with m.If(self.state_nia.wen & (1<<StateRegs.PC)):
+ with m.If(self.state_nia.wen & (1 << StateRegs.PC)):
sync += pc_changed.eq(1)
- with m.If(~core_busy_o): # instruction done!
+ with m.If(~core_busy_o): # instruction done!
comb += exec_pc_o_valid.eq(1)
with m.If(exec_pc_i_ready):
- comb += self.insn_done.eq(1)
+ # when finished, indicate "done".
+ # however, if there was an exception, the instruction
+ # is *not* yet done. this is an implementation
+ # detail: we choose to implement exceptions by
+ # taking the exception information from the LDST
+ # unit, putting that *back* into the PowerDecoder2,
+ # and *re-running the entire instruction*.
+ # if we erroneously indicate "done" here, it is as if
+ # there were *TWO* instructions:
+ # 1) the failed LDST 2) a TRAP.
+ with m.If(~pdecode2.ldst_exc.happened):
+ comb += self.insn_done.eq(1)
m.next = "INSN_START" # back to fetch
def setup_peripherals(self, m):
m.submodules.xics_icp = icp = csd(self.xics_icp)
m.submodules.xics_ics = ics = csd(self.xics_ics)
comb += icp.ics_i.eq(ics.icp_o) # connect ICS to ICP
- sync += cur_state.eint.eq(icp.core_irq_o) # connect ICP to core
+ sync += cur_state.eint.eq(icp.core_irq_o) # connect ICP to core
# GPIO test peripheral
if self.gpio:
# connect one GPIO output to ICS bit 15 (like in microwatt soc.vhdl)
# XXX causes litex ECP5 test to get wrong idea about input and output
# (but works with verilator sim *sigh*)
- #if self.gpio and self.xics:
+ # if self.gpio and self.xics:
# comb += self.int_level_i[15].eq(simple_gpio.gpio_o[0])
# instruction decoder
intrf = self.core.regs.rf['int']
# clock delay power-on reset
- cd_por = ClockDomain(reset_less=True)
+ cd_por = ClockDomain(reset_less=True)
cd_sync = ClockDomain()
core_sync = ClockDomain("coresync")
m.domains += cd_por, cd_sync, core_sync
comb += dbg_rst.eq(ResetSignal())
# busy/halted signals from core
- comb += self.busy_o.eq(core.busy_o)
+ core_busy_o = ~core.p.o_ready | core.n.o_data.busy_o # core is busy
+ comb += self.busy_o.eq(core_busy_o)
comb += pdecode2.dec.bigendian.eq(self.core_bigendian_i)
# temporary hack: says "go" immediately for both address gen and ST
l0 = core.l0
ldst = core.fus.fus['ldst0']
st_go_edge = rising_edge(m, ldst.st.rel_o)
- m.d.comb += ldst.ad.go_i.eq(ldst.ad.rel_o) # link addr-go direct to rel
- m.d.comb += ldst.st.go_i.eq(st_go_edge) # link store-go to rising rel
+ # link addr-go direct to rel
+ m.d.comb += ldst.ad.go_i.eq(ldst.ad.rel_o)
+ m.d.comb += ldst.st.go_i.eq(st_go_edge) # link store-go to rising rel
def elaborate(self, platform):
m = Module()
# PC and instruction from I-Memory
comb += self.pc_o.eq(cur_state.pc)
- pc_changed = Signal() # note write to PC
- sv_changed = Signal() # note write to SVSTATE
+ pc_changed = Signal() # note write to PC
+ sv_changed = Signal() # note write to SVSTATE
+
+ # indicate to outside world if any FU is still executing
+ comb += self.any_busy.eq(core.n.o_data.any_busy_o) # any FU executing
# read state either from incoming override or from regfile
# TODO: really should be doing MSR in the same way
pc = state_get(m, core_rst, self.pc_i,
- "pc", # read PC
- self.state_r_pc, StateRegs.PC)
+ "pc", # read PC
+ self.state_r_pc, StateRegs.PC)
svstate = state_get(m, core_rst, self.svstate_i,
"svstate", # read SVSTATE
self.state_r_sv, StateRegs.SVSTATE)
comb += self.state_w_pc.wen.eq(0)
comb += self.state_w_pc.i_data.eq(0)
- # don't read msr every cycle
- comb += self.state_r_msr.ren.eq(0)
-
# address of the next instruction, in the absence of a branch
# depends on the instruction size
nia = Signal(64)
# connect up debug signals
# TODO comb += core.icache_rst_i.eq(dbg.icache_rst_o)
- comb += dbg.terminate_i.eq(core.core_terminate_o)
+ comb += dbg.terminate_i.eq(core.o.core_terminate_o)
comb += dbg.state.pc.eq(pc)
comb += dbg.state.svstate.eq(svstate)
comb += dbg.state.msr.eq(cur_state.msr)
# these are the handshake signals between each
# fetch FSM can run as soon as the PC is valid
- fetch_pc_i_valid = Signal() # Execute tells Fetch "start next read"
- fetch_pc_o_ready = Signal() # Fetch Tells SVSTATE "proceed"
+ fetch_pc_i_valid = Signal() # Execute tells Fetch "start next read"
+ fetch_pc_o_ready = Signal() # Fetch Tells SVSTATE "proceed"
# fetch FSM hands over the instruction to be decoded / issued
fetch_insn_o_valid = Signal()
# Issue is where the VL for-loop # lives. the ready/valid
# signalling is used to communicate between the four.
- self.fetch_fsm(m, core, pc, svstate, nia, is_svp64_mode,
- fetch_pc_o_ready, fetch_pc_i_valid,
- fetch_insn_o_valid, fetch_insn_i_ready)
+ # set up Fetch FSM
+ fetch = FetchFSM(self.allow_overlap, self.svp64_en,
+ self.imem, core_rst, pdecode2, cur_state,
+ dbg, core, svstate, nia, is_svp64_mode)
+ m.submodules.fetch = fetch
+ # connect up in/out data to existing Signals
+ comb += fetch.p.i_data.pc.eq(pc)
+ # and the ready/valid signalling
+ comb += fetch_pc_o_ready.eq(fetch.p.o_ready)
+ comb += fetch.p.i_valid.eq(fetch_pc_i_valid)
+ comb += fetch_insn_o_valid.eq(fetch.n.o_valid)
+ comb += fetch.n.i_ready.eq(fetch_insn_i_ready)
self.issue_fsm(m, core, pc_changed, sv_changed, nia,
dbg, core_rst, is_svp64_mode,
exec_insn_i_valid, exec_insn_o_ready,
exec_pc_o_valid, exec_pc_i_ready)
- # whatever was done above, over-ride it if core reset is held
- with m.If(core_rst):
- sync += nia.eq(0)
-
# this bit doesn't have to be in the FSM: connect up to read
# regfiles on demand from DMI
self.do_dmi(m, dbg)
dmi, d_reg, d_cr, d_xer, = dbg.dmi, dbg.d_gpr, dbg.d_cr, dbg.d_xer
intrf = self.core.regs.rf['int']
- with m.If(d_reg.req): # request for regfile access being made
+ with m.If(d_reg.req): # request for regfile access being made
# TODO: error-check this
# XXX should this be combinatorial? sync better?
if intrf.unary:
- comb += self.int_r.ren.eq(1<<d_reg.addr)
+ comb += self.int_r.ren.eq(1 << d_reg.addr)
else:
comb += self.int_r.addr.eq(d_reg.addr)
comb += self.int_r.ren.eq(1)
- d_reg_delay = Signal()
+ d_reg_delay = Signal()
sync += d_reg_delay.eq(d_reg.req)
with m.If(d_reg_delay):
# data arrives one clock later
comb += d_reg.ack.eq(1)
# sigh same thing for CR debug
- with m.If(d_cr.req): # request for regfile access being made
- comb += self.cr_r.ren.eq(0b11111111) # enable all
- d_cr_delay = Signal()
+ with m.If(d_cr.req): # request for regfile access being made
+ comb += self.cr_r.ren.eq(0b11111111) # enable all
+ d_cr_delay = Signal()
sync += d_cr_delay.eq(d_cr.req)
with m.If(d_cr_delay):
# data arrives one clock later
comb += d_cr.ack.eq(1)
# aaand XER...
- with m.If(d_xer.req): # request for regfile access being made
- comb += self.xer_r.ren.eq(0b111111) # enable all
- d_xer_delay = Signal()
+ with m.If(d_xer.req): # request for regfile access being made
+ comb += self.xer_r.ren.eq(0b111111) # enable all
+ d_xer_delay = Signal()
sync += d_xer_delay.eq(d_xer.req)
with m.If(d_xer_delay):
# data arrives one clock later
comb, sync = m.d.comb, m.d.sync
fast_rf = self.core.regs.rf['fast']
- fast_r_dectb = fast_rf.r_ports['issue'] # DEC/TB
- fast_w_dectb = fast_rf.w_ports['issue'] # DEC/TB
+ fast_r_dectb = fast_rf.r_ports['issue'] # DEC/TB
+ fast_w_dectb = fast_rf.w_ports['issue'] # DEC/TB
with m.FSM() as fsm:
comb += fast_w_dectb.addr.eq(FastRegs.DEC)
comb += fast_w_dectb.wen.eq(1)
comb += fast_w_dectb.i_data.eq(new_dec)
- sync += spr_dec.eq(new_dec) # copy into cur_state for decoder
+ sync += spr_dec.eq(new_dec) # copy into cur_state for decoder
m.next = "TB_READ"
# initiates read of current TB
def external_ports(self):
ports = self.pc_i.ports()
ports += [self.pc_o, self.memerr_o, self.core_bigendian_i, self.busy_o,
- ]
+ ]
if self.jtag_en:
ports += list(self.jtag.external_ports())
self.pll_test_o = Signal(reset_less=True)
self.pll_vco_o = Signal(reset_less=True)
self.clk_sel_i = Signal(2, reset_less=True)
- self.ref_clk = ClockSignal() # can't rename it but that's ok
+ self.ref_clk = ClockSignal() # can't rename it but that's ok
self.pllclk_clk = ClockSignal("pllclk")
def elaborate(self, platform):
def ports(self):
return list(self.ti.ports()) + list(self.pll.ports()) + \
- [ClockSignal(), ResetSignal()]
+ [ClockSignal(), ResetSignal()]
def external_ports(self):
ports = self.ti.external_ports()
'div': 1,
'mul': 1,
'shiftrot': 1
- }
+ }
pspec = TestMemPspec(ldst_ifacetype='bare_wb',
imem_ifacetype='bare_wb',
addr_wid=48,