diff --git a/README.md b/README.md index 8561798..11105ec 100644 --- a/README.md +++ b/README.md @@ -156,8 +156,8 @@ sudo make install ## CPU generation You can find two example CPU instances in: -- src/main/scala/vexriscv/GenFull.scala -- src/main/scala/vexriscv/GenSmallest.scala +- src/main/scala/vexriscv/demo/GenFull.scala +- src/main/scala/vexriscv/demo/GenSmallest.scala To generate the corresponding RTL as a VexRiscv.v file, run the following commands in the root directory of this repository: diff --git a/build.sbt b/build.sbt index 3c2aae6..c22fc57 100644 --- a/build.sbt +++ b/build.sbt @@ -1,25 +1,3 @@ -//name := "VexRiscv" -// -//organization := "com.github.spinalhdl" -// -//version := "1.0.0" -// -//scalaVersion := "2.11.6" -// -//EclipseKeys.withSource := true -// -//libraryDependencies ++= Seq( -// "com.github.spinalhdl" % "spinalhdl-core_2.11" % "1.2.1", -// "com.github.spinalhdl" % "spinalhdl-lib_2.11" % "1.2.1", -// "org.scalatest" % "scalatest_2.11" % "2.2.1", -// "org.yaml" % "snakeyaml" % "1.8" -//) -// -// -// -//addCompilerPlugin("org.scala-lang.plugins" % "scala-continuations-plugin_2.11.6" % "1.0.2") -//scalacOptions += "-P:continuations:enable" -//fork := true lazy val root = (project in file(".")). settings( @@ -29,16 +7,16 @@ lazy val root = (project in file(".")). version := "1.0.0" )), libraryDependencies ++= Seq( - "com.github.spinalhdl" % "spinalhdl-core_2.11" % "1.3.1", - "com.github.spinalhdl" % "spinalhdl-lib_2.11" % "1.3.1", + "com.github.spinalhdl" % "spinalhdl-core_2.11" % "1.3.2", + "com.github.spinalhdl" % "spinalhdl-lib_2.11" % "1.3.2", "org.scalatest" % "scalatest_2.11" % "2.2.1", "org.yaml" % "snakeyaml" % "1.8" ), name := "VexRiscv" )//.dependsOn(spinalHdlSim,spinalHdlCore,spinalHdlLib) -//lazy val spinalHdlSim = ProjectRef(file("../SpinalHDL"), "SpinalHDL-sim") -//lazy val spinalHdlCore = ProjectRef(file("../SpinalHDL"), "SpinalHDL-core") -//lazy val spinalHdlLib = ProjectRef(file("../SpinalHDL"), "SpinalHDL-lib") +//lazy val spinalHdlSim = ProjectRef(file("../SpinalHDL"), "sim") +//lazy val spinalHdlCore = ProjectRef(file("../SpinalHDL"), "core") +//lazy val spinalHdlLib = ProjectRef(file("../SpinalHDL"), "lib") fork := true \ No newline at end of file diff --git a/src/main/scala/vexriscv/Pipeline.scala b/src/main/scala/vexriscv/Pipeline.scala index f0466c5..898094e 100644 --- a/src/main/scala/vexriscv/Pipeline.scala +++ b/src/main/scala/vexriscv/Pipeline.scala @@ -30,6 +30,13 @@ trait Pipeline { filtered.length != 0 } + def serviceElse[T](clazz : Class[T], default : => T) : T = { + if(!serviceExist(clazz)) return default + val filtered = plugins.filter(o => clazz.isAssignableFrom(o.getClass)) + assert(filtered.length == 1) + filtered.head.asInstanceOf[T] + } + def update[T](that : PipelineConfig[T], value : T) : Unit = configs(that) = value def apply[T](that : PipelineConfig[T]) : T = configs(that).asInstanceOf[T] diff --git a/src/main/scala/vexriscv/Services.scala b/src/main/scala/vexriscv/Services.scala index 33adf07..6f7d641 100644 --- a/src/main/scala/vexriscv/Services.scala +++ b/src/main/scala/vexriscv/Services.scala @@ -39,6 +39,10 @@ trait PrivilegeService{ def isUser(stage : Stage) : Bool } +case class PrivilegeServiceDefault() extends PrivilegeService{ + override def isUser(stage: Stage): Bool = False +} + trait InterruptionInhibitor{ def inhibateInterrupts() : Unit } diff --git a/src/main/scala/vexriscv/TestsWorkspace.scala b/src/main/scala/vexriscv/TestsWorkspace.scala index 34d047d..75e5934 100644 --- a/src/main/scala/vexriscv/TestsWorkspace.scala +++ b/src/main/scala/vexriscv/TestsWorkspace.scala @@ -28,20 +28,20 @@ import spinal.lib.eda.altera.{InterruptReceiverTag, ResetEmitterTag} object TestsWorkspace { def main(args: Array[String]) { - SpinalConfig(mergeAsyncProcess = false, anonymSignalPrefix = "zz_").generateVerilog { - val configFull = VexRiscvConfig( + def configFull = { + val config = VexRiscvConfig( plugins = List( -// new IBusSimplePlugin( -// resetVector = 0x80000000l, -// cmdForkOnSecondStage = false, -// cmdForkPersistence = false, -// prediction = NONE, -// historyRamSizeLog2 = 10, -// catchAccessFault = false, -// compressedGen = false, -// busLatencyMin = 1, -// injectorStage = true -// ), + // new IBusSimplePlugin( + // resetVector = 0x80000000l, + // cmdForkOnSecondStage = false, + // cmdForkPersistence = false, + // prediction = NONE, + // historyRamSizeLog2 = 10, + // catchAccessFault = false, + // compressedGen = false, + // busLatencyMin = 1, + // injectorStage = true + // ), new IBusCachedPlugin( resetVector = 0x80000000l, compressedGen = false, @@ -65,11 +65,12 @@ object TestsWorkspace { portTlbSize = 4 ) ), -// new DBusSimplePlugin( -// catchAddressMisaligned = true, -// catchAccessFault = false, -// earlyInjection = false -// ), +// ).newTightlyCoupledPort(TightlyCoupledPortParameter("iBusTc", a => a(30 downto 28) === 0x0 && a(5))), + // new DBusSimplePlugin( + // catchAddressMisaligned = true, + // catchAccessFault = false, + // earlyInjection = false + // ), new DBusCachedPlugin( config = new DataCacheConfig( cacheSize = 4096, @@ -84,21 +85,21 @@ object TestsWorkspace { catchMemoryTranslationMiss = true, atomicEntriesCount = 2 ), -// memoryTranslatorPortConfig = null + // memoryTranslatorPortConfig = null memoryTranslatorPortConfig = MemoryTranslatorPortConfig( portTlbSize = 6 ) ), -// new StaticMemoryTranslatorPlugin( -// ioRange = _(31 downto 28) === 0xF -// ), + // new StaticMemoryTranslatorPlugin( + // ioRange = _(31 downto 28) === 0xF + // ), new MemoryTranslatorPlugin( tlbSize = 32, virtualRange = _(31 downto 28) === 0xC, ioRange = _(31 downto 28) === 0xF ), new DecoderSimplePlugin( - catchIllegalInstruction = false + catchIllegalInstruction = true ), new RegFilePlugin( regFileReadyKind = plugin.ASYNC, @@ -109,7 +110,7 @@ object TestsWorkspace { separatedAddSub = false ), new FullBarrelShifterPlugin(earlyInjection = true), - // new LightShifterPlugin, + // new LightShifterPlugin, new HazardSimplePlugin( bypassExecute = true, bypassMemory = true, @@ -119,8 +120,8 @@ object TestsWorkspace { pessimisticWriteRegFile = false, pessimisticAddressMatch = false ), - // new HazardSimplePlugin(false, true, false, true), - // new HazardSimplePlugin(false, false, false, false), + // new HazardSimplePlugin(false, true, false, true), + // new HazardSimplePlugin(false, false, false, false), new MulPlugin, new MulDivIterativePlugin( genMul = false, @@ -128,31 +129,31 @@ object TestsWorkspace { mulUnrollFactor = 32, divUnrollFactor = 1 ), -// new DivPlugin, + // new DivPlugin, new CsrPlugin(CsrPluginConfig.all(0x80000020l)), -// new CsrPlugin(//CsrPluginConfig.all2(0x80000020l).copy(ebreakGen = true)/* -// CsrPluginConfig( -// catchIllegalAccess = false, -// mvendorid = null, -// marchid = null, -// mimpid = null, -// mhartid = null, -// misaExtensionsInit = 0, -// misaAccess = CsrAccess.READ_ONLY, -// mtvecAccess = CsrAccess.WRITE_ONLY, -// mtvecInit = 0x80000020l, -// mepcAccess = CsrAccess.READ_WRITE, -// mscratchGen = true, -// mcauseAccess = CsrAccess.READ_ONLY, -// mbadaddrAccess = CsrAccess.READ_ONLY, -// mcycleAccess = CsrAccess.NONE, -// minstretAccess = CsrAccess.NONE, -// ecallGen = true, -// ebreakGen = true, -// wfiGenAsWait = false, -// wfiGenAsNop = true, -// ucycleAccess = CsrAccess.NONE -// )), + // new CsrPlugin(//CsrPluginConfig.all2(0x80000020l).copy(ebreakGen = true)/* + // CsrPluginConfig( + // catchIllegalAccess = false, + // mvendorid = null, + // marchid = null, + // mimpid = null, + // mhartid = null, + // misaExtensionsInit = 0, + // misaAccess = CsrAccess.READ_ONLY, + // mtvecAccess = CsrAccess.WRITE_ONLY, + // mtvecInit = 0x80000020l, + // mepcAccess = CsrAccess.READ_WRITE, + // mscratchGen = true, + // mcauseAccess = CsrAccess.READ_ONLY, + // mbadaddrAccess = CsrAccess.READ_ONLY, + // mcycleAccess = CsrAccess.NONE, + // minstretAccess = CsrAccess.NONE, + // ecallGen = true, + // ebreakGen = true, + // wfiGenAsWait = false, + // wfiGenAsNop = true, + // ucycleAccess = CsrAccess.NONE + // )), new DebugPlugin(ClockDomain.current.clone(reset = Bool().setName("debugReset"))), new BranchPlugin( earlyBranch = true, @@ -162,8 +163,28 @@ object TestsWorkspace { new YamlPlugin("cpu0.yaml") ) ) + config + } +// import spinal.core.sim._ +// SimConfig.withConfig(SpinalConfig(mergeAsyncProcess = false, anonymSignalPrefix = "zz_")).allOptimisation.compile(new VexRiscv(configFull)).doSimUntilVoid{ dut => +// dut.clockDomain.forkStimulus(10) +// dut.clockDomain.forkSimSpeedPrinter(4) +// var iBus : InstructionCacheMemBus = null +// +// dut.plugins.foreach{ +// case plugin: IBusCachedPlugin => iBus = plugin.iBus +// case _ => +// } +// dut.clockDomain.onSamplings{ +//// iBus.cmd.ready.randomize() +// iBus.rsp.data #= 0x13 +// } +// } + + SpinalConfig(mergeAsyncProcess = false, anonymSignalPrefix = "zz_").generateVerilog { + val toplevel = new VexRiscv(configFull) // val toplevel = new VexRiscv(configLight) diff --git a/src/main/scala/vexriscv/demo/Briey.scala b/src/main/scala/vexriscv/demo/Briey.scala index ca76f8b..31c7460 100644 --- a/src/main/scala/vexriscv/demo/Briey.scala +++ b/src/main/scala/vexriscv/demo/Briey.scala @@ -258,10 +258,12 @@ class Briey(config: BrieyConfig) extends Component{ ) val gpioACtrl = Apb3Gpio( - gpioWidth = 32 + gpioWidth = 32, + withReadSync = true ) val gpioBCtrl = Apb3Gpio( - gpioWidth = 32 + gpioWidth = 32, + withReadSync = true ) val timerCtrl = PinsecTimerCtrl() diff --git a/src/main/scala/vexriscv/demo/GenSmallAndPerformant.scala b/src/main/scala/vexriscv/demo/GenSmallAndProductive.scala similarity index 100% rename from src/main/scala/vexriscv/demo/GenSmallAndPerformant.scala rename to src/main/scala/vexriscv/demo/GenSmallAndProductive.scala diff --git a/src/main/scala/vexriscv/demo/GenSmallAndPerformantICache.scala b/src/main/scala/vexriscv/demo/GenSmallAndProductiveICache.scala similarity index 100% rename from src/main/scala/vexriscv/demo/GenSmallAndPerformantICache.scala rename to src/main/scala/vexriscv/demo/GenSmallAndProductiveICache.scala diff --git a/src/main/scala/vexriscv/demo/Murax.scala b/src/main/scala/vexriscv/demo/Murax.scala index 8a06cbc..d78beeb 100644 --- a/src/main/scala/vexriscv/demo/Murax.scala +++ b/src/main/scala/vexriscv/demo/Murax.scala @@ -276,7 +276,7 @@ case class Murax(config : MuraxConfig) extends Component{ //******** APB peripherals ********* val apbMapping = ArrayBuffer[(Apb3, SizeMapping)]() - val gpioACtrl = Apb3Gpio(gpioWidth = gpioWidth) + val gpioACtrl = Apb3Gpio(gpioWidth = gpioWidth, withReadSync = true) io.gpioA <> gpioACtrl.io.gpio apbMapping += gpioACtrl.io.apb -> (0x00000, 4 kB) diff --git a/src/main/scala/vexriscv/ip/InstructionCache.scala b/src/main/scala/vexriscv/ip/InstructionCache.scala index 40cb700..26d1caa 100644 --- a/src/main/scala/vexriscv/ip/InstructionCache.scala +++ b/src/main/scala/vexriscv/ip/InstructionCache.scala @@ -86,15 +86,17 @@ trait InstructionCacheCommons{ val pc : UInt val physicalAddress : UInt val data : Bits - val cacheMiss, error, mmuMiss, illegalAccess,isUser : Bool + val cacheMiss, error, mmuMiss, illegalAccess, isUser : Bool } case class InstructionCacheCpuFetch(p : InstructionCacheConfig) extends Bundle with IMasterSlave with InstructionCacheCommons { - val isValid = Bool - val isStuck = Bool - val isRemoved = Bool + val isValid = Bool() + val isStuck = Bool() + val isRemoved = Bool() val pc = UInt(p.addressWidth bits) - val data = Bits(p.cpuDataWidth bits) + val data = Bits(p.cpuDataWidth bits) + val dataBypassValid = Bool() + val dataBypass = Bits(p.cpuDataWidth bits) val mmuBus = MemoryTranslatorBus() val physicalAddress = UInt(p.addressWidth bits) val cacheMiss, error, mmuMiss, illegalAccess,isUser = ifGen(!p.twoCycleCache)(Bool) @@ -102,7 +104,7 @@ case class InstructionCacheCpuFetch(p : InstructionCacheConfig) extends Bundle w override def asMaster(): Unit = { out(isValid, isStuck, isRemoved, pc) inWithNull(error,mmuMiss,illegalAccess,data, cacheMiss,physicalAddress) - outWithNull(isUser) + outWithNull(isUser, dataBypass, dataBypassValid) slaveWithNull(mmuBus) } } @@ -381,21 +383,21 @@ class InstructionCache(p : InstructionCacheConfig) extends Component{ } - val hit = if(!twoCycleRam) new Area{ + val hit = (!twoCycleRam) generate new Area{ val hits = read.waysValues.map(way => way.tag.valid && way.tag.address === io.cpu.fetch.mmuBus.rsp.physicalAddress(tagRange)) val valid = Cat(hits).orR val id = OHToUInt(hits) val error = read.waysValues.map(_.tag.error).read(id) val data = read.waysValues.map(_.data).read(id) val word = data.subdivideIn(cpuDataWidth bits).read(io.cpu.fetch.pc(memWordToCpuWordRange)) - io.cpu.fetch.data := word + io.cpu.fetch.data := (io.cpu.fetch.dataBypassValid ? io.cpu.fetch.dataBypass | word) if(twoCycleCache){ io.cpu.decode.data := RegNextWhen(io.cpu.fetch.data,!io.cpu.decode.isStuck) } - } else null + } if(twoCycleRam && wayCount == 1){ - io.cpu.fetch.data := read.waysValues.head.data.subdivideIn(cpuDataWidth bits).read(io.cpu.fetch.pc(memWordToCpuWordRange)) + io.cpu.fetch.data := (io.cpu.fetch.dataBypassValid ? io.cpu.fetch.dataBypass | read.waysValues.head.data.subdivideIn(cpuDataWidth bits).read(io.cpu.fetch.pc(memWordToCpuWordRange))) } io.cpu.fetch.mmuBus.cmd.isValid := io.cpu.fetch.isValid @@ -405,7 +407,6 @@ class InstructionCache(p : InstructionCacheConfig) extends Component{ io.cpu.fetch.physicalAddress := io.cpu.fetch.mmuBus.rsp.physicalAddress val resolution = ifGen(!twoCycleCache)( new Area{ -// def stage[T <: Data](that : T) = RegNextWhen(that,!io.cpu.decode.isStuck) val mmuRsp = io.cpu.fetch.mmuBus.rsp io.cpu.fetch.cacheMiss := !hit.valid @@ -432,17 +433,13 @@ class InstructionCache(p : InstructionCacheConfig) extends Component{ val error = tags(id).error val data = fetchStage.read.waysValues.map(way => stage(way.data)).read(id) val word = data.subdivideIn(cpuDataWidth bits).read(io.cpu.decode.pc(memWordToCpuWordRange)) + when(stage(io.cpu.fetch.dataBypassValid)){ + word := stage(io.cpu.fetch.dataBypass) + } io.cpu.decode.data := word } io.cpu.decode.cacheMiss := !hit.valid -// when( io.cpu.decode.isValid && io.cpu.decode.cacheMiss){ -// io.cpu.prefetch.haltIt := True -// lineLoader.valid := True -// lineLoader.address := mmuRsp.physicalAddress //Could be optimise if mmu not used -// } -// when(io.cpu) - io.cpu.decode.error := hit.error io.cpu.decode.mmuMiss := mmuRsp.miss io.cpu.decode.illegalAccess := !mmuRsp.allowExecute || (io.cpu.decode.isUser && !mmuRsp.allowUser) diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index fb383e5..ab59360 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -271,6 +271,20 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception val csrMapping = new CsrMapping() + + case class InterruptSource(var cond : Bool, id : Int) + case class InterruptPrivilege(privilege : Int){ + var privilegeCond : Bool = null + val sources = ArrayBuffer[InterruptSource]() + } + + def getInterruptPrivilege(privilege : Int) = customInterrupts.getOrElseUpdate(privilege, InterruptPrivilege(privilege)) + var customInterrupts = mutable.LinkedHashMap[Int, InterruptPrivilege]() + def addInterrupt(cond : Bool, id : Int, privilege : Int): Unit = { + getInterruptPrivilege(privilege).sources += InterruptSource(cond, id) + } + def createInterrupt(id : Int, privilege : Int) : Bool = { val ret = Bool(); addInterrupt(ret, id, privilege); ret} + override def r(csrAddress: Int, bitOffset: Int, that: Data): Unit = csrMapping.r(csrAddress, bitOffset, that) override def w(csrAddress: Int, bitOffset: Int, that: Data): Unit = csrMapping.w(csrAddress, bitOffset, that) override def onWrite(csrAddress: Int)(body: => Unit): Unit = csrMapping.onWrite(csrAddress)(body) @@ -338,6 +352,11 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception allowInterrupts = True allowException = True + + for (privilege <- customInterrupts.values; + source <- privilege.sources){ + source.cond = source.cond.pull() + } } def inhibateInterrupts() : Unit = allowInterrupts := False @@ -500,20 +519,22 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception minstret := minstret + 1 } - case class InterruptSource(cond : Bool, id : Int) - case class InterruptPrivilege(privilege : Int, privilegeCond : Bool, sources : ArrayBuffer[InterruptSource]) - val interruptModel = ArrayBuffer[InterruptPrivilege]() - if(supervisorGen) interruptModel += InterruptPrivilege(1, sstatus.SIE && privilege <= "01", ArrayBuffer( - InterruptSource(sip.STIP && sie.STIE, 5), - InterruptSource(sip.SSIP && sie.SSIE, 1), - InterruptSource(sip.SEIP && sie.SEIE, 9) - )) - interruptModel += InterruptPrivilege(3, mstatus.MIE , ArrayBuffer( + if(supervisorGen) { + getInterruptPrivilege(1).privilegeCond = sstatus.SIE && privilege <= "01" + getInterruptPrivilege(1).sources ++= List( + InterruptSource(sip.STIP && sie.STIE, 5), + InterruptSource(sip.SSIP && sie.SSIE, 1), + InterruptSource(sip.SEIP && sie.SEIE, 9) + ) + } + + getInterruptPrivilege(3).privilegeCond = mstatus.MIE + getInterruptPrivilege(3).sources ++= List( InterruptSource(mip.MTIP && mie.MTIE, 7), InterruptSource(mip.MSIP && mie.MSIE, 3), InterruptSource(mip.MEIP && mie.MEIE, 11) - )) + ) case class DelegatorModel(value : Bits, source : Int, target : Int) // def solveDelegators(delegators : Seq[DelegatorModel], id : Int, lowerBound : Int): UInt = { @@ -621,7 +642,7 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception val interrupt = False val interruptCode = UInt(4 bits).assignDontCare().addTag(Verilator.public) val interruptDelegatorHit = interruptDelegators.map(d => (d -> False)).toMap - for(model <- interruptModel){ + for(model <- customInterrupts.values.toSeq.sortBy(_.privilege)){ when(model.privilegeCond){ when(model.sources.map(_.cond).orR){ interrupt := True diff --git a/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala b/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala index 3d60498..e9c4633 100644 --- a/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala @@ -84,61 +84,79 @@ class DecoderSimplePlugin(catchIllegalInstruction : Boolean = false, forceLegalI val stageables = (encodings.flatMap(_._2.map(_._1)) ++ defaults.map(_._1)).toSet.toList - var offset = 0 - var defaultValue, defaultCare = BigInt(0) - val offsetOf = mutable.HashMap[Stageable[_ <: BaseType],Int]() + val stupidDecoder = false + if(stupidDecoder){ + if (catchIllegalInstruction || forceLegalInstructionComputation) insert(LEGAL_INSTRUCTION) := False + for(stageable <- stageables){ + if(defaults.contains(stageable)){ + insert(stageable).assignFrom(defaults(stageable)) + } else { + insert(stageable).assignDontCare() + } + } + for((key, tasks) <- encodings){ + when(input(INSTRUCTION) === key){ + if (catchIllegalInstruction || forceLegalInstructionComputation) insert(LEGAL_INSTRUCTION) := True + for((stageable, value) <- tasks){ + insert(stageable).assignFrom(value) + } + } + } + } else { + var offset = 0 + var defaultValue, defaultCare = BigInt(0) + val offsetOf = mutable.HashMap[Stageable[_ <: BaseType], Int]() - //Build defaults value and field offset map - stageables.foreach(e => { - defaults.get(e) match { - case Some(value) => { - value.head.source match { + //Build defaults value and field offset map + stageables.foreach(e => { + defaults.get(e) match { + case Some(value) => { + value.head.source match { + case literal: EnumLiteral[_] => literal.fixEncoding(e.dataType.asInstanceOf[SpinalEnumCraft[_]].getEncoding) + case _ => + } + defaultValue += value.head.source.asInstanceOf[Literal].getValue << offset + defaultCare += ((BigInt(1) << e.dataType.getBitsWidth) - 1) << offset + + } + case _ => + } + offsetOf(e) = offset + offset += e.dataType.getBitsWidth + }) + + //Build spec + val spec = encodings.map { case (key, values) => + var decodedValue = defaultValue + var decodedCare = defaultCare + for ((e, literal) <- values) { + literal.head.source match { case literal: EnumLiteral[_] => literal.fixEncoding(e.dataType.asInstanceOf[SpinalEnumCraft[_]].getEncoding) case _ => } - defaultValue += value.head.source .asInstanceOf[Literal].getValue << offset - defaultCare += ((BigInt(1) << e.dataType.getBitsWidth) - 1) << offset - + val offset = offsetOf(e) + decodedValue |= literal.head.source.asInstanceOf[Literal].getValue << offset + decodedCare |= ((BigInt(1) << e.dataType.getBitsWidth) - 1) << offset } - case _ => + (Masked(key.value, key.careAbout), Masked(decodedValue, decodedCare)) } - offsetOf(e) = offset - offset += e.dataType.getBitsWidth - }) - //Build spec - val spec = encodings.map { case (key, values) => - var decodedValue = defaultValue - var decodedCare = defaultCare - for((e, literal) <- values){ - literal.head.source match{ - case literal : EnumLiteral[_] => literal.fixEncoding(e.dataType.asInstanceOf[SpinalEnumCraft[_]].getEncoding) - case _ => - } - val offset = offsetOf(e) - decodedValue |= literal.head.source.asInstanceOf[Literal].getValue << offset - decodedCare |= ((BigInt(1) << e.dataType.getBitsWidth)-1) << offset - } - (Masked(key.value,key.careAbout),Masked(decodedValue,decodedCare)) + + // logic implementation + val decodedBits = Bits(stageables.foldLeft(0)(_ + _.dataType.getBitsWidth) bits) + decodedBits := Symplify(input(INSTRUCTION), spec, decodedBits.getWidth) + if (catchIllegalInstruction || forceLegalInstructionComputation) insert(LEGAL_INSTRUCTION) := Symplify.logicOf(input(INSTRUCTION), SymplifyBit.getPrimeImplicantsByTrueAndDontCare(spec.unzip._1.toSeq, Nil, 32)) + + + //Unpack decodedBits and insert fields in the pipeline + offset = 0 + stageables.foreach(e => { + insert(e).assignFromBits(decodedBits(offset, e.dataType.getBitsWidth bits)) + // insert(e).assignFromBits(RegNext(decodedBits(offset, e.dataType.getBitsWidth bits))) + offset += e.dataType.getBitsWidth + }) } - - - // logic implementation - val decodedBits = Bits(stageables.foldLeft(0)(_ + _.dataType.getBitsWidth) bits) - decodedBits := Symplify(input(INSTRUCTION),spec, decodedBits.getWidth) - if(catchIllegalInstruction || forceLegalInstructionComputation) insert(LEGAL_INSTRUCTION) := Symplify.logicOf(input(INSTRUCTION), SymplifyBit.getPrimeImplicantsByTrueAndDontCare(spec.unzip._1.toSeq, Nil, 32)) - - - //Unpack decodedBits and insert fields in the pipeline - offset = 0 - stageables.foreach(e => { - insert(e).assignFromBits(decodedBits(offset, e.dataType.getBitsWidth bits)) -// insert(e).assignFromBits(RegNext(decodedBits(offset, e.dataType.getBitsWidth bits))) - offset += e.dataType.getBitsWidth - }) - - if(catchIllegalInstruction){ decodeExceptionPort.valid := arbitration.isValid && input(INSTRUCTION_READY) && !input(LEGAL_INSTRUCTION) // ?? HalitIt to alow decoder stage to wait valid data from 2 stages cache cache ?? decodeExceptionPort.code := 2 @@ -149,8 +167,8 @@ class DecoderSimplePlugin(catchIllegalInstruction : Boolean = false, forceLegalI def bench(toplevel : VexRiscv): Unit ={ toplevel.rework{ import toplevel.config._ - toplevel.getAllIo.foreach{io => - if(io.isInput) io.assignDontCare() + toplevel.getAllIo.toList.foreach{io => + if(io.isInput) { io.assignDontCare()} io.setAsDirectionLess() } toplevel.decode.input(INSTRUCTION).removeAssignments() diff --git a/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala b/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala index 66348f4..03285f0 100644 --- a/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala +++ b/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala @@ -5,10 +5,26 @@ import vexriscv.ip._ import spinal.core._ import spinal.lib._ +import scala.collection.mutable.ArrayBuffer + //class IBusCachedPlugin(config : InstructionCacheConfig, memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv] { // var iBus : InstructionCacheMemBus = null // override def build(pipeline: VexRiscv): Unit = ??? //} + +case class TightlyCoupledBus() extends Bundle with IMasterSlave { + val enable = Bool() + val address = UInt(32 bits) + val data = Bits(32 bits) + + override def asMaster(): Unit = { + out(enable, address) + in(data) + } +} + +case class TightlyCoupledPortParameter(name : String, hit : UInt => Bool) +case class TightlyCoupledPort(p : TightlyCoupledPortParameter, var bus : TightlyCoupledBus) class IBusCachedPlugin(resetVector : BigInt = 0x80000000l, relaxedPcCalculation : Boolean = false, prediction : BranchPrediction = NONE, @@ -17,7 +33,8 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l, keepPcPlus4 : Boolean = false, config : InstructionCacheConfig, memoryTranslatorPortConfig : Any = null, - injectorStage : Boolean = false) extends IBusFetcherImpl( + injectorStage : Boolean = false, + withoutInjectorStage : Boolean = false) extends IBusFetcherImpl( resetVector = resetVector, keepPcPlus4 = keepPcPlus4, decodePcGen = compressedGen, @@ -27,15 +44,25 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l, injectorReadyCutGen = false, prediction = prediction, historyRamSizeLog2 = historyRamSizeLog2, - injectorStage = !config.twoCycleCache || injectorStage){ + injectorStage = (!config.twoCycleCache && !withoutInjectorStage) || injectorStage){ import config._ + assert(!(withoutInjectorStage && injectorStage)) var iBus : InstructionCacheMemBus = null var mmuBus : MemoryTranslatorBus = null var privilegeService : PrivilegeService = null var redoBranch : Flow[UInt] = null var decodeExceptionPort : Flow[ExceptionCause] = null + val tightlyCoupledPorts = ArrayBuffer[TightlyCoupledPort]() + + + def newTightlyCoupledPort(p : TightlyCoupledPortParameter) = { + val port = TightlyCoupledPort(p, null) + tightlyCoupledPorts += port + this + } + object FLUSH_ALL extends Stageable(Bool) object IBUS_ACCESS_ERROR extends Stageable(Bool) @@ -66,8 +93,7 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l, if(pipeline.serviceExist(classOf[MemoryTranslator])) mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(MemoryTranslatorPort.PRIORITY_INSTRUCTION, memoryTranslatorPortConfig) - if(pipeline.serviceExist(classOf[PrivilegeService])) - privilegeService = pipeline.service(classOf[PrivilegeService]) + privilegeService = pipeline.serviceElse(classOf[PrivilegeService], PrivilegeServiceDefault()) if(pipeline.serviceExist(classOf[ReportService])){ val report = pipeline.service(classOf[ReportService]) @@ -98,21 +124,106 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l, val cache = new InstructionCache(IBusCachedPlugin.this.config) iBus = master(new InstructionCacheMemBus(IBusCachedPlugin.this.config)).setName("iBus") iBus <> cache.io.mem - iBus.cmd.address.allowOverride := cache.io.mem.cmd.address // - debugAddressOffset + iBus.cmd.address.allowOverride := cache.io.mem.cmd.address val stageOffset = if(relaxedPcCalculation) 1 else 0 def stages = iBusRsp.stages.drop(stageOffset) - //Connect prefetch cache side - cache.io.cpu.prefetch.isValid := stages(0).input.valid - cache.io.cpu.prefetch.pc := stages(0).input.payload - stages(0).halt setWhen(cache.io.cpu.prefetch.haltIt) + + tightlyCoupledPorts.foreach(p => p.bus = master(TightlyCoupledBus()).setName(p.p.name)) + + val s0 = new Area { + //address decoding + val tightlyCoupledHits = Vec(tightlyCoupledPorts.map(_.p.hit(stages(0).input.payload))) + val tightlyCoupledHit = tightlyCoupledHits.orR + + for((port, hit) <- (tightlyCoupledPorts, tightlyCoupledHits).zipped){ + port.bus.enable := stages(0).input.fire && hit + port.bus.address := stages(0).input.payload(31 downto 2) @@ U"00" + } + + //Connect prefetch cache side + cache.io.cpu.prefetch.isValid := stages(0).input.valid && !tightlyCoupledHit + cache.io.cpu.prefetch.pc := stages(0).input.payload + stages(0).halt setWhen (cache.io.cpu.prefetch.haltIt) - cache.io.cpu.fetch.isRemoved := flush - val iBusRspOutputHalt = False + cache.io.cpu.fetch.isRemoved := flush + } + + + val s1 = new Area { + val tightlyCoupledHits = RegNextWhen(s0.tightlyCoupledHits, stages(1).input.ready) + val tightlyCoupledHit = RegNextWhen(s0.tightlyCoupledHit, stages(1).input.ready) + + cache.io.cpu.fetch.dataBypassValid := tightlyCoupledHit + cache.io.cpu.fetch.dataBypass := (if(tightlyCoupledPorts.isEmpty) B(0) else MuxOH(tightlyCoupledHits, tightlyCoupledPorts.map(e => CombInit(e.bus.data)))) + + //Connect fetch cache side + cache.io.cpu.fetch.isValid := stages(1).input.valid && !tightlyCoupledHit + cache.io.cpu.fetch.isStuck := !stages(1).input.ready + cache.io.cpu.fetch.pc := stages(1).input.payload + + if (!twoCycleCache) { + cache.io.cpu.fetch.isUser := privilegeService.isUser(decode) + } + } + + val s2 = twoCycleCache generate new Area { + val tightlyCoupledHit = RegNextWhen(s1.tightlyCoupledHit, stages(2).input.ready) + cache.io.cpu.decode.isValid := stages(2).input.valid && !tightlyCoupledHit + cache.io.cpu.decode.isStuck := !stages(2).input.ready + cache.io.cpu.decode.pc := stages(2).input.payload + cache.io.cpu.decode.isUser := privilegeService.isUser(decode) + + if ((!twoCycleRam || wayCount == 1) && !compressedGen && !injectorStage) { + decode.insert(INSTRUCTION_ANTICIPATED) := Mux(decode.arbitration.isStuck, decode.input(INSTRUCTION), cache.io.cpu.fetch.data) + } + } + + val rsp = new Area { + val iBusRspOutputHalt = False + + val cacheRsp = if (twoCycleCache) cache.io.cpu.decode else cache.io.cpu.fetch + val cacheRspArbitration = stages(if (twoCycleCache) 2 else 1) + var issueDetected = False + val redoFetch = False //RegNext(False) init(False) + when(cacheRsp.isValid && cacheRsp.cacheMiss && !issueDetected) { + issueDetected \= True + redoFetch := iBusRsp.readyForError + } + + + //Refill / redo + assert(decodePcGen == compressedGen) + cache.io.cpu.fill.valid := redoFetch + cache.io.cpu.fill.payload := cacheRsp.physicalAddress + redoBranch.valid := redoFetch + redoBranch.payload := (if (decodePcGen) decode.input(PC) else cacheRsp.pc) + + if (catchSomething) { + val accessFault = if (catchAccessFault) cacheRsp.error else False + val mmuMiss = if (catchMemoryTranslationMiss) cacheRsp.mmuMiss else False + val illegalAccess = if (catchIllegalAccess) cacheRsp.illegalAccess else False + + decodeExceptionPort.valid := False + decodeExceptionPort.code := mmuMiss ? U(14) | 1 + decodeExceptionPort.badAddr := cacheRsp.pc + when(cacheRsp.isValid && (accessFault || mmuMiss || illegalAccess) && !issueDetected) { + issueDetected \= True + decodeExceptionPort.valid := iBusRsp.readyForError + } + } + + cacheRspArbitration.halt setWhen (issueDetected || iBusRspOutputHalt) + iBusRsp.output.valid := cacheRspArbitration.output.valid + cacheRspArbitration.output.ready := iBusRsp.output.ready + iBusRsp.output.rsp.inst := cacheRsp.data + iBusRsp.output.pc := cacheRspArbitration.output.payload + } + if (mmuBus != null) { cache.io.cpu.fetch.mmuBus <> mmuBus - (if(twoCycleCache) stages(1).halt else iBusRspOutputHalt) setWhen(mmuBus.cmd.isValid && !mmuBus.rsp.hit && !mmuBus.rsp.miss) + (if (twoCycleCache) stages(1).halt else rsp.iBusRspOutputHalt) setWhen (mmuBus.cmd.isValid && !mmuBus.rsp.hit && !mmuBus.rsp.miss) } else { cache.io.cpu.fetch.mmuBus.rsp.physicalAddress := cache.io.cpu.fetch.mmuBus.cmd.virtualAddress cache.io.cpu.fetch.mmuBus.rsp.allowExecute := True @@ -124,64 +235,6 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l, cache.io.cpu.fetch.mmuBus.rsp.hit := False } - //Connect fetch cache side - cache.io.cpu.fetch.isValid := stages(1).input.valid - cache.io.cpu.fetch.isStuck := !stages(1).input.ready - cache.io.cpu.fetch.pc := stages(1).input.payload - - - if(twoCycleCache){ - cache.io.cpu.decode.isValid := stages(2).input.valid - cache.io.cpu.decode.isStuck := !stages(2).input.ready - cache.io.cpu.decode.pc := stages(2).input.payload - cache.io.cpu.decode.isUser := (if (privilegeService != null) privilegeService.isUser(decode) else False) - - if((!twoCycleRam || wayCount == 1) && !compressedGen && !injectorStage){ - decode.insert(INSTRUCTION_ANTICIPATED) := Mux(decode.arbitration.isStuck, decode.input(INSTRUCTION), cache.io.cpu.fetch.data) - } - } else { - cache.io.cpu.fetch.isUser := (if (privilegeService != null) privilegeService.isUser(decode) else False) - } - - - -// val missHalt = cache.io.cpu.fetch.isValid && cache.io.cpu.fetch.cacheMiss - val cacheRsp = if(twoCycleCache) cache.io.cpu.decode else cache.io.cpu.fetch - val cacheRspArbitration = stages(if(twoCycleCache) 2 else 1) - var issueDetected = False - val redoFetch = False //RegNext(False) init(False) - when(cacheRsp.isValid && cacheRsp.cacheMiss && !issueDetected){ - issueDetected \= True - redoFetch := iBusRsp.readyForError - } - - - assert(decodePcGen == compressedGen) - cache.io.cpu.fill.valid := redoFetch - redoBranch.valid := redoFetch - redoBranch.payload := (if(decodePcGen) decode.input(PC) else cacheRsp.pc) - cache.io.cpu.fill.payload := cacheRsp.physicalAddress - - if(catchSomething){ - val accessFault = if (catchAccessFault) cacheRsp.error else False - val mmuMiss = if (catchMemoryTranslationMiss) cacheRsp.mmuMiss else False - val illegalAccess = if (catchIllegalAccess) cacheRsp.illegalAccess else False - - decodeExceptionPort.valid := False - decodeExceptionPort.code := mmuMiss ? U(14) | 1 - decodeExceptionPort.badAddr := cacheRsp.pc - when(cacheRsp.isValid && (accessFault || mmuMiss || illegalAccess) && !issueDetected){ - issueDetected \= True - decodeExceptionPort.valid := iBusRsp.readyForError - } - } - - cacheRspArbitration.halt setWhen(issueDetected || iBusRspOutputHalt) - iBusRsp.output.arbitrationFrom(cacheRspArbitration.output) - iBusRsp.output.rsp.inst := cacheRsp.data - iBusRsp.output.pc := cacheRspArbitration.output.payload - - val flushStage = if(memory != null) memory else execute flushStage plug new Area { import flushStage._ diff --git a/src/test/cpp/regression/main.cpp b/src/test/cpp/regression/main.cpp index 5610b2d..984d424 100644 --- a/src/test/cpp/regression/main.cpp +++ b/src/test/cpp/regression/main.cpp @@ -1192,6 +1192,43 @@ public: }; #endif + +#ifdef IBUS_TC + +class IBusTc : public SimElement{ +public: + + uint32_t nextData; + + Workspace *ws; + VVexRiscv* top; + IBusTc(Workspace* ws){ + this->ws = ws; + this->top = ws->top; + } + + virtual void onReset(){ + } + + virtual void preCycle(){ + if (top->iBusTc_enable) { + if((top->iBusTc_address & 0x70000000) != 0 || (top->iBusTc_address & 0x20) == 0){ + printf("IBusTc access out of range\n"); + ws->fail(); + } + bool error_next; + ws->iBusAccess(top->iBusTc_address, &nextData,&error_next); + } + } + + virtual void postCycle(){ + top->iBusTc_data = nextData; + } +}; + +#endif + + #ifdef IBUS_SIMPLE_AVALON struct IBusSimpleAvalonRsp{ @@ -1274,6 +1311,12 @@ public: bool error; top->iBus_rsp_valid = 0; if(pendingCount != 0 && (!ws->iStall || VL_RANDOM_I(7) < 100)){ + #ifdef IBUS_TC + if((address & 0x70000000) == 0 && (address & 0x20) != 0){ + printf("IBUS_CACHED access out of range\n"); + ws->fail(); + } + #endif ws->iBusAccess(address,&top->iBus_rsp_payload_data,&error); top->iBus_rsp_payload_error = error; pendingCount--; @@ -1960,6 +2003,11 @@ void Workspace::fillSimELements(){ #if defined(IBUS_CACHED_WISHBONE) || defined(IBUS_SIMPLE_WISHBONE) simElements.push_back(new IBusCachedWishbone(this)); #endif + + #ifdef IBUS_TC + simElements.push_back(new IBusTc(this)); + #endif + #ifdef DBUS_SIMPLE simElements.push_back(new DBusSimple(this)); #endif diff --git a/src/test/cpp/regression/makefile b/src/test/cpp/regression/makefile index ba24951..84c6822 100644 --- a/src/test/cpp/regression/makefile +++ b/src/test/cpp/regression/makefile @@ -1,6 +1,7 @@ DEBUG?=no IBUS?=CACHED +IBUS_TC?=no DBUS?=CACHED TRACE?=no TRACE_ACCESS?=no @@ -42,7 +43,7 @@ ADDCFLAGS += -CFLAGS -DTHREAD_COUNT=${THREAD_COUNT} ifeq ($(DEBUG),yes) ADDCFLAGS += -CFLAGS -O0 -CFLAGS -g else - ADDCFLAGS += -CFLAGS -O3 + ADDCFLAGS += -CFLAGS -O3 -O3 endif @@ -58,6 +59,11 @@ ifneq ($(RUN_HEX),no) ADDCFLAGS += -CFLAGS -DRUN_HEX='\"$(RUN_HEX)\"' endif + +ifeq ($(IBUS_TC),yes) + ADDCFLAGS += -CFLAGS -DIBUS_TC=yes +endif + ifeq ($(COMPRESSED),yes) ADDCFLAGS += -CFLAGS -DCOMPRESSED endif diff --git a/src/test/scala/vexriscv/TestIndividualFeatures.scala b/src/test/scala/vexriscv/TestIndividualFeatures.scala index 10f25c9..7654b4b 100644 --- a/src/test/scala/vexriscv/TestIndividualFeatures.scala +++ b/src/test/scala/vexriscv/TestIndividualFeatures.scala @@ -284,6 +284,8 @@ class IBusDimension extends VexRiscvDimension("IBus") { } } else { val compressed = r.nextBoolean() + val tighlyCoupled = r.nextBoolean() +// val tighlyCoupled = false val prediction = random(r, List(NONE, STATIC, DYNAMIC, DYNAMIC_TARGET)) val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL) val relaxedPcCalculation, twoCycleCache, injectorStage = r.nextBoolean() @@ -295,29 +297,33 @@ class IBusDimension extends VexRiscvDimension("IBus") { wayCount = 1 << r.nextInt(3) }while(cacheSize/wayCount < 512) - new VexRiscvPosition("Cached" + (if(twoCycleCache) "2cc" else "") + (if(injectorStage) "Injstage" else "") + (if(twoCycleRam) "2cr" else "") + "S" + cacheSize + "W" + wayCount + (if(relaxedPcCalculation) "Relax" else "") + (if(compressed) "Rvc" else "") + prediction.getClass.getTypeName().replace("$","")) with InstructionAnticipatedPosition{ - override def testParam = "IBUS=CACHED" + (if(compressed) " COMPRESSED=yes" else "") - override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new IBusCachedPlugin( - resetVector = 0x80000000l, - compressedGen = compressed, - prediction = prediction, - relaxedPcCalculation = relaxedPcCalculation, - injectorStage = injectorStage, - config = InstructionCacheConfig( - cacheSize = cacheSize, - bytePerLine = 32, - wayCount = wayCount, - addressWidth = 32, - cpuDataWidth = 32, - memDataWidth = 32, - catchIllegalAccess = catchAll, - catchAccessFault = catchAll, - catchMemoryTranslationMiss = catchAll, - asyncTagMemory = false, - twoCycleRam = twoCycleRam, - twoCycleCache = twoCycleCache + new VexRiscvPosition("Cached" + (if(twoCycleCache) "2cc" else "") + (if(injectorStage) "Injstage" else "") + (if(twoCycleRam) "2cr" else "") + "S" + cacheSize + "W" + wayCount + (if(relaxedPcCalculation) "Relax" else "") + (if(compressed) "Rvc" else "") + prediction.getClass.getTypeName().replace("$","")+ (if(tighlyCoupled)"Tc" else "")) with InstructionAnticipatedPosition{ + override def testParam = "IBUS=CACHED" + (if(compressed) " COMPRESSED=yes" else "") + (if(tighlyCoupled)" IBUS_TC=yes" else "") + override def applyOn(config: VexRiscvConfig): Unit = { + val p = new IBusCachedPlugin( + resetVector = 0x80000000l, + compressedGen = compressed, + prediction = prediction, + relaxedPcCalculation = relaxedPcCalculation, + injectorStage = injectorStage, + config = InstructionCacheConfig( + cacheSize = cacheSize, + bytePerLine = 32, + wayCount = wayCount, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchIllegalAccess = catchAll, + catchAccessFault = catchAll, + catchMemoryTranslationMiss = catchAll, + asyncTagMemory = false, + twoCycleRam = twoCycleRam, + twoCycleCache = twoCycleCache + ) ) - ) + if(tighlyCoupled) p.newTightlyCoupledPort(TightlyCoupledPortParameter("iBusTc", a => a(30 downto 28) === 0x0 && a(5))) + config.plugins += p + } override def instructionAnticipatedOk() = !twoCycleCache || ((!twoCycleRam || wayCount == 1) && !compressed) } } @@ -523,8 +529,8 @@ class TestIndividualFeatures extends FunSuite { // val testId = Some(mutable.HashSet[Int](0,28,45,93)) -// val testId = Some(mutable.HashSet[Int](5)) -// val seed = -2089952013329208578l +// val testId = Some(mutable.HashSet[Int](31)) +// val seed = -7716775349351274630l