Added MMU support into cacheless DBus IBus plugins (for testing purposes)

Probably full of bugs, need testing
This commit is contained in:
Dolu1990 2019-03-18 12:17:43 +01:00
parent ffa489d211
commit c490838202
10 changed files with 198 additions and 56 deletions

View File

@ -684,11 +684,11 @@ class DataCache(p : DataCacheConfig) extends Component{
when(io.cpu.writeBack.isValid) {
if (catchMemoryTranslationMiss) {
io.cpu.writeBack.mmuMiss := mmuRsp.miss
io.cpu.writeBack.mmuMiss := ??? //TODO mmuRsp.miss
}
switch(request.kind) {
is(MANAGMENT) {
when(delayedIsStuck && !mmuRsp.miss) {
when(delayedIsStuck && ???){ //TODO!mmuRsp.miss) {
when(delayedWaysHitValid || (request.way && way.tagReadRspTwo.used)) {
io.cpu.writeBack.haltIt.clearWhen(!(victim.requestIn.valid && !victim.requestIn.ready))
victim.requestIn.valid := request.clean && way.tagReadRspTwo.dirty
@ -708,7 +708,7 @@ class DataCache(p : DataCacheConfig) extends Component{
val unaligned = if(catchUnaligned) ((request.size === 2 && mmuRsp.physicalAddress(1 downto 0) =/= 0) || (request.size === 1 && mmuRsp.physicalAddress(0 downto 0) =/= 0)) else False
io.cpu.writeBack.illegalAccess := illegal
io.cpu.writeBack.unalignedAccess := unaligned
when((Bool(!catchMemoryTranslationMiss) || !mmuRsp.miss) && !illegal && !unaligned) {
when((Bool(!catchMemoryTranslationMiss) || ???) && !illegal && !unaligned) { //TODO !mmuRsp.miss
when(request.forceUncachedAccess || mmuRsp.isIoAccess) {
val memCmdSent = RegInit(False)
when(!victim.request.valid) {

View File

@ -411,7 +411,7 @@ class InstructionCache(p : InstructionCacheConfig) extends Component{
io.cpu.fetch.cacheMiss := !hit.valid
io.cpu.fetch.error := hit.error
io.cpu.fetch.mmuMiss := mmuRsp.miss
io.cpu.fetch.mmuMiss := ??? //TODO mmuRsp.miss
io.cpu.fetch.illegalAccess := !mmuRsp.allowExecute || (io.cpu.fetch.isUser && !mmuRsp.allowUser)
})
}
@ -441,7 +441,7 @@ class InstructionCache(p : InstructionCacheConfig) extends Component{
io.cpu.decode.cacheMiss := !hit.valid
io.cpu.decode.error := hit.error
io.cpu.decode.mmuMiss := mmuRsp.miss
io.cpu.decode.mmuMiss := ??? //TODO mmuRsp.miss
io.cpu.decode.illegalAccess := !mmuRsp.allowExecute || (io.cpu.decode.isUser && !mmuRsp.allowUser)
io.cpu.decode.physicalAddress := mmuRsp.physicalAddress
})

View File

@ -173,7 +173,7 @@ class DBusCachedPlugin(config : DataCacheConfig,
arbitration.haltItself setWhen(cache.io.cpu.memory.haltIt)
cache.io.cpu.memory.mmuBus <> mmuBus
arbitration.haltItself setWhen (mmuBus.cmd.isValid && !mmuBus.rsp.hit && !mmuBus.rsp.miss)
arbitration.haltItself setWhen (mmuBus.cmd.isValid && ???) //TODO !mmuBus.rsp.hit && !mmuBus.rsp.miss
}
writeBack plug new Area{

View File

@ -9,6 +9,8 @@ import spinal.lib.bus.wishbone.{Wishbone, WishboneConfig}
import spinal.lib.bus.simple._
import vexriscv.ip.DataCacheMemCmd
import scala.collection.mutable.ArrayBuffer
case class DBusSimpleCmd() extends Bundle{
val wr = Bool
@ -202,7 +204,8 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false,
catchAccessFault : Boolean = false,
earlyInjection : Boolean = false, /*, idempotentRegions : (UInt) => Bool = (x) => False*/
emitCmdInMemoryStage : Boolean = false,
onlyLoadWords : Boolean = false) extends Plugin[VexRiscv]{
onlyLoadWords : Boolean = false,
memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv] with DBusAccessService {
var dBus : DBusSimpleBus = null
assert(!(emitCmdInMemoryStage && earlyInjection))
@ -211,9 +214,20 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false,
object MEMORY_READ_DATA extends Stageable(Bits(32 bits))
object MEMORY_ADDRESS_LOW extends Stageable(UInt(2 bits))
object ALIGNEMENT_FAULT extends Stageable(Bool)
object MMU_RSP extends Stageable(MemoryTranslatorRsp())
var memoryExceptionPort : Flow[ExceptionCause] = null
var rspStage : Stage = null
var mmuBus : MemoryTranslatorBus = null
var redoBranch : Flow[UInt] = null
val catchSomething = catchAccessFault || catchAddressMisaligned || memoryTranslatorPortConfig != null
var dBusAccess : DBusAccess = null
override def newDBusAccess(): DBusAccess = {
assert(dBusAccess == null)
dBusAccess = DBusAccess()
dBusAccess
}
override def setup(pipeline: VexRiscv): Unit = {
import Riscv._
@ -250,10 +264,15 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false,
rspStage = if(stages.last == execute) execute else (if(emitCmdInMemoryStage) writeBack else memory)
if(catchAccessFault || catchAddressMisaligned) {
if(catchSomething) {
val exceptionService = pipeline.service(classOf[ExceptionService])
memoryExceptionPort = exceptionService.newExceptionPort(rspStage)
}
if(memoryTranslatorPortConfig != null) {
mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(MemoryTranslatorPort.PRIORITY_INSTRUCTION, memoryTranslatorPortConfig)
redoBranch = pipeline.service(classOf[JumpService]).createJumpInterface(pipeline.memory)
}
}
override def build(pipeline: VexRiscv): Unit = {
@ -262,7 +281,6 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false,
dBus = master(DBusSimpleBus()).setName("dBus")
//Emit dBus.cmd request
val cmdStage = if(emitCmdInMemoryStage) memory else execute
cmdStage plug new Area{
@ -279,7 +297,6 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false,
dBus.cmd.valid := arbitration.isValid && input(MEMORY_ENABLE) && !arbitration.isStuckByOthers && !arbitration.isFlushed && !input(ALIGNEMENT_FAULT) && !cmdSent
dBus.cmd.wr := input(INSTRUCTION)(5)
dBus.cmd.address := input(SRC_ADD).asUInt
dBus.cmd.size := input(INSTRUCTION)(13 downto 12).asUInt
dBus.cmd.payload.data := dBus.cmd.size.mux (
U(0) -> input(RS2)(7 downto 0) ## input(RS2)(7 downto 0) ## input(RS2)(7 downto 0) ## input(RS2)(7 downto 0),
@ -302,6 +319,25 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false,
insert(FORMAL_MEM_WMASK) := (dBus.cmd.valid && dBus.cmd.wr) ? formalMask | B"0000"
insert(FORMAL_MEM_RMASK) := (dBus.cmd.valid && !dBus.cmd.wr) ? formalMask | B"0000"
insert(FORMAL_MEM_WDATA) := dBus.cmd.payload.data
val mmu = (mmuBus != null) generate new Area {
mmuBus.cmd.isValid := arbitration.isValid && input(MEMORY_ENABLE)
mmuBus.cmd.virtualAddress := input(SRC_ADD).asUInt
mmuBus.cmd.bypassTranslation := False
dBus.cmd.address := mmuBus.rsp.physicalAddress
//do not emit memory request if MMU miss
when(mmuBus.cmd.isValid && (mmuBus.rsp.exception || mmuBus.rsp.refilling)){
dBus.cmd.valid := False
arbitration.haltItself := False
}
insert(MMU_RSP) := mmuBus.rsp
}
val mmuLess = (mmuBus == null) generate new Area{
dBus.cmd.address := input(SRC_ADD).asUInt
}
}
//Collect dBus.rsp read responses
@ -312,26 +348,38 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false,
insert(MEMORY_READ_DATA) := dBus.rsp.data
arbitration.haltItself setWhen(arbitration.isValid && input(MEMORY_ENABLE) && !input(INSTRUCTION)(5) && !dBus.rsp.ready)
if(catchAccessFault || catchAddressMisaligned){
if(!catchAccessFault){
memoryExceptionPort.code := (input(INSTRUCTION)(5) ? U(6) | U(4)).resized
memoryExceptionPort.valid := input(ALIGNEMENT_FAULT)
} else if(!catchAddressMisaligned){
memoryExceptionPort.valid := dBus.rsp.ready && dBus.rsp.error && !input(INSTRUCTION)(5)
memoryExceptionPort.code := 5
} else {
memoryExceptionPort.valid := dBus.rsp.ready && dBus.rsp.error && !input(INSTRUCTION)(5)
memoryExceptionPort.code := 5
when(input(ALIGNEMENT_FAULT)){
memoryExceptionPort.code := (input(INSTRUCTION)(5) ? U(6) | U(4)).resized
memoryExceptionPort.valid := True
}
}
when(!(arbitration.isValid && input(MEMORY_ENABLE) && (if(cmdStage == rspStage) !arbitration.isStuckByOthers else True))){
memoryExceptionPort.valid := False
if(catchSomething) {
memoryExceptionPort.valid := False
memoryExceptionPort.code.assignDontCare()
memoryExceptionPort.badAddr := input(REGFILE_WRITE_DATA).asUInt
if(catchAccessFault) when(dBus.rsp.ready && dBus.rsp.error && !input(INSTRUCTION)(5)) {
memoryExceptionPort.valid := True
memoryExceptionPort.code := 5
}
memoryExceptionPort.badAddr := input(REGFILE_WRITE_DATA).asUInt //Drived by IntAluPlugin
if(catchAddressMisaligned) when(input(ALIGNEMENT_FAULT)){
memoryExceptionPort.code := (input(INSTRUCTION)(5) ? U(6) | U(4)).resized
memoryExceptionPort.valid := True
}
if(memoryTranslatorPortConfig != null) {
redoBranch.valid := False
redoBranch.payload := input(PC)
when(input(MMU_RSP).refilling){
redoBranch.valid := True
memoryExceptionPort.valid := False
} elsewhen(input(MMU_RSP).exception) {
memoryExceptionPort.valid := True
memoryExceptionPort.code := (input(INSTRUCTION)(5) ? U(15) | U(13)).resized
}
}
when(!(arbitration.isValid && input(MEMORY_ENABLE) && (Bool(cmdStage != rspStage) || !arbitration.isStuckByOthers))){
memoryExceptionPort.valid := False
redoBranch.valid := False
}
}
@ -368,5 +416,42 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false,
//formal
insert(FORMAL_MEM_RDATA) := input(MEMORY_READ_DATA)
}
//Share access to the dBus (used by self refilled MMU)
val dBusSharing = (dBusAccess != null) generate new Area{
val state = Reg(UInt(2 bits)) init(0)
dBusAccess.rsp.valid := False
dBusAccess.rsp.data := dBus.rsp.data
dBusAccess.rsp.error := dBus.rsp.error
switch(state){
is(0){
when(dBusAccess.cmd.valid){
decode.arbitration.haltItself := True
when(!stages.dropWhile(_ != execute).map(_.arbitration.isValid).orR){
state := 1
}
}
}
is(1){
decode.arbitration.haltItself := True
dBus.cmd.valid := True
dBus.cmd.address := dBusAccess.cmd.address
dBus.cmd.wr := dBusAccess.cmd.write
dBus.cmd.data := dBusAccess.cmd.data
dBus.cmd.size := dBusAccess.cmd.size
when(dBus.cmd.ready){
state := (dBusAccess.cmd.write ? U(0) | U(2))
}
}
is(2){
decode.arbitration.haltItself := True
when(dBus.rsp.ready){
dBusAccess.rsp.valid := True
state := 0
}
}
}
}
}
}

View File

@ -605,5 +605,11 @@ abstract class IBusFetcherImpl(val resetVector : BigInt,
})
}
}
def stageXToIBusRsp[T <: Data](stage : Any, input : T): (T) ={
iBusRsp.stages.dropWhile(_ != stage).foldLeft(input)((data,stage) => RegNextWhen(data, stage.output.ready))
}
}
}

View File

@ -223,7 +223,7 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l,
if (mmuBus != null) {
cache.io.cpu.fetch.mmuBus <> mmuBus
(if (twoCycleCache) stages(1).halt else rsp.iBusRspOutputHalt) setWhen (mmuBus.cmd.isValid && !mmuBus.rsp.hit && !mmuBus.rsp.miss)
(if (twoCycleCache) stages(1).halt else rsp.iBusRspOutputHalt) setWhen (mmuBus.cmd.isValid && ???) //TODO !mmuBus.rsp.hit && !mmuBus.rsp.miss
} else {
cache.io.cpu.fetch.mmuBus.rsp.physicalAddress := cache.io.cpu.fetch.mmuBus.cmd.virtualAddress
cache.io.cpu.fetch.mmuBus.rsp.allowExecute := True
@ -231,8 +231,9 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l,
cache.io.cpu.fetch.mmuBus.rsp.allowWrite := True
cache.io.cpu.fetch.mmuBus.rsp.allowUser := True
cache.io.cpu.fetch.mmuBus.rsp.isIoAccess := False
cache.io.cpu.fetch.mmuBus.rsp.miss := False
cache.io.cpu.fetch.mmuBus.rsp.hit := False
??? //TODO
// cache.io.cpu.fetch.mmuBus.rsp.miss := False
// cache.io.cpu.fetch.mmuBus.rsp.hit := False
}
val flushStage = if(memory != null) memory else execute

View File

@ -166,7 +166,8 @@ class IBusSimplePlugin(resetVector : BigInt,
pendingMax : Int = 7,
injectorStage : Boolean = true,
rspHoldValue : Boolean = false,
singleInstructionPipeline : Boolean = false
singleInstructionPipeline : Boolean = false,
memoryTranslatorPortConfig : Any = null
) extends IBusFetcherImpl(
resetVector = resetVector,
keepPcPlus4 = keepPcPlus4,
@ -181,15 +182,23 @@ class IBusSimplePlugin(resetVector : BigInt,
var iBus : IBusSimpleBus = null
var decodeExceptionPort : Flow[ExceptionCause] = null
val catchSomething = memoryTranslatorPortConfig != null || catchAccessFault
var mmuBus : MemoryTranslatorBus = null
var redoBranch : Flow[UInt] = null
if(rspHoldValue) assert(busLatencyMin <= 1)
override def setup(pipeline: VexRiscv): Unit = {
super.setup(pipeline)
iBus = master(IBusSimpleBus(false)).setName("iBus")
if(catchAccessFault) {
val exceptionService = pipeline.service(classOf[ExceptionService])
decodeExceptionPort = exceptionService.newExceptionPort(pipeline.decode,1)
if(catchSomething) {
decodeExceptionPort = pipeline.service(classOf[ExceptionService]).newExceptionPort(pipeline.decode,1)
}
if(memoryTranslatorPortConfig != null) {
mmuBus = pipeline.service(classOf[MemoryTranslator]).newTranslationPort(MemoryTranslatorPort.PRIORITY_INSTRUCTION, memoryTranslatorPortConfig)
redoBranch = pipeline.service(classOf[JumpService]).createJumpInterface(pipeline.decode, priority = 1) //Priority 1 will win against branch predictor
}
}
@ -206,9 +215,12 @@ class IBusSimplePlugin(resetVector : BigInt,
val pendingCmdNext = pendingCmd + cmd.fire.asUInt - iBus.rsp.fire.asUInt
pendingCmd := pendingCmdNext
def cmdForkStage = if(!cmdForkPersistence || !cmdForkOnSecondStage) iBusRsp.stages(if(cmdForkOnSecondStage) 1 else 0) else iBusRsp.stages(1)
val cmdFork = if(!cmdForkPersistence || !cmdForkOnSecondStage) new Area {
//This implementation keep the cmd on the bus until it's executed or the the pipeline is flushed
def stage = iBusRsp.stages(if(cmdForkOnSecondStage) 1 else 0)
def stage = cmdForkStage
stage.halt setWhen(stage.input.valid && (!cmd.valid || !cmd.ready))
if(singleInstructionPipeline) {
cmd.valid := stage.input.valid && pendingCmd =/= pendingMax && !stages.map(_.arbitration.isValid).orR
@ -217,16 +229,33 @@ class IBusSimplePlugin(resetVector : BigInt,
}else {
cmd.valid := stage.input.valid && stage.output.ready && pendingCmd =/= pendingMax
}
cmd.pc := stage.input.payload(31 downto 2) @@ "00"
} else new Area{
//This implementation keep the cmd on the bus until it's executed, even if the pipeline is flushed
def stage = iBusRsp.stages(1)
def stage = cmdForkStage
val pendingFull = pendingCmd === pendingMax
val cmdKeep = RegInit(False) setWhen(cmd.valid) clearWhen(cmd.ready)
val cmdFired = RegInit(False) setWhen(cmd.fire) clearWhen(stage.input.ready)
stage.halt setWhen(cmd.isStall || (pendingFull && !cmdFired))
cmd.valid := (stage.input.valid || cmdKeep) && !pendingFull && !cmdFired
cmd.pc := stage.input.payload(31 downto 2) @@ "00"
}
val mmu = (mmuBus != null) generate new Area {
mmuBus.cmd.isValid := cmdForkStage.input.valid
mmuBus.cmd.virtualAddress := cmdForkStage.input.payload
mmuBus.cmd.bypassTranslation := False
cmd.pc := mmuBus.rsp.physicalAddress(31 downto 2) @@ "00"
//do not emit memory request if MMU miss
when(mmuBus.rsp.exception || mmuBus.rsp.refilling){
cmdForkStage.halt := False
cmd.valid := False
}
val joinCtx = stageXToIBusRsp(cmdForkStage, mmuBus.rsp)
}
val mmuLess = (mmuBus == null) generate new Area{
cmd.pc := cmdForkStage.input.payload(31 downto 2) @@ "00"
}
val rspJoin = new Area {
@ -261,22 +290,35 @@ class IBusSimplePlugin(resetVector : BigInt,
fetchRsp.rsp.error.clearWhen(!rspBufferOutput.valid) //Avoid interference with instruction injection from the debug plugin
var issueDetected = False
val join = Stream(FetchRsp())
val exceptionDetected = False
val redoRequired = False
join.valid := stages.last.output.valid && rspBufferOutput.valid
join.payload := fetchRsp
stages.last.output.ready := stages.last.output.valid ? join.fire | join.ready
rspBufferOutput.ready := join.fire
output << join.haltWhen(issueDetected)
output << join.haltWhen(exceptionDetected || redoRequired)
if(catchAccessFault){
if(memoryTranslatorPortConfig != null){
redoRequired setWhen( stages.last.input.valid && mmu.joinCtx.refilling)
redoBranch.valid := redoRequired && iBusRsp.readyForError
redoBranch.payload := stages.last.input.payload
}
if(catchSomething){
decodeExceptionPort.valid := False
decodeExceptionPort.code := 1
decodeExceptionPort.badAddr := join.pc
when(join.valid && join.rsp.error && !issueDetected){
issueDetected \= True
decodeExceptionPort.valid := iBusRsp.readyForError
decodeExceptionPort.code.assignDontCare()
decodeExceptionPort.badAddr := join.pc //TODO Should it be the physical address insted ?
if(catchAccessFault) when(join.valid && join.rsp.error){
decodeExceptionPort.code := 1
exceptionDetected := True
}
if(memoryTranslatorPortConfig != null) when(stages.last.input.valid && mmu.joinCtx.exception){
decodeExceptionPort.code := 12
exceptionDetected := True
}
decodeExceptionPort.valid := exceptionDetected && iBusRsp.readyForError
}
}
}

View File

@ -113,7 +113,8 @@ class MemoryTranslatorPlugin(tlbSize : Int,
port.bus.rsp.allowWrite := cacheLine.allowWrite
port.bus.rsp.allowExecute := cacheLine.allowExecute
port.bus.rsp.allowUser := cacheLine.allowUser
port.bus.rsp.hit := cacheHit
???
// port.bus.rsp.hit := cacheHit
// port.stage.arbitration.haltItself setWhen (port.bus.cmd.isValid && !cacheHit && !sharedMiss)
} otherwise {
port.bus.rsp.physicalAddress := port.bus.cmd.virtualAddress
@ -121,10 +122,12 @@ class MemoryTranslatorPlugin(tlbSize : Int,
port.bus.rsp.allowWrite := True
port.bus.rsp.allowExecute := True
port.bus.rsp.allowUser := True
port.bus.rsp.hit := True
???
// port.bus.rsp.hit := True
}
port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress)
port.bus.rsp.miss := sharedMiss
???
// port.bus.rsp.miss := sharedMiss
}
}

View File

@ -6,11 +6,15 @@ import spinal.lib._
import scala.collection.mutable.ArrayBuffer
trait DBusAccessService{
def newDBusAccess() : DBusAccess
}
case class DBusAccessCmd() extends Bundle {
val address = UInt(32 bits)
val size = UInt(2 bits)
val write = Bool
val writeData = Bits(32 bits)
val data = Bits(32 bits)
val writeMask = Bits(4 bits)
}
@ -56,7 +60,7 @@ class MmuPlugin(virtualRange : UInt => Bool,
decoderService.add(SFENCE_VMA, List(IS_SFENCE_VMA -> True))
dBus = ???
dBus = pipeline.service(classOf[DBusAccessService]).newDBusAccess()
}
override def build(pipeline: VexRiscv): Unit = {
@ -81,7 +85,7 @@ class MmuPlugin(virtualRange : UInt => Bool,
}
val core = pipeline plug new Area {
val ports = for (port <- sortedPortsInfo yield 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))
@ -144,7 +148,7 @@ class MmuPlugin(virtualRange : UInt => Bool,
dBus.cmd.write := False
dBus.cmd.size := 2
dBus.cmd.address.assignDontCare()
dBus.cmd.writeData.assignDontCare()
dBus.cmd.data.assignDontCare()
dBus.cmd.writeMask.assignDontCare()
switch(state){
is(State.IDLE){

View File

@ -33,8 +33,9 @@ class StaticMemoryTranslatorPlugin(ioRange : UInt => Bool) extends Plugin[VexRis
port.bus.rsp.allowExecute := True
port.bus.rsp.allowUser := True
port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress)
port.bus.rsp.miss := False
port.bus.rsp.hit := True
???
// port.bus.rsp.miss := False
// port.bus.rsp.hit := True
}
}
}