diff --git a/src/main/scala/SpinalRiscv/Plugin/IBusCachedPlugin.scala b/src/main/scala/SpinalRiscv/Plugin/IBusCachedPlugin.scala new file mode 100644 index 0000000..36736bd --- /dev/null +++ b/src/main/scala/SpinalRiscv/Plugin/IBusCachedPlugin.scala @@ -0,0 +1,318 @@ +package SpinalRiscv.Plugin + +import SpinalRiscv._ +import spinal.core._ +import spinal.lib._ + + +case class InstructionCacheConfig( cacheSize : Int, + bytePerLine : Int, + wayCount : Int, + wrappedMemAccess : Boolean, + addressWidth : Int, + cpuDataWidth : Int, + memDataWidth : Int){ + def burstSize = bytePerLine*8/memDataWidth +} + + + + +class IBusCachedPlugin(catchAccessFault : Boolean, cacheConfig : InstructionCacheConfig) extends Plugin[VexRiscv]{ + var iBus : InstructionCacheMemBus = null + + object IBUS_ACCESS_ERROR extends Stageable(Bool) + var decodeExceptionPort : Flow[ExceptionCause] = null + override def setup(pipeline: VexRiscv): Unit = { + pipeline.unremovableStages += pipeline.prefetch + + if(catchAccessFault) { + val exceptionService = pipeline.service(classOf[ExceptionService]) + decodeExceptionPort = exceptionService.newExceptionPort(pipeline.decode,1) + } + } + + override def build(pipeline: VexRiscv): Unit = { + import pipeline._ + import pipeline.config._ + + assert(catchAccessFault == false) //unimplemented + + + val cache = new InstructionCache(cacheConfig) + iBus = master(new InstructionCacheMemBus(cacheConfig)).setName("iBus") + iBus <> cache.io.mem + + + //Connect prefetch cache side + cache.io.cpu.cmd.isValid := prefetch.arbitration.isValid + cache.io.cpu.cmd.isFiring := prefetch.arbitration.isFiring + cache.io.cpu.cmd.address := prefetch.output(PC) + prefetch.arbitration.haltIt setWhen(cache.io.cpu.cmd.haltIt) + + //Connect fetch cache side + cache.io.cpu.rsp.isValid := fetch.arbitration.isValid + cache.io.cpu.rsp.isStuck := fetch.arbitration.isStuck + cache.io.cpu.rsp.address := fetch.output(PC) + fetch.arbitration.haltIt setWhen(cache.io.cpu.rsp.haltIt) + fetch.insert(INSTRUCTION) := cache.io.cpu.rsp.data + + cache.io.flush.cmd.valid := False + +// fetch.insert(IBUS_ACCESS_ERROR) := iRsp.error + +// if(catchAccessFault){ +// decodeExceptionPort.valid := decode.arbitration.isValid && decode.input(IBUS_ACCESS_ERROR) +// decodeExceptionPort.code := 1 +// decodeExceptionPort.badAddr := decode.input(PC) +// } + } +} + + + +case class InstructionCacheCpuCmd(p : InstructionCacheConfig) extends Bundle with IMasterSlave{ + val isValid = Bool + val isFiring = Bool + val haltIt = Bool + val address = UInt(p.addressWidth bit) + + override def asMaster(): Unit = { + out(isValid, isFiring, address) + in(haltIt) + } +} + +case class InstructionCacheCpuRsp(p : InstructionCacheConfig) extends Bundle with IMasterSlave { + val isValid = Bool + val haltIt = Bool + val isStuck = Bool + val address = UInt(p.addressWidth bit) + val data = Bits(32 bit) + + override def asMaster(): Unit = { + out(isValid, isStuck, address) + in(haltIt, data) + } +} + + +case class InstructionCacheCpuBus(p : InstructionCacheConfig) extends Bundle with IMasterSlave{ + val cmd = InstructionCacheCpuCmd(p) + val rsp = InstructionCacheCpuRsp(p) + + override def asMaster(): Unit = { + master(cmd) + master(rsp) + } +} + + +case class InstructionCacheMemCmd(p : InstructionCacheConfig) extends Bundle{ + val address = UInt(p.addressWidth bit) +} +case class InstructionCacheMemRsp(p : InstructionCacheConfig) extends Bundle{ + val data = Bits(32 bit) +} + +case class InstructionCacheMemBus(p : InstructionCacheConfig) extends Bundle with IMasterSlave{ + val cmd = Stream (InstructionCacheMemCmd(p)) + val rsp = Flow (InstructionCacheMemRsp(p)) + + override def asMaster(): Unit = { + master(cmd) + slave(rsp) + } +} + +case class InstructionCacheFlushBus() extends Bundle with IMasterSlave{ + val cmd = Event + val rsp = Bool + + override def asMaster(): Unit = { + master(cmd) + in(rsp) + } +} + +class InstructionCache(p : InstructionCacheConfig) extends Component{ + import p._ + assert(wayCount == 1) + assert(cpuDataWidth == memDataWidth) + val io = new Bundle{ + val flush = slave(InstructionCacheFlushBus()) + val cpu = slave(InstructionCacheCpuBus(p)) + val mem = master(InstructionCacheMemBus(p)) + } +// val haltCpu = False + val lineWidth = bytePerLine*8 + val lineCount = cacheSize/bytePerLine + val wordWidth = Math.max(memDataWidth,32) + val wordWidthLog2 = log2Up(wordWidth) + val wordPerLine = lineWidth/wordWidth + val bytePerWord = wordWidth/8 + val wayLineCount = lineCount/wayCount + val wayLineLog2 = log2Up(wayLineCount) + val wayWordCount = wayLineCount * wordPerLine + + val tagRange = addressWidth-1 downto log2Up(wayLineCount*bytePerLine) + val lineRange = tagRange.low-1 downto log2Up(bytePerLine) + val wordRange = log2Up(bytePerLine)-1 downto log2Up(bytePerWord) + + + class LineInfo() extends Bundle{ + val valid = Bool + val address = UInt(tagRange.length bit) + } + + val ways = Array.fill(wayCount)(new Area{ + val tags = Mem(new LineInfo(),wayLineCount) + val datas = Mem(Bits(wordWidth bit),wayWordCount) + }) + + + io.cpu.cmd.haltIt := False + + val lineLoader = new Area{ + val requestIn = Stream(wrap(new Bundle{ + val addr = UInt(addressWidth bit) + })) + + + if(wrappedMemAccess) + io.mem.cmd.address := requestIn.addr(tagRange.high downto wordRange.low) @@ U(0,wordRange.low bit) + else + io.mem.cmd.address := requestIn.addr(tagRange.high downto lineRange.low) @@ U(0,lineRange.low bit) + + + val flushCounter = Reg(UInt(log2Up(wayLineCount) + 1 bit)) init(0) + when(!flushCounter.msb){ + io.cpu.cmd.haltIt := True + flushCounter := flushCounter + 1 + } + when(!RegNext(flushCounter.msb)){ + io.cpu.cmd.haltIt := True + } + val flushFromInterface = RegInit(False) + when(io.flush.cmd.valid){ + io.cpu.cmd.haltIt := True + when(io.flush.cmd.ready){ + flushCounter := 0 + flushFromInterface := True + } + } + + io.flush.rsp := flushCounter.msb.rise && flushFromInterface + + val lineInfoWrite = new LineInfo() + lineInfoWrite.valid := flushCounter.msb + lineInfoWrite.address := requestIn.addr(tagRange) + when(requestIn.fire || !flushCounter.msb){ + val tagsAddress = Mux(flushCounter.msb,requestIn.addr(lineRange),flushCounter(flushCounter.high-1 downto 0)) + ways(0).tags(tagsAddress) := lineInfoWrite //TODO + } + + + val request = requestIn.haltWhen(!io.mem.cmd.ready).stage() + io.mem.cmd.valid := requestIn.valid && !request.isStall + val wordIndex = Reg(UInt(log2Up(wordPerLine) bit)) + val loadedWordsNext = Bits(wordPerLine bit) + val loadedWords = RegNext(loadedWordsNext) + val loadedWordsReadable = RegNext(loadedWords) + loadedWordsNext := loadedWords + when(io.mem.rsp.fire){ + wordIndex := wordIndex + 1 + loadedWordsNext(wordIndex) := True + ways(0).datas(request.addr(lineRange) @@ wordIndex) := io.mem.rsp.data //TODO + } + + val readyDelay = Reg(UInt(1 bit)) + when(loadedWordsNext === B(loadedWordsNext.range -> true)){ + readyDelay := readyDelay + 1 + } + request.ready := readyDelay === 1 + + when(requestIn.ready){ + wordIndex := io.mem.cmd.address(wordRange) + loadedWords := 0 + loadedWordsReadable := 0 + readyDelay := 0 + } + } + + val task = new Area{ + val waysHitValid = False + val waysHitWord = Bits(wordWidth bit) + waysHitWord.assignDontCare() + + val waysRead = for(way <- ways) yield new Area{ + val readAddress = Mux(io.cpu.rsp.isStuck,io.cpu.rsp.address,io.cpu.cmd.address) + val tag = way.tags.readSync(readAddress(lineRange)) + val data = way.datas.readSync(readAddress(lineRange.high downto wordRange.low)) + // val readAddress = request.address + // val tag = way.tags.readAsync(readAddress(lineRange)) + // val data = way.datas.readAsync(readAddress(lineRange.high downto wordRange.low)) + // way.tags.add(new AttributeString("ramstyle","no_rw_check")) + // way.datas.add(new AttributeString("ramstyle","no_rw_check")) + when(tag.valid && tag.address === io.cpu.rsp.address(tagRange)) { + waysHitValid := True + waysHitWord := data + } + } + + val loaderHitValid = lineLoader.request.valid && lineLoader.request.addr(tagRange) === io.cpu.rsp.address(tagRange) + val loaderHitReady = lineLoader.loadedWordsReadable(io.cpu.rsp.address(wordRange)) + + + io.cpu.rsp.haltIt := io.cpu.rsp.isValid && !( waysHitValid && !(loaderHitValid && !loaderHitReady)) + io.cpu.rsp.data := waysHitWord //TODO + lineLoader.requestIn.valid := io.cpu.rsp.isValid && ! waysHitValid + lineLoader.requestIn.addr := io.cpu.rsp.address + } + + io.flush.cmd.ready := !(lineLoader.request.valid || io.cpu.rsp.isValid) +} + +object InstructionCacheMain{ + class TopLevel extends Component{ + implicit val p = InstructionCacheConfig( + cacheSize =4096, + bytePerLine =32, + wayCount = 1, + wrappedMemAccess = true, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32) +// val io = new Bundle{ +// val cpu = slave(InstructionCacheCpuBus()) +// val mem = master(InstructionCacheMemBus()) +// } + val cache = new InstructionCache(p) + +// cache.io.cpu.cmd <-< io.cpu.cmd +// cache.io.mem.cmd >-> io.mem.cmd +// cache.io.mem.rsp <-< io.mem.rsp +// cache.io.cpu.rsp >-> io.cpu.rsp + // when(cache.io.cpu.rsp.valid){ + // cache.io.cpu.cmd.valid := RegNext(cache.io.cpu.cmd.valid) + // cache.io.cpu.cmd.address := RegNext(cache.io.cpu.cmd.address) + // } + } + def main(args: Array[String]) { + implicit val p = InstructionCacheConfig( + cacheSize =4096, + bytePerLine =32, + wayCount = 1, + wrappedMemAccess = true, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32) + // val io = new Bundle{ + // val cpu = slave(InstructionCacheCpuBus()) + // val mem = master(InstructionCacheMemBus()) + // } + + SpinalVhdl(new InstructionCache(p)) + } +} + diff --git a/src/main/scala/SpinalRiscv/Plugin/IBusSimplePlugin.scala b/src/main/scala/SpinalRiscv/Plugin/IBusSimplePlugin.scala index a9a8cc7..8fd6ba7 100644 --- a/src/main/scala/SpinalRiscv/Plugin/IBusSimplePlugin.scala +++ b/src/main/scala/SpinalRiscv/Plugin/IBusSimplePlugin.scala @@ -44,8 +44,8 @@ class IBusSimplePlugin(interfaceKeepData : Boolean, catchAccessFault : Boolean) //Insert iRsp into INSTRUCTION iRsp = in(IBusSimpleRsp()).setName("iRsp") fetch.insert(INSTRUCTION) := iRsp.inst - fetch.arbitration.haltIt setWhen(fetch.arbitration.isValid && !iRsp.ready) fetch.insert(IBUS_ACCESS_ERROR) := iRsp.error + fetch.arbitration.haltIt setWhen(fetch.arbitration.isValid && !iRsp.ready) if(catchAccessFault){ decodeExceptionPort.valid := decode.arbitration.isValid && decode.input(IBUS_ACCESS_ERROR) diff --git a/src/main/scala/SpinalRiscv/TopLevel.scala b/src/main/scala/SpinalRiscv/TopLevel.scala index c0d6f2e..adb6043 100644 --- a/src/main/scala/SpinalRiscv/TopLevel.scala +++ b/src/main/scala/SpinalRiscv/TopLevel.scala @@ -30,6 +30,16 @@ object TopLevel { pcWidth = 32 ) +// val iCacheConfig = InstructionCacheConfig( +// cacheSize =4096, +// bytePerLine =32, +// wayCount = 1, +// wrappedMemAccess = true, +// addressWidth = 32, +// cpuDataWidth = 32, +// memDataWidth = 32 +// ) + val csrConfig = MachineCsrConfig( mvendorid = 11, @@ -69,9 +79,21 @@ object TopLevel { config.plugins ++= List( new PcManagerSimplePlugin(0x00000000l, false), - new IBusSimplePlugin( - interfaceKeepData = true, - catchAccessFault = true +// new IBusSimplePlugin( +// interfaceKeepData = true, +// catchAccessFault = true +// ), + new IBusCachedPlugin( + catchAccessFault = false, + cacheConfig = InstructionCacheConfig( + cacheSize =4096, + bytePerLine =32, + wayCount = 1, + wrappedMemAccess = true, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32 + ) ), new DecoderSimplePlugin( catchIllegalInstruction = true diff --git a/src/test/cpp/testA/main.cpp b/src/test/cpp/testA/main.cpp index 701f24d..a38d951 100644 --- a/src/test/cpp/testA/main.cpp +++ b/src/test/cpp/testA/main.cpp @@ -13,9 +13,9 @@ #include #include #include +#include + -//#define REF -//#define TRACE class Memory{ public: @@ -133,9 +133,21 @@ double sc_time_stamp(){ } +class SimElement{ +public: + virtual void onReset(){} + virtual void preCycle(){} + virtual void postCycle(){} +}; + + +class Workspace; +void fillSimELements(Workspace *ws); + class Workspace{ public: static uint32_t cycles; + vector simElements; Memory mem; string name; VVexRiscv* top; @@ -161,6 +173,7 @@ public: regTraces.open (name + ".regTrace"); memTraces.open (name + ".memTrace"); logTraces.open (name + ".logTrace"); + fillSimELements(this); } virtual ~Workspace(){ @@ -176,8 +189,16 @@ public: } Workspace* bootAt(uint32_t pc) { bootPc = pc;} - virtual uint32_t iRspOverride(uint32_t value) { return value; } + virtual bool isAccessError(uint32_t addr) { return addr == 0xF00FFF60u; } + virtual void iBusAccess(uint32_t addr, uint32_t *data, bool *error) { + assert(addr % 4 == 0); + *data = ( (mem[addr + 0] << 0) + | (mem[addr + 1] << 8) + | (mem[addr + 2] << 16) + | (mem[addr + 3] << 24)); + *error = addr == 0xF00FFF60u; + } virtual void postReset() {} virtual void checks(){} virtual void pass(){ throw success();} @@ -203,12 +224,10 @@ public: // Reset top->clk = 0; top->reset = 0; - top->iCmd_ready = 1; top->dCmd_ready = 1; - top->iRsp_ready = 1; top->dRsp_ready = 1; - top->iRsp_error = 0; - top->dRsp_error = 0; + + for(SimElement* simElement : simElements) simElement->onReset(); top->eval(); currentTime = 3; top->reset = 1; @@ -232,10 +251,9 @@ public: try { // run simulation for 100 clock periods - uint32_t iRsp_inst_next = top->iRsp_inst; uint32_t dRsp_inst_next = VL_RANDOM_I(32); - bool iRsp_error_next = false, dRsp_error_next = false; - bool iRsp_ready_pending = false, dRsp_ready_pending = false; + bool dRsp_error_next = false; + bool dRsp_ready_pending = false; for (i = 16; i < timeout*2; i+=2) { mTime = i/2; #ifdef CSR @@ -251,26 +269,15 @@ public: dump(i); top->clk = 0; - top->eval(); top->eval();top->eval(); + top->eval(); dump(i + 1); - if (top->iCmd_valid && top->iCmd_ready && !iRsp_ready_pending) { - assertEq(top->iCmd_payload_pc & 3,0); - iRsp_ready_pending = true; - //printf("%d\n",top->iCmd_payload_pc); - iRsp_inst_next = iRspOverride((mem[top->iCmd_payload_pc + 0] << 0) - | (mem[top->iCmd_payload_pc + 1] << 8) - | (mem[top->iCmd_payload_pc + 2] << 16) - | (mem[top->iCmd_payload_pc + 3] << 24)); - iRsp_error_next = isAccessError(top->iCmd_payload_pc); - } if (top->dCmd_valid && top->dCmd_ready && ! dRsp_ready_pending) { dRsp_ready_pending = true; dRsp_inst_next = VL_RANDOM_I(32); - //printf("%d\n",top->iCmd_payload_pc); uint32_t addr = top->dCmd_payload_address; dRsp_error_next = isAccessError(addr); if(top->dCmd_payload_wr){ @@ -334,19 +341,16 @@ public: " : reg[" << (uint32_t)top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_address << "] = " << top->VexRiscv->writeBack_RegFilePlugin_regFileWrite_payload_data << endl; } + for(SimElement* simElement : simElements) simElement->preCycle(); checks(); top->clk = 1; - top->eval(); top->eval(); + top->eval(); cycles += 1; - top->iRsp_ready = !iRsp_ready_pending; + + for(SimElement* simElement : simElements) simElement->postCycle(); + top->dRsp_ready = 0; - if(iRsp_ready_pending && (!iStall || VL_RANDOM_I(8) < 100)){ - top->iRsp_inst = iRsp_inst_next; - iRsp_ready_pending = false; - top->iRsp_ready = 1; - top->iRsp_error = iRsp_error_next; - } if(dRsp_ready_pending && (!dStall || VL_RANDOM_I(8) < 100)){ top->dRsp_data = dRsp_inst_next; dRsp_ready_pending = false; @@ -356,7 +360,6 @@ public: top->dRsp_data = VL_RANDOM_I(32); } - if(iStall) top->iCmd_ready = VL_RANDOM_I(8) < 100 && !iRsp_ready_pending; if(dStall) top->dCmd_ready = VL_RANDOM_I(8) < 100 && !dRsp_ready_pending; @@ -382,6 +385,104 @@ public: return this; } }; + + + +#ifdef IBUS_SIMPLE +class IBusSimple : public SimElement{ +public: + uint32_t inst_next = VL_RANDOM_I(32); + bool error_next = false; + bool pending = false; + + Workspace *ws; + VVexRiscv* top; + IBusSimple(Workspace* ws){ + this->ws = ws; + this->top = ws->top; + } + + virtual void onReset(){ + top->iCmd_ready = 1; + top->iRsp_ready = 1; + } + + virtual void preCycle(){ + if (top->iCmd_valid && top->iCmd_ready && !pending) { + assertEq(top->iCmd_payload_pc & 3,0); + pending = true; + ws->iBusAccess(top->iCmd_payload_pc,&inst_next,&error_next); + } + } + + virtual void postCycle(){ + top->iRsp_ready = !pending; + if(pending && (!ws->iStall || VL_RANDOM_I(8) < 100)){ + top->iRsp_inst = inst_next; + pending = false; + top->iRsp_ready = 1; + top->iRsp_error = error_next; + } + if(ws->iStall) top->iCmd_ready = VL_RANDOM_I(8) < 100 && !pending; + } +}; +#endif + + +#ifdef IBUS_CACHED +class IBusCached : public SimElement{ +public: + uint32_t inst_next = VL_RANDOM_I(32); + bool error_next = false; + uint32_t pendingCount = 0; + uint32_t address; + + Workspace *ws; + VVexRiscv* top; + IBusCached(Workspace* ws){ + this->ws = ws; + this->top = ws->top; + } + + + virtual void onReset(){ + top->iBus_cmd_ready = 1; + top->iBus_rsp_valid = 0; + } + + virtual void preCycle(){ + if (top->iBus_cmd_valid && top->iBus_cmd_ready && pendingCount == 0) { + assertEq(top->iBus_cmd_payload_address & 3,0); + pendingCount = 8; + address = top->iBus_cmd_payload_address; + } + } + + virtual void postCycle(){ + bool dummy; + top->iBus_rsp_valid = 0; + if(pendingCount != 0 && (!ws->iStall || VL_RANDOM_I(8) < 100)){ + ws->iBusAccess(address,&top->iBus_rsp_payload_data,&dummy); + pendingCount--; + address = (address & ~0x1F) + ((address + 4) & 0x1F); + top->iBus_rsp_valid = 1; + } + if(ws->iStall) top->iBus_cmd_ready = VL_RANDOM_I(8) < 100 && pendingCount == 0; + } +}; +#endif + +void fillSimELements(Workspace *ws){ + + #ifdef IBUS_SIMPLE + ws->simElements.push_back(new IBusSimple(ws)); + #endif + #ifdef IBUS_CACHED + ws->simElements.push_back(new IBusCached(ws)); + #endif +} + + uint32_t Workspace::cycles = 0; #ifndef REF @@ -474,12 +575,9 @@ public: } } - - virtual uint32_t iRspOverride(uint32_t value) { - switch(value){ - case 0x0ff0000f: return 0x00000013; - default: return value; - } + virtual void iBusAccess(uint32_t addr, uint32_t *data, bool *error){ + Workspace::iBusAccess(addr,data,error); + if(*data == 0x0ff0000f) *data = 0x00000013; } }; #endif @@ -600,6 +698,7 @@ long timer_end(struct timespec start_time){ int main(int argc, char **argv, char **env) { Verilated::randReset(2); Verilated::commandArgs(argc, argv); + printf("BOOT\n"); timespec startedAt = timer_start(); diff --git a/src/test/cpp/testA/makefile b/src/test/cpp/testA/makefile index edda6f2..364e0f5 100644 --- a/src/test/cpp/testA/makefile +++ b/src/test/cpp/testA/makefile @@ -1,3 +1,5 @@ +IBUS=IBUS_CACHED +DBUS=DBUS_SIMPLE TRACE=no TRACE_START=0 CSR=yes @@ -7,6 +9,8 @@ REDO=10 REF=no TRACE_WITH_TIME=no +ADDCFLAGS += -CFLAGS -D${IBUS} +ADDCFLAGS += -CFLAGS -D${DBUS} ADDCFLAGS += -CFLAGS -DREDO=${REDO} ifeq ($(DHRYSTONE),yes) ADDCFLAGS += -CFLAGS -DDHRYSTONE