aboutsummaryrefslogtreecommitdiff
path: root/VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala
diff options
context:
space:
mode:
authorFriedrich Beckmann <friedrich.beckmann@hs-augsburg.de>2022-07-25 17:55:39 +0200
committerFriedrich Beckmann <friedrich.beckmann@hs-augsburg.de>2022-07-25 17:55:39 +0200
commit3fff6023602822531efdae30bc8ebf862967f1ef (patch)
tree16028102b8d850f8ab3115d28a8539ca6bc5f51d /VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala
Initial Commit
Diffstat (limited to 'VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala')
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala1335
1 files changed, 1335 insertions, 0 deletions
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala
new file mode 100644
index 0000000..7731a41
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/CsrPlugin.scala
@@ -0,0 +1,1335 @@
+package vexriscv.plugin
+
+import spinal.core._
+import spinal.lib._
+import vexriscv._
+import vexriscv.Riscv._
+import vexriscv.plugin.IntAluPlugin.{ALU_BITWISE_CTRL, ALU_CTRL, AluBitwiseCtrlEnum, AluCtrlEnum}
+
+import scala.collection.mutable.ArrayBuffer
+import scala.collection.mutable
+import spinal.core.sim._
+
+/**
+ * Created by spinalvm on 21.03.17.
+ */
+
+trait CsrAccess{
+ def canWrite : Boolean = false
+ def canRead : Boolean = false
+}
+object CsrAccess {
+ object WRITE_ONLY extends CsrAccess{
+ override def canWrite : Boolean = true
+ }
+ object READ_ONLY extends CsrAccess{
+ override def canRead : Boolean = true
+ }
+ object READ_WRITE extends CsrAccess{
+ override def canWrite : Boolean = true
+ override def canRead : Boolean = true
+ }
+ object NONE extends CsrAccess
+}
+
+
+
+case class ExceptionPortInfo(port : Flow[ExceptionCause],stage : Stage, priority : Int, codeWidth : Int)
+case class CsrPluginConfig(
+ catchIllegalAccess : Boolean,
+ mvendorid : BigInt,
+ marchid : BigInt,
+ mimpid : BigInt,
+ mhartid : BigInt,
+ misaExtensionsInit : Int,
+ misaAccess : CsrAccess,
+ mtvecAccess : CsrAccess,
+ mtvecInit : BigInt,
+ mepcAccess : CsrAccess,
+ mscratchGen : Boolean,
+ mcauseAccess : CsrAccess,
+ mbadaddrAccess : CsrAccess,
+ mcycleAccess : CsrAccess,
+ minstretAccess : CsrAccess,
+ ucycleAccess : CsrAccess,
+ uinstretAccess : CsrAccess = CsrAccess.NONE,
+ wfiGenAsWait : Boolean,
+ ecallGen : Boolean,
+ xtvecModeGen : Boolean = false,
+ noCsrAlu : Boolean = false,
+ wfiGenAsNop : Boolean = false,
+ ebreakGen : Boolean = false,
+ userGen : Boolean = false,
+ supervisorGen : Boolean = false,
+ sscratchGen : Boolean = false,
+ stvecAccess : CsrAccess = CsrAccess.NONE,
+ sepcAccess : CsrAccess = CsrAccess.NONE,
+ scauseAccess : CsrAccess = CsrAccess.NONE,
+ sbadaddrAccess : CsrAccess = CsrAccess.NONE,
+ scycleAccess : CsrAccess = CsrAccess.NONE,
+ sinstretAccess : CsrAccess = CsrAccess.NONE,
+ satpAccess : CsrAccess = CsrAccess.NONE,
+ utimeAccess :CsrAccess = CsrAccess.NONE,
+ medelegAccess : CsrAccess = CsrAccess.NONE,
+ midelegAccess : CsrAccess = CsrAccess.NONE,
+ withExternalMhartid : Boolean = false,
+ mhartidWidth : Int = 0,
+ pipelineCsrRead : Boolean = false,
+ pipelinedInterrupt : Boolean = true,
+ csrOhDecoder : Boolean = true,
+ deterministicInteruptionEntry : Boolean = false, //Only used for simulatation purposes
+ wfiOutput : Boolean = false
+ ){
+ assert(!ucycleAccess.canWrite)
+ def privilegeGen = userGen || supervisorGen
+ def noException = this.copy(ecallGen = false, ebreakGen = false, catchIllegalAccess = false)
+ def noExceptionButEcall = this.copy(ecallGen = true, ebreakGen = false, catchIllegalAccess = false)
+}
+
+object CsrPluginConfig{
+ def all : CsrPluginConfig = all(0x00000020l)
+ def small : CsrPluginConfig = small(0x00000020l)
+ def smallest : CsrPluginConfig = smallest(0x00000020l)
+
+ def openSbi(mhartid : Int, misa : Int) = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 0,
+ marchid = 0,
+ mimpid = 0,
+ mhartid = mhartid,
+ misaExtensionsInit = misa,
+ misaAccess = CsrAccess.READ_ONLY,
+ mtvecAccess = CsrAccess.READ_WRITE, //Could have been WRITE_ONLY :(
+ mtvecInit = null,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_ONLY,
+ mbadaddrAccess = CsrAccess.READ_ONLY,
+ mcycleAccess = CsrAccess.NONE,
+ minstretAccess = CsrAccess.NONE,
+ ucycleAccess = CsrAccess.NONE,
+ wfiGenAsWait = true,
+ ecallGen = true,
+ xtvecModeGen = false,
+ noCsrAlu = false,
+ wfiGenAsNop = false,
+ ebreakGen = true,
+ userGen = true,
+ supervisorGen = true,
+ sscratchGen = true,
+ stvecAccess = CsrAccess.READ_WRITE,
+ sepcAccess = CsrAccess.READ_WRITE,
+ scauseAccess = CsrAccess.READ_WRITE,
+ sbadaddrAccess = CsrAccess.READ_WRITE,
+ scycleAccess = CsrAccess.NONE,
+ sinstretAccess = CsrAccess.NONE,
+ satpAccess = CsrAccess.NONE,
+ medelegAccess = CsrAccess.READ_WRITE, //Could have been WRITE_ONLY :(
+ midelegAccess = CsrAccess.READ_WRITE, //Could have been WRITE_ONLY :(
+ pipelineCsrRead = false,
+ deterministicInteruptionEntry = false
+ )
+
+ def linuxMinimal(mtVecInit : BigInt) = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 1,
+ marchid = 2,
+ mimpid = 3,
+ mhartid = 0,
+ misaExtensionsInit = 0, //TODO
+ misaAccess = CsrAccess.NONE, //Read required by some regressions
+ mtvecAccess = CsrAccess.WRITE_ONLY, //Read required by some regressions
+ mtvecInit = mtVecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_ONLY,
+ mbadaddrAccess = CsrAccess.READ_ONLY,
+ mcycleAccess = CsrAccess.NONE,
+ minstretAccess = CsrAccess.NONE,
+ ucycleAccess = CsrAccess.NONE,
+ uinstretAccess = CsrAccess.NONE,
+ wfiGenAsWait = true,
+ ecallGen = true,
+ xtvecModeGen = false,
+ noCsrAlu = false,
+ wfiGenAsNop = false,
+ ebreakGen = true,
+ userGen = true,
+ supervisorGen = true,
+ sscratchGen = true,
+ stvecAccess = CsrAccess.READ_WRITE,
+ sepcAccess = CsrAccess.READ_WRITE,
+ scauseAccess = CsrAccess.READ_WRITE,
+ sbadaddrAccess = CsrAccess.READ_WRITE,
+ scycleAccess = CsrAccess.NONE,
+ sinstretAccess = CsrAccess.NONE,
+ satpAccess = CsrAccess.NONE, //Implemented into the MMU plugin
+ medelegAccess = CsrAccess.WRITE_ONLY,
+ midelegAccess = CsrAccess.WRITE_ONLY,
+ pipelineCsrRead = false,
+ deterministicInteruptionEntry = false
+ )
+
+
+ def linuxFull(mtVecInit : BigInt) = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 1,
+ marchid = 2,
+ mimpid = 3,
+ mhartid = 0,
+ misaExtensionsInit = 0, //TODO
+ misaAccess = CsrAccess.READ_WRITE,
+ mtvecAccess = CsrAccess.READ_WRITE,
+ mtvecInit = mtVecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_WRITE,
+ mbadaddrAccess = CsrAccess.READ_WRITE,
+ mcycleAccess = CsrAccess.READ_WRITE,
+ minstretAccess = CsrAccess.READ_WRITE,
+ ucycleAccess = CsrAccess.READ_ONLY,
+ uinstretAccess = CsrAccess.READ_ONLY,
+ wfiGenAsWait = true,
+ ecallGen = true,
+ xtvecModeGen = false,
+ noCsrAlu = false,
+ wfiGenAsNop = false,
+ ebreakGen = false,
+ userGen = true,
+ supervisorGen = true,
+ sscratchGen = true,
+ stvecAccess = CsrAccess.READ_WRITE,
+ sepcAccess = CsrAccess.READ_WRITE,
+ scauseAccess = CsrAccess.READ_WRITE,
+ sbadaddrAccess = CsrAccess.READ_WRITE,
+ scycleAccess = CsrAccess.READ_WRITE,
+ sinstretAccess = CsrAccess.READ_WRITE,
+ satpAccess = CsrAccess.NONE, //Implemented into the MMU plugin
+ medelegAccess = CsrAccess.READ_WRITE,
+ midelegAccess = CsrAccess.READ_WRITE,
+ pipelineCsrRead = false,
+ deterministicInteruptionEntry = false
+ )
+
+ def all(mtvecInit : BigInt) : CsrPluginConfig = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 11,
+ marchid = 22,
+ mimpid = 33,
+ mhartid = 0,
+ misaExtensionsInit = 66,
+ misaAccess = CsrAccess.READ_WRITE,
+ mtvecAccess = CsrAccess.READ_WRITE,
+ mtvecInit = mtvecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_WRITE,
+ mbadaddrAccess = CsrAccess.READ_WRITE,
+ mcycleAccess = CsrAccess.READ_WRITE,
+ minstretAccess = CsrAccess.READ_WRITE,
+ ecallGen = true,
+ wfiGenAsWait = true,
+ ucycleAccess = CsrAccess.READ_ONLY,
+ uinstretAccess = CsrAccess.READ_ONLY
+ )
+
+ def all2(mtvecInit : BigInt) : CsrPluginConfig = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 11,
+ marchid = 22,
+ mimpid = 33,
+ mhartid = 0,
+ misaExtensionsInit = 66,
+ misaAccess = CsrAccess.READ_WRITE,
+ mtvecAccess = CsrAccess.READ_WRITE,
+ mtvecInit = mtvecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_WRITE,
+ mbadaddrAccess = CsrAccess.READ_WRITE,
+ mcycleAccess = CsrAccess.READ_WRITE,
+ minstretAccess = CsrAccess.READ_WRITE,
+ ecallGen = true,
+ wfiGenAsWait = true,
+ ucycleAccess = CsrAccess.READ_ONLY,
+ uinstretAccess = CsrAccess.READ_ONLY,
+ supervisorGen = true,
+ sscratchGen = true,
+ stvecAccess = CsrAccess.READ_WRITE,
+ sepcAccess = CsrAccess.READ_WRITE,
+ scauseAccess = CsrAccess.READ_WRITE,
+ sbadaddrAccess = CsrAccess.READ_WRITE,
+ scycleAccess = CsrAccess.READ_WRITE,
+ sinstretAccess = CsrAccess.READ_WRITE,
+ satpAccess = CsrAccess.READ_WRITE,
+ medelegAccess = CsrAccess.READ_WRITE,
+ midelegAccess = CsrAccess.READ_WRITE
+ )
+
+ def small(mtvecInit : BigInt) = CsrPluginConfig(
+ catchIllegalAccess = false,
+ mvendorid = null,
+ marchid = null,
+ mimpid = null,
+ mhartid = null,
+ misaExtensionsInit = 66,
+ misaAccess = CsrAccess.NONE,
+ mtvecAccess = CsrAccess.NONE,
+ mtvecInit = mtvecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = false,
+ mcauseAccess = CsrAccess.READ_ONLY,
+ mbadaddrAccess = CsrAccess.READ_ONLY,
+ mcycleAccess = CsrAccess.NONE,
+ minstretAccess = CsrAccess.NONE,
+ ecallGen = false,
+ wfiGenAsWait = false,
+ ucycleAccess = CsrAccess.NONE,
+ uinstretAccess = CsrAccess.NONE
+ )
+
+ def smallest(mtvecInit : BigInt) = CsrPluginConfig(
+ catchIllegalAccess = false,
+ mvendorid = null,
+ marchid = null,
+ mimpid = null,
+ mhartid = null,
+ misaExtensionsInit = 66,
+ misaAccess = CsrAccess.NONE,
+ mtvecAccess = CsrAccess.NONE,
+ mtvecInit = mtvecInit,
+ mepcAccess = CsrAccess.NONE,
+ mscratchGen = false,
+ mcauseAccess = CsrAccess.READ_ONLY,
+ mbadaddrAccess = CsrAccess.NONE,
+ mcycleAccess = CsrAccess.NONE,
+ minstretAccess = CsrAccess.NONE,
+ ecallGen = false,
+ wfiGenAsWait = false,
+ ucycleAccess = CsrAccess.NONE,
+ uinstretAccess = CsrAccess.NONE
+ )
+
+ def secure(mtvecInit : BigInt) = CsrPluginConfig(
+ catchIllegalAccess = true,
+ mvendorid = 1,
+ marchid = 2,
+ mimpid = 3,
+ mhartid = 0,
+ misaExtensionsInit = 0x101064, // RV32GCFMU
+ misaAccess = CsrAccess.READ_WRITE,
+ mtvecAccess = CsrAccess.READ_WRITE,
+ mtvecInit = mtvecInit,
+ mepcAccess = CsrAccess.READ_WRITE,
+ mscratchGen = true,
+ mcauseAccess = CsrAccess.READ_WRITE,
+ mbadaddrAccess = CsrAccess.READ_WRITE,
+ mcycleAccess = CsrAccess.READ_WRITE,
+ minstretAccess = CsrAccess.READ_WRITE,
+ ucycleAccess = CsrAccess.READ_ONLY,
+ uinstretAccess = CsrAccess.READ_ONLY,
+ wfiGenAsWait = true,
+ ecallGen = true,
+ userGen = true,
+ medelegAccess = CsrAccess.READ_WRITE,
+ midelegAccess = CsrAccess.READ_WRITE
+ )
+
+}
+case class CsrWrite(that : Data, bitOffset : Int)
+case class CsrRead(that : Data , bitOffset : Int)
+case class CsrReadToWriteOverride(that : Data, bitOffset : Int) //Used for special cases, as MIP where there shadow stuff
+case class CsrOnWrite(doThat :() => Unit)
+case class CsrDuringWrite(doThat :() => Unit)
+case class CsrDuringRead(doThat :() => Unit)
+case class CsrDuring(doThat :() => Unit)
+case class CsrOnRead(doThat : () => Unit)
+
+
+case class CsrMapping() extends Area with CsrInterface {
+ val mapping = mutable.LinkedHashMap[Int,ArrayBuffer[Any]]()
+ val always = ArrayBuffer[Any]()
+ val readDataSignal, readDataInit, writeDataSignal = Bits(32 bits)
+ val allowCsrSignal = False
+ val hazardFree = Bool()
+
+ readDataSignal := readDataInit
+ def addMappingAt(address : Int,that : Any) = mapping.getOrElseUpdate(address,new ArrayBuffer[Any]) += that
+ override def r(csrAddress : Int, bitOffset : Int, that : Data): Unit = addMappingAt(csrAddress, CsrRead(that,bitOffset))
+ override def w(csrAddress : Int, bitOffset : Int, that : Data): Unit = addMappingAt(csrAddress, CsrWrite(that,bitOffset))
+ override def r2w(csrAddress : Int, bitOffset : Int, that : Data): Unit = addMappingAt(csrAddress, CsrReadToWriteOverride(that,bitOffset))
+ override def onWrite(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrOnWrite(() => body))
+ override def duringWrite(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrDuringWrite(() => body))
+ override def duringRead(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrDuringRead(() => body))
+ override def during(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrDuring(() => body))
+ override def onRead(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrOnRead(() => {body}))
+ override def duringAny(): Bool = ???
+ override def duringAnyRead(body: => Unit) : Unit = always += CsrDuringRead(() => body)
+ override def duringAnyWrite(body: => Unit) : Unit = always += CsrDuringWrite(() => body)
+ override def onAnyRead(body: => Unit) : Unit = always += CsrOnRead(() => body)
+ override def onAnyWrite(body: => Unit) : Unit = always += CsrOnWrite(() => body)
+ override def readData() = readDataSignal
+ override def writeData() = writeDataSignal
+ override def allowCsr() = allowCsrSignal := True
+ override def isHazardFree() = hazardFree
+}
+
+
+trait CsrInterface{
+ def onWrite(csrAddress : Int)(doThat : => Unit) : Unit
+ def onRead(csrAddress : Int)(doThat : => Unit) : Unit
+ def duringWrite(csrAddress: Int)(body: => Unit): Unit
+ def duringRead(csrAddress: Int)(body: => Unit): Unit
+ def during(csrAddress: Int)(body: => Unit): Unit
+ def duringAny(): Bool
+ def r(csrAddress : Int, bitOffset : Int, that : Data): Unit
+ def w(csrAddress : Int, bitOffset : Int, that : Data): Unit
+ def rw(csrAddress : Int, bitOffset : Int,that : Data): Unit ={
+ r(csrAddress,bitOffset,that)
+ w(csrAddress,bitOffset,that)
+ }
+ def duringAnyRead(body: => Unit) : Unit //Called all the durration of a Csr write instruction in the execute stage
+ def duringAnyWrite(body: => Unit) : Unit //same than above for read
+ def onAnyRead(body: => Unit) : Unit
+ def onAnyWrite(body: => Unit) : Unit
+ def allowCsr() : Unit //In case your csr do not use the regular API with csrAddress but is implemented using "side channels", you can call that if the current csr is implemented
+ def isHazardFree() : Bool // You should not have any side effect nor use readData() until this return True
+
+ def r2w(csrAddress : Int, bitOffset : Int,that : Data): Unit
+
+ def rw(csrAddress : Int, thats : (Int, Data)*) : Unit = for(that <- thats) rw(csrAddress,that._1, that._2)
+ def w(csrAddress : Int, thats : (Int, Data)*) : Unit = for(that <- thats) w(csrAddress,that._1, that._2)
+ def r(csrAddress : Int, thats : (Int, Data)*) : Unit = for(that <- thats) r(csrAddress,that._1, that._2)
+ def rw[T <: Data](csrAddress : Int, that : T): Unit = rw(csrAddress,0,that)
+ def w[T <: Data](csrAddress : Int, that : T): Unit = w(csrAddress,0,that)
+ def r [T <: Data](csrAddress : Int, that : T): Unit = r(csrAddress,0,that)
+ def isWriting(csrAddress : Int) : Bool = {
+ val ret = False
+ onWrite(csrAddress){
+ ret := True
+ }
+ ret
+ }
+
+ def isReading(csrAddress : Int) : Bool = {
+ val ret = False
+ onRead(csrAddress){
+ ret := True
+ }
+ ret
+ }
+
+ def readData() : Bits //Return the 32 bits internal signal of the CsrPlugin for you to override (if you want)
+ def writeData() : Bits //Return the 32 bits value that the CsrPlugin want to write in the CSR (depend on readData combinatorialy)
+}
+
+
+trait IContextSwitching{
+ def isContextSwitching : Bool
+}
+trait IWake{
+ def askWake() : Unit
+}
+
+class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with ExceptionService with PrivilegeService with InterruptionInhibitor with ExceptionInhibitor with IContextSwitching with CsrInterface with IWake{
+ import config._
+ import CsrAccess._
+
+ assert(!(wfiGenAsNop && wfiGenAsWait))
+
+ def xlen = 32
+
+ //Mannage ExceptionService calls
+ val exceptionPortsInfos = ArrayBuffer[ExceptionPortInfo]()
+ override def newExceptionPort(stage : Stage, priority : Int = 0, codeWidth : Int = 4) = {
+ val interface = Flow(ExceptionCause(codeWidth))
+ exceptionPortsInfos += ExceptionPortInfo(interface,stage,priority,codeWidth)
+ interface
+ }
+
+
+
+ var exceptionPendings : Vec[Bool] = null
+ override def isExceptionPending(stage : Stage): Bool = exceptionPendings(pipeline.stages.indexOf(stage))
+
+ var redoInterface : Flow[UInt] = null
+ var jumpInterface : Flow[UInt] = null
+ var timerInterrupt, externalInterrupt, softwareInterrupt : Bool = null
+ var externalInterruptS : Bool = null
+ var forceMachineWire : Bool = null
+ var privilege : UInt = null
+ var selfException : Flow[ExceptionCause] = null
+ var contextSwitching : Bool = null
+ var thirdPartyWake : Bool = null
+ var inWfi : Bool = null
+ var externalMhartId : UInt = null
+ var utime : UInt = null
+
+ override def askWake(): Unit = thirdPartyWake := True
+
+ override def isContextSwitching = contextSwitching
+
+ object EnvCtrlEnum extends SpinalEnum(binarySequential){
+ val NONE, XRET = newElement()
+ val WFI = if(wfiGenAsWait) newElement() else null
+ val ECALL = if(ecallGen) newElement() else null
+ val EBREAK = if(ebreakGen) newElement() else null
+ }
+
+ object ENV_CTRL extends Stageable(EnvCtrlEnum())
+ object IS_CSR extends Stageable(Bool)
+ object IS_SFENCE_VMA extends Stageable(Bool)
+ object CSR_WRITE_OPCODE extends Stageable(Bool)
+ object CSR_READ_OPCODE extends Stageable(Bool)
+ object PIPELINED_CSR_READ extends Stageable(Bits(32 bits))
+
+ var allowInterrupts : Bool = null
+ var allowException : Bool = null
+ var allowEbreakException : Bool = null
+
+ var csrMapping : CsrMapping = null
+
+ //Print CSR mapping
+ def printCsr() {
+ for ((address, things) <- csrMapping.mapping) {
+ println("0x" + address.toHexString + " => ")
+ for (thing <- things) {
+ println(" - " + thing)
+ }
+ }
+ }
+
+
+ //Interruption and exception data model
+ case class Delegator(var enable : Bool, privilege : Int)
+ case class InterruptSpec(var cond : Bool, id : Int, privilege : Int, delegators : List[Delegator])
+ case class ExceptionSpec(id : Int, delegators : List[Delegator])
+ var interruptSpecs = ArrayBuffer[InterruptSpec]()
+ var exceptionSpecs = ArrayBuffer[ExceptionSpec]()
+
+ def addInterrupt(cond : Bool, id : Int, privilege : Int, delegators : List[Delegator]): Unit = {
+ interruptSpecs += InterruptSpec(cond, id, privilege, delegators)
+ }
+
+ 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 r2w(csrAddress: Int, bitOffset: Int, that: Data): Unit = csrMapping.r2w(csrAddress, bitOffset, that)
+ override def onWrite(csrAddress: Int)(body: => Unit): Unit = csrMapping.onWrite(csrAddress)(body)
+ override def duringWrite(csrAddress: Int)(body: => Unit): Unit = csrMapping.duringWrite(csrAddress)(body)
+ override def onRead(csrAddress: Int)(body: => Unit): Unit = csrMapping.onRead(csrAddress)(body)
+ override def duringRead(csrAddress: Int)(body: => Unit): Unit = csrMapping.duringRead(csrAddress)(body)
+ override def during(csrAddress: Int)(body: => Unit): Unit = csrMapping.during(csrAddress)(body)
+ override def duringAny(): Bool = pipeline.execute.arbitration.isValid && pipeline.execute.input(IS_CSR)
+ override def duringAnyRead(body: => Unit) = csrMapping.duringAnyRead(body)
+ override def duringAnyWrite(body: => Unit) = csrMapping.duringAnyWrite(body)
+ override def onAnyRead(body: => Unit) = csrMapping.onAnyRead(body)
+ override def onAnyWrite(body: => Unit) = csrMapping.onAnyWrite(body)
+ override def allowCsr() = csrMapping.allowCsr()
+ override def readData() = csrMapping.readData()
+ override def writeData() = csrMapping.writeData()
+ override def isHazardFree() = csrMapping.isHazardFree()
+
+ override def setup(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+
+ if(!config.ebreakGen) {
+ SpinalWarning("This VexRiscv configuration is set without software ebreak instruction support. Some software may rely on it (ex: Rust). (This isn't related to JTAG ebreak)")
+ }
+
+ csrMapping = new CsrMapping()
+
+ inWfi = False.addTag(Verilator.public)
+
+ thirdPartyWake = False
+
+ val defaultEnv = List[(Stageable[_ <: BaseType],Any)](
+ )
+
+ val defaultCsrActions = List[(Stageable[_ <: BaseType],Any)](
+ IS_CSR -> True,
+ REGFILE_WRITE_VALID -> True,
+ BYPASSABLE_EXECUTE_STAGE -> False,
+ BYPASSABLE_MEMORY_STAGE -> True
+ ) ++ (if(catchIllegalAccess) List(HAS_SIDE_EFFECT -> True) else Nil)
+
+ val nonImmediatActions = defaultCsrActions ++ List(
+ SRC1_CTRL -> Src1CtrlEnum.RS,
+ RS1_USE -> True
+ )
+
+ val immediatActions = defaultCsrActions ++ List(
+ SRC1_CTRL -> Src1CtrlEnum.URS1
+ )
+
+ val decoderService = pipeline.service(classOf[DecoderService])
+
+ decoderService.addDefault(ENV_CTRL, EnvCtrlEnum.NONE)
+ decoderService.addDefault(IS_CSR, False)
+ decoderService.add(List(
+ CSRRW -> nonImmediatActions,
+ CSRRS -> nonImmediatActions,
+ CSRRC -> nonImmediatActions,
+ CSRRWI -> immediatActions,
+ CSRRSI -> immediatActions,
+ CSRRCI -> immediatActions,
+ MRET -> (defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.XRET, HAS_SIDE_EFFECT -> True)),
+ SRET -> (defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.XRET, HAS_SIDE_EFFECT -> True))
+ ))
+ if(wfiGenAsWait) decoderService.add(WFI, defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.WFI))
+ if(wfiGenAsNop) decoderService.add(WFI, Nil)
+ if(ecallGen) decoderService.add(ECALL, defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.ECALL, HAS_SIDE_EFFECT -> True))
+ if(ebreakGen) decoderService.add(EBREAK, defaultEnv ++ List(ENV_CTRL -> EnvCtrlEnum.EBREAK, HAS_SIDE_EFFECT -> True))
+
+ val pcManagerService = pipeline.service(classOf[JumpService])
+ jumpInterface = pcManagerService.createJumpInterface(pipeline.stages.last)
+ jumpInterface.valid := False
+ jumpInterface.payload.assignDontCare()
+
+
+ if(supervisorGen) {
+ redoInterface = pcManagerService.createJumpInterface(pipeline.execute, -20) //Should lose against dynamic_target branch prediction correction
+ }
+
+ exceptionPendings = Vec(Bool, pipeline.stages.length)
+ timerInterrupt = in Bool() setName("timerInterrupt")
+ externalInterrupt = in Bool() setName("externalInterrupt")
+ softwareInterrupt = in Bool() setName("softwareInterrupt") default(False)
+ if(supervisorGen){
+// timerInterruptS = in Bool() setName("timerInterruptS")
+ externalInterruptS = in Bool() setName("externalInterruptS")
+ }
+ contextSwitching = Bool().setName("contextSwitching")
+
+ privilege = UInt(2 bits).setName("CsrPlugin_privilege")
+ forceMachineWire = False
+
+ if(catchIllegalAccess || ecallGen || ebreakGen)
+ selfException = newExceptionPort(pipeline.execute)
+
+ allowInterrupts = True
+ allowException = True
+ allowEbreakException = True
+
+ for (i <- interruptSpecs) i.cond = i.cond.pull()
+
+
+ pipeline.update(MPP, UInt(2 bits))
+
+ if(withExternalMhartid) externalMhartId = in UInt(mhartidWidth bits)
+ if(utimeAccess != CsrAccess.NONE) utime = in UInt(64 bits) setName("utime")
+
+ if(supervisorGen) {
+ decoderService.addDefault(IS_SFENCE_VMA, False)
+ decoderService.add(SFENCE_VMA, List(IS_SFENCE_VMA -> True))
+ }
+ }
+
+ def inhibateInterrupts() : Unit = allowInterrupts := False
+ def inhibateException() : Unit = allowException := False
+ def inhibateEbreakException() : Unit = allowEbreakException := False
+
+ override def isUser() : Bool = privilege === 0
+ override def isSupervisor(): Bool = privilege === 1
+ override def isMachine(): Bool = privilege === 3
+ override def forceMachine(): Unit = forceMachineWire := True
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline._
+ import pipeline.config._
+ val fetcher = service(classOf[IBusFetcher])
+ val trapCodeWidth = log2Up((List(16) ++ interruptSpecs.map(_.id + 1) ++ exceptionPortsInfos.map(p => 1 << widthOf(p.port.code))).max)
+
+ //Define CSR mapping utilities
+ implicit class CsrAccessPimper(csrAccess : CsrAccess){
+ def apply(csrAddress : Int, thats : (Int, Data)*) : Unit = {
+ if(csrAccess == `WRITE_ONLY` || csrAccess == `READ_WRITE`) for(that <- thats) csrMapping.w(csrAddress,that._1, that._2)
+ if(csrAccess == `READ_ONLY` || csrAccess == `READ_WRITE`) for(that <- thats) csrMapping.r(csrAddress,that._1, that._2)
+ }
+ def apply(csrAddress : Int, that : Data) : Unit = {
+ if(csrAccess == `WRITE_ONLY` || csrAccess == `READ_WRITE`) csrMapping.w(csrAddress, 0, that)
+ if(csrAccess == `READ_ONLY` || csrAccess == `READ_WRITE`) csrMapping.r(csrAddress, 0, that)
+ }
+ }
+
+
+ case class Xtvec() extends Bundle {
+ val mode = Bits(2 bits)
+ val base = UInt(xlen-2 bits)
+ }
+
+ val privilegeReg = privilegeGen generate RegInit(U"11")
+ privilege := (if(privilegeGen) privilegeReg else U"11")
+
+ when(forceMachineWire) { privilege := 3 }
+
+ val machineCsr = pipeline plug new Area{
+ //Define CSR registers
+ // Status => MXR, SUM, TVM, TW, TSE ?
+ val misa = new Area{
+ val base = Reg(UInt(2 bits)) init(U"01") allowUnsetRegToAvoidLatch
+ val extensions = Reg(Bits(26 bits)) init(misaExtensionsInit) allowUnsetRegToAvoidLatch
+ }
+
+ val mtvec = Reg(Xtvec()).allowUnsetRegToAvoidLatch
+
+ if(mtvecInit != null) mtvec.mode init(mtvecInit & 0x3)
+ if(mtvecInit != null) mtvec.base init(mtvecInit / 4)
+ val mepc = Reg(UInt(xlen bits))
+ val mstatus = new Area{
+ val MIE, MPIE = RegInit(False)
+ val MPP = RegInit(U"11")
+ }
+ val mip = new Area{
+ val MEIP = RegNext(externalInterrupt)
+ val MTIP = RegNext(timerInterrupt)
+ val MSIP = RegNext(softwareInterrupt)
+ }
+ val mie = new Area{
+ val MEIE, MTIE, MSIE = RegInit(False)
+ }
+ val mscratch = if(mscratchGen) Reg(Bits(xlen bits)) else null
+ val mcause = new Area{
+ val interrupt = Reg(Bool)
+ val exceptionCode = Reg(UInt(trapCodeWidth bits))
+ }
+ val mtval = Reg(UInt(xlen bits))
+ val mcycle = Reg(UInt(64 bits)) init(0)
+ val minstret = Reg(UInt(64 bits)) init(0)
+
+
+ val medeleg = supervisorGen generate new Area {
+ val IAM, IAF, II, LAM, LAF, SAM, SAF, EU, ES, IPF, LPF, SPF = RegInit(False)
+ val mapping = mutable.LinkedHashMap(0 -> IAM, 1 -> IAF, 2 -> II, 4 -> LAM, 5 -> LAF, 6 -> SAM, 7 -> SAF, 8 -> EU, 9 -> ES, 12 -> IPF, 13 -> LPF, 15 -> SPF)
+ }
+ val mideleg = supervisorGen generate new Area {
+ val ST, SE, SS = RegInit(False)
+ }
+
+ if(mvendorid != null) READ_ONLY(CSR.MVENDORID, U(mvendorid))
+ if(marchid != null) READ_ONLY(CSR.MARCHID , U(marchid ))
+ if(mimpid != null) READ_ONLY(CSR.MIMPID , U(mimpid ))
+ if(mhartid != null && !withExternalMhartid) READ_ONLY(CSR.MHARTID , U(mhartid ))
+ if(withExternalMhartid) READ_ONLY(CSR.MHARTID , externalMhartId)
+ misaAccess(CSR.MISA, xlen-2 -> misa.base , 0 -> misa.extensions)
+
+ //Machine CSR
+ READ_WRITE(CSR.MSTATUS, 7 -> mstatus.MPIE, 3 -> mstatus.MIE)
+ READ_ONLY(CSR.MIP, 11 -> mip.MEIP, 7 -> mip.MTIP)
+ READ_WRITE(CSR.MIP, 3 -> mip.MSIP)
+ READ_WRITE(CSR.MIE, 11 -> mie.MEIE, 7 -> mie.MTIE, 3 -> mie.MSIE)
+
+ r(CSR.MSTATUS, 11 -> mstatus.MPP)
+ onWrite(CSR.MSTATUS){
+ switch(writeData()(12 downto 11)){
+ is(3){ mstatus.MPP := 3 }
+ if(supervisorGen) is(1){ mstatus.MPP := 1 }
+ if(userGen) is(0){ mstatus.MPP := 0 }
+ }
+ }
+
+ mtvecAccess(CSR.MTVEC, 2 -> mtvec.base, 0 -> mtvec.mode)
+ mepcAccess(CSR.MEPC, mepc)
+ if(mscratchGen) READ_WRITE(CSR.MSCRATCH, mscratch)
+ mcauseAccess(CSR.MCAUSE, xlen-1 -> mcause.interrupt, 0 -> mcause.exceptionCode)
+ mbadaddrAccess(CSR.MBADADDR, mtval)
+ mcycleAccess(CSR.MCYCLE, mcycle(31 downto 0))
+ mcycleAccess(CSR.MCYCLEH, mcycle(63 downto 32))
+ minstretAccess(CSR.MINSTRET, minstret(31 downto 0))
+ minstretAccess(CSR.MINSTRETH, minstret(63 downto 32))
+
+ if(supervisorGen) {
+ for((id, enable) <- medeleg.mapping) medelegAccess(CSR.MEDELEG, id -> enable)
+ midelegAccess(CSR.MIDELEG, 9 -> mideleg.SE, 5 -> mideleg.ST, 1 -> mideleg.SS)
+ }
+
+ //User CSR
+ ucycleAccess(CSR.UCYCLE, mcycle(31 downto 0))
+ ucycleAccess(CSR.UCYCLEH, mcycle(63 downto 32))
+ uinstretAccess(CSR.UINSTRET, minstret(31 downto 0))
+ uinstretAccess(CSR.UINSTRETH, minstret(63 downto 32))
+
+ if(utimeAccess != CsrAccess.NONE) {
+ utimeAccess(CSR.UTIME, utime(31 downto 0))
+ utimeAccess(CSR.UTIMEH, utime(63 downto 32))
+ }
+
+ pipeline(MPP) := mstatus.MPP
+ }
+
+ val supervisorCsr = ifGen(supervisorGen) {
+ pipeline plug new Area {
+ val sstatus = new Area {
+ val SIE, SPIE = RegInit(False)
+ val SPP = RegInit(U"1")
+ }
+
+ val sip = new Area {
+ val SEIP_SOFT = RegInit(False)
+ val SEIP_INPUT = RegNext(externalInterruptS)
+ val SEIP_OR = SEIP_SOFT || SEIP_INPUT
+ val STIP = RegInit(False)
+ val SSIP = RegInit(False)
+ }
+ val sie = new Area {
+ val SEIE, STIE, SSIE = RegInit(False)
+ }
+ val stvec = Reg(Xtvec()).allowUnsetRegToAvoidLatch
+ val sscratch = if (sscratchGen) Reg(Bits(xlen bits)) else null
+
+ val scause = new Area {
+ val interrupt = Reg(Bool)
+ val exceptionCode = Reg(UInt(trapCodeWidth bits))
+ }
+ val stval = Reg(UInt(xlen bits))
+ val sepc = Reg(UInt(xlen bits))
+ val satp = new Area {
+ val PPN = Reg(Bits(22 bits))
+ val ASID = Reg(Bits(9 bits))
+ val MODE = Reg(Bits(1 bits))
+ }
+
+ //Supervisor CSR
+ for(offset <- List(CSR.MSTATUS, CSR.SSTATUS)) READ_WRITE(offset,8 -> sstatus.SPP, 5 -> sstatus.SPIE, 1 -> sstatus.SIE)
+ for(offset <- List(CSR.MIP, CSR.SIP)) {
+ READ_WRITE(offset, 5 -> sip.STIP, 1 -> sip.SSIP)
+ READ_ONLY(offset, 9 -> sip.SEIP_OR)
+ WRITE_ONLY(offset, 9 -> sip.SEIP_SOFT)
+ r2w(offset, 9, sip.SEIP_SOFT)
+ }
+
+ for(offset <- List(CSR.MIE, CSR.SIE)) READ_WRITE(offset, 9 -> sie.SEIE, 5 -> sie.STIE, 1 -> sie.SSIE)
+
+
+ stvecAccess(CSR.STVEC, 2 -> stvec.base, 0 -> stvec.mode)
+ sepcAccess(CSR.SEPC, sepc)
+ if(sscratchGen) READ_WRITE(CSR.SSCRATCH, sscratch)
+ scauseAccess(CSR.SCAUSE, xlen-1 -> scause.interrupt, 0 -> scause.exceptionCode)
+ sbadaddrAccess(CSR.SBADADDR, stval)
+ satpAccess(CSR.SATP, 31 -> satp.MODE, 22 -> satp.ASID, 0 -> satp.PPN)
+
+
+ val rescheduleLogic = supervisorGen generate new Area {
+ redoInterface.valid := False
+ redoInterface.payload := decode.input(PC)
+
+ val rescheduleNext = False
+ when(execute.arbitration.isValid && execute.input(IS_SFENCE_VMA)) { rescheduleNext := True }
+ duringWrite(CSR.SATP) { rescheduleNext := True }
+
+ when(rescheduleNext){
+ redoInterface.valid := True
+ execute.arbitration.flushNext := True
+ decode.arbitration.haltByOther := True
+ }
+ }
+ }
+ }
+
+
+
+ pipeline plug new Area{
+ import machineCsr._
+ import supervisorCsr._
+
+ val lastStage = pipeline.stages.last
+ val beforeLastStage = pipeline.stages(pipeline.stages.size-2)
+ val stagesFromExecute = pipeline.stages.dropWhile(_ != execute)
+
+ //Manage counters
+ mcycle := mcycle + 1
+ when(lastStage.arbitration.isFiring) {
+ minstret := minstret + 1
+ }
+
+
+ if(supervisorGen) {
+ addInterrupt(sip.STIP && sie.STIE, id = 5, privilege = 1, delegators = List(Delegator(mideleg.ST, 3)))
+ addInterrupt(sip.SSIP && sie.SSIE, id = 1, privilege = 1, delegators = List(Delegator(mideleg.SS, 3)))
+ addInterrupt(sip.SEIP_OR && sie.SEIE, id = 9, privilege = 1, delegators = List(Delegator(mideleg.SE, 3)))
+
+ for((id, enable) <- medeleg.mapping) exceptionSpecs += ExceptionSpec(id, List(Delegator(enable, 3)))
+ }
+
+ addInterrupt(mip.MTIP && mie.MTIE, id = 7, privilege = 3, delegators = Nil)
+ addInterrupt(mip.MSIP && mie.MSIE, id = 3, privilege = 3, delegators = Nil)
+ addInterrupt(mip.MEIP && mie.MEIE, id = 11, privilege = 3, delegators = Nil)
+
+
+ val mepcCaptureStage = if(exceptionPortsInfos.nonEmpty) lastStage else decode
+
+
+ //Aggregate all exception port and remove required instructions
+ val exceptionPortCtrl = exceptionPortsInfos.nonEmpty generate new Area{
+ val codeWidth = exceptionPortsInfos.map(_.codeWidth).max
+ val firstStageIndexWithExceptionPort = exceptionPortsInfos.map(i => indexOf(i.stage)).min
+ val exceptionValids = Vec(stages.map(s => Bool().setPartialName(s.getName())))
+ val exceptionValidsRegs = Vec(stages.map(s => Reg(Bool).init(False).setPartialName(s.getName()))).allowUnsetRegToAvoidLatch
+ val exceptionContext = Reg(ExceptionCause(codeWidth))
+ val exceptionTargetPrivilegeUncapped = U"11"
+
+ switch(exceptionContext.code){
+ for(s <- exceptionSpecs){
+ is(s.id){
+ var exceptionPrivilegs = if (supervisorGen) List(1, 3) else List(3)
+ while(exceptionPrivilegs.length != 1){
+ val p = exceptionPrivilegs.head
+ if (exceptionPrivilegs.tail.forall(e => s.delegators.exists(_.privilege == e))) {
+ val delegUpOn = s.delegators.filter(_.privilege > p).map(_.enable).fold(True)(_ && _)
+ val delegDownOff = !s.delegators.filter(_.privilege <= p).map(_.enable).orR
+ when(delegUpOn && delegDownOff) {
+ exceptionTargetPrivilegeUncapped := p
+ }
+ }
+ exceptionPrivilegs = exceptionPrivilegs.tail
+ }
+ }
+ }
+ }
+ val exceptionTargetPrivilege = privilege.max(exceptionTargetPrivilegeUncapped)
+
+ val groupedByStage = exceptionPortsInfos.map(_.stage).distinct.map(s => {
+ val stagePortsInfos = exceptionPortsInfos.filter(_.stage == s).sortWith(_.priority > _.priority)
+ val stagePort = stagePortsInfos.length match{
+ case 1 => {
+ stagePortsInfos.head.port.translateWith(stagePortsInfos.head.port.payload.resizeCode(codeWidth))
+ }
+ case _ => {
+ val groupedPort = Flow(ExceptionCause(codeWidth))
+ val valids = stagePortsInfos.map(_.port.valid)
+ val codes = stagePortsInfos.map(_.port.payload.resizeCode(codeWidth))
+ groupedPort.valid := valids.orR
+ groupedPort.payload := MuxOH(OHMasking.first(stagePortsInfos.map(_.port.valid).asBits), codes)
+ groupedPort
+ }
+ }
+ ExceptionPortInfo(stagePort,s,0, codeWidth)
+ })
+
+ val sortedByStage = groupedByStage.sortWith((a, b) => pipeline.indexOf(a.stage) < pipeline.indexOf(b.stage))
+// sortedByStage.zipWithIndex.foreach(e => e._1.port.setName(e._1.stage.getName() + "_exception_agregat"))
+ exceptionValids := exceptionValidsRegs
+ for(portInfo <- sortedByStage; port = portInfo.port ; stage = portInfo.stage; stageId = indexOf(portInfo.stage)) {
+ when(port.valid) {
+ stage.arbitration.flushNext := True
+ stage.arbitration.removeIt := True
+ exceptionValids(stageId) := True
+ exceptionContext := port.payload
+ }
+ }
+
+ for(stageId <- firstStageIndexWithExceptionPort until stages.length; stage = stages(stageId) ){
+ val previousStage = if(stageId == firstStageIndexWithExceptionPort) stage else stages(stageId-1)
+ when(!stage.arbitration.isStuck){
+ exceptionValidsRegs(stageId) := (if(stageId != firstStageIndexWithExceptionPort) exceptionValids(stageId-1) && !previousStage.arbitration.isStuck else False)
+ }otherwise{
+ if(stage != stages.last)
+ exceptionValidsRegs(stageId) := exceptionValids(stageId)
+ else
+ exceptionValidsRegs(stageId) := False
+ }
+ when(stage.arbitration.isFlushed){
+ exceptionValids(stageId) := False
+ }
+ }
+
+ when(exceptionValids.orR){
+ fetcher.haltIt()
+ }
+
+ //Avoid the PC register of the last stage to change durring an exception handleing (Used to fill Xepc)
+ stages.last.dontSample.getOrElseUpdate(PC, ArrayBuffer[Bool]()) += exceptionValids.last
+ exceptionPendings := exceptionValidsRegs
+ }
+
+
+
+
+
+ //Process interrupt request, code and privilege
+ val interrupt = new Area {
+ val valid = if(pipelinedInterrupt) RegNext(False) init(False) else False
+ val code = if(pipelinedInterrupt) Reg(UInt(trapCodeWidth bits)) else UInt(trapCodeWidth bits).assignDontCare()
+ var privilegs = if (supervisorGen) List(1, 3) else List(3)
+ val targetPrivilege = if(pipelinedInterrupt) Reg(UInt(2 bits)) else UInt(2 bits).assignDontCare()
+ val privilegeAllowInterrupts = mutable.LinkedHashMap[Int, Bool]()
+ if (supervisorGen) privilegeAllowInterrupts += 1 -> ((sstatus.SIE && privilege === U"01") || privilege < U"01")
+ privilegeAllowInterrupts += 3 -> (mstatus.MIE || privilege < U"11")
+ while (privilegs.nonEmpty) {
+ val p = privilegs.head
+ when(privilegeAllowInterrupts(p)) {
+ for (i <- interruptSpecs
+ if i.privilege <= p //EX : Machine timer interrupt can't go into supervisor mode
+ if privilegs.tail.forall(e => i.delegators.exists(_.privilege == e))) { // EX : Supervisor timer need to have machine mode delegator
+ val delegUpOn = i.delegators.filter(_.privilege > p).map(_.enable).fold(True)(_ && _)
+ val delegDownOff = !i.delegators.filter(_.privilege <= p).map(_.enable).orR
+ when(i.cond && delegUpOn && delegDownOff) {
+ valid := True
+ code := i.id
+ targetPrivilege := p
+ }
+ }
+ }
+ privilegs = privilegs.tail
+ }
+
+ code.addTag(Verilator.public)
+ }
+
+
+
+
+ val exception = if(exceptionPortCtrl != null) exceptionPortCtrl.exceptionValids.last && allowException else False
+ val lastStageWasWfi = if(wfiGenAsWait) RegNext(lastStage.arbitration.isFiring && lastStage.input(ENV_CTRL) === EnvCtrlEnum.WFI) init(False) else False
+
+
+
+ //Used to make the pipeline empty softly (for interrupts)
+ val pipelineLiberator = new Area{
+ val pcValids = Vec(RegInit(False), stagesFromExecute.length)
+ val active = interrupt.valid && allowInterrupts && decode.arbitration.isValid
+ when(active){
+ decode.arbitration.haltByOther := True
+ for((stage, reg, previous) <- (stagesFromExecute, pcValids, True :: pcValids.toList).zipped){
+ when(!stage.arbitration.isStuck){
+ reg := previous
+ }
+ }
+ }
+ when(!active || decode.arbitration.isRemoved) {
+ pcValids.foreach(_ := False)
+ }
+
+// val pcValids = for(stage <- stagesFromExecute) yield RegInit(False) clearWhen(!started) setWhen(!stage.arbitration.isValid)
+ val done = CombInit(pcValids.last)
+ if(exceptionPortCtrl != null) done.clearWhen(exceptionPortCtrl.exceptionValidsRegs.tail.orR)
+ }
+
+ //Interrupt/Exception entry logic
+ val interruptJump = Bool.addTag(Verilator.public)
+ interruptJump := interrupt.valid && pipelineLiberator.done && allowInterrupts
+ if(pipelinedInterrupt) interrupt.valid clearWhen(interruptJump) //avoid double fireing
+
+ val hadException = RegNext(exception) init(False) addTag(Verilator.public)
+ pipelineLiberator.done.clearWhen(hadException)
+
+
+ val targetPrivilege = CombInit(interrupt.targetPrivilege)
+ if(exceptionPortCtrl != null) when(hadException) {
+ targetPrivilege := exceptionPortCtrl.exceptionTargetPrivilege
+ }
+
+ val trapCause = CombInit(interrupt.code.resize(trapCodeWidth))
+ if(exceptionPortCtrl != null) when( hadException){
+ trapCause := exceptionPortCtrl.exceptionContext.code.resized
+ }
+
+ val xtvec = Xtvec().assignDontCare()
+ switch(targetPrivilege){
+ if(supervisorGen) is(1) { xtvec := supervisorCsr.stvec }
+ is(3){ xtvec := machineCsr.mtvec }
+ }
+
+ when(hadException || interruptJump){
+ fetcher.haltIt() //Avoid having the fetch confused by the incomming privilege switch
+
+ jumpInterface.valid := True
+ jumpInterface.payload := (if(!xtvecModeGen) xtvec.base @@ U"00" else (xtvec.mode === 0 || hadException) ? (xtvec.base @@ U"00") | ((xtvec.base + trapCause) @@ U"00") )
+ lastStage.arbitration.flushNext := True
+
+ if(privilegeGen) privilegeReg := targetPrivilege
+
+ switch(targetPrivilege){
+ if(supervisorGen) is(1) {
+ sstatus.SIE := False
+ sstatus.SPIE := sstatus.SIE
+ sstatus.SPP := privilege(0 downto 0)
+ scause.interrupt := !hadException
+ scause.exceptionCode := trapCause
+ sepc := mepcCaptureStage.input(PC)
+ if (exceptionPortCtrl != null) when(hadException){
+ stval := exceptionPortCtrl.exceptionContext.badAddr
+ }
+ }
+
+ is(3){
+ mstatus.MIE := False
+ mstatus.MPIE := mstatus.MIE
+ mstatus.MPP := privilege
+ mcause.interrupt := !hadException
+ mcause.exceptionCode := trapCause
+ mepc := mepcCaptureStage.input(PC)
+ if(exceptionPortCtrl != null) when(hadException){
+ mtval := exceptionPortCtrl.exceptionContext.badAddr
+ }
+ }
+ }
+ }
+
+ if(exceptionPortCtrl == null){
+ if(mbadaddrAccess == CsrAccess.READ_ONLY) mtval := 0
+ if(sbadaddrAccess == CsrAccess.READ_ONLY) stval := 0
+ }
+
+ lastStage plug new Area{
+ import lastStage._
+
+ //Manage MRET / SRET instructions
+ when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.XRET) {
+ fetcher.haltIt()
+ jumpInterface.valid := True
+ lastStage.arbitration.flushNext := True
+ switch(input(INSTRUCTION)(29 downto 28)){
+ is(3){
+ mstatus.MPP := U"00"
+ mstatus.MIE := mstatus.MPIE
+ mstatus.MPIE := True
+ jumpInterface.payload := mepc
+ if(privilegeGen) privilegeReg := mstatus.MPP
+ }
+ if(supervisorGen) is(1){
+ sstatus.SPP := U"0"
+ sstatus.SIE := sstatus.SPIE
+ sstatus.SPIE := True
+ jumpInterface.payload := sepc
+ if(privilegeGen) privilegeReg := U"0" @@ sstatus.SPP
+ }
+ }
+ }
+ }
+
+
+ contextSwitching := jumpInterface.valid
+
+ //CSR read/write instructions management
+ decode plug new Area{
+ import decode._
+
+ val imm = IMM(input(INSTRUCTION))
+ insert(CSR_WRITE_OPCODE) := ! (
+ (input(INSTRUCTION)(14 downto 13) === B"01" && input(INSTRUCTION)(rs1Range) === 0)
+ || (input(INSTRUCTION)(14 downto 13) === B"11" && imm.z === 0)
+ )
+ insert(CSR_READ_OPCODE) := input(INSTRUCTION)(13 downto 7) =/= B"0100000"
+ }
+
+
+ execute plug new Area{
+ import execute._
+ //Manage WFI instructions
+ if(wfiOutput) out(inWfi)
+ val wfiWake = RegNext(interruptSpecs.map(_.cond).orR || thirdPartyWake) init(False)
+ if(wfiGenAsWait) when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.WFI){
+ inWfi := True
+ when(!wfiWake){
+ arbitration.haltItself := True
+ }
+ }
+ }
+
+ decode.arbitration.haltByOther setWhen(stagesFromExecute.map(s => s.arbitration.isValid && s.input(ENV_CTRL) === EnvCtrlEnum.XRET).asBits.orR)
+
+ execute plug new Area {
+ import execute._
+ def previousStage = decode
+ val blockedBySideEffects = stagesFromExecute.tail.map(s => s.arbitration.isValid).asBits().orR || pipeline.service(classOf[HazardService]).hazardOnExecuteRS// && s.input(HAS_SIDE_EFFECT) to improve be less pessimistic
+
+ val illegalAccess = True
+ val illegalInstruction = False
+ if(selfException != null) {
+ selfException.valid := False
+ selfException.code.assignDontCare()
+ selfException.badAddr := input(INSTRUCTION).asUInt
+ if(catchIllegalAccess) when(illegalAccess || illegalInstruction){
+ selfException.valid := True
+ selfException.code := 2
+ }
+ }
+
+ //Manage MRET / SRET instructions
+ when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.XRET) {
+ when(input(INSTRUCTION)(29 downto 28).asUInt > privilege) {
+ illegalInstruction := True
+ }
+ }
+
+
+ //Manage ECALL instructions
+ if(ecallGen) when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.ECALL){
+ selfException.valid := True
+ switch(privilege) {
+ is(0) { selfException.code := 8 }
+ if(supervisorGen) is(1) { selfException.code := 9 }
+ default { selfException.code := 11 }
+ }
+ }
+
+
+ if(ebreakGen) when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.EBREAK && allowEbreakException){
+ selfException.valid := True
+ selfException.code := 3
+ }
+
+
+ val imm = IMM(input(INSTRUCTION))
+ def writeSrc = input(SRC1)
+ def readData = csrMapping.readDataSignal
+ def writeData = csrMapping.writeDataSignal
+ val writeInstruction = arbitration.isValid && input(IS_CSR) && input(CSR_WRITE_OPCODE)
+ val readInstruction = arbitration.isValid && input(IS_CSR) && input(CSR_READ_OPCODE)
+ val writeEnable = writeInstruction && !arbitration.isStuck
+ val readEnable = readInstruction && !arbitration.isStuck
+ csrMapping.hazardFree := !blockedBySideEffects
+
+ val readToWriteData = CombInit(readData)
+ writeData := (if(noCsrAlu) writeSrc else input(INSTRUCTION)(13).mux(
+ False -> writeSrc,
+ True -> Mux(input(INSTRUCTION)(12), readToWriteData & ~writeSrc, readToWriteData | writeSrc)
+ ))
+
+ when(arbitration.isValid && input(IS_CSR)) {
+ if(!pipelineCsrRead) output(REGFILE_WRITE_DATA) := readData
+ }
+
+ when(arbitration.isValid && (input(IS_CSR) || (if(supervisorGen) input(IS_SFENCE_VMA) else False))) {
+ arbitration.haltItself setWhen(blockedBySideEffects)
+ }
+
+ if(pipelineCsrRead){
+ insert(PIPELINED_CSR_READ) := readData
+ when(memory.arbitration.isValid && memory.input(IS_CSR)) {
+ memory.output(REGFILE_WRITE_DATA) := memory.input(PIPELINED_CSR_READ)
+ }
+ }
+//
+// Component.current.rework{
+// when(arbitration.isFiring && input(IS_CSR)) {
+// memory.input(REGFILE_WRITE_DATA).getDrivingReg := readData
+// }
+// }
+
+ //Translation of the csrMapping into real logic
+ val csrAddress = input(INSTRUCTION)(csrRange)
+ Component.current.afterElaboration{
+ def doJobs(jobs : ArrayBuffer[Any]): Unit ={
+ val withWrite = jobs.exists(j => j.isInstanceOf[CsrWrite] || j.isInstanceOf[CsrOnWrite] || j.isInstanceOf[CsrDuringWrite])
+ val withRead = jobs.exists(j => j.isInstanceOf[CsrRead] || j.isInstanceOf[CsrOnRead])
+ if(withRead && withWrite) {
+ illegalAccess := False
+ } else {
+ if (withWrite) illegalAccess.clearWhen(input(CSR_WRITE_OPCODE))
+ if (withRead) illegalAccess.clearWhen(input(CSR_READ_OPCODE))
+ }
+
+
+ for (element <- jobs) element match {
+ case element : CsrDuringWrite => when(writeInstruction){element.doThat()}
+ case element : CsrDuringRead => when(readInstruction){element.doThat()}
+ case element : CsrDuring => {element.doThat()}
+ case _ =>
+ }
+ when(writeEnable) {
+ for (element <- jobs) element match {
+ case element: CsrWrite => element.that.assignFromBits(writeData(element.bitOffset, element.that.getBitsWidth bits))
+ case element: CsrOnWrite => element.doThat()
+ case _ =>
+ }
+ }
+
+ when(readEnable) {
+ for (element <- jobs) element match {
+ case element: CsrOnRead =>
+ element.doThat()
+ case _ =>
+ }
+ }
+ }
+
+ def doJobsOverride(jobs : ArrayBuffer[Any]): Unit ={
+ for (element <- jobs) element match {
+ case element: CsrReadToWriteOverride if element.that.getBitsWidth != 0 => readToWriteData(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
+ case _ =>
+ }
+ }
+
+ csrOhDecoder match {
+ case false => {
+ csrMapping.readDataInit := 0
+ switch(csrAddress) {
+ for ((address, jobs) <- csrMapping.mapping) {
+ is(address) {
+ doJobs(jobs)
+ for (element <- jobs) element match {
+ case element: CsrRead if element.that.getBitsWidth != 0 => csrMapping.readDataInit (element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
+ case _ =>
+ }
+ }
+ }
+ }
+ switch(csrAddress) {
+ for ((address, jobs) <- csrMapping.mapping if jobs.exists(_.isInstanceOf[CsrReadToWriteOverride])) {
+ is(address) {
+ doJobsOverride(jobs)
+ }
+ }
+ }
+ }
+ case true => {
+ val oh = csrMapping.mapping.keys.toList.distinct.map(address => address -> RegNextWhen(decode.input(INSTRUCTION)(csrRange) === address, !execute.arbitration.isStuck).setCompositeName(this, "csr_" + address)).toMap
+ val readDatas = ArrayBuffer[Bits]()
+ for ((address, jobs) <- csrMapping.mapping) {
+ when(oh(address)){
+ doJobs(jobs)
+ }
+ if(jobs.exists(_.isInstanceOf[CsrRead])) {
+ val masked = B(0, 32 bits)
+ when(oh(address)) (for (element <- jobs) element match {
+ case element: CsrRead if element.that.getBitsWidth != 0 => masked(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
+ case _ =>
+ })
+ readDatas += masked
+ }
+ }
+ csrMapping.readDataInit := readDatas.reduceBalancedTree(_ | _)
+ for ((address, jobs) <- csrMapping.mapping) {
+ when(oh(address)){
+ doJobsOverride(jobs)
+ }
+ }
+ }
+ }
+
+ csrMapping.always.foreach {
+ case element : CsrDuringWrite => when(writeInstruction){element.doThat()}
+ case element : CsrDuringRead => when(readInstruction){element.doThat()}
+ case element : CsrOnWrite => when(writeEnable){element.doThat()}
+ case element : CsrOnRead => when(readEnable){element.doThat()}
+ }
+
+ illegalAccess clearWhen(csrMapping.allowCsrSignal)
+
+ when(privilege < csrAddress(9 downto 8).asUInt){
+ illegalAccess := True
+ readInstruction := False
+ writeInstruction := False
+ }
+ illegalAccess clearWhen(!arbitration.isValid || !input(IS_CSR))
+ }
+ }
+ }
+ }
+}
+
+
+class UserInterruptPlugin(interruptName : String, code : Int, privilege : Int = 3) extends Plugin[VexRiscv]{
+ var interrupt, interruptEnable : Bool = null
+ override def setup(pipeline: VexRiscv): Unit = {
+ val csr = pipeline.service(classOf[CsrPlugin])
+ interrupt = in.Bool().setName(interruptName)
+ val interruptPending = RegNext(interrupt) init(False)
+ val interruptEnable = RegInit(False).setName(interruptName + "_enable")
+ csr.addInterrupt(interruptPending && interruptEnable, code, privilege, Nil)
+ csr.r(csrAddress = CSR.MIP, bitOffset = code,interruptPending)
+ csr.rw(csrAddress = CSR.MIE, bitOffset = code, interruptEnable)
+ }
+ override def build(pipeline: VexRiscv): Unit = {}
+}