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 /** * 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) 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, 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, medelegAccess : CsrAccess = CsrAccess.NONE, midelegAccess : CsrAccess = CsrAccess.NONE, pipelineCsrRead : Boolean = false, deterministicInteruptionEntry : Boolean = false //Only used for simulatation purposes ){ assert(!ucycleAccess.canWrite) def privilegeGen = userGen || supervisorGen def noException = this.copy(ecallGen = false, ebreakGen = false, catchIllegalAccess = false) } object CsrPluginConfig{ def all : CsrPluginConfig = all(0x00000020l) def small : CsrPluginConfig = small(0x00000020l) def smallest : CsrPluginConfig = smallest(0x00000020l) 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, 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, 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.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 ) 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, 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 ) 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 ) } 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 CsrOnRead(doThat : () => Unit) case class CsrMapping() extends CsrInterface{ val mapping = mutable.HashMap[Int,ArrayBuffer[Any]]() 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 onRead(csrAddress: Int)(body: => Unit): Unit = addMappingAt(csrAddress, CsrOnRead(() => {body})) } trait CsrInterface{ def onWrite(csrAddress : Int)(doThat : => Unit) : Unit def onRead(csrAddress : Int)(doThat : => Unit) : Unit 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 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 } } trait IContextSwitching{ def isContextSwitching : Bool } class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with ExceptionService with PrivilegeService with InterruptionInhibitor with ExceptionInhibitor with IContextSwitching with CsrInterface{ import config._ import CsrAccess._ assert(!(wfiGenAsNop && wfiGenAsWait)) def xlen = 32 //Mannage ExceptionService calls val exceptionPortsInfos = ArrayBuffer[ExceptionPortInfo]() def exceptionCodeWidth = 4 override def newExceptionPort(stage : Stage, priority : Int = 0) = { val interface = Flow(ExceptionCause()) exceptionPortsInfos += ExceptionPortInfo(interface,stage,priority) interface } var exceptionPending : Bool = null override def isExceptionPending(): Bool = exceptionPending 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 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 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 val csrMapping = new CsrMapping() //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 onRead(csrAddress: Int)(body: => Unit): Unit = csrMapping.onRead(csrAddress)(body) override def setup(pipeline: VexRiscv): Unit = { import pipeline.config._ 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() exceptionPending = False 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 for (i <- interruptSpecs) i.cond = i.cond.pull() pipeline.update(MPP, UInt(2 bits)) } def inhibateInterrupts() : Unit = allowInterrupts := False def inhibateException() : Unit = allowException := 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]) //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(exceptionCodeWidth bits)) } val mtval = Reg(UInt(xlen bits)) val mcycle = Reg(UInt(64 bits)) randBoot() val minstret = Reg(UInt(64 bits)) randBoot() val medeleg = supervisorGen generate new Area { val IAM, IAF, II, LAM, LAF, SAM, SAF, EU, ES, IPF, LPF, SPF = RegInit(False) val mapping = mutable.HashMap(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) READ_ONLY(CSR.MHARTID , U(mhartid )) misaAccess(CSR.MISA, xlen-2 -> misa.base , 0 -> misa.extensions) //Machine CSR READ_WRITE(CSR.MSTATUS,11 -> mstatus.MPP, 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) 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)) 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(exceptionCodeWidth 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) } } 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 = if(exceptionPortsInfos.nonEmpty) new Area{ 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()) 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 case _ => { val groupedPort = Flow(ExceptionCause()) val valids = stagePortsInfos.map(_.port.valid) val codes = stagePortsInfos.map(_.port.payload) groupedPort.valid := valids.orR groupedPort.payload := MuxOH(OHMasking.first(stagePortsInfos.map(_.port.valid).asBits), codes) groupedPort } } ExceptionPortInfo(stagePort,s,0) }) 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) { if(indexOf(stage) != 0) stages(indexOf(stage) - 1).arbitration.flushAll := 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(exceptionValidsRegs.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 exceptionPending setWhen(exceptionValidsRegs.orR) } else null //Process interrupt request, code and privilege val interrupt = False val interruptCode = UInt(4 bits).assignDontCare().addTag(Verilator.public) var interruptPrivilegs = if (supervisorGen) List(1, 3) else List(3) val interruptTargetPrivilege = UInt(2 bits).assignDontCare() val privilegeAllowInterrupts = mutable.HashMap[Int, Bool]() if(supervisorGen) privilegeAllowInterrupts += 1 -> ((sstatus.SIE && privilege === "01") || privilege < "01") privilegeAllowInterrupts += 3 -> (mstatus.MIE || privilege < "11") while(interruptPrivilegs.nonEmpty){ val p = interruptPrivilegs.head when(privilegeAllowInterrupts(p)){ for(i <- interruptSpecs if i.privilege <= p //EX : Machine timer interrupt can't go into supervisor mode if interruptPrivilegs.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){ interrupt := True interruptCode := i.id interruptTargetPrivilege := p } } } interruptPrivilegs = interruptPrivilegs.tail } interrupt.clearWhen(!allowInterrupts) 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{ when(interrupt){ decode.arbitration.haltByOther := decode.arbitration.isValid } val done = !stagesFromExecute.map(_.arbitration.isValid).orR && fetcher.pcValid(mepcCaptureStage) if(exceptionPortCtrl != null) done.clearWhen(exceptionPortCtrl.exceptionValidsRegs.tail.orR) } //Interrupt/Exception entry logic val interruptJump = Bool.addTag(Verilator.public) interruptJump := interrupt && pipelineLiberator.done val hadException = RegNext(exception) init(False) pipelineLiberator.done.clearWhen(hadException) val targetPrivilege = CombInit(interruptTargetPrivilege) if(exceptionPortCtrl != null) when(hadException) { targetPrivilege := exceptionPortCtrl.exceptionTargetPrivilege } val trapCause = CombInit(interruptCode) if(exceptionPortCtrl != null) when( hadException){ trapCause := exceptionPortCtrl.exceptionContext.code } 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 @@ "00" else (xtvec.mode === 0 || hadException) ? (xtvec.base @@ "00") | ((xtvec.base + trapCause) @@ "00") ) beforeLastStage.arbitration.flushAll := 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 } } } } lastStage plug new Area{ import lastStage._ //Manage MRET / SRET instructions when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.XRET) { fetcher.haltIt() jumpInterface.valid := True beforeLastStage.arbitration.flushAll := 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) === "01" && input(INSTRUCTION)(rs1Range) === 0) || (input(INSTRUCTION)(14 downto 13) === "11" && imm.z === 0) ) insert(CSR_READ_OPCODE) := input(INSTRUCTION)(13 downto 7) =/= B"0100000" } execute plug new Area{ import execute._ //Manage WFI instructions val inWfi = False.addTag(Verilator.public) if(wfiGenAsWait) when(arbitration.isValid && input(ENV_CTRL) === EnvCtrlEnum.WFI){ inWfi := True when(!interrupt){ 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 // && 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){ selfException.valid := True selfException.code := 3 } val imm = IMM(input(INSTRUCTION)) def writeSrc = input(SRC1) // val readDataValid = True val readData = B(0, 32 bits) 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 && ! blockedBySideEffects && !arbitration.isStuckByOthers// && readDataRegValid val readEnable = readInstruction && ! blockedBySideEffects && !arbitration.isStuckByOthers// && !readDataRegValid //arbitration.isStuckByOthers, in case of the hazardPlugin is in the executeStage // def readDataReg = memory.input(REGFILE_WRITE_DATA) //PIPE OPT // val readDataRegValid = Reg(Bool) setWhen(arbitration.isValid) clearWhen(!arbitration.isStuck) // val writeDataEnable = input(INSTRUCTION)(13) ? writeSrc | B"xFFFFFFFF" // val writeData = if(noCsrAlu) writeSrc else input(INSTRUCTION)(13).mux( // False -> writeSrc, // True -> Mux(input(INSTRUCTION)(12), ~writeSrc, writeSrc) // ) val readToWriteData = CombInit(readData) val writeData = if(noCsrAlu) writeSrc else input(INSTRUCTION)(13).mux( False -> writeSrc, True -> Mux(input(INSTRUCTION)(12), readToWriteData & ~writeSrc, readToWriteData | writeSrc) ) // arbitration.haltItself setWhen(writeInstruction && !readDataRegValid) when(arbitration.isValid && input(IS_CSR)) { if(!pipelineCsrRead) output(REGFILE_WRITE_DATA) := readData 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.addPrePopTask(() => { switch(csrAddress) { for ((address, jobs) <- csrMapping.mapping) { is(address) { val withWrite = jobs.exists(j => j.isInstanceOf[CsrWrite] || j.isInstanceOf[CsrOnWrite]) 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)) } 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 _ => } } for (element <- jobs) element match { case element: CsrRead if element.that.getBitsWidth != 0 => readData(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits case _ => } when(readEnable) { for (element <- jobs) element match { case element: CsrOnRead => element.doThat() case _ => } } } } } switch(csrAddress) { for ((address, jobs) <- csrMapping.mapping if jobs.exists(_.isInstanceOf[CsrReadToWriteOverride])) { is(address) { for (element <- jobs) element match { case element: CsrReadToWriteOverride if element.that.getBitsWidth != 0 => readToWriteData(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits case _ => } } } } illegalAccess setWhen(privilege < csrAddress(9 downto 8).asUInt) illegalAccess clearWhen(!arbitration.isValid || !input(IS_CSR)) }) } } } }