mirror of
https://github.com/SpinalHDL/VexRiscv.git
synced 2025-04-24 14:07:54 -04:00
605 lines
No EOL
25 KiB
Scala
605 lines
No EOL
25 KiB
Scala
package vexriscv.plugin
|
|
|
|
import vexriscv._
|
|
import spinal.core._
|
|
import spinal.lib._
|
|
import vexriscv.Riscv.IMM
|
|
import StreamVexPimper._
|
|
import scala.collection.mutable.ArrayBuffer
|
|
|
|
|
|
//TODO val killLastStage = jump.pcLoad.valid || decode.arbitration.isRemoved
|
|
// DBUSSimple check memory halt execute optimization
|
|
|
|
abstract class IBusFetcherImpl(val resetVector : BigInt,
|
|
val keepPcPlus4 : Boolean,
|
|
val decodePcGen : Boolean,
|
|
val compressedGen : Boolean,
|
|
val cmdToRspStageCount : Int,
|
|
val allowPcRegReusedForSecondStage : Boolean,
|
|
val injectorReadyCutGen : Boolean,
|
|
val prediction : BranchPrediction,
|
|
val historyRamSizeLog2 : Int,
|
|
val injectorStage : Boolean,
|
|
val relaxPredictorAddress : Boolean,
|
|
val fetchRedoGen : Boolean) extends Plugin[VexRiscv] with JumpService with IBusFetcher{
|
|
var prefetchExceptionPort : Flow[ExceptionCause] = null
|
|
var decodePrediction : DecodePredictionBus = null
|
|
var fetchPrediction : FetchPredictionBus = null
|
|
var dynamicTargetFailureCorrection : Flow[UInt] = null
|
|
var externalResetVector : UInt = null
|
|
assert(cmdToRspStageCount >= 1)
|
|
// assert(!(cmdToRspStageCount == 1 && !injectorStage))
|
|
assert(!(compressedGen && !decodePcGen))
|
|
var fetcherHalt : Bool = null
|
|
var fetcherflushIt : Bool = null
|
|
var pcValids : Vec[Bool] = null
|
|
def pcValid(stage : Stage) = pcValids(pipeline.indexOf(stage))
|
|
var incomingInstruction : Bool = null
|
|
override def incoming() = incomingInstruction
|
|
|
|
var injectionPort : Stream[Bits] = null
|
|
override def getInjectionPort() = {
|
|
injectionPort = Stream(Bits(32 bits))
|
|
injectionPort
|
|
}
|
|
def pcRegReusedForSecondStage = allowPcRegReusedForSecondStage && prediction != DYNAMIC_TARGET
|
|
var predictionJumpInterface : Flow[UInt] = null
|
|
|
|
override def haltIt(): Unit = fetcherHalt := True
|
|
override def flushIt(): Unit = fetcherflushIt := True
|
|
|
|
case class JumpInfo(interface : Flow[UInt], stage: Stage, priority : Int)
|
|
val jumpInfos = ArrayBuffer[JumpInfo]()
|
|
override def createJumpInterface(stage: Stage, priority : Int = 0): Flow[UInt] = {
|
|
assert(stage != null)
|
|
val interface = Flow(UInt(32 bits))
|
|
jumpInfos += JumpInfo(interface,stage, priority)
|
|
interface
|
|
}
|
|
|
|
|
|
// var decodeExceptionPort : Flow[ExceptionCause] = null
|
|
override def setup(pipeline: VexRiscv): Unit = {
|
|
fetcherHalt = False
|
|
fetcherflushIt = False
|
|
incomingInstruction = False
|
|
if(resetVector == null) externalResetVector = in(UInt(32 bits).setName("externalResetVector"))
|
|
|
|
pipeline(RVC_GEN) = compressedGen
|
|
|
|
prediction match {
|
|
case NONE =>
|
|
case STATIC | DYNAMIC => {
|
|
predictionJumpInterface = createJumpInterface(pipeline.decode)
|
|
decodePrediction = pipeline.service(classOf[PredictionInterface]).askDecodePrediction()
|
|
}
|
|
case DYNAMIC_TARGET => {
|
|
fetchPrediction = pipeline.service(classOf[PredictionInterface]).askFetchPrediction()
|
|
if(compressedGen){
|
|
dynamicTargetFailureCorrection = createJumpInterface(pipeline.decode)
|
|
}
|
|
}
|
|
}
|
|
|
|
pcValids = Vec(Bool, pipeline.stages.size)
|
|
}
|
|
|
|
object IBUS_RSP
|
|
object DECOMPRESSOR
|
|
object INJECTOR_M2S
|
|
|
|
def isDrivingDecode(s : Any): Boolean = {
|
|
if(injectorStage) return s == INJECTOR_M2S
|
|
if(compressedGen) return s == DECOMPRESSOR
|
|
s == IBUS_RSP
|
|
}
|
|
|
|
def getFlushAt(s : Any, lastCond : Boolean = true): Bool = {
|
|
if(isDrivingDecode(s) && lastCond) pipeline.decode.arbitration.isRemoved else fetcherflushIt
|
|
}
|
|
|
|
class FetchArea(pipeline : VexRiscv) extends Area {
|
|
import pipeline._
|
|
import pipeline.config._
|
|
|
|
//Arbitrate jump requests into pcLoad
|
|
val jump = new Area {
|
|
val sortedByStage = jumpInfos.sortWith((a, b) => {
|
|
(pipeline.indexOf(a.stage) > pipeline.indexOf(b.stage)) ||
|
|
(pipeline.indexOf(a.stage) == pipeline.indexOf(b.stage) && a.priority > b.priority)
|
|
})
|
|
val valids = sortedByStage.map(_.interface.valid)
|
|
val pcs = sortedByStage.map(_.interface.payload)
|
|
|
|
val pcLoad = Flow(UInt(32 bits))
|
|
pcLoad.valid := jumpInfos.map(_.interface.valid).orR
|
|
pcLoad.payload := MuxOH(OHMasking.first(valids.asBits), pcs)
|
|
}
|
|
|
|
fetcherflushIt setWhen(stages.map(_.arbitration.flushNext).orR)
|
|
|
|
//The fetchPC pcReg can also be use for the second stage of the fetch
|
|
//When the fetcherHalt is set and the pipeline isn't stalled,, the pc is propagated to to the pcReg, which allow
|
|
//using the pc pipeline to get the next PC value for interrupts
|
|
val fetchPc = new Area{
|
|
//PC calculation without Jump
|
|
val output = Stream(UInt(32 bits))
|
|
val pcReg = Reg(UInt(32 bits)) init(if(resetVector != null) resetVector else externalResetVector) addAttribute(Verilator.public)
|
|
val corrected = False
|
|
val pcRegPropagate = False
|
|
val booted = RegNext(True) init (False)
|
|
val inc = RegInit(False) clearWhen(corrected || pcRegPropagate) setWhen(output.fire) clearWhen(!output.valid && output.ready)
|
|
val pc = pcReg + (inc ## B"00").asUInt
|
|
val predictionPcLoad = ifGen(prediction == DYNAMIC_TARGET) (Flow(UInt(32 bits)))
|
|
val redo = fetchRedoGen generate Flow(UInt(32 bits))
|
|
val flushed = False
|
|
|
|
if(compressedGen) when(inc) {
|
|
pc(1) := False
|
|
}
|
|
|
|
if(predictionPcLoad != null) {
|
|
when(predictionPcLoad.valid) {
|
|
corrected := True
|
|
pc := predictionPcLoad.payload
|
|
}
|
|
}
|
|
if(fetchRedoGen) when(redo.valid){
|
|
corrected := True
|
|
pc := redo.payload
|
|
flushed := True
|
|
}
|
|
when(jump.pcLoad.valid) {
|
|
corrected := True
|
|
pc := jump.pcLoad.payload
|
|
flushed := True
|
|
}
|
|
|
|
when(booted && (output.ready || corrected || pcRegPropagate)){
|
|
pcReg := pc
|
|
}
|
|
|
|
pc(0) := False
|
|
if(!pipeline(RVC_GEN)) pc(1) := False
|
|
|
|
output.valid := !fetcherHalt && booted
|
|
output.payload := pc
|
|
}
|
|
|
|
val decodePc = ifGen(decodePcGen)(new Area {
|
|
//PC calculation without Jump
|
|
val flushed = False
|
|
val pcReg = Reg(UInt(32 bits)) init(if(resetVector != null) resetVector else externalResetVector) addAttribute(Verilator.public)
|
|
val pcPlus = if(compressedGen)
|
|
pcReg + ((decode.input(IS_RVC)) ? U(2) | U(4))
|
|
else
|
|
pcReg + 4
|
|
|
|
if (keepPcPlus4) KeepAttribute(pcPlus)
|
|
val injectedDecode = False
|
|
when(decode.arbitration.isFiring && !injectedDecode) {
|
|
pcReg := pcPlus
|
|
}
|
|
|
|
val predictionPcLoad = ifGen(prediction == DYNAMIC_TARGET) (Flow(UInt(32 bits)))
|
|
if(prediction == DYNAMIC_TARGET) {
|
|
when(predictionPcLoad.valid) {
|
|
pcReg := predictionPcLoad.payload
|
|
}
|
|
}
|
|
|
|
//application of the selected jump request
|
|
when(jump.pcLoad.valid && (!decode.arbitration.isStuck || decode.arbitration.isRemoved)) {
|
|
pcReg := jump.pcLoad.payload
|
|
flushed := True
|
|
}
|
|
})
|
|
|
|
|
|
case class FetchRsp() extends Bundle {
|
|
val pc = UInt(32 bits)
|
|
val rsp = IBusSimpleRsp()
|
|
val isRvc = Bool()
|
|
}
|
|
|
|
|
|
val iBusRsp = new Area {
|
|
val fetchFlush = False
|
|
val stages = Array.fill(cmdToRspStageCount + 1)(new Bundle {
|
|
val input = Stream(UInt(32 bits))
|
|
val output = Stream(UInt(32 bits))
|
|
val halt = Bool()
|
|
val flush = Bool()
|
|
})
|
|
|
|
stages(0).input << fetchPc.output
|
|
for(s <- stages) {
|
|
s.halt := False
|
|
s.output << s.input.haltWhen(s.halt)
|
|
}
|
|
|
|
stages.head.flush := False //getFlushAt(IBUS_RSP, stages.head == stages.last) || fetchFlush
|
|
for((s,sNext) <- (stages, stages.tail).zipped) {
|
|
val discardInputOnFlush = s != stages.head
|
|
sNext.flush := getFlushAt(IBUS_RSP, sNext == stages.last) || fetchFlush
|
|
if(s == stages.head && pcRegReusedForSecondStage) {
|
|
sNext.input.arbitrationFrom(s.output.toEvent().m2sPipeWithFlush(sNext.flush, false, collapsBubble = false, flushInput = s.flush))
|
|
sNext.input.payload := fetchPc.pcReg
|
|
fetchPc.pcRegPropagate setWhen(sNext.input.ready)
|
|
} else {
|
|
sNext.input << s.output.m2sPipeWithFlush(sNext.flush, false, collapsBubble = false, flushInput = s.flush)
|
|
}
|
|
}
|
|
|
|
val readyForError = True
|
|
val output = Stream(FetchRsp())
|
|
incomingInstruction setWhen(stages.tail.map(_.input.valid).reduce(_ || _))
|
|
}
|
|
|
|
val decompressor = ifGen(decodePcGen)(new Area{
|
|
val input = iBusRsp.output.clearValidWhen(iBusRsp.stages.last.flush)
|
|
val output = Stream(FetchRsp())
|
|
val flush = getFlushAt(DECOMPRESSOR)
|
|
|
|
val bufferValid = RegInit(False)
|
|
val bufferData = Reg(Bits(16 bits))
|
|
|
|
val raw = Mux(
|
|
sel = bufferValid,
|
|
whenTrue = input.rsp.inst(15 downto 0) ## bufferData,
|
|
whenFalse = input.rsp.inst(31 downto 16) ## (input.pc(1) ? input.rsp.inst(31 downto 16) | input.rsp.inst(15 downto 0))
|
|
)
|
|
val isRvc = raw(1 downto 0) =/= 3
|
|
val decompressed = RvcDecompressor(raw(15 downto 0))
|
|
output.valid := (isRvc ? (bufferValid || input.valid) | (input.valid && (bufferValid || !input.pc(1))))
|
|
output.pc := input.pc
|
|
output.isRvc := isRvc
|
|
output.rsp.inst := isRvc ? decompressed | raw
|
|
input.ready := !output.valid || !(!output.ready || (isRvc && !input.pc(1) && input.rsp.inst(16, 2 bits) =/= 3) || (!isRvc && bufferValid && input.rsp.inst(16, 2 bits) =/= 3))
|
|
addPrePopTask(() => {
|
|
when(!input.ready && output.fire && !flush /* && ((isRvc && !bufferValid && !input.pc(1)) || (!isRvc && bufferValid && input.rsp.inst(16, 2 bits) =/= 3))*/) {
|
|
input.pc.getDrivingReg(1) := True
|
|
}
|
|
})
|
|
|
|
bufferValid clearWhen(output.fire)
|
|
val bufferFill = False
|
|
when(input.fire){
|
|
when(!(!isRvc && !input.pc(1) && !bufferValid) && !(isRvc && input.pc(1) && output.ready)) {
|
|
bufferValid := True
|
|
bufferFill := True
|
|
} otherwise {
|
|
bufferValid := False
|
|
}
|
|
bufferData := input.rsp.inst(31 downto 16)
|
|
}
|
|
bufferValid.clearWhen(flush)
|
|
iBusRsp.readyForError.clearWhen(bufferValid && isRvc) //Can't emit error, as there is a earlier instruction pending
|
|
incomingInstruction setWhen(bufferValid && bufferData(1 downto 0) =/= 3)
|
|
})
|
|
|
|
|
|
def condApply[T](that : T, cond : Boolean)(func : (T) => T) = if(cond)func(that) else that
|
|
val injector = new Area {
|
|
val inputBeforeStage = condApply(if (decodePcGen) decompressor.output else iBusRsp.output, injectorReadyCutGen)(_.s2mPipe(fetcherflushIt))
|
|
if (injectorReadyCutGen) {
|
|
iBusRsp.readyForError.clearWhen(inputBeforeStage.valid) //Can't emit error if there is a instruction pending in the s2mPipe
|
|
incomingInstruction setWhen (inputBeforeStage.valid)
|
|
}
|
|
val decodeInput = (if (injectorStage) {
|
|
val flushStage = getFlushAt(INJECTOR_M2S)
|
|
val decodeInput = inputBeforeStage.m2sPipeWithFlush(flushStage, false, collapsBubble = false, flushInput = fetcherflushIt)
|
|
decode.insert(INSTRUCTION_ANTICIPATED) := Mux(decode.arbitration.isStuck, decode.input(INSTRUCTION), inputBeforeStage.rsp.inst)
|
|
iBusRsp.readyForError.clearWhen(decodeInput.valid) //Can't emit error when there is a instruction pending in the injector stage buffer
|
|
incomingInstruction setWhen (decodeInput.valid)
|
|
decodeInput
|
|
} else {
|
|
inputBeforeStage
|
|
})
|
|
|
|
if(!decodePcGen) iBusRsp.readyForError.clearWhen(!pcValid(decode)) //Need to wait a valid PC on the decode stage, as it is use to fill CSR xEPC
|
|
|
|
|
|
def pcUpdatedGen(input : Bool, stucks : Seq[Bool], relaxedInput : Boolean, flush : Bool) : Seq[Bool] = {
|
|
stucks.scanLeft(input)((i, stuck) => {
|
|
val reg = RegInit(False)
|
|
if(!relaxedInput) when(flush) {
|
|
reg := False
|
|
}
|
|
when(!stuck) {
|
|
reg := i
|
|
}
|
|
if(relaxedInput || i != input) when(flush) {
|
|
reg := False
|
|
}
|
|
reg
|
|
}).tail
|
|
}
|
|
|
|
val stagesFromExecute = stages.dropWhile(_ != execute).toList
|
|
val nextPcCalc = if (decodePcGen) new Area{
|
|
val valids = pcUpdatedGen(True, False :: stagesFromExecute.map(_.arbitration.isStuck), true, decodePc.flushed)
|
|
pcValids := Vec(valids.takeRight(stages.size))
|
|
} else new Area{
|
|
val valids = pcUpdatedGen(True, iBusRsp.stages.tail.map(!_.input.ready) ++ (if (injectorStage) List(!decodeInput.ready) else Nil) ++ stagesFromExecute.map(_.arbitration.isStuck), false, fetchPc.flushed)
|
|
pcValids := Vec(valids.takeRight(stages.size))
|
|
}
|
|
|
|
decodeInput.ready := !decode.arbitration.isStuck
|
|
decode.arbitration.isValid := decodeInput.valid
|
|
decode.insert(PC) := (if (decodePcGen) decodePc.pcReg else decodeInput.pc)
|
|
decode.insert(INSTRUCTION) := decodeInput.rsp.inst
|
|
if (compressedGen) decode.insert(IS_RVC) := decodeInput.isRvc
|
|
|
|
if (injectionPort != null) {
|
|
Component.current.addPrePopTask(() => {
|
|
val state = RegInit(U"000")
|
|
|
|
injectionPort.ready := False
|
|
if(decodePcGen){
|
|
decodePc.injectedDecode setWhen(state =/= 0)
|
|
}
|
|
switch(state) {
|
|
is(0) { //request pipelining
|
|
when(injectionPort.valid) {
|
|
state := 1
|
|
}
|
|
}
|
|
is(1) { //Give time to propagate the payload
|
|
state := 2
|
|
}
|
|
is(2){ //read regfile delay
|
|
decode.arbitration.isValid := True
|
|
decode.arbitration.haltItself := True
|
|
state := 3
|
|
}
|
|
is(3){ //Do instruction
|
|
decode.arbitration.isValid := True
|
|
when(!decode.arbitration.isStuck) {
|
|
state := 4
|
|
}
|
|
}
|
|
is(4){ //request pipelining
|
|
injectionPort.ready := True
|
|
state := 0
|
|
}
|
|
}
|
|
|
|
//Check if the decode instruction is driven by a register
|
|
val instructionDriver = try {
|
|
decode.input(INSTRUCTION).getDrivingReg
|
|
} catch {
|
|
case _: Throwable => null
|
|
}
|
|
if (instructionDriver != null) { //If yes =>
|
|
//Insert the instruction by writing the "fetch to decode instruction register",
|
|
// Work even if it need to cross some hierarchy (caches)
|
|
instructionDriver.component.rework {
|
|
when(state.pull() =/= 0) {
|
|
instructionDriver := injectionPort.payload.pull()
|
|
}
|
|
}
|
|
} else {
|
|
//Insert the instruction via a mux in the decode stage
|
|
when(state =/= 0) {
|
|
decode.input(INSTRUCTION) := RegNext(injectionPort.payload)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
|
|
//Formal verification signals generation, miss prediction stuff ?
|
|
val formal = new Area {
|
|
val raw = if(compressedGen) decompressor.raw else inputBeforeStage.rsp.inst
|
|
val rawInDecode = Delay(raw, if(injectorStage) 1 else 0, when = decodeInput.ready)
|
|
decode.insert(FORMAL_INSTRUCTION) := rawInDecode
|
|
|
|
decode.insert(FORMAL_PC_NEXT) := (if (compressedGen)
|
|
decode.input(PC) + ((decode.input(IS_RVC)) ? U(2) | U(4))
|
|
else
|
|
decode.input(PC) + 4)
|
|
|
|
if(decodePc != null && decodePc.predictionPcLoad != null){
|
|
when(decodePc.predictionPcLoad.valid){
|
|
decode.insert(FORMAL_PC_NEXT) := decodePc.predictionPcLoad.payload
|
|
}
|
|
}
|
|
|
|
jumpInfos.foreach(info => {
|
|
when(info.interface.valid) {
|
|
info.stage.output(FORMAL_PC_NEXT) := info.interface.payload
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
def stage1ToInjectorPipe[T <: Data](input : T): (T,T) ={
|
|
val iBusRspContext = iBusRsp.stages.drop(1).dropRight(1).foldLeft(input)((data,stage) => RegNextWhen(data, stage.output.ready))
|
|
// val decompressorContext = ifGen(compressedGen)(new Area{
|
|
// val lastContext = RegNextWhen(iBusRspContext, decompressor.input.fire)
|
|
// val output = decompressor.bufferValid ? lastContext | iBusRspContext
|
|
// })
|
|
val decompressorContext = cloneOf(input)
|
|
decompressorContext := iBusRspContext
|
|
val injectorContext = Delay(if(compressedGen) decompressorContext else iBusRspContext, cycleCount=if(injectorStage) 1 else 0, when=injector.decodeInput.ready)
|
|
val injectorContextWire = cloneOf(input) //Allow combinatorial override
|
|
injectorContextWire := injectorContext
|
|
(ifGen(compressedGen)(decompressorContext), injectorContextWire)
|
|
}
|
|
|
|
val predictor = prediction match {
|
|
case NONE =>
|
|
case STATIC | DYNAMIC => {
|
|
def historyWidth = 2
|
|
val dynamic = ifGen(prediction == DYNAMIC) (new Area {
|
|
case class BranchPredictorLine() extends Bundle{
|
|
val history = SInt(historyWidth bits)
|
|
}
|
|
|
|
val historyCache = Mem(BranchPredictorLine(), 1 << historyRamSizeLog2)
|
|
val historyWrite = historyCache.writePort
|
|
val historyWriteLast = RegNextWhen(historyWrite, iBusRsp.stages(0).output.ready)
|
|
val hazard = historyWriteLast.valid && historyWriteLast.address === (iBusRsp.stages(0).input.payload >> 2).resized
|
|
|
|
case class DynamicContext() extends Bundle{
|
|
val hazard = Bool
|
|
val line = BranchPredictorLine()
|
|
}
|
|
val fetchContext = DynamicContext()
|
|
fetchContext.hazard := hazard
|
|
fetchContext.line := historyCache.readSync((fetchPc.output.payload >> 2).resized, iBusRsp.stages(0).output.ready || fetcherflushIt)
|
|
|
|
object PREDICTION_CONTEXT extends Stageable(DynamicContext())
|
|
decode.insert(PREDICTION_CONTEXT) := stage1ToInjectorPipe(fetchContext)._2
|
|
val decodeContextPrediction = decode.input(PREDICTION_CONTEXT).line.history.msb
|
|
|
|
val branchStage = decodePrediction.stage
|
|
val branchContext = branchStage.input(PREDICTION_CONTEXT)
|
|
val moreJump = decodePrediction.rsp.wasWrong ^ branchContext.line.history.msb
|
|
|
|
historyWrite.address := branchStage.input(PC)(2, historyRamSizeLog2 bits) + (if(pipeline(RVC_GEN))
|
|
((!branchStage.input(IS_RVC) && branchStage.input(PC)(1)) ? U(1) | U(0))
|
|
else
|
|
U(0))
|
|
|
|
historyWrite.data.history := branchContext.line.history + (moreJump ? S(-1) | S(1))
|
|
val sat = (branchContext.line.history === (moreJump ? S(branchContext.line.history.minValue) | S(branchContext.line.history.maxValue)))
|
|
historyWrite.valid := !branchContext.hazard && branchStage.arbitration.isFiring && branchStage.input(BRANCH_CTRL) === BranchCtrlEnum.B && !sat
|
|
})
|
|
|
|
|
|
val imm = IMM(decode.input(INSTRUCTION))
|
|
|
|
val conditionalBranchPrediction = prediction match {
|
|
case STATIC => imm.b_sext.msb
|
|
case DYNAMIC => dynamic.decodeContextPrediction
|
|
}
|
|
|
|
decodePrediction.cmd.hadBranch := decode.input(BRANCH_CTRL) === BranchCtrlEnum.JAL || (decode.input(BRANCH_CTRL) === BranchCtrlEnum.B && conditionalBranchPrediction)
|
|
|
|
val noPredictionOnMissaligned = (!pipeline(RVC_GEN)) generate new Area{
|
|
val missaligned = decode.input(BRANCH_CTRL).mux(
|
|
BranchCtrlEnum.JAL -> imm.j_sext(1),
|
|
default -> imm.b_sext(1)
|
|
)
|
|
decodePrediction.cmd.hadBranch clearWhen(missaligned)
|
|
}
|
|
|
|
//TODO no more fireing depedancies
|
|
predictionJumpInterface.valid := decode.arbitration.isValid && decodePrediction.cmd.hadBranch //TODO OH Doublon de priorité
|
|
predictionJumpInterface.payload := decode.input(PC) + ((decode.input(BRANCH_CTRL) === BranchCtrlEnum.JAL) ? imm.j_sext | imm.b_sext).asUInt
|
|
if(relaxPredictorAddress) KeepAttribute(predictionJumpInterface.payload)
|
|
|
|
when(predictionJumpInterface.valid && decode.arbitration.isFiring){
|
|
flushIt()
|
|
}
|
|
}
|
|
case DYNAMIC_TARGET => new Area{
|
|
// assert(!compressedGen || cmdToRspStageCount == 1, "Can't combine DYNAMIC_TARGET and RVC as it could stop the instruction fetch mid-air")
|
|
|
|
case class BranchPredictorLine() extends Bundle{
|
|
val source = Bits(30 - historyRamSizeLog2 bits)
|
|
val branchWish = UInt(2 bits)
|
|
val target = UInt(32 bits)
|
|
val unaligned = ifGen(compressedGen)(Bool)
|
|
}
|
|
|
|
val history = Mem(BranchPredictorLine(), 1 << historyRamSizeLog2)
|
|
val historyWrite = history.writePort
|
|
|
|
//Avoid write to read hazard
|
|
val historyWriteLast = RegNextWhen(historyWrite, iBusRsp.stages(0).output.ready)
|
|
val hazard = historyWriteLast.valid && historyWriteLast.address === (iBusRsp.stages(1).input.payload >> 2).resized
|
|
val line = history.readSync((iBusRsp.stages(0).input.payload >> 2).resized, iBusRsp.stages(0).output.ready)
|
|
val hit = line.source === (iBusRsp.stages(1).input.payload.asBits >> 2 + historyRamSizeLog2)
|
|
|
|
fetchPc.predictionPcLoad.valid := line.branchWish.msb && hit && !hazard && iBusRsp.stages(1).input.valid
|
|
fetchPc.predictionPcLoad.payload := line.target
|
|
|
|
case class PredictionResult() extends Bundle{
|
|
val hazard = Bool
|
|
val hit = Bool
|
|
val line = BranchPredictorLine()
|
|
}
|
|
|
|
val fetchContext = PredictionResult()
|
|
fetchContext.hazard := hazard
|
|
fetchContext.hit := hit
|
|
fetchContext.line := line
|
|
|
|
val (decompressorContext, injectorContext) = stage1ToInjectorPipe(fetchContext)
|
|
if(compressedGen) {
|
|
//prediction hit on the right instruction into words
|
|
decompressorContext.hit clearWhen(decompressorContext.line.unaligned && (decompressor.bufferValid || (decompressor.isRvc && !decompressor.input.pc(1))))
|
|
|
|
decodePc.predictionPcLoad.valid := injectorContext.line.branchWish.msb && injectorContext.hit && !injectorContext.hazard && injector.decodeInput.fire
|
|
decodePc.predictionPcLoad.payload := injectorContext.line.target
|
|
|
|
when(decompressorContext.line.branchWish.msb && decompressorContext.hit && !decompressorContext.hazard && decompressor.output.fire){
|
|
decompressor.bufferValid := False
|
|
decompressor.input.ready := True
|
|
}
|
|
}
|
|
|
|
object PREDICTION_CONTEXT extends Stageable(PredictionResult())
|
|
pipeline.decode.insert(PREDICTION_CONTEXT) := injectorContext
|
|
val branchStage = fetchPrediction.stage
|
|
val branchContext = branchStage.input(PREDICTION_CONTEXT)
|
|
|
|
fetchPrediction.cmd.hadBranch := branchContext.hit && !branchContext.hazard && branchContext.line.branchWish.msb
|
|
fetchPrediction.cmd.targetPc := branchContext.line.target
|
|
|
|
|
|
historyWrite.valid := False
|
|
historyWrite.address := fetchPrediction.rsp.sourceLastWord(2, historyRamSizeLog2 bits)
|
|
historyWrite.data.source := fetchPrediction.rsp.sourceLastWord.asBits >> 2 + historyRamSizeLog2
|
|
historyWrite.data.target := fetchPrediction.rsp.finalPc
|
|
if(compressedGen) historyWrite.data.unaligned := !fetchPrediction.stage.input(PC)(1) ^ fetchPrediction.stage.input(IS_RVC)
|
|
|
|
when(fetchPrediction.rsp.wasRight) {
|
|
historyWrite.valid := branchContext.hit
|
|
historyWrite.data.branchWish := branchContext.line.branchWish + (branchContext.line.branchWish === 2).asUInt - (branchContext.line.branchWish === 1).asUInt
|
|
} otherwise {
|
|
when(branchContext.hit) {
|
|
historyWrite.valid := True
|
|
historyWrite.data.branchWish := branchContext.line.branchWish - (branchContext.line.branchWish.msb).asUInt + (!branchContext.line.branchWish.msb).asUInt
|
|
} otherwise {
|
|
historyWrite.valid := True
|
|
historyWrite.data.branchWish := "10"
|
|
}
|
|
}
|
|
|
|
historyWrite.valid clearWhen(branchContext.hazard || !branchStage.arbitration.isFiring)
|
|
|
|
|
|
|
|
val predictionFailure = ifGen(compressedGen)(new Area{
|
|
val predictionBranch = decompressorContext.hit && !decompressorContext.hazard && decompressorContext.line.branchWish(1)
|
|
val unalignedWordIssue = decompressor.bufferFill && decompressor.input.rsp.inst(17 downto 16) === 3 && predictionBranch
|
|
val decompressorFailure = RegInit(False) setWhen(unalignedWordIssue) clearWhen(fetcherflushIt)
|
|
val injectorFailure = Delay(decompressorFailure, cycleCount=if(injectorStage) 1 else 0, when=injector.decodeInput.ready)
|
|
val bypassFailure = if(!injectorStage) False else decompressorFailure && !injector.decodeInput.valid
|
|
|
|
dynamicTargetFailureCorrection.valid := False
|
|
dynamicTargetFailureCorrection.payload := decode.input(PC)
|
|
when(injectorFailure || bypassFailure){
|
|
historyWrite.valid := True
|
|
historyWrite.address := (decode.input(PC) >> 2).resized
|
|
historyWrite.data.branchWish := 0
|
|
|
|
decode.arbitration.isValid := False
|
|
decode.arbitration.flushNext := True
|
|
dynamicTargetFailureCorrection.valid := True
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
def stageXToIBusRsp[T <: Data](stage : Any, input : T): (T) ={
|
|
iBusRsp.stages.dropWhile(_ != stage).tail.foldLeft(input)((data,stage) => RegNextWhen(data, stage.output.ready))
|
|
}
|
|
|
|
}
|
|
} |