Fix multi port MMU design

Change machineCSR to handle exceptions from the writeBack stage
Change the DBusCachedPlugin to emit miss exception
This commit is contained in:
Charles Papon 2017-05-01 14:29:37 +02:00
parent 2ed33106d6
commit 889a040f90
5 changed files with 187 additions and 148 deletions

View File

@ -12,20 +12,23 @@ case class DataCacheConfig( cacheSize : Int,
addressWidth : Int,
cpuDataWidth : Int,
memDataWidth : Int,
catchAccessFault : Boolean = false,
catchAccessFault : Boolean,
catchMemoryTranslationMiss : Boolean,
tagSizeShift : Int = 0){ //Used to force infering ram
def burstSize = bytePerLine*8/memDataWidth
val burstLength = bytePerLine/(memDataWidth/8)
assert(catchAccessFault == false)
def catchSomething = catchAccessFault || catchMemoryTranslationMiss
}
case class DataMmuConfig(dTlbSize : Int)
class DBusCachedPlugin(config : DataCacheConfig, mmuConfig : DataMmuConfig = null) extends Plugin[VexRiscv]{
class DBusCachedPlugin(config : DataCacheConfig, askMemoryTranslation : Boolean = false, memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv]{
import config._
var dBus : DataCacheMemBus = null
var mmuBus : MemoryTranslatorBus = null
var exceptionBus : Flow[ExceptionCause] = null
object MEMORY_ENABLE extends Stageable(Bool)
object MEMORY_ADDRESS_LOW extends Stageable(UInt(2 bits))
@ -60,8 +63,11 @@ class DBusCachedPlugin(config : DataCacheConfig, mmuConfig : DataMmuConfig = nul
List(SB, SH, SW).map(_ -> storeActions)
)
if(mmuConfig != null)
mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(pipeline.memory,mmuConfig.dTlbSize)
if(askMemoryTranslation != null)
mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(pipeline.memory,memoryTranslatorPortConfig)
if(catchSomething)
exceptionBus = pipeline.service(classOf[ExceptionService]).newExceptionPort(pipeline.writeBack)
}
override def build(pipeline: VexRiscv): Unit = {
@ -119,6 +125,7 @@ class DBusCachedPlugin(config : DataCacheConfig, mmuConfig : DataMmuConfig = nul
import writeBack._
cache.io.cpu.writeBack.isValid := arbitration.isValid && input(MEMORY_ENABLE)
cache.io.cpu.writeBack.isStuck := arbitration.isStuck
if(catchSomething) cache.io.cpu.writeBack.exceptionBus <> exceptionBus
arbitration.haltIt.setWhen(cache.io.cpu.writeBack.haltIt)
val rspShifted = Bits(32 bits)
@ -266,10 +273,12 @@ case class DataCacheCpuWriteBack(p : DataCacheConfig) extends Bundle with IMaste
val isStuck = Bool
val haltIt = Bool
val data = Bits(p.cpuDataWidth bit)
val exceptionBus = if(p.catchSomething) Flow(ExceptionCause()) else null
override def asMaster(): Unit = {
out(isValid,isStuck)
in(haltIt, data)
slaveWithNull(exceptionBus)
}
}
@ -310,6 +319,7 @@ case class DataCacheMemBus(p : DataCacheConfig) extends Bundle with IMasterSlave
class DataCache(p : DataCacheConfig) extends Component{
import p._
import DataCacheCpuCmdKind._
assert(wayCount == 1)
assert(cpuDataWidth == memDataWidth)
@ -531,7 +541,7 @@ class DataCache(p : DataCacheConfig) extends Component{
val stageA = new Area{
val request = RegNextWhen(io.cpu.execute.args, !io.cpu.memory.isStuck)
io.cpu.memory.mmuBus.cmd.isValid := io.cpu.memory.isValid //TODO filter request kind
io.cpu.memory.mmuBus.cmd.isValid := io.cpu.memory.isValid && request.kind === MEMORY //TODO filter request kind
io.cpu.memory.mmuBus.cmd.virtualAddress := request.address
}
@ -565,7 +575,12 @@ class DataCache(p : DataCacheConfig) extends Component{
val victimNotSent = RegInit(False) clearWhen(victim.requestIn.ready) setWhen(!io.cpu.memory.isStuck)
val loadingNotDone = RegInit(False) clearWhen(loaderReady) setWhen(!io.cpu.memory.isStuck)
import DataCacheCpuCmdKind._
if(catchSomething){
io.cpu.writeBack.exceptionBus.valid := False
io.cpu.writeBack.exceptionBus.code := 13
io.cpu.writeBack.exceptionBus.badAddr := request.address
}
when(requestValid) {
switch(request.kind) {
is(EVICT){
@ -631,41 +646,47 @@ class DataCache(p : DataCacheConfig) extends Component{
// }
// }
is(MEMORY) {
when(request.bypass) {
val memCmdSent = RegInit(False)
when(!victim.request.valid) { //Avoid mixing memory request while victim is pending
io.mem.cmd.wr := request.wr
io.mem.cmd.address := mmuRsp.physicalAddress(tagRange.high downto wordRange.low) @@ U(0,wordRange.low bit)
io.mem.cmd.mask := request.mask
io.mem.cmd.data := request.data
io.mem.cmd.length := 1
if (catchMemoryTranslationMiss) {
io.cpu.writeBack.exceptionBus.valid := mmuRsp.miss
}
when(Bool(!catchMemoryTranslationMiss) || !mmuRsp.miss) {
when(request.bypass) {
val memCmdSent = RegInit(False)
when(!victim.request.valid) {
//Avoid mixing memory request while victim is pending
io.mem.cmd.wr := request.wr
io.mem.cmd.address := mmuRsp.physicalAddress(tagRange.high downto wordRange.low) @@ U(0, wordRange.low bit)
io.mem.cmd.mask := request.mask
io.mem.cmd.data := request.data
io.mem.cmd.length := 1
when(!memCmdSent) {
io.mem.cmd.valid := True
memCmdSent setWhen(io.mem.cmd.ready)
when(!memCmdSent) {
io.mem.cmd.valid := True
memCmdSent setWhen (io.mem.cmd.ready)
}
io.cpu.writeBack.haltIt.clearWhen(memCmdSent && (io.mem.rsp.fire || request.wr)) //Cut mem.cmd.ready path but insert one cycle stall when write
}
io.cpu.writeBack.haltIt.clearWhen(memCmdSent && (io.mem.rsp.fire || request.wr)) //Cut mem.cmd.ready path but insert one cycle stall when write
}
memCmdSent clearWhen(!io.cpu.writeBack.isStuck)
} otherwise {
when(waysHit || !loadingNotDone){
io.cpu.writeBack.haltIt := False
dataWriteCmd.valid := request.wr
dataWriteCmd.address := mmuRsp.physicalAddress(lineRange.high downto wordRange.low)
dataWriteCmd.data := request.data
dataWriteCmd.mask := request.mask
tagsWriteCmd.valid := !loadingNotDone || request.wr
tagsWriteCmd.address := mmuRsp.physicalAddress(lineRange)
tagsWriteCmd.data.used := True
tagsWriteCmd.data.dirty := request.wr
tagsWriteCmd.data.address := mmuRsp.physicalAddress(tagRange)
memCmdSent clearWhen (!io.cpu.writeBack.isStuck)
} otherwise {
val victimRequired = way.tagReadRspTwo.used && way.tagReadRspTwo.dirty
loaderValid := loadingNotDone && !(victimNotSent && victim.request.isStall) //Additional condition used to be sure of that all previous victim are written into the RAM
victim.requestIn.valid := victimRequired && victimNotSent
victim.requestIn.address := way.tagReadRspTwo.address @@ mmuRsp.physicalAddress(lineRange) @@ U((lineRange.low - 1 downto 0) -> false)
when(waysHit || !loadingNotDone) {
io.cpu.writeBack.haltIt := False
dataWriteCmd.valid := request.wr
dataWriteCmd.address := mmuRsp.physicalAddress(lineRange.high downto wordRange.low)
dataWriteCmd.data := request.data
dataWriteCmd.mask := request.mask
tagsWriteCmd.valid := !loadingNotDone || request.wr
tagsWriteCmd.address := mmuRsp.physicalAddress(lineRange)
tagsWriteCmd.data.used := True
tagsWriteCmd.data.dirty := request.wr
tagsWriteCmd.data.address := mmuRsp.physicalAddress(tagRange)
} otherwise {
val victimRequired = way.tagReadRspTwo.used && way.tagReadRspTwo.dirty
loaderValid := loadingNotDone && !(victimNotSent && victim.request.isStall) //Additional condition used to be sure of that all previous victim are written into the RAM
victim.requestIn.valid := victimRequired && victimNotSent
victim.requestIn.address := way.tagReadRspTwo.address @@ mmuRsp.physicalAddress(lineRange) @@ U((lineRange.low - 1 downto 0) -> false)
}
}
}
}
@ -717,17 +738,17 @@ class DataCache(p : DataCacheConfig) extends Component{
object DataCacheMain{
def main(args: Array[String]) {
SpinalVhdl({
implicit val p = DataCacheConfig(
cacheSize =4096,
bytePerLine =32,
wayCount = 1,
addressWidth = 32,
cpuDataWidth = 32,
memDataWidth = 32)
new WrapWithReg.Wrapper(new DataCache(p)).setDefinitionName("TopLevel")
})
//
// SpinalVhdl({
// implicit val p = DataCacheConfig(
// cacheSize =4096,
// bytePerLine =32,
// wayCount = 1,
// addressWidth = 32,
// cpuDataWidth = 32,
// memDataWidth = 32)
// new WrapWithReg.Wrapper(new DataCache(p)).setDefinitionName("TopLevel")
// })
// SpinalVhdl({
// implicit val p = DataCacheConfig(
// cacheSize =512,

View File

@ -245,7 +245,6 @@ class MachineCsr(config : MachineCsrConfig) extends Plugin[VexRiscv] with Except
pipelineLiberator.enable setWhen(pipelineHasException)
val groupedByStage = exceptionPortsInfos.map(_.stage).distinct.map(s => {
assert(s != writeBack)
val stagePortsInfos = exceptionPortsInfos.filter(_.stage == s).sortWith(_.priority > _.priority)
val stagePort = stagePortsInfos.length match{
case 1 => stagePortsInfos.head.port
@ -309,8 +308,8 @@ class MachineCsr(config : MachineCsrConfig) extends Plugin[VexRiscv] with Except
True -> ((mip.MEIP && mie.MEIE) ? U(11) | ((mip.MSIP && mie.MSIE) ? U(3) | U(7))),
False -> (if(exceptionPortCtrl != null) exceptionPortCtrl.exceptionContext.code else U(0))
)
when(exception){
mbadaddr := exceptionPortCtrl.exceptionContext.badAddr
when(RegNext(exception)){
mbadaddr := (if(exceptionPortCtrl != null) exceptionPortCtrl.exceptionContext.badAddr else U(0))
}
}
@ -343,17 +342,22 @@ class MachineCsr(config : MachineCsrConfig) extends Plugin[VexRiscv] with Except
val imm = IMM(input(INSTRUCTION))
val writeEnable = arbitration.isValid && !arbitration.isStuckByOthers && !arbitration.removeIt && input(IS_CSR) &&
(!((input(INSTRUCTION)(14 downto 13) === "01" && input(INSTRUCTION)(rs1Range) === 0)
|| (input(INSTRUCTION)(14 downto 13) === "11" && imm.z === 0)))
val writeSrc = input(INSTRUCTION)(14) ? imm.z.asBits.resized | input(SRC1)
val readData = B(0, 32 bits)
val readDataReg = RegNext(readData)
val readDataRegValid = Reg(Bool) setWhen(arbitration.isValid) clearWhen(!arbitration.isStuck)
val writeData = input(INSTRUCTION)(13).mux(
False -> writeSrc,
True -> Mux(input(INSTRUCTION)(12), readData & ~writeSrc, readData | writeSrc)
True -> Mux(input(INSTRUCTION)(12), readDataReg & ~writeSrc, readDataReg | writeSrc)
)
val writeInstruction = arbitration.isValid && input(IS_CSR)
(!((input(INSTRUCTION)(14 downto 13) === "01" && input(INSTRUCTION)(rs1Range) === 0)
|| (input(INSTRUCTION)(14 downto 13) === "11" && imm.z === 0)))
arbitration.haltIt setWhen(writeInstruction && !readDataRegValid)
val writeEnable = writeInstruction && !arbitration.isStuckByOthers && !arbitration.removeIt && readDataRegValid
when(arbitration.isValid && input(IS_CSR)) {
output(REGFILE_WRITE_DATA) := readData
@ -361,21 +365,21 @@ class MachineCsr(config : MachineCsrConfig) extends Plugin[VexRiscv] with Except
//Translation of the csrMapping into real logic
val csrAddress = input(INSTRUCTION)(csrRange)
switch(input(INSTRUCTION)(csrRange)) {
for ((address, jobs) <- csrMapping.mapping) {
is(address) {
when(writeEnable) {
for (element <- jobs) element match {
case element: CsrWrite => element.that.assignFromBits(writeData(element.bitOffset, element.that.getBitsWidth bits))
case _ =>
}
}
for ((address, jobs) <- csrMapping.mapping) {
when(csrAddress === address) {
when(writeEnable) {
for (element <- jobs) element match {
case element: CsrWrite => element.that.assignFromBits(writeData(element.bitOffset, element.that.getBitsWidth bits))
case element: CsrRead if element.that.getBitsWidth != 0 => readData(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
case _ =>
}
}
for (element <- jobs) element match {
case element: CsrRead if element.that.getBitsWidth != 0 => readData(element.bitOffset, element.that.getBitsWidth bits) := element.that.asBits
case _ =>
}
}
}
}

View File

@ -5,16 +5,18 @@ import spinal.core._
import spinal.lib._
import scala.collection.mutable.ArrayBuffer
case class MemoryTranslatorPort(bus : MemoryTranslatorBus, stage : Stage, cacheSize : Int, exceptionBus: Flow[ExceptionCause])
case class MemoryTranslatorPort(bus : MemoryTranslatorBus, stage : Stage, args : MemoryTranslatorPortConfig/*, exceptionBus: Flow[ExceptionCause]*/)
class MemoryTranslatorPlugin(tlbSize : Int, exceptionCode : Int, mmuRange : UInt => Bool) extends Plugin[VexRiscv] with MemoryTranslator {
case class MemoryTranslatorPortConfig(portTlbSize : Int)
class MemoryTranslatorPlugin(tlbSize : Int, mmuRange : UInt => Bool) extends Plugin[VexRiscv] with MemoryTranslator {
assert(isPow2(tlbSize))
val portsInfo = ArrayBuffer[MemoryTranslatorPort]()
override def newTranslationPort(stage : Stage,cacheSize : Int): MemoryTranslatorBus = {
val exceptionBus = pipeline.service(classOf[ExceptionService]).newExceptionPort(stage)
val port = MemoryTranslatorPort(MemoryTranslatorBus(),stage,cacheSize,exceptionBus)
override def newTranslationPort(stage : Stage,args : Any): MemoryTranslatorBus = {
// val exceptionBus = pipeline.service(classOf[ExceptionService]).newExceptionPort(stage)
val port = MemoryTranslatorPort(MemoryTranslatorBus(),stage,args.asInstanceOf[MemoryTranslatorPortConfig]/*,exceptionBus*/)
portsInfo += port
port.bus
}
@ -54,76 +56,64 @@ class MemoryTranslatorPlugin(tlbSize : Int, exceptionCode : Int, mmuRange : UInt
val core = pipeline plug new Area {
val shared = new Area {
val cache = Mem(CacheLine(), tlbSize)
val cmd = Stream(UInt(log2Up(sortedPortsInfo.length) bits))
val rsp = new Bundle {
val portId = UInt()
val hit = Bool
val line = CacheLine()
}
val cmdVirtualAddress = RegNext(sortedPortsInfo.map(_.bus).read(cmd.payload).cmd.virtualAddress(31 downto 12))
val ptr = Counter(tlbSize)
val setup = RegNext(False) init (False)
val last = RegNext(ptr.willOverflowIfInc)
rsp.portId := RegNext(cmd.payload)
rsp.line := cache.readSync(ptr)
rsp.hit := rsp.line.valid && rsp.line.virtualAddress === cmdVirtualAddress
cmd.ready := !setup && (rsp.hit || last)
when(cmd.valid) {
ptr.increment()
}
when(!cmd.valid || cmd.ready || sortedPortsInfo.map(_.stage.arbitration.removeIt).read(cmd.payload)) {
ptr.clear()
last := False
setup := True
}
var free = True
val readAddr = cache.addressType.assignDontCare()
val readData = RegNext(cache.readSync(readAddr))
}
val ports = for ((port, portId) <- sortedPortsInfo.zipWithIndex) yield new Area {
val cache = Vec(Reg(CacheLine()) init, port.cacheSize)
val entryToReplace = Counter(port.cacheSize)
val sharedMiss = RegInit(False)
when(shared.cmd.fire && shared.rsp.portId === portId) {
cache(entryToReplace) := shared.rsp.line //TODO FMAX pipelining
sharedMiss := !shared.rsp.hit
entryToReplace.increment()
}
sharedMiss clearWhen (!port.stage.arbitration.isStuck)
val sharedRequest = Stream(UInt(log2Up(sortedPortsInfo.length) bits))
sharedRequest.valid := False
sharedRequest.payload := portId
port.exceptionBus.valid := False
port.exceptionBus.code := exceptionCode
port.exceptionBus.badAddr := port.bus.cmd.virtualAddress
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 = mmuRange(port.bus.cmd.virtualAddress)
val sharedMiss = RegInit(False)
val sharedIterator = Reg(UInt(log2Up(tlbSize + 1) bits))
val sharedAccessed = RegInit(B"00")
val entryToReplace = Counter(port.args.portTlbSize)
val sharedAccessAsked = RegNext(port.bus.cmd.isValid && !cacheHit && sharedIterator < tlbSize && isInMmuRange)
val sharedAccessGranted = sharedAccessAsked && shared.free
when(sharedAccessGranted) {
shared.readAddr := sharedIterator.resized
sharedIterator := sharedIterator + 1
}
sharedAccessed := (sharedAccessed ## sharedAccessGranted).resized
when(sharedAccessAsked){
shared.free \= False
}
when(sharedAccessed.msb){
when(shared.readData.virtualAddress === port.bus.cmd.virtualAddress(31 downto 12)){
cache(entryToReplace) := shared.readData
entryToReplace.increment()
}
}
sharedMiss.setWhen(sharedIterator >= tlbSize && sharedAccessed === B"00")
when(!port.stage.arbitration.isStuck){
sharedIterator := 0
sharedMiss.clear()
sharedAccessAsked.clear()
sharedAccessed := 0
}
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.stage.arbitration.haltIt setWhen (port.bus.cmd.isValid && !cacheHit)
port.exceptionBus.valid := sharedMiss
sharedRequest.valid := port.bus.cmd.isValid && !cacheHit && !sharedMiss
port.stage.arbitration.haltIt setWhen (port.bus.cmd.isValid && !cacheHit && !sharedMiss)
} 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.miss := sharedMiss
}
shared.cmd << StreamArbiterFactory.lowerFirst.noLock.on(ports.map(_.sharedRequest))
}
//Manage TLBW0 and TLBW1 instructions

View File

@ -29,6 +29,7 @@ case class MemoryTranslatorCmd() extends Bundle{
case class MemoryTranslatorRsp() extends Bundle{
val physicalAddress = UInt(32 bits)
val allowRead, allowWrite, allowExecute = Bool
val miss = Bool
}
case class MemoryTranslatorBus() extends Bundle with IMasterSlave{
@ -42,5 +43,5 @@ case class MemoryTranslatorBus() extends Bundle with IMasterSlave{
}
trait MemoryTranslator{
def newTranslationPort(stage : Stage, cacheSize : Int) : MemoryTranslatorBus
def newTranslationPort(stage : Stage, args : Any) : MemoryTranslatorBus
}

View File

@ -60,22 +60,43 @@ object TopLevel {
wfiGen = true
)
// val csrConfig = MachineCsrConfig(
// mvendorid = null,
// marchid = null,
// mimpid = null,
// mhartid = null,
// misaExtensionsInit = 66,
// misaAccess = CsrAccess.NONE,
// mtvecAccess = CsrAccess.NONE,
// mtvecInit = 0x00000020l,
// mepcAccess = CsrAccess.READ_ONLY,
// mscratchGen = false,
// mcauseAccess = CsrAccess.READ_ONLY,
// mbadaddrAccess = CsrAccess.NONE,
// mcycleAccess = CsrAccess.NONE,
// minstretAccess = CsrAccess.NONE
// )
val csrConfigSmall = MachineCsrConfig(
mvendorid = null,
marchid = null,
mimpid = null,
mhartid = null,
misaExtensionsInit = 66,
misaAccess = CsrAccess.NONE,
mtvecAccess = CsrAccess.NONE,
mtvecInit = 0x00000020l,
mepcAccess = CsrAccess.READ_WRITE,
mscratchGen = false,
mcauseAccess = CsrAccess.READ_ONLY,
mbadaddrAccess = CsrAccess.READ_ONLY,
mcycleAccess = CsrAccess.NONE,
minstretAccess = CsrAccess.NONE,
ecallGen = false,
wfiGen = false
)
val csrConfigSmallest = MachineCsrConfig(
mvendorid = null,
marchid = null,
mimpid = null,
mhartid = null,
misaExtensionsInit = 66,
misaAccess = CsrAccess.NONE,
mtvecAccess = CsrAccess.NONE,
mtvecInit = 0x00000020l,
mepcAccess = CsrAccess.READ_ONLY,
mscratchGen = false,
mcauseAccess = CsrAccess.READ_ONLY,
mbadaddrAccess = CsrAccess.NONE,
mcycleAccess = CsrAccess.NONE,
minstretAccess = CsrAccess.NONE,
ecallGen = false,
wfiGen = false
)
configFull.plugins ++= List(
new PcManagerSimplePlugin(0x00000000l, false),
@ -109,7 +130,8 @@ object TopLevel {
addressWidth = 32,
cpuDataWidth = 32,
memDataWidth = 32,
catchAccessFault = false
catchAccessFault = false,
catchMemoryTranslationMiss = false
)
),
new DecoderSimplePlugin(
@ -246,19 +268,20 @@ object TopLevel {
cpuDataWidth = 32,
memDataWidth = 32,
catchAccessFault = false,
catchMemoryTranslationMiss = true,
tagSizeShift = 2
),
mmuConfig = DataMmuConfig(
dTlbSize = 6
askMemoryTranslation = true,
memoryTranslatorPortConfig = MemoryTranslatorPortConfig(
portTlbSize = 6
)
),
new MemoryTranslatorPlugin(
tlbSize = 32,
exceptionCode = 13,
mmuRange = _(31 downto 28) === 0xC
),
new MachineCsr(csrConfigAll),
new MachineCsr(csrConfigSmall),
new DecoderSimplePlugin(
catchIllegalInstruction = false
),
@ -275,10 +298,10 @@ object TopLevel {
// new HazardSimplePlugin(true, true, true, true),
// new HazardSimplePlugin(false, true, false, true),
new HazardSimplePlugin(
bypassExecute = true,
bypassMemory = true,
bypassWriteBack = true,
bypassWriteBackBuffer = true,
bypassExecute = false,
bypassMemory = false,
bypassWriteBack = false,
bypassWriteBackBuffer = false,
pessimisticUseSrc = false,
pessimisticWriteRegFile = false,
pessimisticAddressMatch = false