Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/main/scala/riscv/Core.scala
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ object createStaticPipeline {
new Interrupts(pipeline.writeback),
new MulDiv(Set(pipeline.execute)),
new Fence(Set(pipeline.execute)),
new Marker
new Marker,
new PMP
) ++ extraPlugins
)

Expand Down Expand Up @@ -262,7 +263,8 @@ object createDynamicPipeline {
new scheduling.static.Scheduler(canStallExternally = true),
new scheduling.static.PcManager(0x80000000L),
pipeline.backbone,
new memory.Fetcher(pipeline.fetch)
new memory.Fetcher(pipeline.fetch),
new PMP
)
)

Expand Down
6 changes: 6 additions & 0 deletions src/main/scala/riscv/Services.scala
Original file line number Diff line number Diff line change
Expand Up @@ -559,3 +559,9 @@ trait PipelineTaintService {
trait FenceService {
def isFence(stage: Stage): Bool
}

trait PMPService {
def isAllowedToRead(addr: UInt): Bool
def isAllowedToWrite(addr: UInt): Bool
def isAllowedToExecute(addr: UInt): Bool
}
190 changes: 190 additions & 0 deletions src/main/scala/riscv/plugins/PMP.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package riscv.plugins

import riscv._
import spinal.core._
import spinal.lib._
import scala.collection.mutable.ArrayBuffer

//PMP implementation for Xlen = 32, hardware mode only, up to 32 bit addresses.
class PMP extends Plugin[Pipeline] with PMPService {

private val CSR_PMPCSR0 = 0x3a0
private val CSR_PMPADDR0 = 0x3b0

private var csrService: CsrService = _

private val pmpEntries = 16
private val pmpCfgPerReg = 4
private val pmpCsrCount = pmpEntries / pmpCfgPerReg

private class csrPmpCfg(implicit config: Config) extends Csr {
// TODO: Expand to also include 64-bit arch
// 32-bit version stores 4 configs per register
val csrBytes = Reg(UInt(config.isa.xlen bits)).init(0)

override def read(): UInt = csrBytes

def legalizePmpCsr(value: UInt)(implicit config: Config): UInt = {
val R = value(0)
val W = value(1)
val X = value(2)
val A = value(4 downto 3)
val L = value(7)

// Reserved combination: R=0 and W=1 → force W=0
val W_fixed = (W & R)

// Reserved bits [6:5] are forced to zero
val result = UInt(8 bits)
result := 0
result(7) := L
result(4 downto 3) := A
result(2) := X
result(1) := W_fixed
result(0) := R

return result
}

override def write(value: UInt): Unit = {
for (i <- 0 until pmpCfgPerReg) {
val bytes = value((i * 8 + 7) downto (i * 8))
val locked = csrBytes(i * 8 + 7)
when(!locked) {
csrBytes((i * 8 + 7) downto (i * 8)) := legalizePmpCsr(bytes)
}
}
}
}

private class csrPmpAddr(num: Int)(implicit config: Config) extends Csr {
val addr = Reg(UInt(config.isa.xlen bits)).init(0)
override def read(): UInt = addr

val cfgReg = num / pmpCfgPerReg
val cfgPos = num % pmpCfgPerReg
val cfgRegNext = (num + 1) / pmpCfgPerReg
val cfgPosNext = (num + 1) % pmpCfgPerReg

override def write(addr: UInt): Unit = {
val stub_CfgSelf = slave(new CsrIo)
pipeline plug {
stub_CfgSelf <> csrService.getCsr(CSR_PMPCSR0 + cfgReg)
}
val cfgSelf = stub_CfgSelf.read()
val lockedBySelf = Bool()
lockedBySelf := cfgSelf(7 + 8 * cfgPos)
val lockedByNext = Bool()
lockedByNext := False
if (num != pmpEntries - 1) { // reg needs to be locked when the next entry is locked and in TOR mode
val stub_CfgNext = slave(new CsrIo)
pipeline plug {
stub_CfgNext <> csrService.getCsr(CSR_PMPCSR0 + cfgRegNext)
}
val cfgNext = stub_CfgNext.read()(7 + 8 * cfgPosNext downto 8 * cfgPosNext)
val mode = cfgNext(4 downto 3)
when(mode === 1) {
lockedByNext := cfgNext(7)
}
}
when(!(lockedBySelf | lockedByNext)) {
this.addr(29 downto 0) := addr(29 downto 0)
}
}
}

override def setup(): Unit = {
pipeline plug new Area {
csrService = pipeline.service[CsrService]
for (i <- 0 until pmpCsrCount) {
csrService.registerCsr(CSR_PMPCSR0 + i, new csrPmpCfg)
}
for (j <- 0 until pmpEntries) {
csrService.registerCsr(CSR_PMPADDR0 + j, new csrPmpAddr(j))
}
}
}

override def isAllowedToRead(addr: UInt): Bool = {
isAllowed(addr, 0)
}

override def isAllowedToWrite(addr: UInt): Bool = {
isAllowed(addr, 1)
}

override def isAllowedToExecute(addr: UInt): Bool = {
isAllowed(addr, 2)
}

private def isAllowed(addr: UInt, perm: UInt): Bool = {
case class PermData()(implicit config: Config) extends Bundle {
val allow = Bool()
val matched = Bool()
}

def updatePermission(allow: Bool, data: PermData) {
data.allow := allow
data.matched := True
}

val permData = PermData()
permData.allow := True
permData.matched := False

for (i <- (0 until pmpCsrCount).reverse) {
val stub_fullCfg = slave(new CsrIo)
pipeline plug {
stub_fullCfg <> csrService.getCsr(CSR_PMPCSR0 + i)
}
val fullCfg = stub_fullCfg.read()

for (j <- (0 until pmpCfgPerReg).reverse) {
val cfg = fullCfg(7 + 8 * j downto 8 * j)
val stub_pmpaddr = slave(new CsrIo)
pipeline plug {
stub_pmpaddr <> csrService.getCsr(CSR_PMPADDR0 + i * 4 + j)
}
val pmpaddr = stub_pmpaddr.read()

val a = cfg(4 downto 3)
val l = cfg(7)

when(a === 1) { // TOR
val bottom = UInt(config.isa.xlen bits)
if (i == 0 && j == 0) {
bottom := 0
} else {
val stub_bottom = slave(new CsrIo)
pipeline plug {
stub_bottom <> csrService.getCsr(CSR_PMPADDR0 + i * 4 + j - 1)
}
bottom := stub_bottom.read()
}
val bot = bottom << 2
val top = pmpaddr << 2
when(addr >= bot && addr < top) {
updatePermission(!l | cfg(perm), permData)
}
}
when(a === 2) { // NA4
val base = pmpaddr << 2
when(addr >= base && addr < base + 4) {
updatePermission(!l | cfg(perm), permData)
}
}
when(a === 3) { // NAPOT
val size =
(~pmpaddr & (pmpaddr + 1)) << 3 // Could fail when pmpaddr is all ones. Will have a one at the position of the rightmost 0 (shifted over by 3)
val top = (pmpaddr + 1) << 2

when(addr >= top - size && addr < (top)) {
updatePermission(!l | cfg(perm), permData)
}
}
}
}

permData.allow
}
}
13 changes: 13 additions & 0 deletions src/main/scala/riscv/plugins/memory/Fetcher.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ class Fetcher(fetchStage: Stage) extends Plugin[Pipeline] with FetchService {
val pc = input(pipeline.data.PC)
val nextPc = pc + 4

val pmpAllowed = Bool()
pmpAllowed := True
pmpAllowed.allowOverride // otherwise can't override the value with the PMPService value
if (pipeline.hasService[PMPService]) {
pmpAllowed := pipeline.service[PMPService].isAllowedToExecute(pc)
}

when(!pmpAllowed) {
if (pipeline.hasService[TrapService]) {
pipeline.service[TrapService].trap(fetchStage, TrapCause.InstructionAccessFault(pc))
}
}

when(arbitration.isRunning) {
val fetchAddress = addressTranslator.translate(fetchStage, pc)
val (valid, rdata) = ibusCtrl.read(fetchAddress)
Expand Down
30 changes: 28 additions & 2 deletions src/main/scala/riscv/plugins/memory/Lsu.scala
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,20 @@ class Lsu(addressStages: Set[Stage], loadStages: Seq[Stage], storeStage: Stage)
}
}

when(arbitration.isValid && !misaligned) {
val pmpAllowed = Bool()
pmpAllowed := True
pmpAllowed.allowOverride // otherwise can't override the value with the PMPService value
if (pipeline.hasService[PMPService]) {
pmpAllowed := pipeline.service[PMPService].isAllowedToRead(address)
}

when(isActive && !pmpAllowed) {
if (pipeline.hasService[TrapService]) {
trap(TrapCause.LoadAccessFault(address), loadStage)
}
}

when(arbitration.isValid && !misaligned && pmpAllowed) {
when(isActive) {
val busAddress = address & U(0xfffffffcL)
val valid = Bool()
Expand Down Expand Up @@ -485,7 +498,20 @@ class Lsu(addressStages: Set[Stage], loadStages: Seq[Stage], storeStage: Stage)
}
}

when(arbitration.isValid && !misaligned) {
val pmpAllowed = Bool()
pmpAllowed := True
pmpAllowed.allowOverride // otherwise can't override the value with the PMPService value
if (pipeline.hasService[PMPService]) {
pmpAllowed := pipeline.service[PMPService].isAllowedToWrite(address)
}

when(isActive && !pmpAllowed) {
if (pipeline.hasService[TrapService]) {
trap(TrapCause.StoreAccessFault(address), storeStage)
}
}

when(arbitration.isValid && !misaligned && pmpAllowed) {
when(isActive) {
val wValue = value(pipeline.data.RS2_DATA)
arbitration.rs2Needed := True
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ class ReorderBuffer(
private val fenceDetectedNext = Bool()
private val fenceDetected = RegNext(fenceDetectedNext).init(False)

val isAvailable = (!isFull || willRetire) && !fenceDetectedNext
private val csrDetectedNext = Bool()
private val csrDetected = RegNext(csrDetectedNext).init(False)

val isAvailable = (!isFull || willRetire) && !fenceDetectedNext && !csrDetectedNext

val lastSpeculativeCFInstruction = Reg(Flow(UInt(indexBits)))

Expand Down Expand Up @@ -165,6 +168,7 @@ class ReorderBuffer(
isFull := False
lastSpeculativeCFInstruction.setIdle()
fenceDetected := False
csrDetected := False
softResetTrigger.setIdle()
for (nth <- 0 until capacity) {
robEntries(nth).invalidated := False
Expand Down Expand Up @@ -293,6 +297,11 @@ class ReorderBuffer(
fenceDetected := True
}

when(pipeline.service[CsrService].isCsrInstruction(pushedEntry.registerMap)) {
// CSR instructions are treated like fences (following instructions only start executing after the CSR instruction retires)
csrDetected := True
}

pipeline.serviceOption[DataSpeculationService] foreach { spec =>
spec.isSsbSpeculative(pushedEntry.registerMap) := False
}
Expand Down Expand Up @@ -715,6 +724,7 @@ class ReorderBuffer(
def build(): Unit = {
isFullNext := isFull
fenceDetectedNext := fenceDetected
csrDetectedNext := csrDetected
val oldestEntry = robEntries(oldestIndex.value)
val updatedOldestIndex = UInt(indexBits)
updatedOldestIndex := oldestIndex.value
Expand All @@ -728,6 +738,10 @@ class ReorderBuffer(
fenceDetectedNext := False
}

when(pipeline.service[CsrService].isCsrInstruction(oldestEntry.registerMap)) {
csrDetectedNext := False
}

for (register <- retirementRegisters.keys) {
ret.input(register) := oldestEntry.registerMap.element(register)
}
Expand Down
Loading