Fetcher and IBusSimplePlugin flush reworked

This commit is contained in:
Charles Papon 2020-02-28 17:23:44 +01:00
parent 492310e6fa
commit c94d8f1c6c
3 changed files with 55 additions and 56 deletions

View File

@ -93,10 +93,10 @@ abstract class IBusFetcherImpl(val resetVector : BigInt,
class FetchArea(pipeline : VexRiscv) extends Area { class FetchArea(pipeline : VexRiscv) extends Area {
import pipeline._ import pipeline._
import pipeline.config._ import pipeline.config._
val fetcherflushIt = stages.map(_.arbitration.flushNext).orR val externalFlush = stages.map(_.arbitration.flushNext).orR
def getFlushAt(s : Any, lastCond : Boolean = true): Bool = { def getFlushAt(s : Any, lastCond : Boolean = true): Bool = {
if(isDrivingDecode(s) && lastCond) pipeline.decode.arbitration.isRemoved else fetcherflushIt if(isDrivingDecode(s) && lastCond) pipeline.decode.arbitration.isRemoved else externalFlush
} }
//Arbitrate jump requests into pcLoad //Arbitrate jump requests into pcLoad
@ -208,7 +208,6 @@ abstract class IBusFetcherImpl(val resetVector : BigInt,
val input = Stream(UInt(32 bits)) val input = Stream(UInt(32 bits))
val output = Stream(UInt(32 bits)) val output = Stream(UInt(32 bits))
val halt = Bool() val halt = Bool()
val flush = Bool()
}) })
stages(0).input << fetchPc.output stages(0).input << fetchPc.output
@ -222,16 +221,16 @@ abstract class IBusFetcherImpl(val resetVector : BigInt,
fetchPc.redo.payload := stages.last.input.payload fetchPc.redo.payload := stages.last.input.payload
} }
stages.head.flush := False //getFlushAt(IBUS_RSP, stages.head == stages.last) || fetchFlush 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) { for((s,sNext) <- (stages, stages.tail).zipped) {
val discardInputOnFlush = s != stages.head val sFlushed = if(s != stages.head) flush else False
sNext.flush := getFlushAt(IBUS_RSP, sNext == stages.last) || redoFetch val sNextFlushed = flush
if(s == stages.head && pcRegReusedForSecondStage) { if(s == stages.head && pcRegReusedForSecondStage) {
sNext.input.arbitrationFrom(s.output.toEvent().m2sPipeWithFlush(sNext.flush, false, collapsBubble = false, flushInput = s.flush)) sNext.input.arbitrationFrom(s.output.toEvent().m2sPipeWithFlush(sNextFlushed, false, collapsBubble = false, flushInput = sFlushed))
sNext.input.payload := fetchPc.pcReg sNext.input.payload := fetchPc.pcReg
fetchPc.pcRegPropagate setWhen(sNext.input.ready) fetchPc.pcRegPropagate setWhen(sNext.input.ready)
} else { } else {
sNext.input << s.output.m2sPipeWithFlush(sNext.flush, false, collapsBubble = false, flushInput = s.flush) sNext.input << s.output.m2sPipeWithFlush(sNextFlushed, false, collapsBubble = false, flushInput = sFlushed)
} }
} }
@ -294,14 +293,14 @@ abstract class IBusFetcherImpl(val resetVector : BigInt,
def condApply[T](that : T, cond : Boolean)(func : (T) => T) = if(cond)func(that) else that def condApply[T](that : T, cond : Boolean)(func : (T) => T) = if(cond)func(that) else that
val injector = new Area { val injector = new Area {
val inputBeforeStage = condApply(if (decodePcGen) decompressor.output else iBusRsp.output, injectorReadyCutGen)(_.s2mPipe(fetcherflushIt)) val inputBeforeStage = condApply(if (decodePcGen) decompressor.output else iBusRsp.output, injectorReadyCutGen)(_.s2mPipe(externalFlush))
if (injectorReadyCutGen) { if (injectorReadyCutGen) {
iBusRsp.readyForError.clearWhen(inputBeforeStage.valid) //Can't emit error if there is a instruction pending in the s2mPipe iBusRsp.readyForError.clearWhen(inputBeforeStage.valid) //Can't emit error if there is a instruction pending in the s2mPipe
incomingInstruction setWhen (inputBeforeStage.valid) incomingInstruction setWhen (inputBeforeStage.valid)
} }
val decodeInput = (if (injectorStage) { val decodeInput = (if (injectorStage) {
val flushStage = getFlushAt(INJECTOR_M2S) val flushStage = getFlushAt(INJECTOR_M2S)
val decodeInput = inputBeforeStage.m2sPipeWithFlush(flushStage, false, collapsBubble = false, flushInput = fetcherflushIt) val decodeInput = inputBeforeStage.m2sPipeWithFlush(flushStage, false, collapsBubble = false, flushInput = externalFlush)
decode.insert(INSTRUCTION_ANTICIPATED) := Mux(decode.arbitration.isStuck, decode.input(INSTRUCTION), inputBeforeStage.rsp.inst) 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 iBusRsp.readyForError.clearWhen(decodeInput.valid) //Can't emit error when there is a instruction pending in the injector stage buffer
incomingInstruction setWhen (decodeInput.valid) incomingInstruction setWhen (decodeInput.valid)
@ -458,7 +457,7 @@ abstract class IBusFetcherImpl(val resetVector : BigInt,
} }
val fetchContext = DynamicContext() val fetchContext = DynamicContext()
fetchContext.hazard := hazard fetchContext.hazard := hazard
fetchContext.line := historyCache.readSync((fetchPc.output.payload >> 2).resized, iBusRsp.stages(0).output.ready || fetcherflushIt) fetchContext.line := historyCache.readSync((fetchPc.output.payload >> 2).resized, iBusRsp.stages(0).output.ready || externalFlush)
object PREDICTION_CONTEXT extends Stageable(DynamicContext()) object PREDICTION_CONTEXT extends Stageable(DynamicContext())
decode.insert(PREDICTION_CONTEXT) := stage1ToInjectorPipe(fetchContext)._3 decode.insert(PREDICTION_CONTEXT) := stage1ToInjectorPipe(fetchContext)._3

View File

@ -156,7 +156,7 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l,
stages(0).halt setWhen (cache.io.cpu.prefetch.haltIt) stages(0).halt setWhen (cache.io.cpu.prefetch.haltIt)
cache.io.cpu.fetch.isRemoved := fetcherflushIt cache.io.cpu.fetch.isRemoved := externalFlush
} }

View File

@ -255,7 +255,9 @@ class IBusSimplePlugin( resetVector : BigInt,
val catchSomething = memoryTranslatorPortConfig != null || catchAccessFault val catchSomething = memoryTranslatorPortConfig != null || catchAccessFault
var mmuBus : MemoryTranslatorBus = null var mmuBus : MemoryTranslatorBus = null
if(rspHoldValue) assert(busLatencyMin <= 1) // if(rspHoldValue) assert(busLatencyMin <= 1)
assert(!rspHoldValue, "rspHoldValue not supported yet")
assert(!singleInstructionPipeline)
override def setup(pipeline: VexRiscv): Unit = { override def setup(pipeline: VexRiscv): Unit = {
super.setup(pipeline) super.setup(pipeline)
@ -283,9 +285,12 @@ class IBusSimplePlugin( resetVector : BigInt,
iBus.cmd << (if(cmdWithS2mPipe) cmd.s2mPipe() else cmd) iBus.cmd << (if(cmdWithS2mPipe) cmd.s2mPipe() else cmd)
//Avoid sending to many iBus cmd //Avoid sending to many iBus cmd
val pendingCmd = Reg(UInt(log2Up(pendingMax + 1) bits)) init (0) val pending = new Area{
val pendingCmdNext = pendingCmd + cmd.fire.asUInt - iBus.rsp.fire.asUInt val inc, dec = Bool()
pendingCmd := pendingCmdNext val value = Reg(UInt(log2Up(pendingMax + 1) bits)) init (0)
val next = value + U(inc) - U(dec)
value := next
}
val secondStagePersistence = cmdForkPersistence && cmdForkOnSecondStage && !cmdWithS2mPipe val secondStagePersistence = cmdForkPersistence && cmdForkOnSecondStage && !cmdWithS2mPipe
def cmdForkStage = if(!secondStagePersistence) iBusRsp.stages(if(cmdForkOnSecondStage) 1 else 0) else iBusRsp.stages(1) def cmdForkStage = if(!secondStagePersistence) iBusRsp.stages(if(cmdForkOnSecondStage) 1 else 0) else iBusRsp.stages(1)
@ -293,29 +298,30 @@ class IBusSimplePlugin( resetVector : BigInt,
val cmdFork = if(!secondStagePersistence) new Area { val cmdFork = if(!secondStagePersistence) new Area {
//This implementation keep the cmd on the bus until it's executed or the the pipeline is flushed //This implementation keep the cmd on the bus until it's executed or the the pipeline is flushed
def stage = cmdForkStage def stage = cmdForkStage
stage.halt setWhen(stage.input.valid && (!cmd.valid || !cmd.ready)) val canEmit = stage.output.ready && pending.value =/= pendingMax
if(singleInstructionPipeline) { stage.halt setWhen(stage.input.valid && (!canEmit || !cmd.ready))
cmd.valid := stage.input.valid && pendingCmd =/= pendingMax && !stages.map(_.arbitration.isValid).orR cmd.valid := stage.input.valid && canEmit
assert(injectorStage == false) pending.inc := cmd.fire
assert(iBusRsp.stages.dropWhile(_ != stage).length <= 2)
}else {
cmd.valid := stage.input.valid && stage.output.ready && pendingCmd =/= pendingMax
}
} else new Area{ } else new Area{
//This implementation keep the cmd on the bus until it's executed, even if the pipeline is flushed //This implementation keep the cmd on the bus until it's executed, even if the pipeline is flushed
def stage = cmdForkStage def stage = cmdForkStage
val pendingFull = pendingCmd === pendingMax val pendingFull = pending.value === pendingMax
val cmdKeep = RegInit(False) setWhen(cmd.valid) clearWhen(cmd.ready) val enterTheMarket = Bool()
val cmdKeep = RegInit(False) setWhen(enterTheMarket) clearWhen(cmd.ready)
val cmdFired = RegInit(False) setWhen(cmd.fire) clearWhen(stage.input.ready) val cmdFired = RegInit(False) setWhen(cmd.fire) clearWhen(stage.input.ready)
stage.halt setWhen(cmd.isStall || (pendingFull && !cmdFired)) enterTheMarket := stage.input.valid && !pendingFull && !cmdFired && !cmdKeep
cmd.valid := (stage.input.valid || cmdKeep) && !pendingFull && !cmdFired // stage.halt setWhen(cmd.isStall || (pendingFull && !cmdFired)) //(cmd.isStall)
stage.halt setWhen(pendingFull && !cmdFired && !cmdKeep)
stage.halt setWhen(!cmd.ready && !cmdFired)
cmd.valid := enterTheMarket || cmdKeep
pending.inc := enterTheMarket
} }
val mmu = (mmuBus != null) generate new Area { val mmu = (mmuBus != null) generate new Area {
mmuBus.cmd.isValid := cmdForkStage.input.valid mmuBus.cmd.isValid := cmdForkStage.input.valid
mmuBus.cmd.virtualAddress := cmdForkStage.input.payload mmuBus.cmd.virtualAddress := cmdForkStage.input.payload
mmuBus.cmd.bypassTranslation := False mmuBus.cmd.bypassTranslation := False
mmuBus.end := cmdForkStage.output.fire || fetcherflushIt mmuBus.end := cmdForkStage.output.fire || externalFlush
cmd.pc := mmuBus.rsp.physicalAddress(31 downto 2) @@ U"00" cmd.pc := mmuBus.rsp.physicalAddress(31 downto 2) @@ U"00"
@ -341,43 +347,37 @@ class IBusSimplePlugin( resetVector : BigInt,
val rspJoin = new Area { val rspJoin = new Area {
import iBusRsp._ import iBusRsp._
//Manage flush for iBus transactions in flight //Manage flush for iBus transactions in flight
val rspBuffer = new Area {
val output = Stream(IBusSimpleRsp())
val c = StreamFifoLowLatency(IBusSimpleRsp(), busLatencyMin + (if(cmdForkOnSecondStage && cmdForkPersistence) 1 else 0))
val discardCounter = Reg(UInt(log2Up(pendingMax + 1) bits)) init (0) val discardCounter = Reg(UInt(log2Up(pendingMax + 1) bits)) init (0)
discardCounter := discardCounter - (iBus.rsp.fire && discardCounter =/= 0).asUInt discardCounter := discardCounter - (c.io.pop.valid && discardCounter =/= 0).asUInt
when(iBusRsp.stages.last.flush) { when(iBusRsp.flush) {
if(secondStagePersistence) discardCounter := (if(cmdForkOnSecondStage) pending.next else pending.value - U(pending.dec))
discardCounter := pendingCmd + cmd.valid.asUInt - iBus.rsp.fire.asUInt
else
discardCounter := (if(cmdForkOnSecondStage) pendingCmdNext else pendingCmd - iBus.rsp.fire.asUInt)
} }
val rspBufferOutput = Stream(IBusSimpleRsp()) c.io.push << iBus.rsp.toStream
// if(compressedGen) c.io.flush setWhen(decompressor.consumeCurrent)
// if(!compressedGen && isDrivingDecode(IBUS_RSP)) c.io.flush setWhen(decode.arbitration.flushNext && iBusRsp.output.ready)
val flush = discardCounter =/= 0 || iBusRsp.flush
output.valid := c.io.pop.valid && discardCounter === 0
output.payload := c.io.pop.payload
c.io.pop.ready := output.ready || flush
val rspBuffer = if(!rspHoldValue) new Area{ pending.dec := c.io.pop.fire // iBus.rsp.valid && flush || c.io.pop.valid && output.ready instead to avoid unecessary dependancies ?
val c = StreamFifoLowLatency(IBusSimpleRsp(), busLatencyMin + (if(cmdForkOnSecondStage && cmdForkPersistence) 1 else 0))
c.io.push << iBus.rsp.throwWhen(discardCounter =/= 0).toStream
c.io.flush := iBusRsp.stages.last.flush
if(compressedGen) c.io.flush setWhen(decompressor.consumeCurrent)
if(!compressedGen && isDrivingDecode(IBUS_RSP)) c.io.flush setWhen(decode.arbitration.flushNext && iBusRsp.output.ready)
rspBufferOutput << c.io.pop
} else new Area{
val rspStream = iBus.rsp.throwWhen(discardCounter =/= 0).toStream
val validReg = RegInit(False) setWhen(rspStream.valid) clearWhen(rspBufferOutput.ready)
rspBufferOutput << rspStream
rspBufferOutput.valid setWhen(validReg)
} }
val fetchRsp = FetchRsp() val fetchRsp = FetchRsp()
fetchRsp.pc := stages.last.output.payload fetchRsp.pc := stages.last.output.payload
fetchRsp.rsp := rspBufferOutput.payload fetchRsp.rsp := rspBuffer.output.payload
fetchRsp.rsp.error.clearWhen(!rspBufferOutput.valid) //Avoid interference with instruction injection from the debug plugin fetchRsp.rsp.error.clearWhen(!rspBuffer.output.valid) //Avoid interference with instruction injection from the debug plugin
val join = Stream(FetchRsp()) val join = Stream(FetchRsp())
val exceptionDetected = False val exceptionDetected = False
join.valid := stages.last.output.valid && rspBufferOutput.valid join.valid := stages.last.output.valid && rspBuffer.output.valid
join.payload := fetchRsp join.payload := fetchRsp
stages.last.output.ready := stages.last.output.valid ? join.fire | join.ready stages.last.output.ready := stages.last.output.valid ? join.fire | join.ready
rspBufferOutput.ready := join.fire rspBuffer.output.ready := join.fire
output << join.haltWhen(exceptionDetected) output << join.haltWhen(exceptionDetected)
if(memoryTranslatorPortConfig != null){ if(memoryTranslatorPortConfig != null){