diff --git a/README.md b/README.md index 58138b40..7d41c1ea 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ - [YamlPlugin](#yamlplugin) - [FpuPlugin](#fpuplugin) - [AesPlugin](#aesplugin) + - [CounterPlugin](#counterplugin) @@ -1363,3 +1364,21 @@ It was also ported on libressl via the following patch : Speed up of 4 was observed in libressl running in linux. + +#### CounterPlugin + +Provides performance-counter and time CSRs. + +Here is how to provide a custom event condition (which can then be configured by code): +In setup phase +```scala +val ctrSrv = pipeline.service(classOf[CounterService]) +ctrSrv.createEvent(eventId) +``` +In build phase +```scala +val ctrSrv = pipeline.service(classOf[CounterService]) +ctrSrv.getCondition(eventId) := boolCond +``` +eventId is BigInt, but only events between 0 and 2 ** XLEN (excluding boundaries) can be selected by cpu. +The configured counter counts clockcycles with boolCond asserted. diff --git a/src/main/scala/vexriscv/Riscv.scala b/src/main/scala/vexriscv/Riscv.scala index 52ccf36e..d4bbaa15 100644 --- a/src/main/scala/vexriscv/Riscv.scala +++ b/src/main/scala/vexriscv/Riscv.scala @@ -216,6 +216,10 @@ object Riscv{ def MCYCLEH = 0xB80 // MRW Upper 32 bits of mcycle, RV32I only. def MINSTRETH = 0xB82 // MRW Upper 32 bits of minstret, RV32I only. val MCOUNTEREN = 0x306 + val MCOUNTER = 0xB03 // MRW Base address for mhpmcounterX. + val MCOUNTERH = 0xB83 // MRW Base address for mhpmcounterXh, RV32I only. + val MCOUNTINHIBIT = 0x320 + val MEVENT = 0x323 // MRW Base address for mhpmeventX. val SSTATUS = 0x100 val SIE = 0x104 @@ -234,6 +238,8 @@ object Riscv{ def UTIMEH = 0xC81 def UINSTRET = 0xC02 // UR Machine instructions-retired counter. def UINSTRETH = 0xC82 // UR Upper 32 bits of minstret, RV32I only. + val UCOUNTER = 0xC03 // UR Base address for hpmcounter. + val UCOUNTERH = 0xC83 // UR Base address for hpmcounterXh, RV32I only. val FFLAGS = 0x1 val FRM = 0x2 diff --git a/src/main/scala/vexriscv/Services.scala b/src/main/scala/vexriscv/Services.scala index f0ef7136..8b67eb7f 100644 --- a/src/main/scala/vexriscv/Services.scala +++ b/src/main/scala/vexriscv/Services.scala @@ -49,6 +49,8 @@ trait PrivilegeService{ def isUser() : Bool def isSupervisor() : Bool def isMachine() : Bool + def hasUser() : Boolean + def hasSupervisor() : Boolean def forceMachine() : Unit def encodeBits() : Bits = { @@ -70,6 +72,8 @@ case class PrivilegeServiceDefault() extends PrivilegeService{ override def isUser(): Bool = False override def isSupervisor(): Bool = False override def isMachine(): Bool = True + override def hasUser(): Boolean = false + override def hasSupervisor(): Boolean = false override def forceMachine(): Unit = {} } @@ -143,4 +147,4 @@ class CacheReport { class DebugReport { @BeanProperty var hardwareBreakpointCount = 0 -} \ No newline at end of file +} diff --git a/src/main/scala/vexriscv/demo/GenFull.scala b/src/main/scala/vexriscv/demo/GenFull.scala index eb1dba33..0a38d149 100644 --- a/src/main/scala/vexriscv/demo/GenFull.scala +++ b/src/main/scala/vexriscv/demo/GenFull.scala @@ -80,6 +80,7 @@ object GenFull extends App{ earlyBranch = false, catchAddressMisaligned = true ), + new CounterPlugin(CounterPluginConfig()), new YamlPlugin("cpu0.yaml") ) ) diff --git a/src/main/scala/vexriscv/demo/VexRiscvAxi4LinuxPlicClint.scala b/src/main/scala/vexriscv/demo/VexRiscvAxi4LinuxPlicClint.scala index a8ab02a4..a253f870 100644 --- a/src/main/scala/vexriscv/demo/VexRiscvAxi4LinuxPlicClint.scala +++ b/src/main/scala/vexriscv/demo/VexRiscvAxi4LinuxPlicClint.scala @@ -96,7 +96,20 @@ object VexRiscvAxi4LinuxPlicClint{ earlyBranch = false, catchAddressMisaligned = true ), - new CsrPlugin(CsrPluginConfig.openSbi(mhartid = 0, misa = Riscv.misaToInt(s"ima")).copy(utimeAccess = CsrAccess.READ_ONLY)), + new CsrPlugin(CsrPluginConfig.openSbi(mhartid = 0, misa = Riscv.misaToInt(s"ima"))), + new CounterPlugin(CounterPluginConfig( + NumOfCounters = 0, + mcycleAccess = CsrAccess.NONE, + ucycleAccess = CsrAccess.NONE, + minstretAccess = CsrAccess.NONE, + uinstretAccess = CsrAccess.NONE, + mcounterenAccess = CsrAccess.NONE, + scounterenAccess = CsrAccess.NONE, + mcounterAccess = CsrAccess.NONE, + ucounterAccess = CsrAccess.NONE, + meventAccess = CsrAccess.NONE, + mcountinhibitAccess = CsrAccess.NONE + )), new YamlPlugin("cpu0.yaml") ) ) @@ -152,7 +165,9 @@ object VexRiscvAxi4LinuxPlicClint{ plugin.softwareInterrupt setAsDirectionLess() := cpu.clintCtrl.io.softwareInterrupt(0) plugin.externalInterrupt setAsDirectionLess() := cpu.plicCtrl.io.targets(0) plugin.externalInterruptS setAsDirectionLess() := cpu.plicCtrl.io.targets(1) - plugin.utime setAsDirectionLess() := cpu.clintCtrl.io.time + } + case plugin: CounterPlugin => { + plugin.time setAsDirectionLess() := cpu.clintCtrl.io.time } case _ => } diff --git a/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala b/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala index 23e26711..df6692e6 100644 --- a/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala +++ b/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala @@ -221,7 +221,7 @@ class VexRiscvSmpClusterWithPeripherals(p : VexRiscvSmpClusterParameter) extends plic.addTarget(core.cpu.externalSupervisorInterrupt) List(clint.logic, core.cpu.logic).produce { for (plugin <- core.cpu.config.plugins) plugin match { - case plugin: CsrPlugin if plugin.utime != null => plugin.utime := clint.logic.io.time + case plugin: CounterPlugin if plugin.time != null => plugin.time := clint.logic.io.time case _ => } } @@ -276,7 +276,7 @@ object VexRiscvSmpClusterGen { val misa = Riscv.misaToInt(s"ima${if(withFloat) "f" else ""}${if(withDouble) "d" else ""}${if(rvc) "c" else ""}${if(withSupervisor) "su" else ""}") val csrConfig = if(withSupervisor){ - var c = CsrPluginConfig.openSbi(mhartid = hartId, misa = misa).copy(utimeAccess = CsrAccess.READ_ONLY, withPrivilegedDebug = privilegedDebug) + var c = CsrPluginConfig.openSbi(mhartid = hartId, misa = misa).copy(withPrivilegedDebug = privilegedDebug) if(csrFull){ c = c.copy( mcauseAccess = CsrAccess.READ_WRITE, @@ -430,6 +430,19 @@ object VexRiscvSmpClusterGen { catchAddressMisaligned = true, fenceiGenAsAJump = false ), + new CounterPlugin(if(csrFull) CounterPluginConfig() else CounterPluginConfig( + NumOfCounters = 0, + mcycleAccess = CsrAccess.NONE, + ucycleAccess = CsrAccess.NONE, + minstretAccess = CsrAccess.NONE, + uinstretAccess = CsrAccess.NONE, + mcounterenAccess = CsrAccess.NONE, + scounterenAccess = CsrAccess.NONE, + mcounterAccess = CsrAccess.NONE, + ucounterAccess = CsrAccess.NONE, + meventAccess = CsrAccess.NONE, + mcountinhibitAccess = CsrAccess.NONE + )), new YamlPlugin(s"cpu$hartId.yaml") ) ) diff --git a/src/main/scala/vexriscv/plugin/CounterPlugin.scala b/src/main/scala/vexriscv/plugin/CounterPlugin.scala new file mode 100644 index 00000000..983a831d --- /dev/null +++ b/src/main/scala/vexriscv/plugin/CounterPlugin.scala @@ -0,0 +1,230 @@ +package vexriscv.plugin + +import spinal.core._ + +import vexriscv._ +import vexriscv.Riscv.CSR._ + +import scala.collection.mutable._ + +trait CounterService{ + def createEvent(eventId : BigInt) : Unit + def getCondition(eventId : BigInt) : Bool +} + +case class CounterPluginConfig( + NumOfCounters : Byte = 29, + + mcycleAccess : CsrAccess = CsrAccess.READ_WRITE, + ucycleAccess : CsrAccess = CsrAccess.READ_ONLY, + + minstretAccess : CsrAccess = CsrAccess.READ_WRITE, + uinstretAccess : CsrAccess = CsrAccess.READ_ONLY, + + utimeAccess : CsrAccess = CsrAccess.READ_ONLY, + + mcounterenAccess : CsrAccess = CsrAccess.READ_WRITE, + scounterenAccess : CsrAccess = CsrAccess.READ_WRITE, + + mcounterAccess : CsrAccess = CsrAccess.READ_WRITE, + ucounterAccess : CsrAccess = CsrAccess.READ_ONLY, + + // management + meventAccess : CsrAccess = CsrAccess.READ_WRITE, + mcountinhibitAccess : CsrAccess = CsrAccess.READ_WRITE + ) { + assert(!ucycleAccess.canWrite) +} + +object Priv{ + val M = 3 + val S = 1 + val U = 0 +} + +class CounterPlugin(config : CounterPluginConfig) extends Plugin[VexRiscv] with CounterService { + import config._ + + def xlen = 32 + + assert(NumOfCounters <= 29, "Cannot create more than 29 custom counters") + assert(NumOfCounters >= 0, "Cannot create less than 0 custom counters") + +// counters : Array[Reg] = null +// event : Array[Reg] = null + +// mcouen : Reg = null +// scouen : Reg = null + +// inhibit : Reg = null + + val eventType : Map[BigInt, Bool] = new HashMap() + + var time : UInt = null + + implicit class PrivilegeHelper(p : PrivilegeService){ + def canMachine() : Bool = p.isMachine() + def canSupervisor() : Bool = p.isMachine() || p.isSupervisor() + } + + implicit class CsrAccessHelper(csrAccess : CsrAccess){ + import CsrAccess._ + import Priv._ + def apply(csrService : CsrInterface, csrAddress : Int, that : Data) : Unit = { + if(csrAccess == `WRITE_ONLY` || csrAccess == `READ_WRITE`) csrService.w(csrAddress, 0, that) + if(csrAccess == `READ_ONLY` || csrAccess == `READ_WRITE`) csrService.r(csrAddress, 0, that) + } + def apply( + csrSrv : CsrInterface, + prvSrv : PrivilegeService, + csrAddress : Int, + that : Data, + privAllows : (Int, Bool)* + ) : Unit = { + apply(csrSrv, csrAddress, that) + if(csrAccess != CsrAccess.NONE) csrSrv.during(csrAddress){ + for (ii <- privAllows) { + if (ii._1 == M) + when (~prvSrv.canMachine() || ~ii._2) { csrSrv.forceFailCsr() } + if (ii._1 == S && prvSrv.hasSupervisor()) + when (~prvSrv.canSupervisor() || ~ii._2) { csrSrv.forceFailCsr() } + if (prvSrv.hasUser()) + when (~ii._2) { csrSrv.forceFailCsr() } + } + } + } + } + + override def createEvent(eventId : BigInt) : Unit = { + if (!eventType.contains(eventId)) { + eventType(eventId) = Bool + } + } + + override def getCondition(eventId : BigInt) : Bool = { + eventType(eventId) + } + + override def setup(pipeline : VexRiscv) : Unit = { + import pipeline._ + import pipeline.config._ + + if (utimeAccess != CsrAccess.NONE) time = in UInt(64 bits) setName("utime") + } + + override def build(pipeline : VexRiscv) : Unit = { + import pipeline._ + import pipeline.config._ + + pipeline plug new Area{ + val csrSrv = pipeline.service(classOf[CsrInterface]) + val dbgSrv = pipeline.service(classOf[DebugService]) + val prvSrv = pipeline.service(classOf[PrivilegeService]) + + val dbgCtrEn = ~(dbgSrv.inDebugMode() && dbgSrv.debugState().dcsr.stopcount) + + val menable = RegInit(Bits(3 + NumOfCounters bits).getAllTrue) allowUnsetRegToAvoidLatch + val senable = RegInit(Bits(3 + NumOfCounters bits).getAllTrue) allowUnsetRegToAvoidLatch + val inhibit = Reg(Bits(NumOfCounters bits)) init(0) + val inhibitCY = RegInit(False) + val inhibitIR = RegInit(False) + + val cycle = Reg(UInt(64 bits)) init(0) + cycle := cycle + U(dbgCtrEn && ~inhibitCY) + val instret = Reg(UInt(64 bits)) init(0) + when(pipeline.stages.last.arbitration.isFiring) { + instret := instret + U(dbgCtrEn && ~inhibitIR) + } + + val counter = Array.fill(NumOfCounters){Reg(UInt(64 bits)) init(0)} + val events = Array.fill(NumOfCounters){Reg(UInt(xlen bits)) init(0)} + + var customCounters = new Area { + val increment = Array.fill(NumOfCounters){Bool} + + for (ii <- 0 until NumOfCounters) { + counter(ii) := counter(ii) + U(dbgCtrEn && ~inhibit(ii) && increment(ii)) + + increment(ii) := False + + for (event <- eventType) { + when (event._1 =/= U(0, xlen bits) && event._1 === events(ii)) { + increment(ii) := event._2 + } + } + } + } + + val expose = new Area { + import Priv._ + // inhibit + csrSrv.during(MCOUNTINHIBIT){ when (~prvSrv.isMachine()) {csrSrv.forceFailCsr()} } + csrSrv.rw(MCOUNTINHIBIT, 0 -> inhibitCY, 2 -> inhibitIR, 3 -> inhibit) + + // enable + mcounterenAccess(csrSrv, prvSrv, MCOUNTEREN, menable, S -> False, U -> False) + scounterenAccess(csrSrv, prvSrv, SCOUNTEREN, senable, U -> False) + + // custom counters + for (ii <- 0 until NumOfCounters) { + ucounterAccess(csrSrv, prvSrv, UCOUNTER + ii, counter(ii)(31 downto 0), + S -> menable(3 + ii), + U -> (if (prvSrv.hasSupervisor()) senable(3 + ii) else True), + U -> menable(3 + ii) + ) + ucounterAccess(csrSrv, prvSrv, UCOUNTERH + ii, counter(ii)(63 downto 32), + S -> menable(3 + ii), + U -> (if (prvSrv.hasSupervisor()) senable(3 + ii) else True), + U -> menable(3 + ii) + ) + + mcounterAccess(csrSrv, prvSrv, MCOUNTER + ii, counter(ii)(31 downto 0), S -> False, U -> False) + mcounterAccess(csrSrv, prvSrv, MCOUNTERH + ii, counter(ii)(63 downto 32), S -> False, U -> False) + meventAccess(csrSrv, prvSrv, MEVENT + ii, events(ii), S -> False, U -> False) + } + + // fixed counters + ucycleAccess(csrSrv, prvSrv, UCYCLE, cycle(31 downto 0), + S -> menable(0), + U -> (if (prvSrv.hasSupervisor()) senable(0) else True), + U -> menable(0) + ) + ucycleAccess(csrSrv, prvSrv, UCYCLEH, cycle(63 downto 32), + S -> menable(0), + U -> (if (prvSrv.hasSupervisor()) senable(0) else True), + U -> menable(0) + ) + + mcycleAccess(csrSrv, prvSrv, MCYCLE, cycle(31 downto 0), S -> False, U -> False) + mcycleAccess(csrSrv, prvSrv, MCYCLEH, cycle(63 downto 32), S -> False, U -> False) + + if(utimeAccess != CsrAccess.NONE) { + utimeAccess(csrSrv, prvSrv, UTIME, time(31 downto 0), + S -> menable(1), + U -> (if (prvSrv.hasSupervisor()) senable(1) else True), + U -> menable(1) + ) + utimeAccess(csrSrv, prvSrv, UTIMEH, time(63 downto 32), + S -> menable(1), + U -> (if (prvSrv.hasSupervisor()) senable(1) else True), + U -> menable(1) + ) + } + + uinstretAccess(csrSrv, prvSrv, UINSTRET, instret(31 downto 0), + S -> menable(2), + U -> (if (prvSrv.hasSupervisor()) senable(2) else True), + U -> menable(2) + ) + uinstretAccess(csrSrv, prvSrv, UINSTRETH, instret(63 downto 32), + S -> menable(2), + U -> (if (prvSrv.hasSupervisor()) senable(2) else True), + U -> menable(2) + ) + + minstretAccess(csrSrv, prvSrv, MINSTRET, instret(31 downto 0), S -> False, U -> False) + minstretAccess(csrSrv, prvSrv, MINSTRETH, instret(63 downto 32), S -> False, U -> False) + } + } + } +} diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index 7e35c94c..4e61236c 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -61,10 +61,6 @@ case class CsrPluginConfig( mscratchGen : Boolean, mcauseAccess : CsrAccess, mbadaddrAccess : CsrAccess, - mcycleAccess : CsrAccess, - minstretAccess : CsrAccess, - ucycleAccess : CsrAccess, - uinstretAccess : CsrAccess = CsrAccess.NONE, wfiGenAsWait : Boolean, ecallGen : Boolean, xtvecModeGen : Boolean = false, @@ -78,10 +74,7 @@ case class CsrPluginConfig( 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, @@ -94,9 +87,14 @@ case class CsrPluginConfig( exportPrivilege : Boolean = false, var withPrivilegedDebug : Boolean = false, //For the official RISC-V debug spec implementation var debugTriggers : Int = 2, - var debugTriggersLsu : Boolean = false + var debugTriggersLsu : Boolean = false, + // these options only have effect, when no CounterService is present in Pipeline + utimeAccess : CsrAccess = CsrAccess.NONE, + mcycleAccess : CsrAccess = CsrAccess.NONE, + minstretAccess : CsrAccess = CsrAccess.NONE, + ucycleAccess : CsrAccess = CsrAccess.NONE, + uinstretAccess : CsrAccess = CsrAccess.NONE ){ - assert(!ucycleAccess.canWrite) def privilegeGen = userGen || supervisorGen || withPrivilegedDebug def noException = this.copy(ecallGen = false, ebreakGen = false, catchIllegalAccess = false) def noExceptionButEcall = this.copy(ecallGen = true, ebreakGen = false, catchIllegalAccess = false) @@ -122,9 +120,6 @@ object CsrPluginConfig{ 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, @@ -138,8 +133,6 @@ object CsrPluginConfig{ 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 :( @@ -161,10 +154,6 @@ object CsrPluginConfig{ 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, @@ -178,8 +167,6 @@ object CsrPluginConfig{ 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, @@ -202,10 +189,6 @@ object CsrPluginConfig{ 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, @@ -219,13 +202,15 @@ object CsrPluginConfig{ 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 + deterministicInteruptionEntry = false, + mcycleAccess = CsrAccess.READ_WRITE, + minstretAccess = CsrAccess.READ_WRITE, + ucycleAccess = CsrAccess.READ_ONLY, + uinstretAccess = CsrAccess.READ_ONLY ) def all(mtvecInit : BigInt) : CsrPluginConfig = CsrPluginConfig( @@ -242,10 +227,10 @@ object CsrPluginConfig{ mscratchGen = true, mcauseAccess = CsrAccess.READ_WRITE, mbadaddrAccess = CsrAccess.READ_WRITE, - mcycleAccess = CsrAccess.READ_WRITE, - minstretAccess = CsrAccess.READ_WRITE, ecallGen = true, wfiGenAsWait = true, + mcycleAccess = CsrAccess.READ_WRITE, + minstretAccess = CsrAccess.READ_WRITE, ucycleAccess = CsrAccess.READ_ONLY, uinstretAccess = CsrAccess.READ_ONLY ) @@ -264,23 +249,21 @@ object CsrPluginConfig{ 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 + midelegAccess = CsrAccess.READ_WRITE, + mcycleAccess = CsrAccess.READ_WRITE, + minstretAccess = CsrAccess.READ_WRITE, + ucycleAccess = CsrAccess.READ_ONLY, + uinstretAccess = CsrAccess.READ_ONLY ) def small(mtvecInit : BigInt) = CsrPluginConfig( @@ -297,12 +280,8 @@ object CsrPluginConfig{ 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 + wfiGenAsWait = false ) def smallest(mtvecInit : BigInt) = CsrPluginConfig( @@ -319,12 +298,8 @@ object CsrPluginConfig{ 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 + wfiGenAsWait = false ) def secure(mtvecInit : BigInt) = CsrPluginConfig( @@ -341,15 +316,15 @@ object CsrPluginConfig{ 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 + midelegAccess = CsrAccess.READ_WRITE, + mcycleAccess = CsrAccess.READ_WRITE, + minstretAccess = CsrAccess.READ_WRITE, + ucycleAccess = CsrAccess.READ_ONLY, + uinstretAccess = CsrAccess.READ_ONLY ) } @@ -445,6 +420,29 @@ trait CsrInterface{ def inDebugMode() : Bool } +trait DebugService{ + case class DebugState(xlen : Int) extends Bundle{ + val dpc = UInt(xlen bits) + val dcsr = new Bundle{ + val prv = UInt(2 bits) + val step = Bool + val nmip = Bool + val mprven = Bool + val cause = UInt(3 bits) + val stoptime = Bool + val stopcount = Bool + val stepie = Bool + val ebreaku = Bool + val ebreaks = Bool + val ebreakm = Bool + val xdebugver = UInt(4 bits) + } + } + + def hasDebugMode() : Boolean + def inDebugMode() : Bool + def debugState() : DebugState +} trait IContextSwitching{ def isContextSwitching : Bool @@ -453,7 +451,7 @@ 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 with VexRiscvRegressionArg { +class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with ExceptionService with PrivilegeService with DebugService with InterruptionInhibitor with ExceptionInhibitor with IContextSwitching with CsrInterface with IWake with VexRiscvRegressionArg { import config._ import CsrAccess._ @@ -492,13 +490,20 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep var debugBus : DebugHartBus = null var debugMode : Bool = null + var debugStateB : DebugState = null var injectionPort : Stream[Bits] = null override def askWake(): Unit = thirdPartyWake := True override def isContextSwitching = contextSwitching - override def inDebugMode(): Bool = if(withPrivilegedDebug) debugMode else False + override def hasDebugMode(): Boolean = withPrivilegedDebug + override def inDebugMode(): Bool = if(hasDebugMode()) debugMode else False + override def debugState(): DebugState = { + val state = new DebugState(xlen) + state := debugStateB + state + } object EnvCtrlEnum extends SpinalEnum(binarySequential){ val NONE, XRET = newElement() @@ -649,7 +654,7 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep 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(!pipeline.serviceExist(classOf[CounterService]) && utimeAccess != CsrAccess.NONE) utime = in UInt(64 bits) setName("utime") if(supervisorGen) { decoderService.addDefault(RESCHEDULE_NEXT, False) @@ -673,6 +678,8 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep override def isUser() : Bool = privilege === 0 override def isSupervisor(): Bool = privilege === 1 override def isMachine(): Bool = privilege === 3 + override def hasUser() : Boolean = userGen + override def hasSupervisor() : Boolean = supervisorGen override def forceMachine(): Unit = forceMachineWire := True override def build(pipeline: VexRiscv): Unit = { @@ -1059,6 +1066,38 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep } }) + // make debug state accessible + debugStateB = new DebugState(xlen) + if (hasDebugMode()) { + debugStateB.dcsr.xdebugver := debug.dcsr.xdebugver + debugStateB.dcsr.ebreakm := debug.dcsr.ebreakm + debugStateB.dcsr.ebreaks := debug.dcsr.ebreaks + debugStateB.dcsr.ebreaku := debug.dcsr.ebreaku + debugStateB.dcsr.stepie := debug.dcsr.stepie + debugStateB.dcsr.stopcount := debug.dcsr.stopcount + debugStateB.dcsr.stoptime := debug.dcsr.stoptime + debugStateB.dcsr.cause := debug.dcsr.cause + debugStateB.dcsr.mprven := debug.dcsr.mprven + debugStateB.dcsr.nmip := debug.dcsr.nmip + debugStateB.dcsr.step := debug.dcsr.step + debugStateB.dcsr.prv := debug.dcsr.prv + debugStateB.dpc := debug.dpc + } else { + debugStateB.dcsr.xdebugver := 0 + debugStateB.dcsr.ebreakm := False + debugStateB.dcsr.ebreaks := False + debugStateB.dcsr.ebreaku := False + debugStateB.dcsr.stepie := False + debugStateB.dcsr.stopcount := False + debugStateB.dcsr.stoptime := False + debugStateB.dcsr.cause := 0 + debugStateB.dcsr.mprven := False + debugStateB.dcsr.nmip := False + debugStateB.dcsr.step := False + debugStateB.dcsr.prv := 0 + debugStateB.dpc := 0 + } + def guardedWrite(csrId : Int, bitRange: Range, allowed : Seq[Int], target : Bits) = { onWrite(csrId){ when(allowed.map(writeData()(bitRange) === _).orR){ @@ -1098,9 +1137,6 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep 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, BP, LAM, LAF, SAM, SAF, EU, ES, IPF, LPF, SPF = RegInit(False) @@ -1144,49 +1180,64 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep 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)) + // legacy counter/timer generation, when no counter-plugin exists + var counters : Area = (!pipeline.serviceExist(classOf[CounterService])) generate new Area { + SpinalInfo("No CounterService was found. Generating legacy counters and timer") + val mcycle = Reg(UInt(64 bits)) init(0) + val minstret = Reg(UInt(64 bits)) init(0) + + 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)) + + 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)) + } + + class Xcounteren(csrId : Int) extends Area{ + val IR,TM,CY = RegInit(True) //For backward compatibility + if(ucycleAccess != CsrAccess.NONE) rw(csrId, 0 -> CY) + if(utimeAccess != CsrAccess.NONE) rw(csrId, 1 -> TM) + if(uinstretAccess != CsrAccess.NONE) rw(csrId, 2 -> IR) + } - if(utimeAccess != CsrAccess.NONE) { - utimeAccess(CSR.UTIME, utime(31 downto 0)) - utimeAccess(CSR.UTIMEH, utime(63 downto 32)) - } + def xcounterChecks(access : CsrAccess, csrId : Int, enable : Xcounteren => Bool) = { + if(access != CsrAccess.NONE) during(csrId){ + if(userGen) when(privilege < 3 && !enable(mcounteren)){forceFailCsr()} + if(supervisorGen) when(privilege < 1 && !enable(scounteren)){forceFailCsr()} + } + } - class Xcounteren(csrId : Int) extends Area{ - val IR,TM,CY = RegInit(True) //For backward compatibility - if(ucycleAccess != CsrAccess.NONE) rw(csrId, 0 -> CY) - if(utimeAccess != CsrAccess.NONE) rw(csrId, 1 -> TM) - if(uinstretAccess != CsrAccess.NONE) rw(csrId, 2 -> IR) - } - def xcounterChecks(access : CsrAccess, csrId : Int, enable : Xcounteren => Bool) = { - if(access != CsrAccess.NONE) during(csrId){ - if(userGen) when(privilege < 3 && !enable(mcounteren)){ forceFailCsr() } - if(supervisorGen) when(privilege < 1 && !enable(scounteren)){ forceFailCsr() } + val mcounteren = userGen generate new Xcounteren(CSR.MCOUNTEREN) + val scounteren = supervisorGen generate new Xcounteren(CSR.SCOUNTEREN) + + xcounterChecks(ucycleAccess , CSR.UCYCLE , _.CY) + xcounterChecks(ucycleAccess , CSR.UCYCLEH , _.CY) + xcounterChecks(utimeAccess , CSR.UTIME , _.TM) + xcounterChecks(utimeAccess , CSR.UTIMEH , _.TM) + xcounterChecks(uinstretAccess, CSR.UINSTRET , _.IR) + xcounterChecks(uinstretAccess, CSR.UINSTRETH, _.IR) + + // update + mcycle := mcycle + (if(hasDebugMode()) U(!debugMode || !debug.dcsr.stopcount) else U(1)) + when(pipeline.stages.last.arbitration.isFiring) { + minstret := minstret + (if(hasDebugMode()) U(!debugMode || !debug.dcsr.stopcount) else U(1)) } } - val mcounteren = userGen generate new Xcounteren(CSR.MCOUNTEREN) - val scounteren = supervisorGen generate new Xcounteren(CSR.SCOUNTEREN) - xcounterChecks(ucycleAccess , CSR.UCYCLE , _.CY) - xcounterChecks(ucycleAccess , CSR.UCYCLEH , _.CY) - xcounterChecks(utimeAccess , CSR.UTIME , _.TM) - xcounterChecks(utimeAccess , CSR.UTIMEH , _.TM) - xcounterChecks(uinstretAccess, CSR.UINSTRET , _.IR) - xcounterChecks(uinstretAccess, CSR.UINSTRETH, _.IR) - pipeline(MPP) := mstatus.MPP } @@ -1272,13 +1323,6 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep val beforeLastStage = pipeline.stages(pipeline.stages.size-2) val stagesFromExecute = pipeline.stages.dropWhile(_ != execute) - //Manage counters - mcycle := mcycle + (if(withPrivilegedDebug) U(!debugMode || !debug.dcsr.stopcount) else U(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)))