From 8be40e637b60ab48cef5ec8c665ec62b6c1207f5 Mon Sep 17 00:00:00 2001 From: Charles Papon Date: Tue, 2 Apr 2019 23:44:53 +0200 Subject: [PATCH] #60 Got the new data cache design passing all tests and running linux --- src/main/scala/vexriscv/Services.scala | 1 + src/main/scala/vexriscv/demo/Linux.scala | 95 +++++++++---------- src/main/scala/vexriscv/ip/DataCache.scala | 37 +++++--- .../scala/vexriscv/plugin/CsrPlugin.scala | 8 +- .../vexriscv/plugin/DBusCachedPlugin.scala | 59 ++++++++++-- .../plugin/HaltOnExceptionPlugin.scala | 2 + src/test/cpp/regression/main.cpp | 11 +++ 7 files changed, 143 insertions(+), 70 deletions(-) diff --git a/src/main/scala/vexriscv/Services.scala b/src/main/scala/vexriscv/Services.scala index fb2a29a..38a6b70 100644 --- a/src/main/scala/vexriscv/Services.scala +++ b/src/main/scala/vexriscv/Services.scala @@ -33,6 +33,7 @@ case class ExceptionCause() extends Bundle{ trait ExceptionService{ def newExceptionPort(stage : Stage, priority : Int = 0) : Flow[ExceptionCause] + def isExceptionPending() : Bool } trait PrivilegeService{ diff --git a/src/main/scala/vexriscv/demo/Linux.scala b/src/main/scala/vexriscv/demo/Linux.scala index cc0c5f7..f7c9fe6 100644 --- a/src/main/scala/vexriscv/demo/Linux.scala +++ b/src/main/scala/vexriscv/demo/Linux.scala @@ -96,45 +96,45 @@ object LinuxGen { new DummyFencePlugin(), //TODO should be removed for design with caches //Uncomment the whole IBusSimplePlugin and comment IBusCachedPlugin if you want uncached iBus config - new IBusSimplePlugin( - resetVector = 0x80000000l, - cmdForkOnSecondStage = false, - cmdForkPersistence = false, - prediction = NONE, - historyRamSizeLog2 = 10, - catchAccessFault = true, - compressedGen = true, - busLatencyMin = 1, - injectorStage = true, - memoryTranslatorPortConfig = withMmu generate MmuPortConfig( - portTlbSize = 4 - ) - ), - - //Uncomment the whole IBusCachedPlugin and comment IBusSimplePlugin if you want cached iBus config -// new IBusCachedPlugin( +// new IBusSimplePlugin( // resetVector = 0x80000000l, -// compressedGen = true, +// cmdForkOnSecondStage = false, +// cmdForkPersistence = false, // prediction = NONE, +// historyRamSizeLog2 = 10, +// catchAccessFault = true, +// compressedGen = true, +// busLatencyMin = 1, // injectorStage = true, -// config = InstructionCacheConfig( -// cacheSize = 4096, -// bytePerLine = 32, -// wayCount = 1, -// addressWidth = 32, -// cpuDataWidth = 32, -// memDataWidth = 32, -// catchIllegalAccess = true, -// catchAccessFault = true, -// asyncTagMemory = false, -// twoCycleRam = false, -// twoCycleCache = true -// ) -// ), -// memoryTranslatorPortConfig = MmuPortConfig( +// memoryTranslatorPortConfig = withMmu generate MmuPortConfig( // portTlbSize = 4 // ) // ), + + //Uncomment the whole IBusCachedPlugin and comment IBusSimplePlugin if you want cached iBus config + new IBusCachedPlugin( + resetVector = 0x80000000l, + compressedGen = true, + prediction = NONE, + injectorStage = true, + config = InstructionCacheConfig( + cacheSize = 4096, + bytePerLine = 32, + wayCount = 1, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchIllegalAccess = true, + catchAccessFault = true, + asyncTagMemory = false, + twoCycleRam = false, + twoCycleCache = true +// ) + ), + memoryTranslatorPortConfig = MmuPortConfig( + portTlbSize = 4 + ) + ), // ).newTightlyCoupledPort(TightlyCoupledPortParameter("iBusTc", a => a(30 downto 28) === 0x0 && a(5))), // new DBusSimplePlugin( // catchAddressMisaligned = true, @@ -156,17 +156,16 @@ object LinuxGen { catchAccessError = true, catchIllegal = true, catchUnaligned = true, - atomicEntriesCount = 2 - ) -// ), - // memoryTranslatorPortConfig = null -// memoryTranslatorPortConfig = MmuPortConfig( -// portTlbSize = 4 + atomicEntriesCount = 1 // ) + ), + memoryTranslatorPortConfig = withMmu generate MmuPortConfig( + portTlbSize = 4 + ) ), - new StaticMemoryTranslatorPlugin( - ioRange = _(31 downto 28) === 0xF - ), +// new StaticMemoryTranslatorPlugin( +// ioRange = _(31 downto 28) === 0xF +// ), // new MemoryTranslatorPlugin( // tlbSize = 32, // virtualRange = _(31 downto 28) === 0xC, @@ -238,12 +237,12 @@ object LinuxGen { new YamlPlugin("cpu0.yaml") ) ) -// if(withMmu) config.plugins += new MmuPlugin( -// virtualRange = a => True, -// // virtualRange = x => x(31 downto 24) =/= 0x81, //TODO It fix the DTB kernel access (workaround) -// ioRange = (x => if(litex) x(31 downto 28) === 0xB || x(31 downto 28) === 0xE || x(31 downto 28) === 0xF else x(31 downto 28) === 0xF), -// allowUserIo = true -// ) + if(withMmu) config.plugins += new MmuPlugin( + virtualRange = a => True, + // virtualRange = x => x(31 downto 24) =/= 0x81, //TODO It fix the DTB kernel access (workaround) + ioRange = (x => if(litex) x(31 downto 28) === 0xB || x(31 downto 28) === 0xE || x(31 downto 28) === 0xF else x(31 downto 28) === 0xF), + allowUserIo = true + ) config } diff --git a/src/main/scala/vexriscv/ip/DataCache.scala b/src/main/scala/vexriscv/ip/DataCache.scala index 96dea4c..941874f 100644 --- a/src/main/scala/vexriscv/ip/DataCache.scala +++ b/src/main/scala/vexriscv/ip/DataCache.scala @@ -8,6 +8,8 @@ import spinal.lib.bus.avalon.{AvalonMM, AvalonMMConfig} import spinal.lib.bus.wishbone.{Wishbone, WishboneConfig} import spinal.lib.bus.simple._ +//TODO flush + case class DataCacheConfig(cacheSize : Int, bytePerLine : Int, wayCount : Int, @@ -71,13 +73,12 @@ object DataCacheCpuExecute{ case class DataCacheCpuExecute(p : DataCacheConfig) extends Bundle with IMasterSlave{ val isValid = Bool - val isStuck = Bool val address = UInt(p.addressWidth bit) // val haltIt = Bool val args = DataCacheCpuExecuteArgs(p) override def asMaster(): Unit = { - out(isValid, isStuck, args, address) + out(isValid, args, address) // in(haltIt) } } @@ -111,6 +112,7 @@ case class DataCacheCpuWriteBack(p : DataCacheConfig) extends Bundle with IMaste val isStuck = Bool val isUser = Bool val haltIt = Bool + val isWrite = Bool val data = Bits(p.cpuDataWidth bit) val address = UInt(p.addressWidth bit) val mmuException, unalignedAccess , accessError = Bool @@ -120,7 +122,7 @@ case class DataCacheCpuWriteBack(p : DataCacheConfig) extends Bundle with IMaste override def asMaster(): Unit = { out(isValid,isStuck,isUser, address) - in(haltIt, data, mmuException, unalignedAccess, accessError) + in(haltIt, data, mmuException, unalignedAccess, accessError, isWrite) outWithNull(clearAtomicEntries) } } @@ -354,8 +356,8 @@ class DataCache(p : DataCacheConfig) extends Component{ val data = Mem(Bits(wordWidth bit), wayWordCount) //Reads - val tagsReadRsp = tags.readSync(tagsReadCmd.payload, tagsReadCmd.valid && !io.cpu.execute.isStuck) - val dataReadRsp = data.readSync(dataReadCmd.payload, dataReadCmd.valid && !io.cpu.execute.isStuck) + val tagsReadRsp = tags.readSync(tagsReadCmd.payload, tagsReadCmd.valid && !io.cpu.memory.isStuck) + val dataReadRsp = data.readSync(dataReadCmd.payload, dataReadCmd.valid && !io.cpu.memory.isStuck) //Writes when(tagsWriteCmd.valid && tagsWriteCmd.way(i)){ @@ -380,7 +382,7 @@ class DataCache(p : DataCacheConfig) extends Component{ dataWriteCmd.valid := False dataWriteCmd.payload.assignDontCare() - when(io.cpu.execute.isValid && !io.cpu.execute.isStuck){ + when(io.cpu.execute.isValid && !io.cpu.memory.isStuck){ tagsReadCmd.valid := True dataReadCmd.valid := True tagsReadCmd.payload := io.cpu.execute.address(lineRange) @@ -460,7 +462,6 @@ class DataCache(p : DataCacheConfig) extends Component{ val atomic = genAtomic generate new Area{ case class AtomicEntry() extends Bundle{ val valid = Bool() - val size = UInt(2 bits) val address = UInt(addressWidth bits) def init: this.type ={ @@ -470,10 +471,9 @@ class DataCache(p : DataCacheConfig) extends Component{ } val entries = Vec(Reg(AtomicEntry()).init, atomicEntriesCount) val entriesAllocCounter = Counter(atomicEntriesCount) - val entriesHit = entries.map(e => e.valid && e.size === request.size && e.address === io.cpu.writeBack.address).orR + val entriesHit = entries.map(e => e.valid && e.address === io.cpu.writeBack.address).orR when(io.cpu.writeBack.isValid && request.isAtomic && !request.wr){ entries(entriesAllocCounter).valid := True - entries(entriesAllocCounter).size := request.size //TODO remove size stuff entries(entriesAllocCounter).address := io.cpu.writeBack.address when(!io.cpu.writeBack.isStuck){ entriesAllocCounter.increment() @@ -486,11 +486,11 @@ class DataCache(p : DataCacheConfig) extends Component{ val memCmdSent = RegInit(False) setWhen (io.mem.cmd.ready) clearWhen (!io.cpu.writeBack.isStuck) - io.cpu.redo := mmuRsp.refilling + io.cpu.redo := False io.cpu.writeBack.accessError := False io.cpu.writeBack.mmuException := io.cpu.writeBack.isValid && (if(catchIllegal) mmuRsp.exception || (!mmuRsp.allowWrite && request.wr) || (!mmuRsp.allowRead && !request.wr) || (!mmuRsp.allowUser && io.cpu.writeBack.isUser) else False) io.cpu.writeBack.unalignedAccess := io.cpu.writeBack.isValid && (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.isWrite := request.wr when(io.cpu.writeBack.isValid) { when(request.forceUncachedAccess || mmuRsp.isIoAccess) { @@ -503,6 +503,11 @@ class DataCache(p : DataCacheConfig) extends Component{ io.mem.cmd.data := request.data io.mem.cmd.length := 0 io.mem.cmd.last := True + + if(genAtomic) when(request.isAtomic && !atomic.entriesHit){ + io.mem.cmd.valid := False + io.cpu.writeBack.haltIt := False + } } otherwise { when(waysHit || request.wr) { //Do not require a cache refill ? //Data cache update @@ -524,6 +529,12 @@ class DataCache(p : DataCacheConfig) extends Component{ //On write to read colisions io.cpu.redo := !request.wr && (colisions & waysHits) =/= 0 + + if(genAtomic) when(request.isAtomic && !atomic.entriesHit){ + io.mem.cmd.valid := False + dataWriteCmd.valid := False + io.cpu.writeBack.haltIt := False + } } otherwise { //Do refill //Emit cmd @@ -554,6 +565,7 @@ class DataCache(p : DataCacheConfig) extends Component{ loaderValid := False io.cpu.writeBack.haltIt := False } + io.cpu.redo setWhen(io.cpu.writeBack.isValid && mmuRsp.refilling) assert(!(io.cpu.writeBack.isValid && !io.cpu.writeBack.haltIt && io.cpu.writeBack.isStuck), "writeBack stuck by another plugin is not allowed") @@ -561,9 +573,6 @@ class DataCache(p : DataCacheConfig) extends Component{ when(request.isAtomic && request.wr){ io.cpu.writeBack.data := (!atomic.entriesHit).asBits.resized } - when(request.isAtomic && !atomic.entriesHit){ - io.mem.cmd.mask := 0 - } } } diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index 452aa8d..4df02b9 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -285,6 +285,9 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception interface } + var exceptionPending : Bool = null + override def isExceptionPending(): Bool = exceptionPending + var jumpInterface : Flow[UInt] = null var timerInterrupt, externalInterrupt, softwareInterrupt : Bool = null var externalInterruptS : Bool = null @@ -378,7 +381,7 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception jumpInterface.valid := False jumpInterface.payload.assignDontCare() - + exceptionPending = False timerInterrupt = in Bool() setName("timerInterrupt") externalInterrupt = in Bool() setName("externalInterrupt") softwareInterrupt = in Bool() setName("softwareInterrupt") @@ -677,7 +680,7 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception else exceptionValidsRegs(stageId) := False } - if(stage != stages.last) when(stage.arbitration.isFlushed){ + when(stage.arbitration.isFlushed){ exceptionValids(stageId) := False } } @@ -688,6 +691,7 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception //Avoid the PC register of the last stage to change durring an exception handleing (Used to fill Xepc) stages.last.dontSample.getOrElseUpdate(PC, ArrayBuffer[Bool]()) += exceptionValids.last + exceptionPending setWhen(exceptionValidsRegs.orR) } else null diff --git a/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala b/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala index c62984a..837cdc7 100644 --- a/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala +++ b/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala @@ -7,7 +7,7 @@ import spinal.lib._ import spinal.lib.bus.amba4.axi.Axi4 -class DAxiCachedPlugin(config : DataCacheConfig, memoryTranslatorPortConfig : Any = null) extends DBusCachedPlugin(config, memoryTranslatorPortConfig){ +class DAxiCachedPlugin(config : DataCacheConfig, memoryTranslatorPortConfig : Any = null) extends DBusCachedPlugin(config, memoryTranslatorPortConfig) { var dAxi : Axi4 = null override def build(pipeline: VexRiscv): Unit = { @@ -20,7 +20,7 @@ class DAxiCachedPlugin(config : DataCacheConfig, memoryTranslatorPortConfig : An class DBusCachedPlugin(config : DataCacheConfig, memoryTranslatorPortConfig : Any = null, - csrInfo : Boolean = false) extends Plugin[VexRiscv]{ + csrInfo : Boolean = false) extends Plugin[VexRiscv] with DBusAccessService { import config._ var dBus : DataCacheMemBus = null var mmuBus : MemoryTranslatorBus = null @@ -28,11 +28,19 @@ class DBusCachedPlugin(config : DataCacheConfig, var privilegeService : PrivilegeService = null var redoBranch : Flow[UInt] = null + @dontName var dBusAccess : DBusAccess = null + override def newDBusAccess(): DBusAccess = { + assert(dBusAccess == null) + dBusAccess = DBusAccess() + dBusAccess + } + object MEMORY_ENABLE extends Stageable(Bool) object MEMORY_MANAGMENT extends Stageable(Bool) object MEMORY_WR extends Stageable(Bool) object MEMORY_ADDRESS_LOW extends Stageable(UInt(2 bits)) object MEMORY_ATOMIC extends Stageable(Bool) + object IS_DBUS_SHARING extends Stageable(Bool()) override def setup(pipeline: VexRiscv): Unit = { import Riscv._ @@ -44,8 +52,9 @@ class DBusCachedPlugin(config : DataCacheConfig, SRC1_CTRL -> Src1CtrlEnum.RS, SRC_USE_SUB_LESS -> False, MEMORY_ENABLE -> True, - RS1_USE -> True - ) ++ (if (catchSomething) List(IntAluPlugin.ALU_CTRL -> IntAluPlugin.AluCtrlEnum.ADD_SUB) else Nil) //Used for access fault bad address in memory stage + RS1_USE -> True, + IntAluPlugin.ALU_CTRL -> IntAluPlugin.AluCtrlEnum.ADD_SUB + ) val loadActions = stdActions ++ List( SRC2_CTRL -> Src2CtrlEnum.IMI, @@ -143,7 +152,6 @@ class DBusCachedPlugin(config : DataCacheConfig, val size = input(INSTRUCTION)(13 downto 12).asUInt cache.io.cpu.execute.isValid := arbitration.isValid && input(MEMORY_ENABLE) - cache.io.cpu.execute.isStuck := arbitration.isStuck cache.io.cpu.execute.address := input(SRC_ADD).asUInt cache.io.cpu.execute.args.wr := input(MEMORY_WR) cache.io.cpu.execute.args.data := size.mux( @@ -194,7 +202,7 @@ class DBusCachedPlugin(config : DataCacheConfig, redoBranch.payload := input(PC) arbitration.flushAll setWhen(redoBranch.valid) - when(cache.io.cpu.writeBack.isValid) { + when(arbitration.isValid && input(MEMORY_ENABLE)) { if (catchAccessError) when(cache.io.cpu.writeBack.accessError) { exceptionBus.valid := True exceptionBus.code := (input(MEMORY_WR) ? U(7) | U(5)).resized @@ -235,6 +243,45 @@ class DBusCachedPlugin(config : DataCacheConfig, } } + //Share access to the dBus (used by self refilled MMU) + val dBusSharing = (dBusAccess != null) generate pipeline plug new Area{ + dBusAccess.cmd.ready := False + val forceDatapath = False + when(dBusAccess.cmd.valid){ + decode.arbitration.haltByOther := True + when(!stagesFromExecute.map(_.arbitration.isValid).orR && !pipeline.service(classOf[ExceptionService]).isExceptionPending()){ + when(!cache.io.cpu.redo) { + cache.io.cpu.execute.isValid := True + dBusAccess.cmd.ready := !execute.arbitration.isStuck + } + cache.io.cpu.execute.args.wr := dBusAccess.cmd.write + cache.io.cpu.execute.args.data := dBusAccess.cmd.data + cache.io.cpu.execute.args.size := dBusAccess.cmd.size + cache.io.cpu.execute.args.forceUncachedAccess := True //TODO Cached and redo management + if(genAtomic) cache.io.cpu.execute.args.isAtomic := False + cache.io.cpu.execute.address := dBusAccess.cmd.address //Will only be 12 muxes + forceDatapath := True + } + } + execute.insert(IS_DBUS_SHARING) := dBusAccess.cmd.fire + + + mmuBus.cmd.bypassTranslation setWhen(memory.input(IS_DBUS_SHARING)) + cache.io.cpu.memory.isValid setWhen(memory.input(IS_DBUS_SHARING)) + cache.io.cpu.writeBack.isValid setWhen(writeBack.input(IS_DBUS_SHARING)) + dBusAccess.rsp.valid := writeBack.input(IS_DBUS_SHARING) && !cache.io.cpu.writeBack.isWrite && !cache.io.cpu.writeBack.haltIt + dBusAccess.rsp.data := cache.io.cpu.writeBack.data + dBusAccess.rsp.error := cache.io.cpu.writeBack.unalignedAccess || cache.io.cpu.writeBack.accessError + + component.addPrePopTask{() => + when(forceDatapath){ + execute.output(REGFILE_WRITE_DATA) := dBusAccess.cmd.address.asBits + } + memory.input(IS_DBUS_SHARING) init(False) + writeBack.input(IS_DBUS_SHARING) init(False) + } + } + if(csrInfo){ val csr = service(classOf[CsrPlugin]) csr.r(0xCC0, 0 -> U(cacheSize/wayCount), 20 -> U(bytePerLine)) diff --git a/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala b/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala index e6761de..fb51f10 100644 --- a/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala +++ b/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala @@ -21,6 +21,8 @@ class HaltOnExceptionPlugin() extends Plugin[VexRiscv] with ExceptionService { exceptionPortsInfos += ExceptionPortInfo(interface,stage,priority) interface } + override def isExceptionPending(): Bool = False + override def build(pipeline: VexRiscv): Unit = { import pipeline._ diff --git a/src/test/cpp/regression/main.cpp b/src/test/cpp/regression/main.cpp index 82c5429..907b2c4 100644 --- a/src/test/cpp/regression/main.cpp +++ b/src/test/cpp/regression/main.cpp @@ -1316,6 +1316,16 @@ public: if(isDBusCheckedRegion(addr)){ CpuRef::MemWrite w; w.address = addr; + while((mask & 1) == 0){ + mask >>= 1; + w.address++; + w.data >>= 8; + } + switch(mask){ + case 1: size = 0; break; + case 3: size = min(1u, size); break; + case 15: size = min(2u, size); break; + } w.size = 1 << size; switch(size){ case 0: w.data = *data & 0xFF; break; @@ -3446,6 +3456,7 @@ int main(int argc, char **argv, char **env) { // redo(REDO,WorkspaceRegression("deleg").withRiscvRef()->loadHex("../raw/deleg/build/deleg.hex")->bootAt(0x80000000u)->run(50e3);); // return 0; + redo(REDO,WorkspaceRegression("mmu").withRiscvRef()->loadHex("../raw/mmu/build/mmu.hex")->bootAt(0x80000000u)->run(50e3);); for(int idx = 0;idx < 1;idx++){