diff --git a/src/main/scala/vexriscv/Riscv.scala b/src/main/scala/vexriscv/Riscv.scala index a97f114..06e666d 100644 --- a/src/main/scala/vexriscv/Riscv.scala +++ b/src/main/scala/vexriscv/Riscv.scala @@ -105,6 +105,7 @@ object Riscv{ def FENCE = M"-----------------000-----0001111" def FENCE_I = M"-----------------001-----0001111" + def SFENCE_VMA = M"0001001----------000000001110011" object CSR{ def MVENDORID = 0xF11 // MRO Vendor ID. diff --git a/src/main/scala/vexriscv/Services.scala b/src/main/scala/vexriscv/Services.scala index 6f7d641..5e4e2eb 100644 --- a/src/main/scala/vexriscv/Services.scala +++ b/src/main/scala/vexriscv/Services.scala @@ -65,8 +65,8 @@ case class MemoryTranslatorRsp() extends Bundle{ val physicalAddress = UInt(32 bits) val isIoAccess = Bool val allowRead, allowWrite, allowExecute, allowUser = Bool - val miss = Bool - val hit = Bool + val exception = Bool + val refilling = Bool } case class MemoryTranslatorBus() extends Bundle with IMasterSlave{ diff --git a/src/main/scala/vexriscv/plugin/MmuPlugin.scala b/src/main/scala/vexriscv/plugin/MmuPlugin.scala new file mode 100644 index 0000000..42a99cc --- /dev/null +++ b/src/main/scala/vexriscv/plugin/MmuPlugin.scala @@ -0,0 +1,221 @@ +package vexriscv.plugin + +import vexriscv.{VexRiscv, _} +import spinal.core._ +import spinal.lib._ + +import scala.collection.mutable.ArrayBuffer + +case class DBusAccessCmd() extends Bundle { + val address = UInt(32 bits) + val size = UInt(2 bits) + val write = Bool + val writeData = Bits(32 bits) + val writeMask = Bits(4 bits) +} + +case class DBusAccessRsp() extends Bundle { + val data = Bits(32 bits) + val error = Bool() +} + +case class DBusAccess() extends Bundle { + val cmd = Stream(DBusAccessCmd()) + val rsp = Flow(DBusAccessRsp()) +} + + +object MmuPort{ + val PRIORITY_DATA = 1 + val PRIORITY_INSTRUCTION = 0 +} +case class MmuPort(bus : MemoryTranslatorBus, priority : Int, args : MmuPortConfig, id : Int/*, exceptionBus: Flow[ExceptionCause]*/) + +case class MmuPortConfig(portTlbSize : Int) + +class MmuPlugin(virtualRange : UInt => Bool, + ioRange : UInt => Bool, + allowUserIo : Boolean) extends Plugin[VexRiscv] with MemoryTranslator { + + var dBus : DBusAccess = null + val portsInfo = ArrayBuffer[MmuPort]() + + override def newTranslationPort(priority : Int,args : Any): MemoryTranslatorBus = { +// val exceptionBus = pipeline.service(classOf[ExceptionService]).newExceptionPort(stage) + val port = MmuPort(MemoryTranslatorBus(),priority,args.asInstanceOf[MmuPortConfig], portsInfo.length /*,exceptionBus*/) + portsInfo += port + port.bus + } + + object IS_SFENCE_VMA extends Stageable(Bool) + override def setup(pipeline: VexRiscv): Unit = { + import Riscv._ + import pipeline.config._ + val decoderService = pipeline.service(classOf[DecoderService]) + decoderService.addDefault(IS_SFENCE_VMA, False) + decoderService.add(SFENCE_VMA, List(IS_SFENCE_VMA -> True)) + + + dBus = ??? + } + + override def build(pipeline: VexRiscv): Unit = { + import pipeline._ + import pipeline.config._ + import Riscv._ + val csrService = pipeline.service(classOf[CsrInterface]) + + //Sorted by priority + val sortedPortsInfo = portsInfo.sortWith((a,b) => a.priority > b.priority) + + case class CacheLine() extends Bundle { + val valid, exception = Bool + val virtualAddress = UInt(20 bits) + val physicalAddress = UInt(20 bits) + val allowRead, allowWrite, allowExecute, allowUser = Bool + + def init = { + valid init (False) + this + } + } + + val core = pipeline plug new Area { + val ports = for (port <- sortedPortsInfo yield new Area { + val id = port.id + val cache = Vec(Reg(CacheLine()) init, port.args.portTlbSize) + val cacheHits = cache.map(line => line.valid && line.virtualAddress === port.bus.cmd.virtualAddress(31 downto 12)) + val cacheHit = cacheHits.asBits.orR + val cacheLine = MuxOH(cacheHits, cache) + val isInMmuRange = virtualRange(port.bus.cmd.virtualAddress) && !port.bus.cmd.bypassTranslation + val entryToReplace = Counter(port.args.portTlbSize) + + + when(isInMmuRange) { + port.bus.rsp.physicalAddress := cacheLine.physicalAddress @@ port.bus.cmd.virtualAddress(11 downto 0) + port.bus.rsp.allowRead := cacheLine.allowRead + port.bus.rsp.allowWrite := cacheLine.allowWrite + port.bus.rsp.allowExecute := cacheLine.allowExecute + port.bus.rsp.allowUser := cacheLine.allowUser + port.bus.rsp.exception := cacheHit && cacheLine.exception + port.bus.rsp.refilling := !cacheHit + + } otherwise { + port.bus.rsp.physicalAddress := port.bus.cmd.virtualAddress + port.bus.rsp.allowRead := True + port.bus.rsp.allowWrite := True + port.bus.rsp.allowExecute := True + port.bus.rsp.allowUser := Bool(allowUserIo) + port.bus.rsp.exception := False + port.bus.rsp.refilling := False + } + port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress) + } + + val shared = new Area { + val busy = Reg(Bool) init(False) + + val satp = new Bundle { + val mode = Bool() + val ppn = UInt(20 bits) + } + csrService.rw(CSR.SATP, 31 -> satp.mode, 0 -> satp.ppn) //TODO write only ? + val State = new SpinalEnum{ + val IDLE, L1_CMD, L1_RSP, L0_CMD, L0_RSP = newElement() + } + val state = RegInit(State.IDLE) + val vpn1, vpn0 = Reg(UInt(10 bits)) + val portId = Reg(UInt(log2Up(portsInfo.length) bits)) + case class PTE() extends Bundle { + val V, R, W ,X, U, G, A, D = Bool() + val RSW = Bits(2 bits) + val PPN0 = UInt(10 bits) + val PPN1 = UInt(12 bits) + } + val dBusRsp = new Area{ + val pte = PTE() + pte.assignFromBits(dBus.rsp.data) + val exception = !pte.V || (!pte.R && pte.W) || dBus.rsp.error + val leaf = pte.R || pte.X + } + + val pteBuffer = RegNextWhen(dBusRsp.pte, dBus.rsp.valid) + + dBus.cmd.write := False + dBus.cmd.size := 2 + dBus.cmd.address.assignDontCare() + dBus.cmd.writeData.assignDontCare() + dBus.cmd.writeMask.assignDontCare() + switch(state){ + is(State.IDLE){ + for(port <- portsInfo.sortBy(_.priority)){ + when(port.bus.cmd.isValid && port.bus.rsp.refilling){ + busy := True + vpn1 := port.bus.cmd.virtualAddress(31 downto 22) + vpn0 := port.bus.cmd.virtualAddress(21 downto 12) + portId := port.id + state := State.L1_CMD + } + } + } + is(State.L1_CMD){ + dBus.cmd.valid := True + dBus.cmd.address := satp.ppn @@ vpn1 @@ U"00" + when(dBus.cmd.ready){ + state := State.L1_RSP + } + } + is(State.L1_RSP){ + when(dBus.rsp.valid){ + when(dBusRsp.leaf || dBusRsp.exception){ + state := State.IDLE + } otherwise { + state := State.L0_CMD + } + } + } + is(State.L0_CMD){ + dBus.cmd.valid := True + dBus.cmd.address := pteBuffer.PPN1(9 downto 0) @@ pteBuffer.PPN0 @@ vpn0 @@ U"00" + when(dBus.cmd.ready){ + state := State.L0_RSP + } + } + is(State.L0_RSP){ + when(dBus.rsp.valid) { + state := State.IDLE + } + } + } + + when(dBus.rsp.valid && (dBusRsp.leaf || dBusRsp.exception)){ + for(port <- ports){ + when(portId === port.id) { + port.entryToReplace.increment() + for ((line, lineId) <- port.cache.zipWithIndex) { + when(port.entryToReplace === lineId){ + line.valid := True + line.exception := dBusRsp.exception + line.virtualAddress := vpn1 @@ vpn0 + line.physicalAddress := dBusRsp.pte.PPN1(9 downto 0) @@ dBusRsp.pte.PPN0 + line.allowRead := dBusRsp.pte.R + line.allowWrite := dBusRsp.pte.W + line.allowExecute := dBusRsp.pte.X + line.allowUser := dBusRsp.pte.U + } + } + } + } + } + } + } + + execute plug new Area{ + import execute._ + val tlbWriteBuffer = Reg(UInt(20 bits)) + when(arbitration.isFiring && input(IS_SFENCE_VMA)){ + for(port <- core.ports; line <- port.cache) line.valid := False //Assume that the instruction already fetched into the pipeline are ok + } + } + } +}