From 54f785b1a3bd24b7754ed5232ce9bf88673fa5cf Mon Sep 17 00:00:00 2001 From: Charles Papon Date: Fri, 21 Jul 2017 17:40:45 +0200 Subject: [PATCH] Add full avalon support (pass regression) --- README.md | 1 + .../VexRiscv/Plugin/DBusSimplePlugin.scala | 37 ++++++ .../VexRiscv/Plugin/IBusSimplePlugin.scala | 24 ++++ src/main/scala/VexRiscv/TestsWorkspace.scala | 14 ++- .../scala/VexRiscv/demo/VexRiscvAvalon.scala | 12 ++ src/test/cpp/regression/main.cpp | 106 ++++++++++++++++++ 6 files changed, 193 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f40706..d8feba4 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ This repository host an RISC-V implementation written in SpinalHDL. There is som - Pipelined on 5 stages (Fetch, Decode, Execute, Memory, WriteBack) - 1.16 DMIPS/Mhz when all features are enabled - Optimized for FPGA +- AXI4 and Avalon ready - Optional MUL/DIV extension - Optional instruction and data caches - Optional MMU diff --git a/src/main/scala/VexRiscv/Plugin/DBusSimplePlugin.scala b/src/main/scala/VexRiscv/Plugin/DBusSimplePlugin.scala index cd0f14c..30a1bf3 100644 --- a/src/main/scala/VexRiscv/Plugin/DBusSimplePlugin.scala +++ b/src/main/scala/VexRiscv/Plugin/DBusSimplePlugin.scala @@ -4,6 +4,7 @@ import VexRiscv._ import spinal.core._ import spinal.lib._ import spinal.lib.bus.amba4.axi._ +import spinal.lib.bus.avalon.{AvalonMMConfig, AvalonMM} case class DBusSimpleCmd() extends Bundle{ @@ -36,6 +37,13 @@ object DBusSimpleBus{ useLen = false, useResp = true ) + + def getAvalonConfig() = AvalonMMConfig.pipelined( + addressWidth = 32, + dataWidth = 32).copy( + useByteEnable = true, + maximumPendingReadTransactions = 1 + ) } case class DBusSimpleBus() extends Bundle with IMasterSlave{ val cmd = Stream(DBusSimpleCmd()) @@ -92,6 +100,35 @@ case class DBusSimpleBus() extends Bundle with IMasterSlave{ // axi2 << axi axi2 } + + + + def toAvalon(stageCmd : Boolean = true): AvalonMM = { + val avalonConfig = DBusSimpleBus.getAvalonConfig() + val mm = AvalonMM(avalonConfig) + val cmdStage = if(stageCmd) cmd.stage else cmd + mm.read := cmdStage.valid && !cmdStage.wr + mm.write := cmdStage.valid && cmdStage.wr + mm.address := (cmdStage.address >> 2) @@ U"00" + mm.writeData := cmdStage.size.mux ( + U(0) -> cmdStage.data(7 downto 0) ## cmdStage.data(7 downto 0) ## cmdStage.data(7 downto 0) ## cmdStage.data(7 downto 0), + U(1) -> cmdStage.data(15 downto 0) ## cmdStage.data(15 downto 0), + default -> cmdStage.data(31 downto 0) + ) + mm.byteEnable := (cmdStage.size.mux ( + U(0) -> B"0001", + U(1) -> B"0011", + default -> B"1111" + ) << cmdStage.address(1 downto 0)).resized + + + cmdStage.ready := mm.waitRequestn + rsp.ready :=mm.readDataValid + rsp.error := False //TODO + rsp.data := mm.readData + + mm + } } diff --git a/src/main/scala/VexRiscv/Plugin/IBusSimplePlugin.scala b/src/main/scala/VexRiscv/Plugin/IBusSimplePlugin.scala index 5011135..4fea620 100644 --- a/src/main/scala/VexRiscv/Plugin/IBusSimplePlugin.scala +++ b/src/main/scala/VexRiscv/Plugin/IBusSimplePlugin.scala @@ -4,6 +4,7 @@ import VexRiscv.{Stageable, ExceptionService, ExceptionCause, VexRiscv} import spinal.core._ import spinal.lib._ import spinal.lib.bus.amba4.axi._ +import spinal.lib.bus.avalon.{AvalonMMConfig, AvalonMM} case class IBusSimpleCmd() extends Bundle{ @@ -33,6 +34,13 @@ object IBusSimpleBus{ useResp = true, useSize = false ) + + def getAvalonConfig() = AvalonMMConfig.pipelined( + addressWidth = 32, + dataWidth = 32 + ).getReadOnlyConfig.copy( + maximumPendingReadTransactions = 1 + ) } case class IBusSimpleBus(interfaceKeepData : Boolean) extends Bundle with IMasterSlave{ var cmd = Stream(IBusSimpleCmd()) @@ -68,6 +76,22 @@ case class IBusSimpleBus(interfaceKeepData : Boolean) extends Bundle with IMaste // axi2 << axi axi2 } + + def toAvalon(): AvalonMM = { + assert(!interfaceKeepData) + val avalonConfig = IBusSimpleBus.getAvalonConfig() + val mm = AvalonMM(avalonConfig) + + mm.read := cmd.valid + mm.address := (cmd.pc >> 2) @@ U"00" + cmd.ready := mm.waitRequestn + + rsp.ready := mm.readDataValid + rsp.inst := mm.readData + rsp.error := False //TODO + + mm + } } class IBusSimplePlugin(interfaceKeepData : Boolean, catchAccessFault : Boolean) extends Plugin[VexRiscv]{ diff --git a/src/main/scala/VexRiscv/TestsWorkspace.scala b/src/main/scala/VexRiscv/TestsWorkspace.scala index ad85675..54dc83e 100644 --- a/src/main/scala/VexRiscv/TestsWorkspace.scala +++ b/src/main/scala/VexRiscv/TestsWorkspace.scala @@ -33,7 +33,7 @@ object TestsWorkspace { plugins = List( new PcManagerSimplePlugin(0x00000000l, false), // new IBusSimplePlugin( -// interfaceKeepData = true, +// interfaceKeepData = false, // catchAccessFault = true // ), new IBusCachedPlugin( @@ -229,12 +229,24 @@ object TestsWorkspace { toplevel.rework { var iBus : AvalonMM = null for (plugin <- toplevel.config.plugins) plugin match { + case plugin: IBusSimplePlugin => { + plugin.iBus.asDirectionLess() //Unset IO properties of iBus + iBus = master(plugin.iBus.toAvalon()) + .setName("iBusAvalon") + .addTag(ClockDomainTag(ClockDomain.current)) //Specify a clock domain to the iBus (used by QSysify) + } case plugin: IBusCachedPlugin => { plugin.iBus.asDirectionLess() //Unset IO properties of iBus iBus = master(plugin.iBus.toAvalon()) .setName("iBusAvalon") .addTag(ClockDomainTag(ClockDomain.current)) //Specify a clock domain to the iBus (used by QSysify) } + case plugin: DBusSimplePlugin => { + plugin.dBus.asDirectionLess() + master(plugin.dBus.toAvalon()) + .setName("dBusAvalon") + .addTag(ClockDomainTag(ClockDomain.current)) + } case plugin: DBusCachedPlugin => { plugin.dBus.asDirectionLess() master(plugin.dBus.toAvalon()) diff --git a/src/main/scala/VexRiscv/demo/VexRiscvAvalon.scala b/src/main/scala/VexRiscv/demo/VexRiscvAvalon.scala index 659ce3b..affefa1 100644 --- a/src/main/scala/VexRiscv/demo/VexRiscvAvalon.scala +++ b/src/main/scala/VexRiscv/demo/VexRiscvAvalon.scala @@ -131,12 +131,24 @@ object VexRiscvAvalon{ cpu.rework { var iBus : AvalonMM = null for (plugin <- cpuConfig.plugins) plugin match { + case plugin: IBusSimplePlugin => { + plugin.iBus.asDirectionLess() //Unset IO properties of iBus + iBus = master(plugin.iBus.toAvalon()) + .setName("iBusAvalon") + .addTag(ClockDomainTag(ClockDomain.current)) //Specify a clock domain to the iBus (used by QSysify) + } case plugin: IBusCachedPlugin => { plugin.iBus.asDirectionLess() //Unset IO properties of iBus iBus = master(plugin.iBus.toAvalon()) .setName("iBusAvalon") .addTag(ClockDomainTag(ClockDomain.current)) //Specify a clock domain to the iBus (used by QSysify) } + case plugin: DBusSimplePlugin => { + plugin.dBus.asDirectionLess() + master(plugin.dBus.toAvalon()) + .setName("dBusAvalon") + .addTag(ClockDomainTag(ClockDomain.current)) + } case plugin: DBusCachedPlugin => { plugin.dBus.asDirectionLess() master(plugin.dBus.toAvalon()) diff --git a/src/test/cpp/regression/main.cpp b/src/test/cpp/regression/main.cpp index aff5cea..8d04aa1 100644 --- a/src/test/cpp/regression/main.cpp +++ b/src/test/cpp/regression/main.cpp @@ -491,6 +491,53 @@ public: }; #endif +#ifdef IBUS_SIMPLE_AVALON +#include +struct IBusSimpleAvalonRsp{ + uint32_t data; + bool error; +}; + + +class IBusSimpleAvalon : public SimElement{ +public: + queue rsps; + + Workspace *ws; + VVexRiscv* top; + IBusSimpleAvalon(Workspace* ws){ + this->ws = ws; + this->top = ws->top; + } + + virtual void onReset(){ + top->iBusAvalon_waitRequestn = 1; + top->iBusAvalon_readDataValid = 0; + } + + virtual void preCycle(){ + if (top->iBusAvalon_read && top->iBusAvalon_waitRequestn) { + IBusSimpleAvalonRsp rsp; + ws->iBusAccess(top->iBusAvalon_address,&rsp.data,&rsp.error); + rsps.push(rsp); + } + } + //TODO doesn't catch when instruction removed ? + virtual void postCycle(){ + if(!rsps.empty() && (!ws->iStall || VL_RANDOM_I(7) < 100)){ + IBusSimpleAvalonRsp rsp = rsps.front(); rsps.pop(); + top->iBusAvalon_readDataValid = 1; + top->iBusAvalon_readData = rsp.data; + } else { + top->iBusAvalon_readDataValid = 0; + top->iBusAvalon_readData = VL_RANDOM_I(32); + } + if(ws->iStall) + top->iBusAvalon_waitRequestn = VL_RANDOM_I(7) < 100; + } +}; +#endif + #ifdef IBUS_CACHED class IBusCached : public SimElement{ @@ -635,6 +682,59 @@ public: }; #endif +#ifdef DBUS_SIMPLE_AVALON +#include +struct DBusSimpleAvalonRsp{ + uint32_t data; + bool error; +}; + + +class DBusSimpleAvalon : public SimElement{ +public: + queue rsps; + + Workspace *ws; + VVexRiscv* top; + DBusSimpleAvalon(Workspace* ws){ + this->ws = ws; + this->top = ws->top; + } + + virtual void onReset(){ + top->dBusAvalon_waitRequestn = 1; + top->dBusAvalon_readDataValid = 0; + } + + virtual void preCycle(){ + if (top->dBusAvalon_write && top->dBusAvalon_waitRequestn) { + bool dummy; + ws->dBusAccess(top->dBusAvalon_address,1,2,top->dBusAvalon_byteEnable,&top->dBusAvalon_writeData,&dummy); + } + if (top->dBusAvalon_read && top->dBusAvalon_waitRequestn) { + DBusSimpleAvalonRsp rsp; + ws->dBusAccess(top->dBusAvalon_address,0,2,0xF,&rsp.data,&rsp.error); + rsps.push(rsp); + } + } + //TODO doesn't catch when instruction removed ? + virtual void postCycle(){ + if(!rsps.empty() && (!ws->iStall || VL_RANDOM_I(7) < 100)){ + DBusSimpleAvalonRsp rsp = rsps.front(); rsps.pop(); + top->dBusAvalon_readDataValid = 1; + top->dBusAvalon_readData = rsp.data; + } else { + top->dBusAvalon_readDataValid = 0; + top->dBusAvalon_readData = VL_RANDOM_I(32); + } + if(ws->iStall) + top->dBusAvalon_waitRequestn = VL_RANDOM_I(7) < 100; + } +}; +#endif + + + #ifdef DBUS_CACHED class DBusCached : public SimElement{ public: @@ -1039,6 +1139,9 @@ void Workspace::fillSimELements(){ #ifdef IBUS_SIMPLE simElements.push_back(new IBusSimple(this)); #endif + #ifdef IBUS_SIMPLE_AVALON + simElements.push_back(new IBusSimpleAvalon(this)); + #endif #ifdef IBUS_CACHED simElements.push_back(new IBusCached(this)); #endif @@ -1048,6 +1151,9 @@ void Workspace::fillSimELements(){ #ifdef DBUS_SIMPLE simElements.push_back(new DBusSimple(this)); #endif + #ifdef DBUS_SIMPLE_AVALON + simElements.push_back(new DBusSimpleAvalon(this)); + #endif #ifdef DBUS_CACHED simElements.push_back(new DBusCached(this)); #endif