From bc561c30eb4d1ea46b9264e46fd14addf6e60879 Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Wed, 1 Sep 2021 11:27:12 +0200 Subject: [PATCH] Add PmpPluginOld (support TOR) --- .../scala/vexriscv/plugin/PmpPluginOld.scala | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 src/main/scala/vexriscv/plugin/PmpPluginOld.scala diff --git a/src/main/scala/vexriscv/plugin/PmpPluginOld.scala b/src/main/scala/vexriscv/plugin/PmpPluginOld.scala new file mode 100644 index 0000000..0426902 --- /dev/null +++ b/src/main/scala/vexriscv/plugin/PmpPluginOld.scala @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2020 Samuel Lindemer + * + * 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 + + } + } + } +}