aboutsummaryrefslogtreecommitdiff
path: root/VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala
diff options
context:
space:
mode:
authorFriedrich Beckmann <friedrich.beckmann@hs-augsburg.de>2022-07-25 17:55:39 +0200
committerFriedrich Beckmann <friedrich.beckmann@hs-augsburg.de>2022-07-25 17:55:39 +0200
commit3fff6023602822531efdae30bc8ebf862967f1ef (patch)
tree16028102b8d850f8ab3115d28a8539ca6bc5f51d /VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala
Initial Commit
Diffstat (limited to 'VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala')
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala637
1 files changed, 637 insertions, 0 deletions
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala b/VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala
new file mode 100644
index 0000000..14450a1
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/Fetcher.scala
@@ -0,0 +1,637 @@
+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,
+ val predictionBuffer : Boolean = true) 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 forceNoDecodeCond : Bool = null
+ var pcValids : Vec[Bool] = null
+ def pcValid(stage : Stage) = pcValids(pipeline.indexOf(stage))
+ var incomingInstruction : Bool = null
+ override def incoming() = incomingInstruction
+
+
+ override def withRvc(): Boolean = compressedGen
+
+ var injectionPort : Stream[Bits] = null
+ override def getInjectionPort() = {
+ injectionPort = Stream(Bits(32 bits))
+ injectionPort
+ }
+ def pcRegReusedForSecondStage = allowPcRegReusedForSecondStage && prediction != DYNAMIC_TARGET //TODO might not be required for DYNAMIC_TARGET
+ var predictionJumpInterface : Flow[UInt] = null
+
+ override def haltIt(): Unit = fetcherHalt := True
+ override def forceNoDecode(): Unit = forceNoDecodeCond := 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
+ forceNoDecodeCond = False
+ incomingInstruction = False
+ if(resetVector == null) externalResetVector = in(UInt(32 bits).setName("externalResetVector"))
+
+ 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()
+ }
+ }
+
+ 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
+ s == IBUS_RSP || s == DECOMPRESSOR
+ }
+
+
+
+ class FetchArea(pipeline : VexRiscv) extends Area {
+ import pipeline._
+ import pipeline.config._
+ val externalFlush = stages.map(_.arbitration.flushNext).orR
+
+ def getFlushAt(s : Any, lastCond : Boolean = true): Bool = {
+ if(isDrivingDecode(s) && lastCond) pipeline.decode.arbitration.isRemoved else externalFlush
+ }
+
+ //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)
+ }
+
+
+
+ //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 correction = False
+ val correctionReg = RegInit(False) setWhen(correction) clearWhen(output.fire)
+ val corrected = correction || correctionReg
+ val pcRegPropagate = False
+ val booted = RegNext(True) init (False)
+ val inc = RegInit(False) clearWhen(correction || 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 || prediction == DYNAMIC_TARGET) generate Flow(UInt(32 bits))
+ val flushed = False
+
+ if(compressedGen) when(inc) {
+ pc(1) := False
+ }
+
+ if(predictionPcLoad != null) {
+ when(predictionPcLoad.valid) {
+ correction := True
+ pc := predictionPcLoad.payload
+ }
+ }
+ if(redo != null) when(redo.valid){
+ correction := True
+ pc := redo.payload
+ flushed := True
+ }
+ when(jump.pcLoad.valid) {
+ correction := True
+ pc := jump.pcLoad.payload
+ flushed := True
+ }
+
+ when(booted && (output.ready || correction || pcRegPropagate)){
+ pcReg := pc
+ }
+
+ pc(0) := False
+ if(!compressedGen) 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 && !forceNoDecodeCond) {
+ 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 redoFetch = False
+ val stages = Array.fill(cmdToRspStageCount + 1)(new Bundle {
+ val input = Stream(UInt(32 bits))
+ val output = Stream(UInt(32 bits))
+ val halt = Bool()
+ })
+
+ stages(0).input << fetchPc.output
+ for(s <- stages) {
+ s.halt := False
+ s.output << s.input.haltWhen(s.halt)
+ }
+
+ if(fetchPc.redo != null) {
+ fetchPc.redo.valid := redoFetch
+ fetchPc.redo.payload := stages.last.input.payload
+ }
+
+ val flush = (if(isDrivingDecode(IBUS_RSP)) pipeline.decode.arbitration.isRemoved || decode.arbitration.flushNext && !decode.arbitration.isStuck else externalFlush) || redoFetch
+ for((s,sNext) <- (stages, stages.tail).zipped) {
+ val sFlushed = if(s != stages.head) flush else False
+ val sNextFlushed = flush
+ if(s == stages.head && pcRegReusedForSecondStage) {
+ sNext.input.arbitrationFrom(s.output.toEvent().m2sPipeWithFlush(sNextFlushed, false, collapsBubble = false, flushInput = sFlushed))
+ sNext.input.payload := fetchPc.pcReg
+ fetchPc.pcRegPropagate setWhen(sNext.input.ready)
+ } else {
+ sNext.input << s.output.m2sPipeWithFlush(sNextFlushed, false, collapsBubble = false, flushInput = sFlushed)
+ }
+ }
+
+ 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.redoFetch)
+ val output = Stream(FetchRsp())
+ val flush = getFlushAt(DECOMPRESSOR)
+ val flushNext = if(isDrivingDecode(DECOMPRESSOR)) decode.arbitration.flushNext else False
+ val consumeCurrent = if(isDrivingDecode(DECOMPRESSOR)) flushNext && output.ready else False
+
+ val bufferValid = RegInit(False)
+ val bufferData = Reg(Bits(16 bits))
+
+ val isInputLowRvc = input.rsp.inst(1 downto 0) =/= 3
+ val isInputHighRvc = input.rsp.inst(17 downto 16) =/= 3
+ val throw2BytesReg = RegInit(False)
+ val throw2Bytes = throw2BytesReg || input.pc(1)
+ val unaligned = throw2Bytes || bufferValid
+ def aligned = !unaligned
+
+ //Latch and patches are there to ensure that the decoded instruction do not mutate while being halted and unscheduled to ensure FpuPlugin cmd fork from consistancy
+ val bufferValidLatch = RegNextWhen(bufferValid, input.valid)
+ val throw2BytesLatch = RegNextWhen(throw2Bytes, input.valid)
+ val bufferValidPatched = input.valid ? bufferValid | bufferValidLatch
+ val throw2BytesPatched = input.valid ? throw2Bytes | throw2BytesLatch
+
+ val raw = Mux(
+ sel = bufferValidPatched,
+ whenTrue = input.rsp.inst(15 downto 0) ## bufferData,
+ whenFalse = input.rsp.inst(31 downto 16) ## (throw2BytesPatched ? 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), pipeline.config.withRvf, pipeline.config.withRvd)
+ output.valid := input.valid && !(throw2Bytes && !bufferValid && !isInputHighRvc)
+ output.pc := input.pc
+ output.isRvc := isRvc
+ output.rsp.inst := isRvc ? decompressed | raw
+ input.ready := output.ready && (!iBusRsp.stages.last.input.valid || flushNext || (!(bufferValid && isInputHighRvc) && !(aligned && isInputLowRvc && isInputHighRvc)))
+
+ when(output.fire){
+ throw2BytesReg := (aligned && isInputLowRvc && isInputHighRvc) || (bufferValid && isInputHighRvc)
+ }
+ val bufferFill = (aligned && isInputLowRvc && !isInputHighRvc) || (bufferValid && !isInputHighRvc) || (throw2Bytes && !isRvc && !isInputHighRvc)
+ when(output.ready && input.valid){
+ bufferValid := False
+ }
+ when(output.ready && input.valid){
+ bufferData := input.rsp.inst(31 downto 16)
+ bufferValid setWhen(bufferFill)
+ }
+
+ when(flush || consumeCurrent){
+ throw2BytesReg := False
+ bufferValid := False
+ }
+
+ if(fetchPc.redo != null) {
+ fetchPc.redo.payload(1) setWhen(throw2BytesReg)
+ }
+ })
+
+
+ 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(externalFlush))
+ 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 = externalFlush)
+ 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)
+ }
+ }
+ })
+ }
+
+ Component.current.addPrePopTask(() => {
+ decode.arbitration.isValid clearWhen(forceNoDecodeCond)
+ })
+
+ //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, T) ={
+ val iBusRspContext = iBusRsp.stages.drop(1).dropRight(1).foldLeft(input)((data,stage) => RegNextWhen(data, stage.output.ready))
+
+ val iBusRspContextOutput = cloneOf(input)
+ iBusRspContextOutput := iBusRspContext
+ val injectorContext = Delay(iBusRspContextOutput, cycleCount=if(injectorStage) 1 else 0, when=injector.decodeInput.ready)
+ val injectorContextWire = cloneOf(input) //Allow combinatorial override
+ injectorContextWire := injectorContext
+ (iBusRspContext, iBusRspContextOutput, 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 || externalFlush)
+
+ object PREDICTION_CONTEXT extends Stageable(DynamicContext())
+ decode.insert(PREDICTION_CONTEXT) := stage1ToInjectorPipe(fetchContext)._3
+ 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.config.withRvc)
+ ((!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.config.withRvc) 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
+ predictionJumpInterface.payload := decode.input(PC) + ((decode.input(BRANCH_CTRL) === BranchCtrlEnum.JAL) ? imm.j_sext | imm.b_sext).asUInt
+ decode.arbitration.flushNext setWhen(predictionJumpInterface.valid)
+
+ if(relaxPredictorAddress) KeepAttribute(predictionJumpInterface.payload)
+ }
+ 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 last2Bytes = ifGen(compressedGen)(Bool)
+ val target = UInt(32 bits)
+ }
+
+ val history = Mem(BranchPredictorLine(), 1 << historyRamSizeLog2)
+ val historyWriteDelayPatched = history.writePort
+ val historyWrite = cloneOf(historyWriteDelayPatched)
+ historyWriteDelayPatched.valid := historyWrite.valid
+ historyWriteDelayPatched.address := (if(predictionBuffer) historyWrite.address - 1 else historyWrite.address)
+ historyWriteDelayPatched.data := historyWrite.data
+
+
+ val writeLast = RegNextWhen(historyWriteDelayPatched, iBusRsp.stages(0).output.ready)
+
+ //Avoid write to read hazard
+ val buffer = predictionBuffer generate new Area{
+ val line = history.readSync((iBusRsp.stages(0).input.payload >> 2).resized, iBusRsp.stages(0).output.ready)
+ val pcCorrected = RegNextWhen(fetchPc.corrected, iBusRsp.stages(0).input.ready)
+ val hazard = (writeLast.valid && writeLast.address === (iBusRsp.stages(1).input.payload >> 2).resized)
+ }
+
+ val (line, hazard) = predictionBuffer match {
+ case true =>
+ (RegNextWhen(buffer.line, iBusRsp.stages(0).output.ready),
+ RegNextWhen(buffer.hazard, iBusRsp.stages(0).output.ready) || buffer.pcCorrected)
+ case false =>
+ (history.readSync((iBusRsp.stages(0).input.payload >> 2).resized,
+ iBusRsp.stages(0).output.ready), writeLast.valid && writeLast.address === (iBusRsp.stages(1).input.payload >> 2).resized)
+ }
+
+ val hit = line.source === (iBusRsp.stages(1).input.payload.asBits >> 2 + historyRamSizeLog2)
+ if(compressedGen) hit clearWhen(!line.last2Bytes && iBusRsp.stages(1).input.payload(1))
+
+ 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 (iBusRspContext, iBusRspContextOutput, injectorContext) = stage1ToInjectorPipe(fetchContext)
+
+ 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.last2Bytes := 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 compressor = compressedGen generate new Area{
+ val predictionBranch = iBusRspContext.hit && !iBusRspContext.hazard && iBusRspContext.line.branchWish(1)
+ val unalignedWordIssue = iBusRsp.output.valid && predictionBranch && iBusRspContext.line.last2Bytes && Mux(decompressor.unaligned, !decompressor.isInputHighRvc, decompressor.isInputLowRvc && !decompressor.isInputHighRvc)
+
+ when(unalignedWordIssue){
+ historyWrite.valid := True
+ historyWrite.address := (iBusRsp.stages(1).input.payload >> 2).resized
+ historyWrite.data.branchWish := 0
+
+ iBusRsp.redoFetch := True
+ }
+
+ //Do not trigger prediction hit when it is one for the upper RVC word and we aren't there yet
+ iBusRspContextOutput.hit clearWhen(iBusRspContext.line.last2Bytes && (decompressor.bufferValid || (!decompressor.throw2Bytes && decompressor.isInputLowRvc)))
+
+ decodePc.predictionPcLoad.valid := injectorContext.line.branchWish.msb && injectorContext.hit && !injectorContext.hazard && injector.decodeInput.fire
+ decodePc.predictionPcLoad.payload := injectorContext.line.target
+
+ //Clean the RVC buffer when a prediction was made
+ when(iBusRspContext.line.branchWish.msb && iBusRspContextOutput.hit && !iBusRspContext.hazard && decompressor.output.fire){
+ decompressor.bufferValid := False
+ decompressor.throw2BytesReg := False
+ decompressor.input.ready := True //Drop the remaining byte if any
+ }
+ }
+ }
+ }
+
+ def stageXToIBusRsp[T <: Data](stage : Any, input : T): (T) ={
+ iBusRsp.stages.dropWhile(_ != stage).tail.foldLeft(input)((data,stage) => RegNextWhen(data, stage.output.ready))
+ }
+
+ }
+} \ No newline at end of file