mirror of
https://github.com/SpinalHDL/VexRiscv.git
synced 2025-01-03 03:43:39 -05:00
Initial commit of PMP plugin
This commit is contained in:
parent
d490f903ea
commit
fc2c8a7c37
3 changed files with 292 additions and 0 deletions
85
src/main/scala/vexriscv/demo/GenZephyr.scala
Normal file
85
src/main/scala/vexriscv/demo/GenZephyr.scala
Normal file
|
@ -0,0 +1,85 @@
|
|||
package vexriscv.demo
|
||||
|
||||
import vexriscv.plugin._
|
||||
import vexriscv.ip.{DataCacheConfig, InstructionCacheConfig}
|
||||
import vexriscv.{plugin, VexRiscv, VexRiscvConfig}
|
||||
import spinal.core._
|
||||
|
||||
object GenZephyr extends App{
|
||||
def cpu() = new VexRiscv(
|
||||
config = VexRiscvConfig(
|
||||
plugins = List(
|
||||
new IBusCachedPlugin(
|
||||
prediction = STATIC,
|
||||
config = InstructionCacheConfig(
|
||||
cacheSize = 4096,
|
||||
bytePerLine = 32,
|
||||
wayCount = 1,
|
||||
addressWidth = 32,
|
||||
cpuDataWidth = 32,
|
||||
memDataWidth = 32,
|
||||
catchIllegalAccess = true,
|
||||
catchAccessFault = true,
|
||||
asyncTagMemory = false,
|
||||
twoCycleRam = true,
|
||||
twoCycleCache = true
|
||||
)
|
||||
),
|
||||
new DBusCachedPlugin(
|
||||
config = new DataCacheConfig(
|
||||
cacheSize = 4096,
|
||||
bytePerLine = 32,
|
||||
wayCount = 1,
|
||||
addressWidth = 32,
|
||||
cpuDataWidth = 32,
|
||||
memDataWidth = 32,
|
||||
catchAccessError = true,
|
||||
catchIllegal = true,
|
||||
catchUnaligned = true
|
||||
)
|
||||
),
|
||||
new PmpPlugin(
|
||||
regions = 16,
|
||||
ioRange = _(31 downto 28) === 0xF
|
||||
),
|
||||
new DecoderSimplePlugin(
|
||||
catchIllegalInstruction = true
|
||||
),
|
||||
new RegFilePlugin(
|
||||
regFileReadyKind = plugin.SYNC,
|
||||
zeroBoot = false
|
||||
),
|
||||
new IntAluPlugin,
|
||||
new SrcPlugin(
|
||||
separatedAddSub = false,
|
||||
executeInsertion = true
|
||||
),
|
||||
new FullBarrelShifterPlugin,
|
||||
new HazardSimplePlugin(
|
||||
bypassExecute = true,
|
||||
bypassMemory = true,
|
||||
bypassWriteBack = true,
|
||||
bypassWriteBackBuffer = true,
|
||||
pessimisticUseSrc = false,
|
||||
pessimisticWriteRegFile = false,
|
||||
pessimisticAddressMatch = false
|
||||
),
|
||||
new MulDivIterativePlugin(
|
||||
genMul = true,
|
||||
genDiv = true,
|
||||
mulUnrollFactor = 1,
|
||||
divUnrollFactor = 1
|
||||
),
|
||||
new CsrPlugin(CsrPluginConfig.zephyr(0x00000020l)),
|
||||
new DebugPlugin(ClockDomain.current.clone(reset = Bool().setName("debugReset"))),
|
||||
new BranchPlugin(
|
||||
earlyBranch = false,
|
||||
catchAddressMisaligned = true
|
||||
),
|
||||
new YamlPlugin("cpu0.yaml")
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
SpinalVerilog(cpu())
|
||||
}
|
|
@ -264,6 +264,31 @@ object CsrPluginConfig{
|
|||
uinstretAccess = CsrAccess.NONE
|
||||
)
|
||||
|
||||
def zephyr(mtvecInit : BigInt) = CsrPluginConfig(
|
||||
catchIllegalAccess = true,
|
||||
mvendorid = 1,
|
||||
marchid = 2,
|
||||
mimpid = 3,
|
||||
mhartid = 0,
|
||||
misaExtensionsInit = 0,
|
||||
misaAccess = CsrAccess.NONE,
|
||||
mtvecAccess = CsrAccess.READ_WRITE,
|
||||
mtvecInit = mtvecInit,
|
||||
mepcAccess = CsrAccess.READ_WRITE,
|
||||
mscratchGen = true,
|
||||
mcauseAccess = CsrAccess.READ_ONLY,
|
||||
mbadaddrAccess = CsrAccess.READ_ONLY,
|
||||
mcycleAccess = CsrAccess.NONE,
|
||||
minstretAccess = CsrAccess.NONE,
|
||||
ucycleAccess = CsrAccess.NONE,
|
||||
uinstretAccess = CsrAccess.NONE,
|
||||
wfiGenAsWait = true,
|
||||
ecallGen = true,
|
||||
userGen = true,
|
||||
medelegAccess = CsrAccess.READ_WRITE,
|
||||
midelegAccess = CsrAccess.READ_WRITE
|
||||
)
|
||||
|
||||
}
|
||||
case class CsrWrite(that : Data, bitOffset : Int)
|
||||
case class CsrRead(that : Data , bitOffset : Int)
|
||||
|
|
182
src/main/scala/vexriscv/plugin/PmpPlugin.scala
Normal file
182
src/main/scala/vexriscv/plugin/PmpPlugin.scala
Normal file
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Samuel Lindemer <samuel.lindemer@ri.se>
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
package vexriscv.plugin
|
||||
|
||||
import vexriscv.{VexRiscv, _}
|
||||
import spinal.core._
|
||||
import spinal.lib._
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
/* Each 32-bit pmpcfg# register contains four 8-bit configuration sections.
|
||||
* These section numbers contain flags which apply to region defined by the
|
||||
* corresponding pmpaddr# register.
|
||||
*
|
||||
* 3 2 1
|
||||
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | pmp3cfg | pmp2cfg | pmp1cfg | pmp0cfg | pmpcfg0
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | pmp7cfg | pmp6cfg | pmp5cfg | pmp4cfg | pmpcfg2
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* 7 6 5 4 3 2 1 0
|
||||
* +-------+-------+-------+-------+-------+-------+-------+-------+
|
||||
* | L | 0 | A | X | W | R | pmp#cfg
|
||||
* +-------+-------+-------+-------+-------+-------+-------+-------+
|
||||
*
|
||||
* L: locks configuration until system reset (including M-mode)
|
||||
* 0: hardwired to zero
|
||||
* A: 0 = OFF (null region / disabled)
|
||||
* 1 = TOR (top of range)
|
||||
* 2 = NA4 (naturally aligned four-byte region)
|
||||
* 3 = NAPOT (naturally aligned power-of-two region, > 7 bytes)
|
||||
* X: execute
|
||||
* W: write
|
||||
* R: read
|
||||
*
|
||||
* TOR: Each 32-bit pmpaddr# register defines the upper bound of the pmp region
|
||||
* right-shifted by two bits. The lower bound of the region is the previous
|
||||
* pmpaddr# register. In the case of pmpaddr0, the lower bound is address 0x0.
|
||||
*
|
||||
* 3 2 1
|
||||
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | address[33:2] | pmpaddr#
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* NAPOT: Each 32-bit pmpaddr# register defines the regionStart address and the size
|
||||
* of the pmp region. The number of concurrent 1s begging at the LSB indicates
|
||||
* the size of the region as a power of two (e.g. 0x...0 = 8-byte, 0x...1 =
|
||||
* 16-byte, 0x...11 = 32-byte, etc.).
|
||||
*
|
||||
* 3 2 1
|
||||
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | address[33:2] |0|1|1|1|1| pmpaddr#
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* NA4: This is essentially an edge case of NAPOT where the entire pmpaddr#
|
||||
* register defines a 4-byte wide region.
|
||||
*/
|
||||
|
||||
case class PmpRegister() extends Bundle {
|
||||
|
||||
// CSR segments
|
||||
val x, w, r = Reg(Bool)
|
||||
val l = Reg(Bool) init(False)
|
||||
val a = Reg(UInt(2 bits)) init(0)
|
||||
val address = Reg(UInt(32 bits))
|
||||
|
||||
// Computed region bounds
|
||||
val regionStart = UInt(32 bits)
|
||||
val regionEnd = UInt(32 bits)
|
||||
val valid = Bool
|
||||
|
||||
// Addressing options
|
||||
def NA4 = 2
|
||||
def NAPOT = 3
|
||||
|
||||
switch(a) {
|
||||
is(NA4) {
|
||||
regionStart := address |<< 2
|
||||
regionEnd := regionStart + 4
|
||||
valid := True
|
||||
}
|
||||
is(NAPOT) {
|
||||
val mask = address & ~(address + 1)
|
||||
regionStart := (address & ~mask) |<< 2
|
||||
regionEnd := regionStart + ((mask + 1) |<< 3)
|
||||
valid := True
|
||||
}
|
||||
default {
|
||||
regionStart := 0
|
||||
regionEnd := 0
|
||||
valid := False
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
case class ProtectedMemoryTranslatorPort(bus : MemoryTranslatorBus)
|
||||
|
||||
class PmpPlugin(regions : Int, ioRange : UInt => Bool) extends Plugin[VexRiscv] with MemoryTranslator {
|
||||
|
||||
// Each pmpcfg# CSR configures four regions.
|
||||
assert((regions % 4) == 0)
|
||||
|
||||
val pmps = ArrayBuffer[PmpRegister]()
|
||||
val portsInfo = ArrayBuffer[ProtectedMemoryTranslatorPort]()
|
||||
|
||||
override def newTranslationPort(priority : Int, args : Any): MemoryTranslatorBus = {
|
||||
val port = ProtectedMemoryTranslatorPort(MemoryTranslatorBus())
|
||||
portsInfo += port
|
||||
port.bus
|
||||
}
|
||||
|
||||
override def build(pipeline: VexRiscv): Unit = {
|
||||
import pipeline._
|
||||
import pipeline.config._
|
||||
import Riscv._
|
||||
|
||||
val csrService = pipeline.service(classOf[CsrInterface])
|
||||
val privilegeService = pipeline.service(classOf[PrivilegeService])
|
||||
|
||||
val core = pipeline plug new Area {
|
||||
|
||||
// Instantiate pmpaddr0 ... pmpaddr# CSRs.
|
||||
for (n <- 0 until regions) {
|
||||
pmps += PmpRegister()
|
||||
csrService.rw(0x3B0 + n, pmps(n).address)
|
||||
}
|
||||
|
||||
// Instantiate pmpcfg0 ... pmpcfg# CSRs.
|
||||
for (n <- 0 until (regions / 4)) {
|
||||
csrService.rw(0x3A0 + n,
|
||||
31 -> pmps((n * 4) + 3).l, 27 -> pmps((n * 4) + 3).a,
|
||||
26 -> pmps((n * 4) + 3).x, 25 -> pmps((n * 4) + 3).w, 24 -> pmps((n * 4) + 3).r,
|
||||
23 -> pmps((n * 4) + 2).l, 19 -> pmps((n * 4) + 2).a,
|
||||
18 -> pmps((n * 4) + 2).x, 17 -> pmps((n * 4) + 2).w, 16 -> pmps((n * 4) + 2).r,
|
||||
15 -> pmps((n * 4) + 1).l, 11 -> pmps((n * 4) + 1).a,
|
||||
10 -> pmps((n * 4) + 1).x, 9 -> pmps((n * 4) + 1).w, 8 -> pmps((n * 4) + 1).r,
|
||||
7 -> pmps((n * 4) ).l, 3 -> pmps((n * 4) ).a,
|
||||
2 -> pmps((n * 4) ).x, 1 -> pmps((n * 4) ).w, 0 -> pmps((n * 4) ).r
|
||||
)
|
||||
}
|
||||
|
||||
// Connect memory ports to PMP logic.
|
||||
val ports = for ((port, portId) <- portsInfo.zipWithIndex) yield new Area {
|
||||
|
||||
val address = port.bus.cmd.virtualAddress
|
||||
port.bus.rsp.physicalAddress := address
|
||||
|
||||
// Only the first matching PMP region is applied.
|
||||
val hits = pmps.map(pmp => pmp.valid &&
|
||||
pmp.regionStart <= address &&
|
||||
pmp.regionEnd > address &&
|
||||
(pmp.l || ~privilegeService.isMachine()))
|
||||
|
||||
// M-mode has full access by default. All others have none by default.
|
||||
when(CountOne(hits) === 0) {
|
||||
port.bus.rsp.allowRead := privilegeService.isMachine()
|
||||
port.bus.rsp.allowWrite := privilegeService.isMachine()
|
||||
port.bus.rsp.allowExecute := privilegeService.isMachine()
|
||||
} otherwise {
|
||||
port.bus.rsp.allowRead := MuxOH(OHMasking.first(hits), pmps).r
|
||||
port.bus.rsp.allowWrite := MuxOH(OHMasking.first(hits), pmps).w
|
||||
port.bus.rsp.allowExecute := MuxOH(OHMasking.first(hits), pmps).x
|
||||
}
|
||||
|
||||
port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress)
|
||||
port.bus.rsp.exception := False
|
||||
port.bus.rsp.refilling := False
|
||||
port.bus.busy := False
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in a new issue