aboutsummaryrefslogtreecommitdiff
path: root/VexRiscv/src/main/scala/vexriscv/plugin/PmpPluginOld.scala
diff options
context:
space:
mode:
Diffstat (limited to 'VexRiscv/src/main/scala/vexriscv/plugin/PmpPluginOld.scala')
-rw-r--r--VexRiscv/src/main/scala/vexriscv/plugin/PmpPluginOld.scala244
1 files changed, 244 insertions, 0 deletions
diff --git a/VexRiscv/src/main/scala/vexriscv/plugin/PmpPluginOld.scala b/VexRiscv/src/main/scala/vexriscv/plugin/PmpPluginOld.scala
new file mode 100644
index 0000000..0426902
--- /dev/null
+++ b/VexRiscv/src/main/scala/vexriscv/plugin/PmpPluginOld.scala
@@ -0,0 +1,244 @@
+/*
+ * 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 regions 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 region 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(previous : PmpRegister) extends Area {
+
+ def OFF = 0
+ def TOR = 1
+ def NA4 = 2
+ def NAPOT = 3
+
+ val state = new Area {
+ val r, w, x = Reg(Bool)
+ val l = RegInit(False)
+ val a = Reg(UInt(2 bits)) init(0)
+ val addr = Reg(UInt(32 bits))
+ }
+
+ // CSR writes connect to these signals rather than the internal state
+ // registers. This makes locking and WARL possible.
+ val csr = new Area {
+ val r, w, x = Bool
+ val l = Bool
+ val a = UInt(2 bits)
+ val addr = UInt(32 bits)
+ }
+
+ // Last valid assignment wins; nothing happens if a user-initiated write did
+ // not occur on this clock cycle.
+ csr.r := state.r
+ csr.w := state.w
+ csr.x := state.x
+ csr.l := state.l
+ csr.a := state.a
+ csr.addr := state.addr
+
+ // Computed PMP region bounds
+ val region = new Area {
+ val valid, locked = Bool
+ val start, end = UInt(32 bits)
+ }
+
+ when(~state.l) {
+ state.r := csr.r
+ state.w := csr.w
+ state.x := csr.x
+ state.l := csr.l
+ state.a := csr.a
+ state.addr := csr.addr
+
+ if (csr.l == True & csr.a == TOR) {
+ previous.state.l := True
+ }
+ }
+
+ val shifted = state.addr |<< 2
+ val mask = state.addr & ~(state.addr + 1)
+ val masked = (state.addr & ~mask) |<< 2
+
+ // PMP changes take effect two clock cycles after the initial CSR write (i.e.,
+ // settings propagate from csr -> state -> region).
+ region.locked := state.l
+ region.valid := True
+
+ switch(csr.a) {
+ is(TOR) {
+ if (previous == null) region.start := 0
+ else region.start := previous.region.end
+ region.end := shifted
+ }
+ is(NA4) {
+ region.start := shifted
+ region.end := shifted + 4
+ }
+ is(NAPOT) {
+ region.start := masked
+ region.end := masked + ((mask + 1) |<< 3)
+ }
+ default {
+ region.start := 0
+ region.end := shifted
+ region.valid := False
+ }
+ }
+}
+
+
+class PmpPluginOld(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(new MemoryTranslatorBusParameter(0, 0)))
+ portsInfo += port
+ port.bus
+ }
+
+ override def build(pipeline: VexRiscv): Unit = {
+ import pipeline.config._
+ import pipeline._
+ 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 (i <- 0 until regions) {
+ if (i == 0) {
+ pmps += PmpRegister(null)
+ } else {
+ pmps += PmpRegister(pmps.last)
+ }
+ csrService.r(0x3b0 + i, pmps(i).state.addr)
+ csrService.w(0x3b0 + i, pmps(i).csr.addr)
+ }
+
+ // Instantiate pmpcfg0 ... pmpcfg# CSRs.
+ for (i <- 0 until (regions / 4)) {
+ csrService.r(0x3a0 + i,
+ 31 -> pmps((i * 4) + 3).state.l, 23 -> pmps((i * 4) + 2).state.l,
+ 15 -> pmps((i * 4) + 1).state.l, 7 -> pmps((i * 4) ).state.l,
+ 27 -> pmps((i * 4) + 3).state.a, 26 -> pmps((i * 4) + 3).state.x,
+ 25 -> pmps((i * 4) + 3).state.w, 24 -> pmps((i * 4) + 3).state.r,
+ 19 -> pmps((i * 4) + 2).state.a, 18 -> pmps((i * 4) + 2).state.x,
+ 17 -> pmps((i * 4) + 2).state.w, 16 -> pmps((i * 4) + 2).state.r,
+ 11 -> pmps((i * 4) + 1).state.a, 10 -> pmps((i * 4) + 1).state.x,
+ 9 -> pmps((i * 4) + 1).state.w, 8 -> pmps((i * 4) + 1).state.r,
+ 3 -> pmps((i * 4) ).state.a, 2 -> pmps((i * 4) ).state.x,
+ 1 -> pmps((i * 4) ).state.w, 0 -> pmps((i * 4) ).state.r
+ )
+ csrService.w(0x3a0 + i,
+ 31 -> pmps((i * 4) + 3).csr.l, 23 -> pmps((i * 4) + 2).csr.l,
+ 15 -> pmps((i * 4) + 1).csr.l, 7 -> pmps((i * 4) ).csr.l,
+ 27 -> pmps((i * 4) + 3).csr.a, 26 -> pmps((i * 4) + 3).csr.x,
+ 25 -> pmps((i * 4) + 3).csr.w, 24 -> pmps((i * 4) + 3).csr.r,
+ 19 -> pmps((i * 4) + 2).csr.a, 18 -> pmps((i * 4) + 2).csr.x,
+ 17 -> pmps((i * 4) + 2).csr.w, 16 -> pmps((i * 4) + 2).csr.r,
+ 11 -> pmps((i * 4) + 1).csr.a, 10 -> pmps((i * 4) + 1).csr.x,
+ 9 -> pmps((i * 4) + 1).csr.w, 8 -> pmps((i * 4) + 1).csr.r,
+ 3 -> pmps((i * 4) ).csr.a, 2 -> pmps((i * 4) ).csr.x,
+ 1 -> pmps((i * 4) ).csr.w, 0 -> pmps((i * 4) ).csr.r
+ )
+ }
+
+ // Connect memory ports to PMP logic.
+ val ports = for ((port, portId) <- portsInfo.zipWithIndex) yield new Area {
+
+ val address = port.bus.cmd(0).virtualAddress
+ port.bus.rsp.physicalAddress := address
+
+ // Only the first matching PMP region applies.
+ val hits = pmps.map(pmp => pmp.region.valid &
+ pmp.region.start <= address &
+ pmp.region.end > address &
+ (pmp.region.locked | ~privilegeService.isMachine()))
+
+ // M-mode has full access by default, others have none.
+ 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.map(_.state.r))
+ port.bus.rsp.allowWrite := MuxOH(OHMasking.first(hits), pmps.map(_.state.w))
+ port.bus.rsp.allowExecute := MuxOH(OHMasking.first(hits), pmps.map(_.state.x))
+ }
+
+ port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress)
+ port.bus.rsp.isPaging := False
+ port.bus.rsp.exception := False
+ port.bus.rsp.refilling := False
+ port.bus.busy := False
+
+ }
+ }
+ }
+}