hardware refilled MmuPlugin wip
This commit is contained in:
parent
03663ce91a
commit
ffa489d211
|
@ -105,6 +105,7 @@ object Riscv{
|
||||||
|
|
||||||
def FENCE = M"-----------------000-----0001111"
|
def FENCE = M"-----------------000-----0001111"
|
||||||
def FENCE_I = M"-----------------001-----0001111"
|
def FENCE_I = M"-----------------001-----0001111"
|
||||||
|
def SFENCE_VMA = M"0001001----------000000001110011"
|
||||||
|
|
||||||
object CSR{
|
object CSR{
|
||||||
def MVENDORID = 0xF11 // MRO Vendor ID.
|
def MVENDORID = 0xF11 // MRO Vendor ID.
|
||||||
|
|
|
@ -65,8 +65,8 @@ case class MemoryTranslatorRsp() extends Bundle{
|
||||||
val physicalAddress = UInt(32 bits)
|
val physicalAddress = UInt(32 bits)
|
||||||
val isIoAccess = Bool
|
val isIoAccess = Bool
|
||||||
val allowRead, allowWrite, allowExecute, allowUser = Bool
|
val allowRead, allowWrite, allowExecute, allowUser = Bool
|
||||||
val miss = Bool
|
val exception = Bool
|
||||||
val hit = Bool
|
val refilling = Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
case class MemoryTranslatorBus() extends Bundle with IMasterSlave{
|
case class MemoryTranslatorBus() extends Bundle with IMasterSlave{
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue