From fce8f65839899a9125ada3980d370d94b9153c56 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Tue, 19 Nov 2024 15:13:12 +0100 Subject: [PATCH 01/10] Add pmp support (WIP) --- ext/NaxSoftware | 2 +- ext/SpinalHDL | 2 +- ext/rvls | 2 +- src/main/scala/vexiiriscv/Param.scala | 37 +++- .../vexiiriscv/execute/lsu/LsuPlugin.scala | 25 ++- .../vexiiriscv/fetch/FetchL1Plugin.scala | 18 +- .../scala/vexiiriscv/memory/PmpPlugin.scala | 191 ++++++++++++++++++ .../scala/vexiiriscv/memory/Service.scala | 17 ++ src/main/scala/vexiiriscv/riscv/Const.scala | 3 + .../vexiiriscv/test/VexiiRiscvProbe.scala | 3 +- 10 files changed, 281 insertions(+), 19 deletions(-) create mode 100644 src/main/scala/vexiiriscv/memory/PmpPlugin.scala diff --git a/ext/NaxSoftware b/ext/NaxSoftware index 6ff56798..53c31b59 160000 --- a/ext/NaxSoftware +++ b/ext/NaxSoftware @@ -1 +1 @@ -Subproject commit 6ff567988b3e7f1e2a59b5f28f81a8519f22e0e0 +Subproject commit 53c31b5944a1fb07bbfc876b5b9888a09e5f8068 diff --git a/ext/SpinalHDL b/ext/SpinalHDL index 6eac7c63..33cd8368 160000 --- a/ext/SpinalHDL +++ b/ext/SpinalHDL @@ -1 +1 @@ -Subproject commit 6eac7c6372f72e0d84b5fb0381190cb1250b774c +Subproject commit 33cd8368c6b8a7ab6c6f8b60682668979556c954 diff --git a/ext/rvls b/ext/rvls index 12840c98..b5882b90 160000 --- a/ext/rvls +++ b/ext/rvls @@ -1 +1 @@ -Subproject commit 12840c98eb79bc43abdc40662f123d66f75b9315 +Subproject commit b5882b905ab8bed26f67891c98e41fc5f69a5b03 diff --git a/src/main/scala/vexiiriscv/Param.scala b/src/main/scala/vexiiriscv/Param.scala index 5c841877..a1ed8b50 100644 --- a/src/main/scala/vexiiriscv/Param.scala +++ b/src/main/scala/vexiiriscv/Param.scala @@ -13,7 +13,7 @@ import vexiiriscv.decode.DecoderPlugin import vexiiriscv.execute._ import vexiiriscv.execute.lsu._ import vexiiriscv.fetch.{FetchCachelessPlugin, FetchL1Plugin, PrefetcherNextLinePlugin} -import vexiiriscv.memory.{MmuPortParameter, MmuSpec, MmuStorageLevel, MmuStorageParameter} +import vexiiriscv.memory.{MmuPortParameter, MmuSpec, MmuStorageLevel, MmuStorageParameter, PmpParam, PmpPlugin, PmpPortParameter} import vexiiriscv.misc._ import vexiiriscv.prediction.{LearnCmd, LearnPlugin} import vexiiriscv.riscv.{FloatRegFile, IntRegFile} @@ -165,6 +165,29 @@ class ParamSimple(){ priority = 0 ) + var pmpParam = new PmpParam( + pmpSize = 0, + granularity = 4 + ) + + var fetchPmpParam = new PmpPortParameter( + napotMatchAt = 1, + napotHitsAt = 1, + torCmpAt = 1, + torHitsAt = 2, + hitsAt = 2, + rspAt = 2 + ) + + var lsuPmpParam = new PmpPortParameter( + napotMatchAt = 1, + napotHitsAt = 1, + torCmpAt = 1, + torHitsAt = 2, + hitsAt = 2, + rspAt = 2 + ) + var lsuTsp = MmuStorageParameter( levels = List( MmuStorageLevel( @@ -524,9 +547,9 @@ class ParamSimple(){ opt[Unit]("with-boot-mem-init") action { (v, c) => bootMemClear = true } opt[Int]("physical-width") action { (v, c) => physicalWidth = v } opt[Unit]("mul-keep-src") action { (v, c) => mulKeepSrc = true } - opt[Unit]("mmu-sync-read") action { (v, c) => - withMmuSyncRead() - } + opt[Unit]("mmu-sync-read") action { (v, c) => withMmuSyncRead() } + opt[Int]("pmp-size") action { (v, c) => pmpParam.pmpSize = v } + opt[Int]("pmp-granularity") action { (v, c) => pmpParam.granularity = v } } def plugins(hartId : Int = 0) = pluginsArea(hartId).plugins @@ -545,6 +568,8 @@ class ParamSimple(){ ) } + plugins += new PmpPlugin(pmpParam) + plugins += new misc.PipelineBuilderPlugin() plugins += new schedule.ReschedulePlugin() @@ -615,7 +640,8 @@ class ParamSimple(){ translationPortParameter = withMmu match { case false => null case true => fetchTpp - } + }, + pmpPortParameter = fetchPmpParam ) fetchL1Prefetch match { @@ -701,6 +727,7 @@ class ParamSimple(){ storeBufferSlots = lsuStoreBufferSlots, storeBufferOps = lsuStoreBufferOps, softwarePrefetch = lsuSoftwarePrefetch, + pmpPortParameter = fetchPmpParam, translationStorageParameter = lsuTsp, translationPortParameter = withMmu match { case false => null diff --git a/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala b/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala index 6b85b900..2397726a 100644 --- a/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala +++ b/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala @@ -12,7 +12,7 @@ import spinal.lib.misc.plugin.FiberPlugin import spinal.lib.system.tag.PmaRegion import vexiiriscv.decode.Decode import vexiiriscv.decode.Decode.UOP -import vexiiriscv.memory.{AddressTranslationPortUsage, AddressTranslationService, DBusAccessService, PmaLoad, PmaLogic, PmaPort, PmaStore} +import vexiiriscv.memory.{AddressTranslationPortUsage, AddressTranslationService, DBusAccessService, PmaLoad, PmaLogic, PmaPort, PmaStore, PmpService} import vexiiriscv.misc.{AddressToMask, LsuTriggerService, PerformanceCounterService, TrapArg, TrapReason, TrapService} import vexiiriscv.riscv.Riscv.{FLEN, LSLEN, XLEN} import vexiiriscv.riscv._ @@ -48,6 +48,7 @@ class LsuPlugin(var layer : LaneLayer, var withRva : Boolean, var translationStorageParameter: Any, var translationPortParameter: Any, + var pmpPortParameter : Any, var softwarePrefetch: Boolean, var addressAt: Int = 0, var triggerAt : Int = 1, @@ -95,12 +96,13 @@ class LsuPlugin(var layer : LaneLayer, val ifp = host.find[IntFormatPlugin](_.lane == layer.lane) val srcp = host.find[SrcPlugin](_.layer == layer) val ats = host[AddressTranslationService] + val ps = host[PmpService] val ts = host[TrapService] val ss = host[ScheduleService] val pcs = host.get[PerformanceCounterService] val hp = host.get[PrefetcherPlugin] val fpwbp = host.findOption[WriteBackPlugin](p => p.lane == layer.lane && p.rf == FloatRegFile) - val buildBefore = retains(elp.pipelineLock, ats.portsLock) + val buildBefore = retains(elp.pipelineLock, ats.portsLock, ps.portsLock) val earlyLock = retains(List(ats.storageLock) ++ pcs.map(_.elaborationLock).toList) val retainer = retains(List(elp.uopLock, srcp.elaborationLock, ifp.elaborationLock, ts.trapLock, ss.elaborationLock) ++ fpwbp.map(_.elaborationLock)) awaitBuild() @@ -437,7 +439,15 @@ class LsuPlugin(var layer : LaneLayer, val tpk = onAddress0.translationPort.keys - + val pmpPort = ps.createPmpPort( + nodes = List.tabulate(ctrlAt+1)(elp.execute(_).down), + physicalAddress = tpk.TRANSLATED, + read = _(LOAD), + write = _(STORE), + execute = _ => False, + portSpec = pmpPortParameter, + storageSpec = null + ) val onAddress1 = new elp.Execute(addressAt+1) { l1.PHYSICAL_ADDRESS := tpk.TRANSLATED @@ -626,7 +636,7 @@ class LsuPlugin(var layer : LaneLayer, trapPort.code.assignDontCare() trapPort.arg.allowOverride() := 0 - val accessFault = (onPma.CACHED_RSP.fault).mux[Bool](io.rsp.valid && io.rsp.error || l1.ATOMIC, l1.FAULT) + val accessFault = (onPma.CACHED_RSP.fault).mux[Bool](io.rsp.valid && io.rsp.error || l1.ATOMIC, l1.FAULT) || pmpPort.ACCESS_FAULT when(accessFault) { lsuTrap := True trapPort.exception := True @@ -748,7 +758,7 @@ class LsuPlugin(var layer : LaneLayer, if(withStoreBuffer) abords += wb.loadHazard || !FROM_WB && fenceTrap.valid skipsWrite += l1.MISS || l1.MISS_UNIQUE - skipsWrite += l1.FAULT + skipsWrite += l1.FAULT || pmpPort.ACCESS_FAULT skipsWrite += preCtrl.MISS_ALIGNED skipsWrite += FROM_LSU && onTrigger.HIT skipsWrite += FROM_PREFETCH @@ -776,12 +786,13 @@ class LsuPlugin(var layer : LaneLayer, } } + //TODO what happen if used for IO access ? val access = dbusAccesses.nonEmpty generate new Area { assert(dbusAccesses.size == 1) val rsp = dbusAccesses.head.rsp rsp.valid := l1.SEL && FROM_ACCESS && !elp.isFreezed() - rsp.data := loadData.RESULT.resized //loadData.RESULT instead of l1.READ_DATA (because it rv32fd - rsp.error := l1.FAULT + rsp.data := loadData.RESULT.resized //loadData.RESULT instead of l1.READ_DATA (because it rv32fd) + rsp.error := l1.FAULT || pmpPort.ACCESS_FAULT rsp.redo := traps.l1Failed rsp.waitSlot := 0 rsp.waitAny := False //TODO diff --git a/src/main/scala/vexiiriscv/fetch/FetchL1Plugin.scala b/src/main/scala/vexiiriscv/fetch/FetchL1Plugin.scala index a1a43663..b7b2ba74 100644 --- a/src/main/scala/vexiiriscv/fetch/FetchL1Plugin.scala +++ b/src/main/scala/vexiiriscv/fetch/FetchL1Plugin.scala @@ -10,7 +10,7 @@ import spinal.lib.bus.amba4.axilite.{AxiLite4Config, AxiLite4ReadOnly} import spinal.lib.bus.bmb.{Bmb, BmbAccessParameter, BmbParameter, BmbSourceParameter} import spinal.lib.bus.tilelink.{M2sSupport, SizeRange} import spinal.lib.misc.Plru -import vexiiriscv.memory.{AddressTranslationPortUsage, AddressTranslationService, PmaLoad, PmaLogic, PmaPort} +import vexiiriscv.memory.{AddressTranslationPortUsage, AddressTranslationService, PmaLoad, PmaLogic, PmaPort, PmpService} import vexiiriscv.misc._ import vexiiriscv._ import vexiiriscv.Global._ @@ -56,6 +56,7 @@ trait LsuL1Service{ class FetchL1Plugin(var translationStorageParameter: Any, var translationPortParameter: Any, + var pmpPortParameter : Any, var memDataWidth : Int, var fetchDataWidth : Int, var setCount: Int, @@ -92,9 +93,10 @@ class FetchL1Plugin(var translationStorageParameter: Any, val rp = host[ReschedulePlugin] val ts = host[TrapService] val ats = host[AddressTranslationService] + val ps = host[PmpService] val pcs = host.get[PerformanceCounterService] val prefetcher = host.get[PrefetcherPlugin].map(_.io) - val buildBefore = retains(pp.elaborationLock, ats.portsLock) + val buildBefore = retains(pp.elaborationLock, ats.portsLock, ps.portsLock) val atsStorageLock = retains(ats.storageLock) val setupLock = retains(List(ts.trapLock, pcp.elaborationLock, rp.elaborationLock) ++ pcs.map(_.elaborationLock).toList) awaitBuild() @@ -365,6 +367,16 @@ class FetchL1Plugin(var translationStorageParameter: Any, ) val tpk = translationPort.keys + val pmpPort = ps.createPmpPort( + nodes = List.tabulate(ctrlAt+1)(pp.fetch(_).down), + physicalAddress = tpk.TRANSLATED, + read = _ => False, + write = _ => False, + execute = _ => True, + portSpec = pmpPortParameter, + storageSpec = null + ) + val cmd = new pp.Fetch(readAt) { val ra1 = pp.fetch(readAt + 1).up val doIt = ra1.ready || (!ra1.valid && prefetcher.nonEmpty.mux(!ra1(PREFETCH), True)) // Better timings than up.isReady, ra1.cancel not necessary as cancel do not collapse bubbles @@ -486,7 +498,7 @@ class FetchL1Plugin(var translationStorageParameter: Any, trapPort.code := TrapReason.REDO } - when(dataAccessFault || pmaPort.rsp.fault) { + when(dataAccessFault || pmaPort.rsp.fault || pmpPort.ACCESS_FAULT) { allowRefill := False trapPort.valid := True trapPort.exception := True diff --git a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala new file mode 100644 index 00000000..f5b8b8b6 --- /dev/null +++ b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala @@ -0,0 +1,191 @@ +package vexiiriscv.memory + +import spinal.core._ +import spinal.lib._ +import spinal.lib.misc.pipeline.{NodeBaseApi, Payload} +import spinal.lib.misc.plugin.FiberPlugin +import vexiiriscv.Global +import vexiiriscv.execute.{CsrAccessPlugin, CsrListFilter, CsrRamService} +import vexiiriscv.misc.{PipelineBuilderPlugin, PrivilegedPlugin, TrapReason} +import vexiiriscv.riscv.{CSR, Riscv} + +import scala.collection.mutable.ArrayBuffer + +case class PmpStorageParameter(slots : Int) +case class PmpPortParameter(var napotMatchAt : Int, + var napotHitsAt : Int, + var torCmpAt : Int, + var torHitsAt : Int, + var hitsAt : Int, + var rspAt : Int){ + +} + +//ref : +// https://github.com/riscv-software-src/riscv-isa-sim/blob/2c67071743d4b55719cee22fdb319df2a0756db7/riscv/mmu.cc#L348 +// https://github.com/riscv-software-src/riscv-isa-sim/blob/2c67071743d4b55719cee22fdb319df2a0756db7/riscv/csrs.cc#L183 +// https://github.com/riscv-software-src/riscv-isa-sim/blob/2c67071743d4b55719cee22fdb319df2a0756db7/riscv/mmu.cc#L348 + +case class PmpParam(var pmpSize : Int, var granularity : Int) +class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ + import p._ + + override def getPmpNum(): Int = p.pmpSize + + assert(isPow2(granularity)) + val granularityWidth = log2Up(granularity) + + case class PortSpec(stages: Seq[NodeBaseApi], + physicalAddress: Payload[UInt], + read: NodeBaseApi => Bool, + write: NodeBaseApi => Bool, + execute: NodeBaseApi => Bool, +// usage : AddressTranslationPortUsage, + pp: PmpPortParameter, +// ss : StorageSpec, + rsp : PmpRsp){ + val permStage = stages(0) + val napotMatchStage = stages(pp.napotMatchAt) + val napotHitsStage = stages(pp.napotHitsAt) + val torCmpStage = stages(pp.torCmpAt) + val torHitsStage = stages(pp.torHitsAt) + val hitsStage = stages(pp.hitsAt) + val rspStage = stages(pp.rspAt) + } + val portSpecs = ArrayBuffer[PortSpec]() + +// case class StorageSpec(p: PmpStorageParameter) extends Nameable +// val storageSpecs = ArrayBuffer[StorageSpec]() + +// override def newStorage(pAny: Any) : Any = { +// val p = pAny.asInstanceOf[PmpStorageParameter] +// storageSpecs.addRet(StorageSpec(p)) +// null +// } + +// override def getStorageId(s: Any): Int = storageSpecs.indexOf(s) +// override def getStorageIdWidth(): Int = { +// storageLock.await() +// log2Up(storageSpecs.size) +// } + + override def createPmpPort(nodes: Seq[NodeBaseApi], + physicalAddress: Payload[UInt], + read: NodeBaseApi => Bool, + write: NodeBaseApi => Bool, + execute: NodeBaseApi => Bool, + portSpec: Any, + storageSpec: Any): PmpRsp = { + val pp = portSpec.asInstanceOf[PmpPortParameter] +// val ss = storageSpec.asInstanceOf[StorageSpec] + portSpecs.addRet( + new PortSpec( + stages = nodes, + physicalAddress = physicalAddress, + read = read, + write = write, + execute = execute, + pp = pp, +// ss = ss, + rsp = new PmpRsp() + ) + ).rsp + } + + val api = during build new Area{ + val fetchTranslationEnable = Bool() + val lsuTranslationEnable = Bool() + } + + val logic = during setup new Area{ + val priv = host[PrivilegedPlugin] + val csr = host[CsrAccessPlugin] + val ram = host[CsrRamService] + + val csrLock = retains(csr.csrLock, ram.csrLock) + val buildBefore = retains(List(host[PipelineBuilderPlugin].elaborationLock)) + + awaitBuild() + + case class Cfg() extends Bundle{ + val kind = Bits(2 bits) + val read, write, execute = Bool() + val locked = Bool() + } + + val pRange = Global.PHYSICAL_WIDTH-1 downto granularityWidth + val entries = for(i <- 0 until pmpSize) yield new Area{ + val address = Reg(UInt(Global.PHYSICAL_WIDTH.get + 2 - granularityWidth bits)) init(0) + val cfg = Reg(Cfg()).initZero() + val cfgNext = CombInit(cfg) + when(!cfg.locked) { + cfg := cfgNext + cfg.write clearWhen (!cfgNext.read) + } + + + import cfg._ + + val isNapot = RegNext(kind(1)) //TODO + val isTor = RegNext(kind === 1) //TODO + + val napot = RegNext(Napot(address.asBits).orMask(!kind(0))) + val mask = napot + + val (cfgId, cfgOffset) = Riscv.XLEN.get match{ + case 32 => (i/4, i%4*8) + case 64 => (i/8*2, i%8/8) + } + + csr.writeCancel(CSR.PMPADDR + i, locked) + csr.readWrite(address, CSR.PMPADDR + i, Math.max(0, granularityWidth - 2)) + csr.read(CSR.PMPCFG + cfgId, 0+cfgOffset -> read, 1+cfgOffset -> write, 2+cfgOffset -> execute, 3+cfgOffset -> kind, 7+cfgOffset -> locked) + csr.write(CSR.PMPCFG + cfgId, 0+cfgOffset -> cfgNext.read, 1+cfgOffset -> cfgNext.write, 2+cfgOffset -> cfgNext.execute, 3+cfgOffset -> cfgNext.kind, 7+cfgOffset -> cfgNext.locked) + //TODO use ram + } + + csr.onDecode(CsrListFilter((0 to 63).map(_ + CSR.PMPADDR) ++ (0 to 15).filter(i => Riscv.XLEN.get == 32 || (i % 2) == 0).map(_ + CSR.PMPCFG))) { + when(csr.bus.decode.write) { + csr.bus.decode.doTrap(TrapReason.NEXT) + } + } + + assert(Global.HART_COUNT.get == 1) + + csrLock.release() + portsLock.await() + + val isMachine = priv.isMachine(0) + val ports = for(ps <- portSpecs) yield new Composite(ps.rsp, "logic", false){ + import ps._ + val torCmpAddress = torCmpStage(ps.physicalAddress) >> granularityWidth + val TOR_SMALLER = new Array[Payload[Bool]](p.pmpSize) + val onEntries = for((e, id) <- entries.zipWithIndex) yield new Area{ + val NAPOT_MATCH = napotMatchStage.insert(((e.address << granularityWidth)(pRange) ^ napotMatchStage(ps.physicalAddress)(pRange)).asBits & e.mask.resized) + val NAPOT_HIT = napotHitsStage.insert(napotHitsStage(NAPOT_MATCH) === 0) + val TOR_BIGGER = torCmpStage.insert(e.address > torCmpAddress) + val TOR_HIT = { import torHitsStage._ ; insert(TOR_BIGGER && (id != 0).mux(!TOR_SMALLER(id-1), True))} + val HIT_ANY = { import hitsStage._ ; insert(e.isNapot && NAPOT_HIT || e.isTor && TOR_HIT) } + + val bypass = False // isMachine && !e.locked + val normalRwx = (!ps.read(permStage) || e.cfg.read) && (!ps.write(permStage) || e.cfg.write) && (!ps.execute(permStage) || e.cfg.execute) + val PERM_OK = { import permStage._ ; insert(bypass || normalRwx) } //TODO check rv64 fit in + + TOR_SMALLER(id) = TOR_BIGGER + } + + val onCtrl = new Area{ + import ps.rspStage._ + + val hits = Cat(onEntries.map(e => ps.rspStage(e.HIT_ANY))) + val oh = OHMasking.firstV2(hits) + val reader = onEntries.reader(oh) + + rsp.ACCESS_FAULT := !isMachine && !(reader(e => ps.rspStage(e.PERM_OK))) + } + } + + + buildBefore.release() + } +} \ No newline at end of file diff --git a/src/main/scala/vexiiriscv/memory/Service.scala b/src/main/scala/vexiiriscv/memory/Service.scala index afb7fa56..b56ef027 100644 --- a/src/main/scala/vexiiriscv/memory/Service.scala +++ b/src/main/scala/vexiiriscv/memory/Service.scala @@ -79,6 +79,23 @@ class AddressTranslationRsp(s : AddressTranslationService, val wayCount : Int) e } } +trait PmpService extends Area { + val portsLock = Retainer() + def createPmpPort(nodes: Seq[NodeBaseApi], + physicalAddress: Payload[UInt], + read: NodeBaseApi => Bool, + write: NodeBaseApi => Bool, + execute: NodeBaseApi => Bool, + portSpec: Any, + storageSpec: Any): PmpRsp + def getPmpNum() : Int +} + +class PmpRsp extends Area{ + val ACCESS_FAULT = Payload(Bool()) +} + + trait DBusAccessService{ def accessRefillCount : Int diff --git a/src/main/scala/vexiiriscv/riscv/Const.scala b/src/main/scala/vexiiriscv/riscv/Const.scala index 5fe6fe31..0a78256f 100644 --- a/src/main/scala/vexiiriscv/riscv/Const.scala +++ b/src/main/scala/vexiiriscv/riscv/Const.scala @@ -111,6 +111,9 @@ object CSR { val MCOUNTINHIBIT = 0x320 def MHPMEVENT0H = 0x720 // MRW Machine instructions-retired counter. + val PMPCFG = 0x3a0 + val PMPADDR = 0x3b0 + val SSTATUS = 0x100 val SIE = 0x104 val STVEC = 0x105 diff --git a/src/main/scala/vexiiriscv/test/VexiiRiscvProbe.scala b/src/main/scala/vexiiriscv/test/VexiiRiscvProbe.scala index 6f773058..4517a9ca 100644 --- a/src/main/scala/vexiiriscv/test/VexiiRiscvProbe.scala +++ b/src/main/scala/vexiiriscv/test/VexiiRiscvProbe.scala @@ -10,6 +10,7 @@ import vexiiriscv._ import vexiiriscv.decode.Decode import vexiiriscv.execute.lsu._ import vexiiriscv.fetch.FetchPipelinePlugin +import vexiiriscv.memory.{PmpPlugin, PmpService} import vexiiriscv.misc.PrivilegedPlugin import vexiiriscv.riscv.FloatRegFile //import vexiiriscv.execute.LsuCachelessPlugin @@ -185,7 +186,7 @@ class VexiiRiscvProbe(cpu : VexiiRiscv, kb : Option[konata.Backend], var withRvl if (get(Riscv.RVZbc)) isa += "_zbc" if (get(Riscv.RVZbs)) isa += "_zbs" tracer.newCpuMemoryView(hartId, 16, 1 << Decode.STORE_ID_WIDTH) - tracer.newCpu(hartId, isa, csrp, 63, hartId) + tracer.newCpu(hartId, isa, csrp, 63, cpu.host.get[PmpService].map(_.getPmpNum).getOrElse(0), hartId) val pc = if(xlen == 32) 0x80000000l else 0x80000000l tracer.setPc(hartId, pc) } From d58e9cdc64fbf3d64b99277086dcd11200b27171 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 20 Nov 2024 16:44:13 +0100 Subject: [PATCH 02/10] pmp wip --- .../scala/vexiiriscv/memory/PmpPlugin.scala | 127 ++++++++++-------- 1 file changed, 70 insertions(+), 57 deletions(-) diff --git a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala index f5b8b8b6..c6ce5880 100644 --- a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala +++ b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala @@ -11,22 +11,27 @@ import vexiiriscv.riscv.{CSR, Riscv} import scala.collection.mutable.ArrayBuffer +// Here are some good Spike implementation references : +// - https://github.com/riscv-software-src/riscv-isa-sim/blob/2c67071743d4b55719cee22fdb319df2a0756db7/riscv/mmu.cc#L348 +// - https://github.com/riscv-software-src/riscv-isa-sim/blob/2c67071743d4b55719cee22fdb319df2a0756db7/riscv/csrs.cc#L183 +// - https://github.com/riscv-software-src/riscv-isa-sim/blob/2c67071743d4b55719cee22fdb319df2a0756db7/riscv/mmu.cc#L348 + case class PmpStorageParameter(slots : Int) case class PmpPortParameter(var napotMatchAt : Int, var napotHitsAt : Int, var torCmpAt : Int, var torHitsAt : Int, var hitsAt : Int, - var rspAt : Int){ + var rspAt : Int) -} -//ref : -// https://github.com/riscv-software-src/riscv-isa-sim/blob/2c67071743d4b55719cee22fdb319df2a0756db7/riscv/mmu.cc#L348 -// https://github.com/riscv-software-src/riscv-isa-sim/blob/2c67071743d4b55719cee22fdb319df2a0756db7/riscv/csrs.cc#L183 -// https://github.com/riscv-software-src/riscv-isa-sim/blob/2c67071743d4b55719cee22fdb319df2a0756db7/riscv/mmu.cc#L348 +case class PmpParam( + var pmpSize: Int, + var granularity: Int, + var withTor: Boolean = true, + var withNapot: Boolean = true +) -case class PmpParam(var pmpSize : Int, var granularity : Int) class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ import p._ @@ -37,12 +42,11 @@ class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ case class PortSpec(stages: Seq[NodeBaseApi], physicalAddress: Payload[UInt], + forceCheck: NodeBaseApi => Bool, read: NodeBaseApi => Bool, write: NodeBaseApi => Bool, execute: NodeBaseApi => Bool, -// usage : AddressTranslationPortUsage, pp: PmpPortParameter, -// ss : StorageSpec, rsp : PmpRsp){ val permStage = stages(0) val napotMatchStage = stages(pp.napotMatchAt) @@ -54,49 +58,29 @@ class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ } val portSpecs = ArrayBuffer[PortSpec]() -// case class StorageSpec(p: PmpStorageParameter) extends Nameable -// val storageSpecs = ArrayBuffer[StorageSpec]() - -// override def newStorage(pAny: Any) : Any = { -// val p = pAny.asInstanceOf[PmpStorageParameter] -// storageSpecs.addRet(StorageSpec(p)) -// null -// } - -// override def getStorageId(s: Any): Int = storageSpecs.indexOf(s) -// override def getStorageIdWidth(): Int = { -// storageLock.await() -// log2Up(storageSpecs.size) -// } - override def createPmpPort(nodes: Seq[NodeBaseApi], physicalAddress: Payload[UInt], + forceCheck: NodeBaseApi => Bool, read: NodeBaseApi => Bool, write: NodeBaseApi => Bool, execute: NodeBaseApi => Bool, portSpec: Any, storageSpec: Any): PmpRsp = { val pp = portSpec.asInstanceOf[PmpPortParameter] -// val ss = storageSpec.asInstanceOf[StorageSpec] portSpecs.addRet( new PortSpec( stages = nodes, physicalAddress = physicalAddress, + forceCheck = forceCheck, read = read, write = write, execute = execute, pp = pp, -// ss = ss, rsp = new PmpRsp() ) ).rsp } - val api = during build new Area{ - val fetchTranslationEnable = Bool() - val lsuTranslationEnable = Bool() - } - val logic = during setup new Area{ val priv = host[PrivilegedPlugin] val csr = host[CsrAccessPlugin] @@ -107,6 +91,8 @@ class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ awaitBuild() + if(Riscv.XLEN.get == 64) assert(granularity >= 8) + case class Cfg() extends Bundle{ val kind = Bits(2 bits) val read, write, execute = Bool() @@ -115,36 +101,54 @@ class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ val pRange = Global.PHYSICAL_WIDTH-1 downto granularityWidth val entries = for(i <- 0 until pmpSize) yield new Area{ - val address = Reg(UInt(Global.PHYSICAL_WIDTH.get + 2 - granularityWidth bits)) init(0) - val cfg = Reg(Cfg()).initZero() + val isLocked = Bool() + val address = Reg(UInt(Global.PHYSICAL_WIDTH.get - granularityWidth bits)) + val cfg = Reg(Cfg()) val cfgNext = CombInit(cfg) - when(!cfg.locked) { + when(!isLocked) { cfg := cfgNext cfg.write clearWhen (!cfgNext.read) + if(!p.withTor) when(cfgNext.kind === 1) {cfg.kind := 0} } + if(i == 0){ + assert(p.withNapot) + address.init(address.getAllTrue) + cfg.read init(True) + cfg.write init(True) + cfg.execute init(True) + cfg.kind init(3) + cfg.locked init(False) + } else { + address.init(0) + cfg.initZero() + } - import cfg._ - - val isNapot = RegNext(kind(1)) //TODO - val isTor = RegNext(kind === 1) //TODO - val napot = RegNext(Napot(address.asBits).orMask(!kind(0))) - val mask = napot + val isNapot = RegNext(cfgNext.kind(1)) + val isTor = RegNext(cfgNext.kind === 1) + val napot = Napot(address.dropHigh(1)) + if(granularity == 4) when(!cfgNext.kind(0)){ napot.setAll() } + val mask = RegNext(napot) val (cfgId, cfgOffset) = Riscv.XLEN.get match{ case 32 => (i/4, i%4*8) case 64 => (i/8*2, i%8/8) } - csr.writeCancel(CSR.PMPADDR + i, locked) - csr.readWrite(address, CSR.PMPADDR + i, Math.max(0, granularityWidth - 2)) - csr.read(CSR.PMPCFG + cfgId, 0+cfgOffset -> read, 1+cfgOffset -> write, 2+cfgOffset -> execute, 3+cfgOffset -> kind, 7+cfgOffset -> locked) + csr.writeCancel(CSR.PMPADDR + i, cfg.locked) + csr.readWrite(address, CSR.PMPADDR + i, granularityWidth-2) + if(granularity > 4 && p.withNapot) csr.read(U(granularityWidth-2 bits, default -> isNapot), CSR.PMPADDR + i) //WTF + csr.read(CSR.PMPCFG + cfgId, 0+cfgOffset -> cfg.read, 1+cfgOffset -> cfg.write, 2+cfgOffset -> cfg.execute, 3+cfgOffset -> cfg.kind, 7+cfgOffset -> cfg.locked) csr.write(CSR.PMPCFG + cfgId, 0+cfgOffset -> cfgNext.read, 1+cfgOffset -> cfgNext.write, 2+cfgOffset -> cfgNext.execute, 3+cfgOffset -> cfgNext.kind, 7+cfgOffset -> cfgNext.locked) - //TODO use ram } - csr.onDecode(CsrListFilter((0 to 63).map(_ + CSR.PMPADDR) ++ (0 to 15).filter(i => Riscv.XLEN.get == 32 || (i % 2) == 0).map(_ + CSR.PMPCFG))) { + for(i <- 0 until pmpSize; self = entries(i)) { + self.isLocked := self.cfg.locked || (i+1 != pmpSize).mux(entries(i+1).isLocked && entries(i+1).isTor , False) + } + + val allFilter = CsrListFilter((0 to 63).map(_ + CSR.PMPADDR) ++ (0 to 15).filter(i => Riscv.XLEN.get == 32 || (i % 2) == 0).map(_ + CSR.PMPCFG)) + if(p.pmpSize > 0) csr.onDecode(allFilter) { when(csr.bus.decode.write) { csr.bus.decode.doTrap(TrapReason.NEXT) } @@ -156,33 +160,42 @@ class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ portsLock.await() val isMachine = priv.isMachine(0) + val checkInstruction = !isMachine + val checkData = !isMachine || priv.logic.harts(0).m.status.mprv && priv.logic.harts(0).m.status.mpp =/= 3 + val ports = for(ps <- portSpecs) yield new Composite(ps.rsp, "logic", false){ import ps._ + val portCheckData = checkData || ps.forceCheck(permStage) val torCmpAddress = torCmpStage(ps.physicalAddress) >> granularityWidth val TOR_SMALLER = new Array[Payload[Bool]](p.pmpSize) val onEntries = for((e, id) <- entries.zipWithIndex) yield new Area{ - val NAPOT_MATCH = napotMatchStage.insert(((e.address << granularityWidth)(pRange) ^ napotMatchStage(ps.physicalAddress)(pRange)).asBits & e.mask.resized) - val NAPOT_HIT = napotHitsStage.insert(napotHitsStage(NAPOT_MATCH) === 0) - val TOR_BIGGER = torCmpStage.insert(e.address > torCmpAddress) - val TOR_HIT = { import torHitsStage._ ; insert(TOR_BIGGER && (id != 0).mux(!TOR_SMALLER(id-1), True))} - val HIT_ANY = { import hitsStage._ ; insert(e.isNapot && NAPOT_HIT || e.isTor && TOR_HIT) } + val napot = p.withNapot generate new Area { + val MATCH = napotMatchStage.insert(((e.address << granularityWidth)(pRange) ^ napotMatchStage(ps.physicalAddress)(pRange)).asBits & e.mask) + val HIT = napotHitsStage.insert(napotHitsStage(MATCH) === 0) + } + val tor = p.withTor generate new Area{ + val BIGGER = torCmpStage.insert(e.address > torCmpAddress) + val HIT = { import torHitsStage._ ; insert(BIGGER && (id != 0).mux(!TOR_SMALLER(id-1), True))} + TOR_SMALLER(id) = BIGGER + } + val HIT_ANY = { import hitsStage._ ; insert(p.withNapot.mux(e.isNapot && napot.HIT, False) || p.withTor.mux(e.isTor && tor.HIT, False)) } val bypass = False // isMachine && !e.locked - val normalRwx = (!ps.read(permStage) || e.cfg.read) && (!ps.write(permStage) || e.cfg.write) && (!ps.execute(permStage) || e.cfg.execute) - val PERM_OK = { import permStage._ ; insert(bypass || normalRwx) } //TODO check rv64 fit in - - TOR_SMALLER(id) = TOR_BIGGER + val normalRwx = (!ps.read(permStage) || e.cfg.read || !checkInstruction) && + (((!ps.write(permStage) || e.cfg.write) && (!ps.execute(permStage) || e.cfg.execute)) || !portCheckData) + val PERM_OK = { import permStage._ ; insert(bypass || normalRwx) } } + val NEED_HIT = permStage.insert(checkInstruction && ps.execute(permStage) || portCheckData && (ps.read(permStage) || ps.write(permStage))) - val onCtrl = new Area{ + val onCtrl = (p.pmpSize > 0) generate new Area{ import ps.rspStage._ - val hits = Cat(onEntries.map(e => ps.rspStage(e.HIT_ANY))) val oh = OHMasking.firstV2(hits) val reader = onEntries.reader(oh) - rsp.ACCESS_FAULT := !isMachine && !(reader(e => ps.rspStage(e.PERM_OK))) + rsp.ACCESS_FAULT := NEED_HIT && !(reader(e => ps.rspStage(e.PERM_OK))) } + if(p.pmpSize == 0) ps.rspStage(rsp.ACCESS_FAULT) := False } From 669c75943485d3d613493b2a6eb37f922ac49302 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 20 Nov 2024 17:44:51 +0100 Subject: [PATCH 03/10] Move mpriv out of the mmu --- src/main/scala/vexiiriscv/memory/MmuPlugin.scala | 7 +++---- src/main/scala/vexiiriscv/misc/PrivilegedPlugin.scala | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/scala/vexiiriscv/memory/MmuPlugin.scala b/src/main/scala/vexiiriscv/memory/MmuPlugin.scala index d6e83213..6b8782ab 100644 --- a/src/main/scala/vexiiriscv/memory/MmuPlugin.scala +++ b/src/main/scala/vexiiriscv/memory/MmuPlugin.scala @@ -198,11 +198,9 @@ class MmuPlugin(var spec : MmuSpec, val status = new Area{ val mxr = RegInit(False) val sum = RegInit(False) - val mprv = RegInit(False) clearWhen(priv.hart(0).xretAwayFromMachine) } for(offset <- List(CSR.MSTATUS, CSR.SSTATUS)) csr.readWrite(offset, 19 -> status.mxr, 18 -> status.sum) - csr.readWrite(CSR.MSTATUS, 17 -> status.mprv) csr.readWrite(CSR.SATP, satp.modeOffset -> satp.mode/*, 22 -> satp.asid*/, 0 -> satp.ppn) val satpModeWrite = csr.bus.write.bits(satp.modeOffset, satp.modeWidth bits) @@ -256,14 +254,15 @@ class MmuPlugin(var spec : MmuSpec, val isMachine = priv.getPrivilege(0) === U"11" val isSupervisor = priv.getPrivilege(0) === U"01" val isUser = priv.getPrivilege(0) === U"00" + def mprv = priv.logic.harts(0).m.status.mprv api.fetchTranslationEnable := satp.mode === spec.satpMode api.fetchTranslationEnable clearWhen(isMachine) api.lsuTranslationEnable := satp.mode === spec.satpMode - api.lsuTranslationEnable clearWhen(!status.mprv && isMachine) + api.lsuTranslationEnable clearWhen(!mprv && isMachine) when(isMachine) { - api.lsuTranslationEnable clearWhen (!status.mprv || priv.logic.harts(0).m.status.mpp === 3) + api.lsuTranslationEnable clearWhen (!mprv || priv.logic.harts(0).m.status.mpp === 3) } diff --git a/src/main/scala/vexiiriscv/misc/PrivilegedPlugin.scala b/src/main/scala/vexiiriscv/misc/PrivilegedPlugin.scala index 5535d0ac..01f01a6d 100644 --- a/src/main/scala/vexiiriscv/misc/PrivilegedPlugin.scala +++ b/src/main/scala/vexiiriscv/misc/PrivilegedPlugin.scala @@ -523,6 +523,7 @@ class PrivilegedPlugin(val p : PrivilegedParam, val hartIds : Seq[Int]) extends val sd = False val tsr, tvm = p.withSupervisor generate RegInit(False) val tw = p.withUser.mux(RegInit(False), False) + val mprv = RegInit(False) clearWhen(xretAwayFromMachine) if (RVF) { fpuEnable(hartId) setWhen (fs =/= 0) @@ -545,6 +546,7 @@ class PrivilegedPlugin(val p : PrivilegedParam, val hartIds : Seq[Int]) extends } } read(XLEN - 1 -> sd) + readWrite(17 -> mprv) if (withFs) readWrite(13 -> fs) if (p.withUser && XLEN.get == 64) read(32 -> U"10") if (p.withSupervisor && XLEN.get == 64) read(34 -> U"10") From ea9108905922118929f58342a5d7de1ff7031d6e Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 20 Nov 2024 17:45:24 +0100 Subject: [PATCH 04/10] pmp fixes --- src/main/scala/vexiiriscv/Param.scala | 5 +++- .../vexiiriscv/execute/lsu/LsuPlugin.scala | 1 + .../vexiiriscv/fetch/FetchL1Plugin.scala | 1 + .../scala/vexiiriscv/memory/PmpPlugin.scala | 24 ++++++++++--------- .../scala/vexiiriscv/memory/Service.scala | 1 + 5 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/main/scala/vexiiriscv/Param.scala b/src/main/scala/vexiiriscv/Param.scala index a1ed8b50..d07c4073 100644 --- a/src/main/scala/vexiiriscv/Param.scala +++ b/src/main/scala/vexiiriscv/Param.scala @@ -167,7 +167,9 @@ class ParamSimple(){ var pmpParam = new PmpParam( pmpSize = 0, - granularity = 4 + granularity = 4096, + withTor = true, + withNapot = true ) var fetchPmpParam = new PmpPortParameter( @@ -550,6 +552,7 @@ class ParamSimple(){ opt[Unit]("mmu-sync-read") action { (v, c) => withMmuSyncRead() } opt[Int]("pmp-size") action { (v, c) => pmpParam.pmpSize = v } opt[Int]("pmp-granularity") action { (v, c) => pmpParam.granularity = v } + opt[Unit]("pmp-tor-disable") action { (v, c) => pmpParam.withTor = false } } def plugins(hartId : Int = 0) = pluginsArea(hartId).plugins diff --git a/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala b/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala index 2397726a..1cdabc6e 100644 --- a/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala +++ b/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala @@ -442,6 +442,7 @@ class LsuPlugin(var layer : LaneLayer, val pmpPort = ps.createPmpPort( nodes = List.tabulate(ctrlAt+1)(elp.execute(_).down), physicalAddress = tpk.TRANSLATED, + forceCheck = _(FROM_ACCESS), read = _(LOAD), write = _(STORE), execute = _ => False, diff --git a/src/main/scala/vexiiriscv/fetch/FetchL1Plugin.scala b/src/main/scala/vexiiriscv/fetch/FetchL1Plugin.scala index b7b2ba74..58862ddd 100644 --- a/src/main/scala/vexiiriscv/fetch/FetchL1Plugin.scala +++ b/src/main/scala/vexiiriscv/fetch/FetchL1Plugin.scala @@ -370,6 +370,7 @@ class FetchL1Plugin(var translationStorageParameter: Any, val pmpPort = ps.createPmpPort( nodes = List.tabulate(ctrlAt+1)(pp.fetch(_).down), physicalAddress = tpk.TRANSLATED, + forceCheck = _ => False, read = _ => False, write = _ => False, execute = _ => True, diff --git a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala index c6ce5880..0772a10d 100644 --- a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala +++ b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala @@ -100,12 +100,13 @@ class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ } val pRange = Global.PHYSICAL_WIDTH-1 downto granularityWidth + val extraBit = granularity > 4 val entries = for(i <- 0 until pmpSize) yield new Area{ val isLocked = Bool() - val address = Reg(UInt(Global.PHYSICAL_WIDTH.get - granularityWidth bits)) + val address = Reg(UInt(Global.PHYSICAL_WIDTH.get - granularityWidth + extraBit.toInt bits)) val cfg = Reg(Cfg()) val cfgNext = CombInit(cfg) - when(!isLocked) { + when(!cfg.locked) { cfg := cfgNext cfg.write clearWhen (!cfgNext.read) if(!p.withTor) when(cfgNext.kind === 1) {cfg.kind := 0} @@ -125,9 +126,9 @@ class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ } - val isNapot = RegNext(cfgNext.kind(1)) - val isTor = RegNext(cfgNext.kind === 1) - val napot = Napot(address.dropHigh(1)) + val isNapot = RegNext(cfg.kind(1)) + val isTor = RegNext(cfg.kind === 1) + val napot = Napot(address.dropHigh(1)).dropLow(extraBit.toInt) //TODO drop high ? if(granularity == 4) when(!cfgNext.kind(0)){ napot.setAll() } val mask = RegNext(napot) @@ -136,15 +137,16 @@ class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ case 64 => (i/8*2, i%8/8) } - csr.writeCancel(CSR.PMPADDR + i, cfg.locked) - csr.readWrite(address, CSR.PMPADDR + i, granularityWidth-2) - if(granularity > 4 && p.withNapot) csr.read(U(granularityWidth-2 bits, default -> isNapot), CSR.PMPADDR + i) //WTF + csr.writeCancel(CSR.PMPADDR + i, isLocked) + csr.read(address(address.bitsRange.drop(1)), CSR.PMPADDR + i, granularityWidth-2) + csr.write(address(address.bitsRange), CSR.PMPADDR + i, granularityWidth-2-extraBit.toInt) + if(granularity > 4 && p.withNapot) csr.read(U(granularityWidth-2 bits, default -> isNapot, granularityWidth-3 -> (address.lsb && isNapot)), CSR.PMPADDR + i) //WTF csr.read(CSR.PMPCFG + cfgId, 0+cfgOffset -> cfg.read, 1+cfgOffset -> cfg.write, 2+cfgOffset -> cfg.execute, 3+cfgOffset -> cfg.kind, 7+cfgOffset -> cfg.locked) csr.write(CSR.PMPCFG + cfgId, 0+cfgOffset -> cfgNext.read, 1+cfgOffset -> cfgNext.write, 2+cfgOffset -> cfgNext.execute, 3+cfgOffset -> cfgNext.kind, 7+cfgOffset -> cfgNext.locked) } for(i <- 0 until pmpSize; self = entries(i)) { - self.isLocked := self.cfg.locked || (i+1 != pmpSize).mux(entries(i+1).isLocked && entries(i+1).isTor , False) + self.isLocked := self.cfg.locked || (i+1 != pmpSize).mux(entries(i+1).cfg.locked && entries(i+1).isTor , False) } val allFilter = CsrListFilter((0 to 63).map(_ + CSR.PMPADDR) ++ (0 to 15).filter(i => Riscv.XLEN.get == 32 || (i % 2) == 0).map(_ + CSR.PMPCFG)) @@ -170,11 +172,11 @@ class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ val TOR_SMALLER = new Array[Payload[Bool]](p.pmpSize) val onEntries = for((e, id) <- entries.zipWithIndex) yield new Area{ val napot = p.withNapot generate new Area { - val MATCH = napotMatchStage.insert(((e.address << granularityWidth)(pRange) ^ napotMatchStage(ps.physicalAddress)(pRange)).asBits & e.mask) + val MATCH = napotMatchStage.insert(((e.address.dropLow(extraBit.toInt).asUInt << granularityWidth)(pRange) ^ napotMatchStage(ps.physicalAddress)(pRange)).asBits & e.mask) val HIT = napotHitsStage.insert(napotHitsStage(MATCH) === 0) } val tor = p.withTor generate new Area{ - val BIGGER = torCmpStage.insert(e.address > torCmpAddress) + val BIGGER = torCmpStage.insert((e.address >> extraBit.toInt) > torCmpAddress) val HIT = { import torHitsStage._ ; insert(BIGGER && (id != 0).mux(!TOR_SMALLER(id-1), True))} TOR_SMALLER(id) = BIGGER } diff --git a/src/main/scala/vexiiriscv/memory/Service.scala b/src/main/scala/vexiiriscv/memory/Service.scala index b56ef027..cf7eeda0 100644 --- a/src/main/scala/vexiiriscv/memory/Service.scala +++ b/src/main/scala/vexiiriscv/memory/Service.scala @@ -83,6 +83,7 @@ trait PmpService extends Area { val portsLock = Retainer() def createPmpPort(nodes: Seq[NodeBaseApi], physicalAddress: Payload[UInt], + forceCheck: NodeBaseApi => Bool, read: NodeBaseApi => Bool, write: NodeBaseApi => Bool, execute: NodeBaseApi => Bool, From 28ee6a87d4ae1bc8cf97c03d0123e92cedba99c4 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 21 Nov 2024 12:28:48 +0100 Subject: [PATCH 05/10] pmp fixes, vexii probe now implement phys width --- ext/NaxSoftware | 2 +- ext/riscv-isa-sim | 2 +- ext/rvls | 2 +- src/main/scala/vexiiriscv/memory/PmpPlugin.scala | 4 ++-- src/main/scala/vexiiriscv/test/VexiiRiscvProbe.scala | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ext/NaxSoftware b/ext/NaxSoftware index 53c31b59..4b0d359a 160000 --- a/ext/NaxSoftware +++ b/ext/NaxSoftware @@ -1 +1 @@ -Subproject commit 53c31b5944a1fb07bbfc876b5b9888a09e5f8068 +Subproject commit 4b0d359a4fe366cf977b07b3aad7deb53c3bdc48 diff --git a/ext/riscv-isa-sim b/ext/riscv-isa-sim index 5f4eca2c..c6fa02d5 160000 --- a/ext/riscv-isa-sim +++ b/ext/riscv-isa-sim @@ -1 +1 @@ -Subproject commit 5f4eca2c0a5ee7808ad235f5230f3bd2c14a6ffa +Subproject commit c6fa02d5b28bc0d98ea14e7fbfa749d5dd265034 diff --git a/ext/rvls b/ext/rvls index b5882b90..d4899fac 160000 --- a/ext/rvls +++ b/ext/rvls @@ -1 +1 @@ -Subproject commit b5882b905ab8bed26f67891c98e41fc5f69a5b03 +Subproject commit d4899fac2e7f943fb51fc2db0b9859df59cac4c6 diff --git a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala index 0772a10d..6c33389e 100644 --- a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala +++ b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala @@ -32,7 +32,7 @@ case class PmpParam( var withNapot: Boolean = true ) -class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ +class PmpPlugin(val p : PmpParam) extends FiberPlugin with PmpService{ import p._ override def getPmpNum(): Int = p.pmpSize @@ -134,7 +134,7 @@ class PmpPlugin(p : PmpParam) extends FiberPlugin with PmpService{ val (cfgId, cfgOffset) = Riscv.XLEN.get match{ case 32 => (i/4, i%4*8) - case 64 => (i/8*2, i%8/8) + case 64 => (i/8*2, i%8*8) } csr.writeCancel(CSR.PMPADDR + i, isLocked) diff --git a/src/main/scala/vexiiriscv/test/VexiiRiscvProbe.scala b/src/main/scala/vexiiriscv/test/VexiiRiscvProbe.scala index 4517a9ca..1de3abe8 100644 --- a/src/main/scala/vexiiriscv/test/VexiiRiscvProbe.scala +++ b/src/main/scala/vexiiriscv/test/VexiiRiscvProbe.scala @@ -186,7 +186,7 @@ class VexiiRiscvProbe(cpu : VexiiRiscv, kb : Option[konata.Backend], var withRvl if (get(Riscv.RVZbc)) isa += "_zbc" if (get(Riscv.RVZbs)) isa += "_zbs" tracer.newCpuMemoryView(hartId, 16, 1 << Decode.STORE_ID_WIDTH) - tracer.newCpu(hartId, isa, csrp, 63, cpu.host.get[PmpService].map(_.getPmpNum).getOrElse(0), hartId) + tracer.newCpu(hartId, isa, csrp, get(Global.PHYSICAL_WIDTH), cpu.host.get[PmpService].map(_.getPmpNum).getOrElse(0), hartId) val pc = if(xlen == 32) 0x80000000l else 0x80000000l tracer.setPc(hartId, pc) } From bb462b9d211a266295a5081076e7d021d32d8075 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Thu, 21 Nov 2024 16:30:23 +0100 Subject: [PATCH 06/10] pmp fixes --- src/main/scala/vexiiriscv/memory/PmpPlugin.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala index 6c33389e..c8f354bf 100644 --- a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala +++ b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala @@ -110,6 +110,7 @@ class PmpPlugin(val p : PmpParam) extends FiberPlugin with PmpService{ cfg := cfgNext cfg.write clearWhen (!cfgNext.read) if(!p.withTor) when(cfgNext.kind === 1) {cfg.kind := 0} + if(granularity > 4) when(cfgNext.kind === 2) {cfg.kind := 0} } if(i == 0){ @@ -138,7 +139,7 @@ class PmpPlugin(val p : PmpParam) extends FiberPlugin with PmpService{ } csr.writeCancel(CSR.PMPADDR + i, isLocked) - csr.read(address(address.bitsRange.drop(1)), CSR.PMPADDR + i, granularityWidth-2) + csr.read(address(address.bitsRange.drop(extraBit.toInt)), CSR.PMPADDR + i, granularityWidth-2) csr.write(address(address.bitsRange), CSR.PMPADDR + i, granularityWidth-2-extraBit.toInt) if(granularity > 4 && p.withNapot) csr.read(U(granularityWidth-2 bits, default -> isNapot, granularityWidth-3 -> (address.lsb && isNapot)), CSR.PMPADDR + i) //WTF csr.read(CSR.PMPCFG + cfgId, 0+cfgOffset -> cfg.read, 1+cfgOffset -> cfg.write, 2+cfgOffset -> cfg.execute, 3+cfgOffset -> cfg.kind, 7+cfgOffset -> cfg.locked) From 2f918b0f3263b15054135e8b97568c8cc276f792 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 22 Nov 2024 14:55:21 +0100 Subject: [PATCH 07/10] LsuPlugin fix pmp integration and prevent non head store buffer entries from refilling the D$ --- src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala b/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala index 1cdabc6e..ba5d0e4d 100644 --- a/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala +++ b/src/main/scala/vexiiriscv/execute/lsu/LsuPlugin.scala @@ -756,12 +756,12 @@ class LsuPlugin(var layer : LaneLayer, abords += !l1.FLUSH && onPma.CACHED_RSP.fault abords += FROM_LSU && (!isValid || isCancel) abords += mmuNeeded && MMU_FAILURE - if(withStoreBuffer) abords += wb.loadHazard || !FROM_WB && fenceTrap.valid + if(withStoreBuffer) abords += wb.loadHazard || !FROM_WB && fenceTrap.valid || wb.selfHazard skipsWrite += l1.MISS || l1.MISS_UNIQUE skipsWrite += l1.FAULT || pmpPort.ACCESS_FAULT skipsWrite += preCtrl.MISS_ALIGNED - skipsWrite += FROM_LSU && onTrigger.HIT + skipsWrite += FROM_LSU && (onTrigger.HIT || pmpPort.ACCESS_FAULT) skipsWrite += FROM_PREFETCH if(Riscv.RVA) skipsWrite += l1.ATOMIC && !l1.LOAD && scMiss if (withStoreBuffer) skipsWrite += wb.selfHazard || !FROM_WB && wb.hit From eeadda313e14664b66955f1b062c89dc82491778 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 22 Nov 2024 14:57:09 +0100 Subject: [PATCH 08/10] Add pmp support for cacheless configs Add pmp regression test --- ext/NaxSoftware | 2 +- src/main/scala/vexiiriscv/Param.scala | 29 ++++++++++++++++--- .../execute/lsu/LsuCachelessPlugin.scala | 20 +++++++++++-- .../fetch/FetchCachelessPlugin.scala | 19 ++++++++++-- .../scala/vexiiriscv/memory/PmpPlugin.scala | 2 +- .../scala/vexiiriscv/tester/TestBench.scala | 1 + .../scala/vexiiriscv/tester/Regression.scala | 5 +++- 7 files changed, 65 insertions(+), 13 deletions(-) diff --git a/ext/NaxSoftware b/ext/NaxSoftware index 4b0d359a..f9f2681a 160000 --- a/ext/NaxSoftware +++ b/ext/NaxSoftware @@ -1 +1 @@ -Subproject commit 4b0d359a4fe366cf977b07b3aad7deb53c3bdc48 +Subproject commit f9f2681ae03ce28f973d41574996fca5552f9af1 diff --git a/src/main/scala/vexiiriscv/Param.scala b/src/main/scala/vexiiriscv/Param.scala index d07c4073..49462e46 100644 --- a/src/main/scala/vexiiriscv/Param.scala +++ b/src/main/scala/vexiiriscv/Param.scala @@ -172,7 +172,25 @@ class ParamSimple(){ withNapot = true ) - var fetchPmpParam = new PmpPortParameter( + var fetchNoL1PmpParam = new PmpPortParameter( + napotMatchAt = 0, + napotHitsAt = 1, + torCmpAt = 0, + torHitsAt = 1, + hitsAt = 1, + rspAt = 1 + ) + + var lsuNoL1PmpParam = new PmpPortParameter( + napotMatchAt = 0, + napotHitsAt = 0, + torCmpAt = 0, + torHitsAt = 0, + hitsAt = 0, + rspAt = 0 + ) + + var fetchL1PmpParam = new PmpPortParameter( napotMatchAt = 1, napotHitsAt = 1, torCmpAt = 1, @@ -181,7 +199,7 @@ class ParamSimple(){ rspAt = 2 ) - var lsuPmpParam = new PmpPortParameter( + var lsuL1PmpParam = new PmpPortParameter( napotMatchAt = 1, napotHitsAt = 1, torCmpAt = 1, @@ -497,6 +515,7 @@ class ParamSimple(){ opt[Unit]("with-dispatcher-buffer") action { (v, c) => withDispatcherBuffer = true } opt[Unit]("with-supervisor") action { (v, c) => privParam.withSupervisor = true; privParam.withUser = true; withMmu = true } opt[Unit]("with-user") action { (v, c) => privParam.withUser = true } + opt[Unit]("without-mmu") action { (v, c) => withMmu = false } opt[Unit]("without-mul") action { (v, c) => withMul = false } opt[Unit]("without-div") action { (v, c) => withDiv = false } opt[Unit]("with-mul") action { (v, c) => withMul = true } @@ -616,6 +635,7 @@ class ParamSimple(){ forkAt = fetchForkAt, joinAt = fetchForkAt+1, //You can for instance allow the external memory to have more latency by changing this wordWidth = fetchMemDataWidth, + pmpPortParameter = fetchNoL1PmpParam, translationStorageParameter = fetchTsp, translationPortParameter = withMmu match { case false => null @@ -644,7 +664,7 @@ class ParamSimple(){ case false => null case true => fetchTpp }, - pmpPortParameter = fetchPmpParam + pmpPortParameter = fetchL1PmpParam ) fetchL1Prefetch match { @@ -711,6 +731,7 @@ class ParamSimple(){ forkAt = lsuForkAt+0, joinAt = lsuForkAt+1, wbAt = 2, //TODO + pmpPortParameter = lsuNoL1PmpParam, translationStorageParameter = lsuTsp, translationPortParameter = withMmu match { case false => null @@ -730,7 +751,7 @@ class ParamSimple(){ storeBufferSlots = lsuStoreBufferSlots, storeBufferOps = lsuStoreBufferOps, softwarePrefetch = lsuSoftwarePrefetch, - pmpPortParameter = fetchPmpParam, + pmpPortParameter = fetchL1PmpParam, translationStorageParameter = lsuTsp, translationPortParameter = withMmu match { case false => null diff --git a/src/main/scala/vexiiriscv/execute/lsu/LsuCachelessPlugin.scala b/src/main/scala/vexiiriscv/execute/lsu/LsuCachelessPlugin.scala index 8847ff3a..2984cf0e 100644 --- a/src/main/scala/vexiiriscv/execute/lsu/LsuCachelessPlugin.scala +++ b/src/main/scala/vexiiriscv/execute/lsu/LsuCachelessPlugin.scala @@ -10,7 +10,7 @@ import spinal.core.fiber.{Handle, Retainer} import spinal.core.sim.SimDataPimper import vexiiriscv.decode.Decode import vexiiriscv.fetch.FetchPipelinePlugin -import vexiiriscv.memory.{AddressTranslationPortUsage, AddressTranslationService, DBusAccessService, PmaLoad, PmaLogic, PmaPort, PmaStore} +import vexiiriscv.memory.{AddressTranslationPortUsage, AddressTranslationService, DBusAccessService, PmaLoad, PmaLogic, PmaPort, PmaStore, PmpService} import vexiiriscv.misc.{AddressToMask, LsuTriggerService, PerformanceCounterService, TrapArg, TrapReason, TrapService} import vexiiriscv.riscv.Riscv.{FLEN, LSLEN, XLEN} import spinal.lib.misc.pipeline._ @@ -27,6 +27,7 @@ class LsuCachelessPlugin(var layer : LaneLayer, var withSpeculativeLoadFlush : Boolean, //WARNING, the fork cmd may be flushed out of existance before firing if any plugin doesn't flush from the first cycle after !freeze var translationStorageParameter: Any, var translationPortParameter: Any, + var pmpPortParameter : Any, var addressAt: Int = 0, var triggerAt: Int = 0, var pmaAt : Int = 0, @@ -55,9 +56,10 @@ class LsuCachelessPlugin(var layer : LaneLayer, val fpwbp = host.findOption[WriteBackPlugin](p => p.lane == layer.lane && p.rf == FloatRegFile) val srcp = host.find[SrcPlugin](_.layer == layer) val ats = host[AddressTranslationService] + val ps = host[PmpService] val ts = host[TrapService] val ss = host[ScheduleService] - val buildBefore = retains(elp.pipelineLock, ats.portsLock) + val buildBefore = retains(elp.pipelineLock, ats.portsLock, ps.portsLock) val atsStorageLock = retains(ats.storageLock) val retainer = retains(List(elp.uopLock, srcp.elaborationLock, ifp.elaborationLock, ts.trapLock, ss.elaborationLock) ++ fpwbp.map(_.elaborationLock)) awaitBuild() @@ -138,6 +140,8 @@ class LsuCachelessPlugin(var layer : LaneLayer, } } + + val onAddress = new addressCtrl.Area{ val RAW_ADDRESS = insert(srcp.ADD_SUB.asUInt) @@ -152,6 +156,16 @@ class LsuCachelessPlugin(var layer : LaneLayer, val MISS_ALIGNED = insert((1 to log2Up(LSLEN / 8)).map(i => SIZE === i && RAW_ADDRESS(i - 1 downto 0) =/= 0).orR) //TODO remove from speculLoad and handle it with trap } val tpk = onAddress.translationPort.keys + val pmpPort = ps.createPmpPort( + nodes = List.tabulate(forkAt+1)(elp.execute(_).down), + physicalAddress = tpk.TRANSLATED, + forceCheck = _ => False, + read = _(LOAD), + write = _(STORE), + execute = _ => False, + portSpec = pmpPortParameter, + storageSpec = null + ) val onPma = new elp.Execute(pmaAt){ val port = new PmaPort(Global.PHYSICAL_WIDTH, (0 to log2Up(Riscv.LSLEN / 8)).map(1 << _), List(PmaLoad, PmaStore)) @@ -233,7 +247,7 @@ class LsuCachelessPlugin(var layer : LaneLayer, trapPort.code := TrapReason.REDO } - when(tpk.ACCESS_FAULT || onPma.RSP.fault) { + when(tpk.ACCESS_FAULT || onPma.RSP.fault || pmpPort.ACCESS_FAULT) { skip := True trapPort.exception := True trapPort.code := CSR.MCAUSE_ENUM.LOAD_ACCESS_FAULT diff --git a/src/main/scala/vexiiriscv/fetch/FetchCachelessPlugin.scala b/src/main/scala/vexiiriscv/fetch/FetchCachelessPlugin.scala index bd58d335..09fa1095 100644 --- a/src/main/scala/vexiiriscv/fetch/FetchCachelessPlugin.scala +++ b/src/main/scala/vexiiriscv/fetch/FetchCachelessPlugin.scala @@ -12,7 +12,7 @@ import spinal.lib.bus.tilelink.DebugId import spinal.lib.system.tag.{MappedTransfers, PmaRegion} import vexiiriscv._ import vexiiriscv.Global._ -import vexiiriscv.memory.{AddressTranslationPortUsage, AddressTranslationService, PmaLoad, PmaLogic, PmaPort} +import vexiiriscv.memory.{AddressTranslationPortUsage, AddressTranslationService, PmaLoad, PmaLogic, PmaPort, PmpService} import vexiiriscv.misc.{PerformanceCounterService, TrapArg, TrapReason, TrapService} import vexiiriscv.riscv.CSR @@ -27,6 +27,7 @@ object FetchCachelessPlugin{ class FetchCachelessPlugin(var wordWidth : Int, var translationStorageParameter: Any, var translationPortParameter: Any, + var pmpPortParameter : Any, var addressAt: Int = 0, var pmaAt: Int = 0, var forkAt : Int = 0, @@ -36,9 +37,10 @@ class FetchCachelessPlugin(var wordWidth : Int, val logic = during setup new Area{ val pp = host[FetchPipelinePlugin] + val ps = host[PmpService] val ts = host[TrapService] val ats = host[AddressTranslationService] - val buildBefore = retains(pp.elaborationLock, ats.portsLock) + val buildBefore = retains(pp.elaborationLock, ats.portsLock, ps.portsLock) val atsStorageLock = retains(ats.storageLock) val trapLock = ts.trapLock() awaitBuild() @@ -93,6 +95,17 @@ class FetchCachelessPlugin(var wordWidth : Int, } val tpk = onAddress.translationPort.keys + val pmpPort = ps.createPmpPort( + nodes = List.tabulate(joinAt+1)(pp.fetch(_).down), + physicalAddress = tpk.TRANSLATED, + forceCheck = _ => False, + read = _ => False, + write = _ => False, + execute = _ => True, + portSpec = pmpPortParameter, + storageSpec = null + ) + val onPma = new pp.Fetch(pmaAt) { val port = new PmaPort(Global.PHYSICAL_WIDTH, List(Fetch.WORD_WIDTH / 8), List(PmaLoad)) port.cmd.address := tpk.TRANSLATED @@ -152,7 +165,7 @@ class FetchCachelessPlugin(var wordWidth : Int, trapPort.code.assignDontCare() trapPort.arg.allowOverride() := 0 - when(rsp.error || fork.PMA_FAULT){ + when(rsp.error || fork.PMA_FAULT || pmpPort.ACCESS_FAULT){ TRAP := True trapPort.exception := True trapPort.code := CSR.MCAUSE_ENUM.INSTRUCTION_ACCESS_FAULT diff --git a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala index c8f354bf..98aec9a5 100644 --- a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala +++ b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala @@ -129,7 +129,7 @@ class PmpPlugin(val p : PmpParam) extends FiberPlugin with PmpService{ val isNapot = RegNext(cfg.kind(1)) val isTor = RegNext(cfg.kind === 1) - val napot = Napot(address.dropHigh(1)).dropLow(extraBit.toInt) //TODO drop high ? + val napot = Napot(address.dropHigh(1)).dropLow(extraBit.toInt) if(granularity == 4) when(!cfgNext.kind(0)){ napot.setAll() } val mask = RegNext(napot) diff --git a/src/main/scala/vexiiriscv/tester/TestBench.scala b/src/main/scala/vexiiriscv/tester/TestBench.scala index c334b385..e485692d 100644 --- a/src/main/scala/vexiiriscv/tester/TestBench.scala +++ b/src/main/scala/vexiiriscv/tester/TestBench.scala @@ -219,6 +219,7 @@ class TestOptions{ probe.enabled = withProbe probe.trace = false +// delayed(1000){ simFailure() } // Things to enable when we want to collect traces val tracerFile = traceRvlsLog.option(new FileBackend(new File(currentTestPath(), "tracer.log"))) onTrace { diff --git a/src/test/scala/vexiiriscv/tester/Regression.scala b/src/test/scala/vexiiriscv/tester/Regression.scala index 9e8f2238..d09b37bb 100644 --- a/src/test/scala/vexiiriscv/tester/Regression.scala +++ b/src/test/scala/vexiiriscv/tester/Regression.scala @@ -5,7 +5,7 @@ import spinal.core._ import spinal.core.sim._ import spinal.lib.misc.plugin.Hostable import spinal.lib.misc.test.{AsyncJob, MultithreadedFunSuite} -import vexiiriscv.memory.MmuPlugin +import vexiiriscv.memory.{MmuPlugin, PmpPlugin} import vexiiriscv.misc.{EmbeddedRiscvJtag, PrivilegedPlugin} import vexiiriscv.riscv.Riscv import vexiiriscv.{Global, ParamSimple, VexiiRiscv} @@ -52,6 +52,7 @@ class RegressionSingle(compiled : SimCompiled[VexiiRiscv], val xlen = dut.database(Riscv.XLEN) val priv = dut.host.get[PrivilegedPlugin] val mmu = dut.host.get[MmuPlugin] + val pmp = dut.host.get[PmpPlugin] val rvm = dut.database(Riscv.RVM) val rvc = dut.database(Riscv.RVC) @@ -351,6 +352,7 @@ class RegressionSingle(compiled : SimCompiled[VexiiRiscv], priv.filter(_.p.withSupervisor).foreach(_ => regulars ++= List("supervisor")) if(mmu.nonEmpty) regulars ++= List(s"mmu_sv${if(xlen == 32) 32 else 39}") + if(pmp.get.p.pmpSize > 4 && priv.get.p.withSupervisor) regulars ++= List(s"pmp") if(config.regular) for(name <- regulars){ val args = newArgs() @@ -627,6 +629,7 @@ class Regression extends MultithreadedFunSuite(sys.env.getOrElse("VEXIIRISCV_REG return List("", "--with-rvf", "--with-rvf --with-rvd").randomPick(random) } } + addDim("pmp", List("", "--pmp-size=8")) // addTest(default) From d1add1420cbc63157b7d075bb25c3dc676956832 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 25 Nov 2024 11:58:00 +0100 Subject: [PATCH 09/10] sync --- ext/SpinalHDL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/SpinalHDL b/ext/SpinalHDL index 33cd8368..bec17f5d 160000 --- a/ext/SpinalHDL +++ b/ext/SpinalHDL @@ -1 +1 @@ -Subproject commit 33cd8368c6b8a7ab6c6f8b60682668979556c954 +Subproject commit bec17f5d280bf1db29e602a7168e4e9f2f515f07 From 9517fac2f004692b1b5571d3745ef8d60ff2b247 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Mon, 25 Nov 2024 13:36:16 +0100 Subject: [PATCH 10/10] add pmp comments --- .../scala/vexiiriscv/memory/PmpPlugin.scala | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala index 98aec9a5..670fe1d0 100644 --- a/src/main/scala/vexiiriscv/memory/PmpPlugin.scala +++ b/src/main/scala/vexiiriscv/memory/PmpPlugin.scala @@ -91,7 +91,11 @@ class PmpPlugin(val p : PmpParam) extends FiberPlugin with PmpService{ awaitBuild() - if(Riscv.XLEN.get == 64) assert(granularity >= 8) + if(Riscv.XLEN.get == 64) assert( + granularity >= 8, + """VexiiRiscv RV64 doesn't support granularity smaller than 8 bytes, as durring 8 bytes accesses, + | it would need to check that the upper 4 bytes of access are contained in the pmp regions aswell""".stripMargin + ) case class Cfg() extends Bundle{ val kind = Bits(2 bits) @@ -100,12 +104,16 @@ class PmpPlugin(val p : PmpParam) extends FiberPlugin with PmpService{ } val pRange = Global.PHYSICAL_WIDTH-1 downto granularityWidth + // This is a tricky thing, basicaly the granularity doesn't affect TOR and NAPOT csr in the same way. + // In particular the granularityWidth-2 bit val extraBit = granularity > 4 + + // Generate all the PMP entries storage + CSR mapping val entries = for(i <- 0 until pmpSize) yield new Area{ val isLocked = Bool() val address = Reg(UInt(Global.PHYSICAL_WIDTH.get - granularityWidth + extraBit.toInt bits)) val cfg = Reg(Cfg()) - val cfgNext = CombInit(cfg) + val cfgNext = CombInit(cfg) // This allows to handle WARL (Write Any, Read Legal) on the CSRs when(!cfg.locked) { cfg := cfgNext cfg.write clearWhen (!cfgNext.read) @@ -114,6 +122,8 @@ class PmpPlugin(val p : PmpParam) extends FiberPlugin with PmpService{ } if(i == 0){ + // Allows software which is unaware of the PMP but uses the supervisor/user modes + // to get access to all the memory by default after reset assert(p.withNapot) address.init(address.getAllTrue) cfg.read init(True) @@ -183,10 +193,9 @@ class PmpPlugin(val p : PmpParam) extends FiberPlugin with PmpService{ } val HIT_ANY = { import hitsStage._ ; insert(p.withNapot.mux(e.isNapot && napot.HIT, False) || p.withTor.mux(e.isTor && tor.HIT, False)) } - val bypass = False // isMachine && !e.locked val normalRwx = (!ps.read(permStage) || e.cfg.read || !checkInstruction) && (((!ps.write(permStage) || e.cfg.write) && (!ps.execute(permStage) || e.cfg.execute)) || !portCheckData) - val PERM_OK = { import permStage._ ; insert(bypass || normalRwx) } + val PERM_OK = permStage.insert(normalRwx) } val NEED_HIT = permStage.insert(checkInstruction && ps.execute(permStage) || portCheckData && (ps.read(permStage) || ps.write(permStage)))