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(var 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

  val injectionPorts = ArrayBuffer[Stream[Bits]]()
  override def getInjectionPort() = {
    injectionPorts.addRet(Stream(Bits(32 bits)))
  }
  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 (injectionPorts.nonEmpty) {
        Component.current.addPrePopTask(() => new Composite(this, "port"){
          val state = RegInit(U"000")
          val injectionPort = injectionPorts.size match {
            case 1 => injectionPorts.head
            case _ => {
              val p = Stream(Bits(32 bits))
              //assume only one port is used at the time
              p.valid := injectionPorts.map(_.valid).orR
              p.payload := OHMux(injectionPorts.map(_.valid), injectionPorts.map(_.payload))
              injectionPorts.foreach(_.ready := p.ready)
              p
            }
          }

          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)
      })

      val privilegeService = pipeline.serviceElse(classOf[PrivilegeService], PrivilegeServiceDefault())

      //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
          }
        })

        // Forward the current CPU "mode" (privilege level) from the fetch
	// stage, which is where it can begin to affect the current
	// execution (e.g., through PMP checks).
        decode.insert(FORMAL_MODE) := privilegeService.encodeBits()
      }
    }

    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))
    }

  }
}