Skip to content

Commit

Permalink
Merge pull request #388 from jjjt-git/perf_counters
Browse files Browse the repository at this point in the history
Perf counters
  • Loading branch information
Dolu1990 authored Mar 26, 2024
2 parents c250114 + 5148bba commit 0e4032c
Show file tree
Hide file tree
Showing 8 changed files with 433 additions and 101 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
- [YamlPlugin](#yamlplugin)
- [FpuPlugin](#fpuplugin)
- [AesPlugin](#aesplugin)
- [CounterPlugin](#counterplugin)



Expand Down Expand Up @@ -1363,3 +1364,21 @@ It was also ported on libressl via the following patch :
<https://github.com/SpinalHDL/buildroot-spinal-saxon/blob/main/patches/libressl/0000-vexriscv-aes.patch>

Speed up of 4 was observed in libressl running in linux. <https://github.com/SpinalHDL/SaxonSoc/pull/53#issuecomment-730133020>

#### 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.
6 changes: 6 additions & 0 deletions src/main/scala/vexriscv/Riscv.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
6 changes: 5 additions & 1 deletion src/main/scala/vexriscv/Services.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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 = {}
}

Expand Down Expand Up @@ -143,4 +147,4 @@ class CacheReport {

class DebugReport {
@BeanProperty var hardwareBreakpointCount = 0
}
}
1 change: 1 addition & 0 deletions src/main/scala/vexriscv/demo/GenFull.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ object GenFull extends App{
earlyBranch = false,
catchAddressMisaligned = true
),
new CounterPlugin(CounterPluginConfig()),
new YamlPlugin("cpu0.yaml")
)
)
Expand Down
19 changes: 17 additions & 2 deletions src/main/scala/vexriscv/demo/VexRiscvAxi4LinuxPlicClint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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")
)
)
Expand Down Expand Up @@ -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 _ =>
}
Expand Down
17 changes: 15 additions & 2 deletions src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 _ =>
}
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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")
)
)
Expand Down
230 changes: 230 additions & 0 deletions src/main/scala/vexriscv/plugin/CounterPlugin.scala
Original file line number Diff line number Diff line change
@@ -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)
}
}
}
}
Loading

0 comments on commit 0e4032c

Please sign in to comment.