diff --git a/src/main/scala/vexriscv/VexRiscv.scala b/src/main/scala/vexriscv/VexRiscv.scala index 77ce1c1..35e5d6d 100644 --- a/src/main/scala/vexriscv/VexRiscv.scala +++ b/src/main/scala/vexriscv/VexRiscv.scala @@ -31,6 +31,11 @@ case class VexRiscvConfig(){ case None => None } } + def get[T](clazz: Class[T]): T = { + plugins.find(_.getClass == clazz) match { + case Some(x) => x.asInstanceOf[T] + } + } //Default Stageables object IS_RVC extends Stageable(Bool) diff --git a/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala b/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala index fc4926d..58ab5ee 100644 --- a/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala +++ b/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala @@ -17,7 +17,7 @@ import spinal.idslplugin.PostInitCallback import spinal.lib.misc.plic.PlicMapping import spinal.lib.system.debugger.SystemDebuggerConfig import vexriscv.ip.{DataCacheAck, DataCacheConfig, DataCacheMemBus, InstructionCache, InstructionCacheConfig} -import vexriscv.plugin.{BranchPlugin, CsrAccess, CsrPlugin, CsrPluginConfig, DBusCachedPlugin, DBusSimplePlugin, DYNAMIC_TARGET, DebugPlugin, DecoderSimplePlugin, FpuPlugin, FullBarrelShifterPlugin, HazardSimplePlugin, IBusCachedPlugin, IBusSimplePlugin, IntAluPlugin, MmuPlugin, MmuPortConfig, MulDivIterativePlugin, MulPlugin, RegFilePlugin, STATIC, SrcPlugin, StaticMemoryTranslatorPlugin, YamlPlugin} +import vexriscv.plugin._ import vexriscv.{Riscv, VexRiscv, VexRiscvBmbGenerator, VexRiscvConfig, plugin} import scala.collection.mutable @@ -168,6 +168,7 @@ object VexRiscvSmpClusterGen { iCacheWays : Int = 2, dCacheWays : Int = 2, iBusRelax : Boolean = false, + injectorStage : Boolean = false, earlyBranch : Boolean = false, dBusCmdMasterPipe : Boolean = false, withMmu : Boolean = true, @@ -175,7 +176,8 @@ object VexRiscvSmpClusterGen { withFloat : Boolean = false, withDouble : Boolean = false, externalFpu : Boolean = true, - simHalt : Boolean = false + simHalt : Boolean = false, + regfileRead : RegFileReadKind = plugin.ASYNC ) = { assert(iCacheSize/iCacheWays <= 4096, "Instruction cache ways can't be bigger than 4096 bytes") assert(dCacheSize/dCacheWays <= 4096, "Data cache ways can't be bigger than 4096 bytes") @@ -195,7 +197,7 @@ object VexRiscvSmpClusterGen { prediction = vexriscv.plugin.NONE, historyRamSizeLog2 = 9, relaxPredictorAddress = true, - injectorStage = false, + injectorStage = injectorStage, relaxedPcCalculation = iBusRelax, config = InstructionCacheConfig( cacheSize = iCacheSize, @@ -250,7 +252,7 @@ object VexRiscvSmpClusterGen { catchIllegalInstruction = true ), new RegFilePlugin( - regFileReadyKind = plugin.ASYNC, + regFileReadyKind = regfileRead, zeroBoot = false, x0Init = true ), diff --git a/src/main/scala/vexriscv/ip/DataCache.scala b/src/main/scala/vexriscv/ip/DataCache.scala index f12250b..4ba1b42 100644 --- a/src/main/scala/vexriscv/ip/DataCache.scala +++ b/src/main/scala/vexriscv/ip/DataCache.scala @@ -511,7 +511,7 @@ case class DataCacheMemBus(p : DataCacheConfig) extends Bundle with IMasterSlave cmdCtx.payload := aggregationCounter halt setWhen(!cmdCtx.ready) - val syncCtx = cmdCtx.queue(syncPendingMax) + val syncCtx = cmdCtx.queue(syncPendingMax).s2mPipe().m2sPipe() //Assume latency of sync is at least 3 cycles syncCtx.ready := bus.sync.fire sync.arbitrationFrom(bus.sync) diff --git a/src/main/scala/vexriscv/plugin/CfuPlugin.scala b/src/main/scala/vexriscv/plugin/CfuPlugin.scala index 60dd95b..bb2cb56 100644 --- a/src/main/scala/vexriscv/plugin/CfuPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CfuPlugin.scala @@ -49,13 +49,11 @@ case class CfuCmd( p : CfuBusParameter ) extends Bundle{ } case class CfuRsp(p : CfuBusParameter) extends Bundle{ - val response_ok = Bool() val response_id = UInt(p.CFU_REQ_RESP_ID_W bits) val outputs = Vec(Bits(p.CFU_OUTPUT_DATA_W bits), p.CFU_OUTPUTS) def weakAssignFrom(m : CfuRsp): Unit ={ def s = this - s.response_ok := m.response_ok s.response_id := m.response_id s.outputs := m.outputs } @@ -105,7 +103,6 @@ class CfuPlugin(val stageCount : Int, // assert(p.CFU_FUNCTION_ID_W == 3) var bus : CfuBus = null - var joinException : Flow[ExceptionCause] = null lazy val forkStage = pipeline.execute lazy val joinStage = pipeline.stages(Math.min(pipeline.stages.length - 1, pipeline.indexOf(forkStage) + stageCount)) @@ -121,7 +118,6 @@ class CfuPlugin(val stageCount : Int, import pipeline.config._ bus = master(CfuBus(p)) - joinException = pipeline.service(classOf[ExceptionService]).newExceptionPort(joinStage) val decoderService = pipeline.service(classOf[DecoderService]) decoderService.addDefault(CFU_ENABLE, False) @@ -207,22 +203,15 @@ class CfuPlugin(val stageCount : Int, bus.rsp.combStage() } - joinException.valid := False - joinException.code := 15 - joinException.badAddr := 0 - rsp.ready := False when(input(CFU_IN_FLIGHT)){ arbitration.haltItself setWhen(!rsp.valid) rsp.ready := !arbitration.isStuckByOthers output(REGFILE_WRITE_DATA) := rsp.outputs(0) - - when(arbitration.isValid){ - joinException.valid := !rsp.response_ok - } } } + pipeline.stages.drop(1).foreach(s => s.output(CFU_IN_FLIGHT) clearWhen(s.arbitration.isStuck)) addPrePopTask(() => stages.dropWhile(_ != memory).reverse.dropWhile(_ != joinStage).foreach(s => s.input(CFU_IN_FLIGHT).init(False))) } } @@ -251,7 +240,6 @@ case class CfuTest() extends Component{ val bus = slave(CfuBus(CfuTest.getCfuParameter())) } io.bus.rsp.arbitrationFrom(io.bus.cmd) - io.bus.rsp.response_ok := True io.bus.rsp.response_id := io.bus.cmd.request_id io.bus.rsp.outputs(0) := ~(io.bus.cmd.inputs(0) & io.bus.cmd.inputs(1)) } @@ -320,7 +308,6 @@ case class CfuDecoder(p : CfuBusParameter, io.input.rsp.payload := io.outputs.map(_.rsp.payload).read(OHToUInt(rspHits)) if(!hasDefault) when(rspNoHit.doIt) { io.input.rsp.valid := True - io.input.rsp.response_ok := False io.input.rsp.response_id := rspNoHit.response_id } for(output <- io.outputs) output.rsp.ready := io.input.rsp.ready diff --git a/src/main/scala/vexriscv/plugin/HazardSimplePlugin.scala b/src/main/scala/vexriscv/plugin/HazardSimplePlugin.scala index 1ed1d83..1b650e3 100644 --- a/src/main/scala/vexriscv/plugin/HazardSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/HazardSimplePlugin.scala @@ -31,29 +31,62 @@ class HazardSimplePlugin(bypassExecute : Boolean = false, override def build(pipeline: VexRiscv): Unit = { import pipeline._ import pipeline.config._ - val src0Hazard = False - val src1Hazard = False - val readStage = service(classOf[RegFileService]).readStage() + pipeline plug new Area { + val src0Hazard = False + val src1Hazard = False - def trackHazardWithStage(stage : Stage,bypassable : Boolean, runtimeBypassable : Stageable[Bool]): Unit ={ - val runtimeBypassableValue = if(runtimeBypassable != null) stage.input(runtimeBypassable) else True - val addr0Match = if(pessimisticAddressMatch) True else stage.input(INSTRUCTION)(rdRange) === readStage.input(INSTRUCTION)(rs1Range) - val addr1Match = if(pessimisticAddressMatch) True else stage.input(INSTRUCTION)(rdRange) === readStage.input(INSTRUCTION)(rs2Range) - when(stage.arbitration.isValid && stage.input(REGFILE_WRITE_VALID)) { - if (bypassable) { - when(runtimeBypassableValue) { + val readStage = service(classOf[RegFileService]).readStage() + + def trackHazardWithStage(stage: Stage, bypassable: Boolean, runtimeBypassable: Stageable[Bool]): Unit = { + val runtimeBypassableValue = if (runtimeBypassable != null) stage.input(runtimeBypassable) else True + val addr0Match = if (pessimisticAddressMatch) True else stage.input(INSTRUCTION)(rdRange) === readStage.input(INSTRUCTION)(rs1Range) + val addr1Match = if (pessimisticAddressMatch) True else stage.input(INSTRUCTION)(rdRange) === readStage.input(INSTRUCTION)(rs2Range) + when(stage.arbitration.isValid && stage.input(REGFILE_WRITE_VALID)) { + if (bypassable) { + when(runtimeBypassableValue) { + when(addr0Match) { + readStage.input(RS1) := stage.output(REGFILE_WRITE_DATA) + } + when(addr1Match) { + readStage.input(RS2) := stage.output(REGFILE_WRITE_DATA) + } + } + } + } + when(stage.arbitration.isValid && (if (pessimisticWriteRegFile) True else stage.input(REGFILE_WRITE_VALID))) { + when((Bool(!bypassable) || !runtimeBypassableValue)) { when(addr0Match) { - readStage.input(RS1) := stage.output(REGFILE_WRITE_DATA) + src0Hazard := True } when(addr1Match) { - readStage.input(RS2) := stage.output(REGFILE_WRITE_DATA) + src1Hazard := True } } } } - when(stage.arbitration.isValid && (if(pessimisticWriteRegFile) True else stage.input(REGFILE_WRITE_VALID))) { - when((Bool(!bypassable) || !runtimeBypassableValue)) { + + + val writeBackWrites = Flow(cloneable(new Bundle { + val address = Bits(5 bits) + val data = Bits(32 bits) + })) + writeBackWrites.valid := stages.last.output(REGFILE_WRITE_VALID) && stages.last.arbitration.isFiring + writeBackWrites.address := stages.last.output(INSTRUCTION)(rdRange) + writeBackWrites.data := stages.last.output(REGFILE_WRITE_DATA) + val writeBackBuffer = writeBackWrites.stage() + + val addr0Match = if (pessimisticAddressMatch) True else writeBackBuffer.address === readStage.input(INSTRUCTION)(rs1Range) + val addr1Match = if (pessimisticAddressMatch) True else writeBackBuffer.address === readStage.input(INSTRUCTION)(rs2Range) + when(writeBackBuffer.valid) { + if (bypassWriteBackBuffer) { + when(addr0Match) { + readStage.input(RS1) := writeBackBuffer.data + } + when(addr1Match) { + readStage.input(RS2) := writeBackBuffer.data + } + } else { when(addr0Match) { src0Hazard := True } @@ -62,54 +95,24 @@ class HazardSimplePlugin(bypassExecute : Boolean = false, } } } - } + + if (withWriteBackStage) trackHazardWithStage(writeBack, bypassWriteBack, null) + if (withMemoryStage) trackHazardWithStage(memory, bypassMemory, if (stages.last == memory) null else BYPASSABLE_MEMORY_STAGE) + if (readStage != execute) trackHazardWithStage(execute, bypassExecute, if (stages.last == execute) null else BYPASSABLE_EXECUTE_STAGE) - val writeBackWrites = Flow(cloneable(new Bundle{ - val address = Bits(5 bits) - val data = Bits(32 bits) - })) - writeBackWrites.valid := stages.last.output(REGFILE_WRITE_VALID) && stages.last.arbitration.isFiring - writeBackWrites.address := stages.last.output(INSTRUCTION)(rdRange) - writeBackWrites.data := stages.last.output(REGFILE_WRITE_DATA) - val writeBackBuffer = writeBackWrites.stage() - - val addr0Match = if(pessimisticAddressMatch) True else writeBackBuffer.address === readStage.input(INSTRUCTION)(rs1Range) - val addr1Match = if(pessimisticAddressMatch) True else writeBackBuffer.address === readStage.input(INSTRUCTION)(rs2Range) - when(writeBackBuffer.valid) { - if (bypassWriteBackBuffer) { - when(addr0Match) { - readStage.input(RS1) := writeBackBuffer.data + if (!pessimisticUseSrc) { + when(!readStage.input(RS1_USE)) { + src0Hazard := False } - when(addr1Match) { - readStage.input(RS2) := writeBackBuffer.data - } - } else { - when(addr0Match) { - src0Hazard := True - } - when(addr1Match) { - src1Hazard := True + when(!readStage.input(RS2_USE)) { + src1Hazard := False } } - } - if(withWriteBackStage) trackHazardWithStage(writeBack,bypassWriteBack,null) - if(withMemoryStage) trackHazardWithStage(memory ,bypassMemory, if(stages.last == memory) null else BYPASSABLE_MEMORY_STAGE) - if(readStage != execute) trackHazardWithStage(execute ,bypassExecute , if(stages.last == execute) null else BYPASSABLE_EXECUTE_STAGE) - - - if(!pessimisticUseSrc) { - when(!readStage.input(RS1_USE)) { - src0Hazard := False + when(readStage.arbitration.isValid && (src0Hazard || src1Hazard)) { + readStage.arbitration.haltByOther := True } - when(!readStage.input(RS2_USE)) { - src1Hazard := False - } - } - - when(readStage.arbitration.isValid && (src0Hazard || src1Hazard)){ - readStage.arbitration.haltByOther := True } } } diff --git a/src/main/scala/vexriscv/plugin/MulPlugin.scala b/src/main/scala/vexriscv/plugin/MulPlugin.scala index 2a13900..3e909a0 100644 --- a/src/main/scala/vexriscv/plugin/MulPlugin.scala +++ b/src/main/scala/vexriscv/plugin/MulPlugin.scala @@ -5,7 +5,8 @@ import spinal.core._ import spinal.lib.KeepAttribute //Input buffer generaly avoid the FPGA synthesis to duplicate reg inside the DSP cell, which could stress timings quite much. -class MulPlugin(inputBuffer : Boolean = false) extends Plugin[VexRiscv] with VexRiscvRegressionArg { +class MulPlugin(var inputBuffer : Boolean = false, + var outputBuffer : Boolean = false) extends Plugin[VexRiscv] with VexRiscvRegressionArg { object MUL_LL extends Stageable(UInt(32 bits)) object MUL_LH extends Stageable(SInt(34 bits)) object MUL_HL extends Stageable(SInt(34 bits)) @@ -19,7 +20,6 @@ class MulPlugin(inputBuffer : Boolean = false) extends Plugin[VexRiscv] with Vex List("MUL=yes") } - override def setup(pipeline: VexRiscv): Unit = { import Riscv._ import pipeline.config._ @@ -58,16 +58,25 @@ class MulPlugin(inputBuffer : Boolean = false) extends Plugin[VexRiscv] with Vex // a := input(SRC1) // b := input(SRC2) + val delay = (if(inputBuffer) 1 else 0) + (if(outputBuffer) 1 else 0) + + val delayLogic = (delay != 0) generate new Area{ + val counter = Reg(UInt(log2Up(delay+1) bits)) + when(arbitration.isValid && input(IS_MUL) && counter =/= delay){ + arbitration.haltItself := True + } + + counter := counter + 1 + when(!arbitration.isStuck || arbitration.isStuckByOthers){ + counter := 0 + } + } + val withInputBuffer = inputBuffer generate new Area{ val rs1 = RegNext(input(RS1)) val rs2 = RegNext(input(RS2)) a := rs1 b := rs2 - - val delay = RegNext(arbitration.isStuck) - when(arbitration.isValid && input(IS_MUL) && !delay){ - arbitration.haltItself := True - } } val noInputBuffer = (!inputBuffer) generate new Area{ @@ -96,10 +105,25 @@ class MulPlugin(inputBuffer : Boolean = false) extends Plugin[VexRiscv] with Vex val bSLow = (False ## b(15 downto 0)).asSInt val aHigh = (((aSigned && a.msb) ## a(31 downto 16))).asSInt val bHigh = (((bSigned && b.msb) ## b(31 downto 16))).asSInt - insert(MUL_LL) := aULow * bULow - insert(MUL_LH) := aSLow * bHigh - insert(MUL_HL) := aHigh * bSLow - insert(MUL_HH) := aHigh * bHigh + + val withOuputBuffer = outputBuffer generate new Area{ + val mul_ll = RegNext(aULow * bULow) + val mul_lh = RegNext(aSLow * bHigh) + val mul_hl = RegNext(aHigh * bSLow) + val mul_hh = RegNext(aHigh * bHigh) + + insert(MUL_LL) := mul_ll + insert(MUL_LH) := mul_lh + insert(MUL_HL) := mul_hl + insert(MUL_HH) := mul_hh + } + + val noOutputBuffer = (!outputBuffer) generate new Area{ + insert(MUL_LL) := aULow * bULow + insert(MUL_LH) := aSLow * bHigh + insert(MUL_HL) := aHigh * bSLow + insert(MUL_HH) := aHigh * bHigh + } Component.current.afterElaboration{ //Avoid synthesis tools to retime RS1 RS2 from execute stage to decode stage leading to bad timings (ex : Vivado, even if retiming is disabled) diff --git a/src/test/scala/vexriscv/TestIndividualFeatures.scala b/src/test/scala/vexriscv/TestIndividualFeatures.scala index cd4b9e9..6167f71 100644 --- a/src/test/scala/vexriscv/TestIndividualFeatures.scala +++ b/src/test/scala/vexriscv/TestIndividualFeatures.scala @@ -173,19 +173,26 @@ class MulDivDimension extends VexRiscvDimension("MulDiv") { } :: l - if(!noMemory && !noWriteBack) l = - new VexRiscvPosition("MulDivFpga") { - override def testParam = "MUL=yes DIV=yes" - override def applyOn(config: VexRiscvConfig): Unit = { - config.plugins += new MulPlugin - config.plugins += new MulDivIterativePlugin( - genMul = false, - genDiv = true, - mulUnrollFactor = 32, - divUnrollFactor = 1 - ) - } - } :: l + if(!noMemory && !noWriteBack) { + val inputBuffer = r.nextBoolean() + val outputBuffer = r.nextBoolean() + l = new VexRiscvPosition(s"MulDivFpga$inputBuffer$outputBuffer") { + override def testParam = "MUL=yes DIV=yes" + + override def applyOn(config: VexRiscvConfig): Unit = { + config.plugins += new MulPlugin( + inputBuffer = inputBuffer, + outputBuffer = outputBuffer + ) + config.plugins += new MulDivIterativePlugin( + genMul = false, + genDiv = true, + mulUnrollFactor = 32, + divUnrollFactor = 1 + ) + } + } :: l + } random(r, l) }