From e47b76fa677926f9c06150fd8b7cea7722692a2e Mon Sep 17 00:00:00 2001 From: Charles Papon Date: Fri, 19 Apr 2019 17:35:48 +0200 Subject: [PATCH] #60 Added automated linux regression in travis Fix DBusCached plugin access sharing for the MMU deadlock when exception is in the decode stage Fix IBusSimplePlugin issues with used with non regular configs + MMU Bring back the LinuxGen config into a light one --- .gitmodules | 3 + .travis.yml | 15 +- src/main/scala/vexriscv/Services.scala | 2 +- src/main/scala/vexriscv/demo/Linux.scala | 16 ++- .../scala/vexriscv/demo/MuraxUtiles.scala | 5 + .../scala/vexriscv/plugin/CsrPlugin.scala | 8 +- .../vexriscv/plugin/DBusCachedPlugin.scala | 3 +- .../vexriscv/plugin/DecoderSimplePlugin.scala | 10 +- .../plugin/HaltOnExceptionPlugin.scala | 2 +- .../vexriscv/plugin/IBusSimplePlugin.scala | 20 +-- .../scala/vexriscv/plugin/MmuPlugin.scala | 3 - src/test/cpp/regression/main.cpp | 130 +++++++++++++++--- src/test/cpp/regression/makefile | 30 ++++ src/test/resources/VexRiscvRegressionData | 1 + .../vexriscv/TestIndividualFeatures.scala | 56 ++++---- 15 files changed, 222 insertions(+), 82 deletions(-) create mode 100644 .gitmodules create mode 160000 src/test/resources/VexRiscvRegressionData diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ab5c2da --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/test/resources/VexRiscvRegressionData"] + path = src/test/resources/VexRiscvRegressionData + url = ../VexRiscvRegressionData.git diff --git a/.travis.yml b/.travis.yml index ee00974..832ba00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,8 +11,8 @@ scala: sbt_args: -no-colors -J-Xss2m script: - - export VEXRISCV_REGRESSION_CONFIG_COUNT=100 - export VEXRISCV_REGRESSION_FREERTOS_COUNT=no + - export VEXRISCV_REGRESSION_THREAD_COUNT=1 - sbt -jvm-opts travis/jvmopts.compile compile - sbt -jvm-opts travis/jvmopts.test test @@ -22,7 +22,11 @@ jdk: # - openjdk7 env: - - secure: "v7FHP8yK/zixpv1ML05qcRhZfDVDFdTmTPjfMZHL7gmrJveVDgze22x4tY4tB1+JEXhKuVTYvimOrX/Ok+rOOT5gVKLowv4PUQwCR+HgWVIbqjcfZNLsa369v03/p4K/zbjJSiXFahZYOXa0ApED2KWHcVfCrNsPv0UF7YZGiIa1Q/lPBwfmpN1rLih2Mpgn4KVaJky22t7JXJyVrNdGVmIA51slVbyFwFAE8Ww/0tkC+i2PUcWWRMIxtXP4iyq/9Npcq5VdqOatKfWHqAElLfKSPNMYLMlcyxyNpNx4paq8cL6fQxFcBLi9M2msz2i/qpKv30a0tzNo5bQQgucAXOQJB2Buks728upLuqsr+k25hwcqrtjyMOr9UQkt7qXAJH/0kimW7aW1yoMxbm/6mNG98X9D1EzNRewHAKatwJeFy1bw5qIuSQxPBwQMGloManrHOHGotmHKk7Y+dgM/z1UlaAdxSQuKWGXBc8QlQvif8puPYEdJMoInJNRxiWfYu06XnmzTXgMketK7RdULM9DVYzw8hzS2EIWKu8Oa0zn0PTevD2YeJNd4G8mDqO0vz5hloIc7pFsq/exQUB/kFozfCsnvhW8P+MPN0LpuSpptBQTsLWbM5BH0hd46HoWcneDdlMvVrUcgsTPmmSroIkLIEUo+Y2iN5eQHPPp85Cw=" + - VEXRISCV_REGRESSION_CONFIG_COUNT=0 + - VEXRISCV_REGRESSION_CONFIG_COUNT=5 + - VEXRISCV_REGRESSION_CONFIG_COUNT=5 + - VEXRISCV_REGRESSION_CONFIG_COUNT=5 + - VEXRISCV_REGRESSION_CONFIG_COUNT=5 before_install: # JDK fix @@ -34,11 +38,9 @@ before_install: # Verilator - sudo apt-get install git make autoconf g++ flex bison -y # First time prerequisites - - git clone http://git.veripool.org/git/verilator # Only first time - - unset VERILATOR_ROOT # For bash + - wget https://www.veripool.org/ftp/verilator-4.012.tgz + - tar xvzf verilator*.t*gz - cd verilator - - git pull # Make sure we're up-to-date - - git checkout verilator_3_916 - autoconf # Create ./configure script - ./configure - make -j$(nproc) @@ -48,6 +50,7 @@ before_install: - git clone https://github.com/SpinalHDL/SpinalHDL.git -b dev - cd VexRiscv + - git submodule update --init --recursive #- curl -T README.md -udolu1990:$BINTRAY_KEY https://api.bintray.com/content/spinalhdl/VexRiscv/test/0.0.4/README.md #- curl -X POST -udolu1990:$BINTRAY_KEY https://api.bintray.com/content/spinalhdl/VexRiscv/test/0.0.4/publish #- sbt compile diff --git a/src/main/scala/vexriscv/Services.scala b/src/main/scala/vexriscv/Services.scala index 26ecf84..09cd098 100644 --- a/src/main/scala/vexriscv/Services.scala +++ b/src/main/scala/vexriscv/Services.scala @@ -33,7 +33,7 @@ case class ExceptionCause() extends Bundle{ trait ExceptionService{ def newExceptionPort(stage : Stage, priority : Int = 0) : Flow[ExceptionCause] - def isExceptionPending() : Bool + def isExceptionPending(stage : Stage) : Bool } trait PrivilegeService{ diff --git a/src/main/scala/vexriscv/demo/Linux.scala b/src/main/scala/vexriscv/demo/Linux.scala index fe60056..9b32dc9 100644 --- a/src/main/scala/vexriscv/demo/Linux.scala +++ b/src/main/scala/vexriscv/demo/Linux.scala @@ -40,13 +40,13 @@ cd VexRiscv Run regressions => sbt "runMain vexriscv.demo.LinuxGen -r" cd src/test/cpp/regression -make run IBUS=CACHED DBUS=CACHED DEBUG_PLUGIN=STD DHRYSTONE=yes SUPERVISOR=yes CSR=yes COMPRESSED=no LRSC=yes AMO=yes REDO=10 TRACE=no +make clean run IBUS=CACHED DBUS=CACHED DEBUG_PLUGIN=STD DHRYSTONE=yes SUPERVISOR=yes MMU=yes CSR=yes COMPRESSED=no MUL=yes DIV=yes LRSC=yes AMO=yes REDO=10 TRACE=no COREMARK=yes LINUX_REGRESSION=yes Run linux in simulation (Require the machime mode emulator compiled in SIM mode) => sbt "runMain vexriscv.demo.LinuxGen" cd src/test/cpp/regression export BUILDROOT=/home/miaou/pro/riscv/buildrootSpinal -make run IBUS=CACHED DBUS=CACHED DEBUG_PLUGIN=STD SUPERVISOR=yes CSR=yes COMPRESSED=no LRSC=yes AMO=yes REDO=0 DHRYSTONE=no LINUX_SOC=yes EMULATOR=../../../main/c/emulator/build/emulator.bin VMLINUX=$BUILDROOT/output/images/Image DTB=$BUILDROOT/board/spinal/vexriscv_sim/rv32.dtb RAMDISK=$BUILDROOT/output/images/rootfs.cpio TRACE=no FLOW_INFO=no +make clean run IBUS=CACHED DBUS=CACHED DEBUG_PLUGIN=STD SUPERVISOR=yes CSR=yes COMPRESSED=no LRSC=yes AMO=yes REDO=0 DHRYSTONE=no LINUX_SOC=yes EMULATOR=../../../main/c/emulator/build/emulator.bin VMLINUX=$BUILDROOT/output/images/Image DTB=$BUILDROOT/board/spinal/vexriscv_sim/rv32.dtb RAMDISK=$BUILDROOT/output/images/rootfs.cpio WITH_USER_IO=yes TRACE=no FLOW_INFO=no Run linux with QEMU (Require the machime mode emulator compiled in QEMU mode) export BUILDROOT=/home/miaou/pro/riscv/buildrootSpinal @@ -94,10 +94,12 @@ rm VexRiscv.v cp $DATA/VexRiscv.v ../../../.. make run IBUS=CACHED DBUS=CACHED DEBUG_PLUGIN=STD SUPERVISOR=yes CSR=yes COMPRESSED=no LRSC=yes AMO=yes REDO=0 DHRYSTONE=no LINUX_SOC=yes EMULATOR=$DATA/emulator.bin VMLINUX=$DATA/vmlinux.bin DTB=$DATA/rv32.dtb RAMDISK=$DATA/rootfs.cpio TRACE=no FLOW_INFO=no +make clean run IBUS=CACHED DBUS=CACHED DEBUG_PLUGIN=STD DHRYSTONE=no SUPERVISOR=yes CSR=yes COMPRESSED=no MUL=yes DIV=yes LRSC=yes AMO=yes MMU=yes REDO=1 TRACE=no LINUX_REGRESSION=yes qemu-system-riscv32 -nographic -machine virt -m 1536M -device loader,file=$DATA/emulator.bin,addr=0x80000000,cpu-num=0 -device loader,file=$DATA/rv32.dtb,addr=0xC3000000 -device loader,file=$DATA/vmlinux.bin,addr=0xC0000000 -device loader,file=$DATA/rootfs.cpio,addr=0xc2000000 +make run IBUS=CACHED DBUS=CACHED DEBUG_PLUGIN=STD DHRYSTONE=yess SUPERVISOR=yes CSR=yes COMPRESSED=yes MUL=yes DIV=yes LRSC=yes AMO=yes REDO=1 TRACE=no LINUX_REGRESSION=yes program ../../../main/c/emulator/build/emulator.bin 0x80000000 verify soc.loadBin(EMULATOR, 0x80000000); @@ -149,12 +151,12 @@ object LinuxGen { new IBusCachedPlugin( resetVector = 0x80000000l, compressedGen = false, - prediction = DYNAMIC_TARGET, + prediction = NONE, injectorStage = false, config = InstructionCacheConfig( - cacheSize = 4096*4, + cacheSize = 4096*1, bytePerLine = 32, - wayCount = 4, + wayCount = 1, addressWidth = 32, cpuDataWidth = 32, memDataWidth = 32, @@ -184,9 +186,9 @@ object LinuxGen { dBusCmdSlavePipe = true, dBusRspSlavePipe = true, config = new DataCacheConfig( - cacheSize = 4096*4, + cacheSize = 4096*1, bytePerLine = 32, - wayCount = 4, + wayCount = 1, addressWidth = 32, cpuDataWidth = 32, memDataWidth = 32, diff --git a/src/main/scala/vexriscv/demo/MuraxUtiles.scala b/src/main/scala/vexriscv/demo/MuraxUtiles.scala index 2422450..4e650d3 100644 --- a/src/main/scala/vexriscv/demo/MuraxUtiles.scala +++ b/src/main/scala/vexriscv/demo/MuraxUtiles.scala @@ -163,3 +163,8 @@ class MuraxApb3Timer extends Component{ interruptCtrl.io.inputs(1) := timerB.io.full io.interrupt := interruptCtrl.io.pendings.orR } + + +object MuraxApb3TimerGen extends App{ + SpinalVhdl(new MuraxApb3Timer()) +} \ No newline at end of file diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index 8057071..3c7b036 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -327,8 +327,8 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception interface } - var exceptionPending : Bool = null - override def isExceptionPending(): Bool = exceptionPending + var exceptionPendings : Vec[Bool] = null + override def isExceptionPending(stage : Stage): Bool = exceptionPendings(pipeline.stages.indexOf(stage)) var jumpInterface : Flow[UInt] = null var timerInterrupt, externalInterrupt, softwareInterrupt : Bool = null @@ -420,7 +420,7 @@ class CsrPlugin(config: CsrPluginConfig) extends Plugin[VexRiscv] with Exception jumpInterface.valid := False jumpInterface.payload.assignDontCare() - exceptionPending = False + exceptionPendings = Vec(Bool, pipeline.stages.length) timerInterrupt = in Bool() setName("timerInterrupt") externalInterrupt = in Bool() setName("externalInterrupt") softwareInterrupt = in Bool() setName("softwareInterrupt") default(False) @@ -721,7 +721,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) + exceptionPendings := exceptionValidsRegs } else null diff --git a/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala b/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala index b48386c..78af84f 100644 --- a/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala +++ b/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala @@ -292,7 +292,8 @@ class DBusCachedPlugin(config : DataCacheConfig, val forceDatapath = False when(dBusAccess.cmd.valid){ decode.arbitration.haltByOther := True - when(!stagesFromExecute.map(_.arbitration.isValid).orR && !pipeline.service(classOf[ExceptionService]).isExceptionPending()){ + val exceptionService = pipeline.service(classOf[ExceptionService]) + when(!stagesFromExecute.map(s => s.arbitration.isValid || exceptionService.isExceptionPending(s)).orR){ when(!cache.io.cpu.redo) { cache.io.cpu.execute.isValid := True dBusAccess.cmd.ready := !execute.arbitration.isStuck diff --git a/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala b/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala index c713557..22afa26 100644 --- a/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/DecoderSimplePlugin.scala @@ -66,8 +66,8 @@ class DecoderSimplePlugin(catchIllegalInstruction : Boolean = false, forceLegalI } } - val defaults = mutable.HashMap[Stageable[_ <: BaseType], BaseType]() - val encodings = mutable.HashMap[MaskedLiteral,ArrayBuffer[(Stageable[_ <: BaseType], BaseType)]]() + val defaults = mutable.LinkedHashMap[Stageable[_ <: BaseType], BaseType]() + val encodings = mutable.LinkedHashMap[MaskedLiteral,ArrayBuffer[(Stageable[_ <: BaseType], BaseType)]]() var decodeExceptionPort : Flow[ExceptionCause] = null @@ -105,7 +105,7 @@ class DecoderSimplePlugin(catchIllegalInstruction : Boolean = false, forceLegalI } else { var offset = 0 var defaultValue, defaultCare = BigInt(0) - val offsetOf = mutable.HashMap[Stageable[_ <: BaseType], Int]() + val offsetOf = mutable.LinkedHashMap[Stageable[_ <: BaseType], Int]() //Build defaults value and field offset map stageables.foreach(e => { @@ -191,8 +191,8 @@ object DecodingBench extends App{ object Symplify{ - val cache = mutable.HashMap[Bits,mutable.HashMap[Masked,Bool]]() - def getCache(addr : Bits) = cache.getOrElseUpdate(addr,mutable.HashMap[Masked,Bool]()) + val cache = mutable.LinkedHashMap[Bits,mutable.LinkedHashMap[Masked,Bool]]() + def getCache(addr : Bits) = cache.getOrElseUpdate(addr,mutable.LinkedHashMap[Masked,Bool]()) //Generate terms logic for the given input def logicOf(input : Bits,terms : Seq[Masked]) = terms.map(t => getCache(input).getOrElseUpdate(t,t === input)).asBits.orR diff --git a/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala b/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala index fb51f10..6e39948 100644 --- a/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala +++ b/src/main/scala/vexriscv/plugin/HaltOnExceptionPlugin.scala @@ -21,7 +21,7 @@ class HaltOnExceptionPlugin() extends Plugin[VexRiscv] with ExceptionService { exceptionPortsInfos += ExceptionPortInfo(interface,stage,priority) interface } - override def isExceptionPending(): Bool = False + override def isExceptionPending(stage : Stage): Bool = False override def build(pipeline: VexRiscv): Unit = { diff --git a/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala b/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala index 0532d26..5b1d7ef 100644 --- a/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala @@ -220,17 +220,18 @@ class IBusSimplePlugin(resetVector : BigInt, pipeline plug new FetchArea(pipeline) { var cmd = Stream(IBusSimpleCmd()) - iBus.cmd << (if(cmdForkPersistence && !cmdForkOnSecondStage) cmd.s2mPipe() else cmd) + val cmdWithS2mPipe = cmdForkPersistence && (!cmdForkOnSecondStage || mmuBus != null) + iBus.cmd << (if(cmdWithS2mPipe) cmd.s2mPipe() else cmd) //Avoid sending to many iBus cmd val pendingCmd = Reg(UInt(log2Up(pendingMax + 1) bits)) init (0) 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 secondStagePersistence = cmdForkPersistence && cmdForkOnSecondStage && !cmdWithS2mPipe + def cmdForkStage = if(!secondStagePersistence) iBusRsp.stages(if(cmdForkOnSecondStage) 1 else 0) else iBusRsp.stages(1) - - val cmdFork = if(!cmdForkPersistence || !cmdForkOnSecondStage) new Area { + val cmdFork = if(!secondStagePersistence) new Area { //This implementation keep the cmd on the bus until it's executed or the the pipeline is flushed def stage = cmdForkStage stage.halt setWhen(stage.input.valid && (!cmd.valid || !cmd.ready)) @@ -255,7 +256,7 @@ class IBusSimplePlugin(resetVector : BigInt, mmuBus.cmd.isValid := cmdForkStage.input.valid mmuBus.cmd.virtualAddress := cmdForkStage.input.payload mmuBus.cmd.bypassTranslation := False - mmuBus.end := !cmdForkStage.output.fire || flush + mmuBus.end := cmdForkStage.output.fire || flush cmd.pc := mmuBus.rsp.physicalAddress(31 downto 2) @@ "00" @@ -265,7 +266,10 @@ class IBusSimplePlugin(resetVector : BigInt, cmd.valid := False } - cmdForkStage.halt.setWhen(mmuBus.busy) + when(mmuBus.busy){ + cmdForkStage.input.valid := False + cmdForkStage.input.ready := False + } val joinCtx = stageXToIBusRsp(cmdForkStage, mmuBus.rsp) } @@ -280,7 +284,7 @@ class IBusSimplePlugin(resetVector : BigInt, val discardCounter = Reg(UInt(log2Up(pendingMax + 1) bits)) init (0) discardCounter := discardCounter - (iBus.rsp.fire && discardCounter =/= 0).asUInt when(flush) { - if(cmdForkOnSecondStage && cmdForkPersistence) + if(secondStagePersistence) discardCounter := pendingCmd + cmd.valid.asUInt - iBus.rsp.fire.asUInt else discardCounter := (if(cmdForkOnSecondStage) pendingCmdNext else pendingCmd - iBus.rsp.fire.asUInt) @@ -318,7 +322,7 @@ class IBusSimplePlugin(resetVector : BigInt, if(memoryTranslatorPortConfig != null){ redoRequired setWhen( stages.last.input.valid && mmu.joinCtx.refilling) redoBranch.valid := redoRequired && iBusRsp.readyForError - redoBranch.payload := stages.last.input.payload + redoBranch.payload := decode.input(PC) decode.arbitration.flushAll setWhen(redoBranch.valid) } diff --git a/src/main/scala/vexriscv/plugin/MmuPlugin.scala b/src/main/scala/vexriscv/plugin/MmuPlugin.scala index f8a3418..ebf46c4 100644 --- a/src/main/scala/vexriscv/plugin/MmuPlugin.scala +++ b/src/main/scala/vexriscv/plugin/MmuPlugin.scala @@ -149,8 +149,6 @@ class MmuPlugin(ioRange : UInt => Bool, } val shared = new Area { - val busy = Reg(Bool) init(False) - val State = new SpinalEnum{ val IDLE, L1_CMD, L1_RSP, L0_CMD, L0_RSP = newElement() } @@ -182,7 +180,6 @@ class MmuPlugin(ioRange : UInt => Bool, is(State.IDLE){ for(port <- portsInfo.sortBy(_.priority)){ when(port.bus.cmd.isValid && port.bus.rsp.refilling){ - busy := True vpn(1) := port.bus.cmd.virtualAddress(31 downto 22) vpn(0) := port.bus.cmd.virtualAddress(21 downto 12) portId := port.id diff --git a/src/test/cpp/regression/main.cpp b/src/test/cpp/regression/main.cpp index 518f30c..5097a26 100644 --- a/src/test/cpp/regression/main.cpp +++ b/src/test/cpp/regression/main.cpp @@ -1634,6 +1634,7 @@ public: cout << "FAIL " << name << " at PC=" << hex << setw(8) << top->VexRiscv->writeBack_PC << dec; //<< " seed : " << seed << if(riscvRefEnable) cout << hex << " REF PC=" << riscvRef.lastPc << " REF I=" << riscvRef.lastInstruction << dec; + cout << " time=" << i; cout << endl; cycles += instanceCycles; @@ -3059,29 +3060,22 @@ public: //#endif + #include #include #include - termios stdinRestoreSettings; void stdinNonBuffered(){ static struct termios old, new1; - tcgetattr(STDIN_FILENO, &old); /* grab old terminal i/o settings */ - new1 = old; /* make new settings same as old settings */ - new1.c_lflag &= ~ICANON; /* disable buffered i/o */ + tcgetattr(STDIN_FILENO, &old); // grab old terminal i/o settings + new1 = old; // make new settings same as old settings + new1.c_lflag &= ~ICANON; // disable buffered i/o new1.c_lflag &= ~ECHO; - tcsetattr(STDIN_FILENO, TCSANOW, &new1); /* use these new terminal i/o settings now */ + tcsetattr(STDIN_FILENO, TCSANOW, &new1); // use these new terminal i/o settings now setvbuf(stdin, NULL, _IONBF, 0); stdinRestoreSettings = old; } -void stdoutNonBuffered(){ - setvbuf(stdout, NULL, _IONBF, 0); -} - -void stdinRestore(){ - tcsetattr(STDIN_FILENO, TCSANOW, &stdinRestoreSettings); /* use these new terminal i/o settings now */ -} bool stdinNonEmpty(){ struct timeval tv; @@ -3094,6 +3088,17 @@ bool stdinNonEmpty(){ return (FD_ISSET(0, &fds)); } + +void stdoutNonBuffered(){ + setvbuf(stdout, NULL, _IONBF, 0); +} + +void stdinRestore(){ + tcsetattr(STDIN_FILENO, TCSANOW, &stdinRestoreSettings); +} + + + void my_handler(int s){ printf("Caught signal %d\n",s); stdinRestore(); @@ -3111,23 +3116,39 @@ void captureCtrlC(){ sigaction(SIGINT, &sigIntHandler, NULL); } -#ifdef LINUX_SOC + + + +#if defined(LINUX_SOC) || defined(LINUX_REGRESSION) +#include class LinuxSoc : public Workspace{ public: + queue customCin; + void pushCin(string m){ + for(char& c : m) { + customCin.push(c); + } + } LinuxSoc(string name) : Workspace(name) { + #ifdef WITH_USER_IO stdinNonBuffered(); - stdoutNonBuffered(); captureCtrlC(); + #endif + stdoutNonBuffered(); } virtual ~LinuxSoc(){ + #ifdef WITH_USER_IO stdinRestore(); + #endif } virtual bool isDBusCheckedRegion(uint32_t address){ return true;} virtual bool isPerifRegion(uint32_t addr) { return (addr & 0xF0000000) == 0xF0000000 || (addr & 0xE0000000) == 0xE0000000;} virtual bool isMmuRegion(uint32_t addr) { return true; } + + virtual void dBusAccess(uint32_t addr,bool wr, uint32_t size,uint32_t mask, uint32_t *data, bool *error) { if(isPerifRegion(addr)) switch(addr){ //TODO Emulate peripherals here @@ -3137,18 +3158,24 @@ public: case 0xFFFFFFEC: if(wr) mTimeCmp = (mTimeCmp & 0x00000000FFFFFFFF) | (((uint64_t)*data) << 32); else *data = mTimeCmp >> 32; break; case 0xFFFFFFF8: if(wr){ - cout << (char)*data; - logTraces << (char)*data; + char c = (char)*data; + cout << c; + logTraces << c; logTraces.flush(); + onStdout(c); } else { + #ifdef WITH_USER_IO if(stdinNonEmpty()){ char c; read(0, &c, 1); *data = c; - //cout << "getchar " << c << endl; + } else + #endif + if(!customCin.empty()){ + *data = customCin.front(); + customCin.pop(); } else { *data = -1; - //cout << "getchar NONE" << endl; } } break; @@ -3158,7 +3185,43 @@ public: Workspace::dBusAccess(addr,wr,size,mask,data,error); } + + virtual void onStdout(char c){ + + } }; + + +class LinuxRegression: public LinuxSoc{ +public: + string pendingLine = ""; + bool pendingLineContain(string m) { + return strstr(pendingLine.c_str(), m.c_str()) != NULL; + } + + enum State{LOGIN, ECHO_FILE, HEXDUMP, HEXDUMP_CHECK, PASS}; + State state = LOGIN; + LinuxRegression(string name) : LinuxSoc(name) { + + } + + ~LinuxRegression() { + } + + + virtual void onStdout(char c){ + pendingLine += c; + switch(state){ + case LOGIN: if (pendingLineContain("buildroot login:")) { pushCin("root\n"); state = ECHO_FILE; } break; + case ECHO_FILE: if (pendingLineContain("# ")) { pushCin("echo \"miaou\" > test.txt\n"); state = HEXDUMP; pendingLine = "";} break; + case HEXDUMP: if (pendingLineContain("# ")) { pushCin("hexdump -C test.txt\n"); state = HEXDUMP_CHECK; pendingLine = "";} break; + case HEXDUMP_CHECK: if (pendingLineContain("00000000 6d 69 61 6f 75 0a ")) { pushCin(""); state = PASS; pendingLine = "";} break; + case PASS: if (pendingLineContain("# ")) { pass(); } break; + } + if(c == '\n' || pendingLine.length() > 200) pendingLine = ""; + } +}; + #endif string riscvTestMain[] = { @@ -3490,7 +3553,7 @@ int main(int argc, char **argv, char **env) { soc.loadBin(DTB, 0xC3000000); soc.loadBin(RAMDISK, 0xC2000000); #endif - //soc.setIStall(true); //TODO It currently improve speed but should be removed later + //soc.setIStall(true); //soc.setDStall(true); soc.bootAt(0x80000000); soc.run(0); @@ -3500,6 +3563,10 @@ int main(int argc, char **argv, char **env) { } #endif + + + + // #ifdef MMU // redo(REDO,WorkspaceRegression("mmu").withRiscvRef()->loadHex("../raw/mmu/build/mmu.hex")->bootAt(0x80000000u)->run(50e3);); // #endif @@ -3722,15 +3789,36 @@ int main(int argc, char **argv, char **env) { queue > tasksSelected(std::deque>(tasks.begin(), tasks.end())); multiThreadedExecute(tasksSelected); #endif + + #if defined(LINUX_REGRESSION) + { + + LinuxRegression soc("linux"); + #ifndef DEBUG_PLUGIN_EXTERNAL + soc.withRiscvRef(); + soc.loadBin(EMULATOR, 0x80000000); + soc.loadBin(VMLINUX, 0xC0000000); + soc.loadBin(DTB, 0xC3000000); + soc.loadBin(RAMDISK, 0xC2000000); + #endif + //soc.setIStall(true); + //soc.setDStall(true); + soc.bootAt(0x80000000); + soc.run(153995602l*6); +// soc.run((470000000l + 2000000) / 2); +// soc.run(438700000l/2); + } + #endif + } uint64_t duration = timer_end(startedAt); cout << endl << "****************************************************************" << endl; cout << "Had simulate " << Workspace::cycles << " clock cycles in " << duration*1e-9 << " s (" << Workspace::cycles / (duration*1e-6) << " Khz)" << endl; if(Workspace::successCounter == Workspace::testsCounter) - cout << "SUCCESS " << Workspace::successCounter << "/" << Workspace::testsCounter << endl; + cout << "REGRESSION SUCCESS " << Workspace::successCounter << "/" << Workspace::testsCounter << endl; else - cout<< "FAILURE " << Workspace::testsCounter - Workspace::successCounter << "/" << Workspace::testsCounter << endl; + cout<< "REGRESSION FAILURE " << Workspace::testsCounter - Workspace::successCounter << "/" << Workspace::testsCounter << endl; cout << "****************************************************************" << endl << endl; diff --git a/src/test/cpp/regression/makefile b/src/test/cpp/regression/makefile index d82a3e4..9c4294f 100644 --- a/src/test/cpp/regression/makefile +++ b/src/test/cpp/regression/makefile @@ -34,6 +34,7 @@ COMPRESSED?=no SUPERVISOR?=no STOP_ON_ERROR?=no COREMARK=no +WITH_USER_IO?=no ADDCFLAGS += -CFLAGS -DIBUS_${IBUS} ADDCFLAGS += -CFLAGS -DDBUS_${DBUS} @@ -66,6 +67,31 @@ ifeq ($(LINUX_SOC),yes) ADDCFLAGS += -CFLAGS -DEMULATOR='\"$(EMULATOR)\"' endif +ARCH_LINUX=rv32i +ifeq ($(MUL),yes) +ifeq ($(DIV),yes) +ARCH_LINUX:=$(ARCH_LINUX)m +endif +endif +ARCH_LINUX:=$(ARCH_LINUX)a +ifeq ($(COMPRESSED),yes) +ARCH_LINUX:=$(ARCH_LINUX)c +endif + +ifeq ($(LINUX_REGRESSION),yes) +ifneq ($(ARCH_LINUX),rv32iac) +ifneq ($(ARCH_LINUX),rv32ia) + ADDCFLAGS += -CFLAGS -DLINUX_REGRESSION + ADDCFLAGS += -CFLAGS -DARCH_LINUX='\"$(ARCH_LINUX)\"' + ADDCFLAGS += -CFLAGS -DVMLINUX='\"../../resources/VexRiscvRegressionData/sim/linux/$(ARCH_LINUX)/Image\"' + ADDCFLAGS += -CFLAGS -DDTB='\"../../resources/VexRiscvRegressionData/sim/linux/$(ARCH_LINUX)/rv32.dtb\"' + ADDCFLAGS += -CFLAGS -DRAMDISK='\"../../resources/VexRiscvRegressionData/sim/linux/$(ARCH_LINUX)/rootfs.cpio\"' + ADDCFLAGS += -CFLAGS -DEMULATOR='\"../../resources/VexRiscvRegressionData/sim/linux/emulator/emulator.bin\"' +endif +endif +endif + + ifeq ($(FLOW_INFO),yes) ADDCFLAGS += -CFLAGS -DFLOW_INFO endif @@ -93,6 +119,10 @@ endif ifeq ($(IBUS_TC),yes) ADDCFLAGS += -CFLAGS -DIBUS_TC=yes endif +ifeq ($(WITH_USER_IO),yes) + ADDCFLAGS += -CFLAGS -DWITH_USER_IO=yes +endif + ifeq ($(COMPRESSED),yes) ADDCFLAGS += -CFLAGS -DCOMPRESSED diff --git a/src/test/resources/VexRiscvRegressionData b/src/test/resources/VexRiscvRegressionData new file mode 160000 index 0000000..77b66d3 --- /dev/null +++ b/src/test/resources/VexRiscvRegressionData @@ -0,0 +1 @@ +Subproject commit 77b66d304f888369176fb9ac2f9d6302dd4d276d diff --git a/src/test/scala/vexriscv/TestIndividualFeatures.scala b/src/test/scala/vexriscv/TestIndividualFeatures.scala index 5275cfa..034527c 100644 --- a/src/test/scala/vexriscv/TestIndividualFeatures.scala +++ b/src/test/scala/vexriscv/TestIndividualFeatures.scala @@ -49,6 +49,11 @@ object VexRiscvUniverse{ val universes = List(CATCH_ALL, MMU) } + +object Hack{ + var dCounter = 0 +} + class ShiftDimension extends VexRiscvDimension("Shift") { override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = random(r, List( new VexRiscvPosition("FullLate") { @@ -287,13 +292,14 @@ class IBusDimension extends VexRiscvDimension("IBus") { override def instructionAnticipatedOk() = injectorStage } } else { + val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL) val compressed = r.nextBoolean() - val tighlyCoupled = r.nextBoolean() + val tighlyCoupled = r.nextBoolean() && !catchAll // val tighlyCoupled = false val prediction = random(r, List(NONE, STATIC, DYNAMIC, DYNAMIC_TARGET)) - val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL) val relaxedPcCalculation, twoCycleCache, injectorStage = r.nextBoolean() val twoCycleRam = r.nextBoolean() && twoCycleCache + val bytePerLine = List(8,16,32,64)(r.nextInt(4)) var cacheSize = 0 var wayCount = 0 do{ @@ -301,7 +307,7 @@ class IBusDimension extends VexRiscvDimension("IBus") { wayCount = 1 << r.nextInt(3) }while(cacheSize/wayCount < 512 || (catchAll && cacheSize/wayCount > 4096)) - new VexRiscvPosition("Cached" + (if(twoCycleCache) "2cc" else "") + (if(injectorStage) "Injstage" else "") + (if(twoCycleRam) "2cr" else "") + "S" + cacheSize + "W" + wayCount + (if(relaxedPcCalculation) "Relax" else "") + (if(compressed) "Rvc" else "") + prediction.getClass.getTypeName().replace("$","")+ (if(tighlyCoupled)"Tc" else "")) with InstructionAnticipatedPosition{ + new VexRiscvPosition("Cached" + (if(twoCycleCache) "2cc" else "") + (if(injectorStage) "Injstage" else "") + (if(twoCycleRam) "2cr" else "") + "S" + cacheSize + "W" + wayCount + "BPL" + bytePerLine + (if(relaxedPcCalculation) "Relax" else "") + (if(compressed) "Rvc" else "") + prediction.getClass.getTypeName().replace("$","")+ (if(tighlyCoupled)"Tc" else "")) with InstructionAnticipatedPosition{ override def testParam = "IBUS=CACHED" + (if(compressed) " COMPRESSED=yes" else "") + (if(tighlyCoupled)" IBUS_TC=yes" else "") override def applyOn(config: VexRiscvConfig): Unit = { val p = new IBusCachedPlugin( @@ -313,7 +319,7 @@ class IBusDimension extends VexRiscvDimension("IBus") { memoryTranslatorPortConfig = mmuConfig, config = InstructionCacheConfig( cacheSize = cacheSize, - bytePerLine = 32, + bytePerLine = bytePerLine, wayCount = wayCount, addressWidth = 32, cpuDataWidth = 32, @@ -344,32 +350,37 @@ class DBusDimension extends VexRiscvDimension("DBus") { val mmuConfig = if(catchAll) MmuPortConfig( portTlbSize = 4) else null if(r.nextDouble() < 0.4){ + val withLrSc = catchAll val earlyInjection = r.nextBoolean() new VexRiscvPosition("Simple" + (if(earlyInjection) "Early" else "Late")) { - override def testParam = "DBUS=SIMPLE" + override def testParam = "DBUS=SIMPLE " + (if(withLrSc) "LRSC=yes " else "") override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new DBusSimplePlugin( catchAddressMisaligned = catchAll, catchAccessFault = catchAll, earlyInjection = earlyInjection, - memoryTranslatorPortConfig = mmuConfig + memoryTranslatorPortConfig = mmuConfig, + withLrSc = withLrSc ) // override def isCompatibleWith(positions: Seq[ConfigPosition[VexRiscvConfig]]) = catchAll == positions.exists(_.isInstanceOf[CatchAllPosition]) } } else { + val bytePerLine = List(8,16,32,64)(r.nextInt(4)) var cacheSize = 0 var wayCount = 0 + val withLrSc = catchAll + val withAmo = catchAll && r.nextBoolean() do{ cacheSize = 512 << r.nextInt(5) wayCount = 1 << r.nextInt(3) }while(cacheSize/wayCount < 512 || (catchAll && cacheSize/wayCount > 4096)) - new VexRiscvPosition("Cached" + "S" + cacheSize + "W" + wayCount) { - override def testParam = "DBUS=CACHED" + new VexRiscvPosition("Cached" + "S" + cacheSize + "W" + wayCount + "BPL" + bytePerLine) { + override def testParam = "DBUS=CACHED " + (if(withLrSc) "LRSC=yes " else "") + (if(withAmo) "AMO=yes " else "") override def applyOn(config: VexRiscvConfig): Unit = { config.plugins += new DBusCachedPlugin( config = new DataCacheConfig( cacheSize = cacheSize, - bytePerLine = 32, + bytePerLine = bytePerLine, wayCount = wayCount, addressWidth = 32, cpuDataWidth = 32, @@ -377,7 +388,8 @@ class DBusDimension extends VexRiscvDimension("DBus") { catchAccessError = catchAll, catchIllegal = catchAll, catchUnaligned = catchAll, - withLrSc = false + withLrSc = withLrSc, + withAmo = withAmo ), memoryTranslatorPortConfig = mmuConfig ) @@ -421,14 +433,14 @@ class MmuDimension extends VexRiscvDimension("DBus") { trait CatchAllPosition -//TODO CSR without exception + class CsrDimension(freertos : String) extends VexRiscvDimension("Csr") { override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = { val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL) if(catchAll){ new VexRiscvPosition("All") with CatchAllPosition{ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.linuxFull(0x80000020l)) - override def testParam = s"FREERTOS=$freertos" + override def testParam = s"FREERTOS=$freertos LINUX_REGRESSION=yes SUPERVISOR=yes" } } else if(r.nextDouble() < 0.2){ new VexRiscvPosition("AllNoException") with CatchAllPosition{ @@ -504,7 +516,7 @@ class TestIndividualFeatures extends FunSuite { new HazardDimension, new RegFileDimension, new SrcDimension, - new CsrDimension(sys.env.getOrElse("VEXRISCV_REGRESSION_FREERTOS_COUNT", "yes")), + new CsrDimension("no"),//sys.env.getOrElse("VEXRISCV_REGRESSION_FREERTOS_COUNT", "4")), TODO new DecoderDimension, new DebugDimension, new MmuDimension @@ -541,14 +553,14 @@ class TestIndividualFeatures extends FunSuite { test(prefix + name + "_test") { - val debug = false - val stdCmd = (if(debug) "make clean run REDO=1 TRACE=yes TRACE_ACCESS=yes STOP_ON_ERROR=yes DHRYSTONE=no THREAD_COUNT=1 TRACE_START=0 " else s"make clean run REDO=10 TRACE=no THREAD_COUNT=${sys.env.getOrElse("VEXRISCV_REGRESSION_THREAD_COUNT", Runtime.getRuntime().availableProcessors().toString)} ") + s" SEED=${testSeed} " + val debug = true + val stdCmd = (s"make clean run WITH_USER_IO=no REDO=10 TRACE=${if(debug) "yes" else "no"} TRACE_START=9999924910246l FLOW_INFO=no STOP_ON_ERROR=no DHRYSTONE=yes COREMARK=yes THREAD_COUNT=${sys.env.getOrElse("VEXRISCV_REGRESSION_THREAD_COUNT", Runtime.getRuntime().availableProcessors().toString)} ") + s" SEED=${testSeed} " // val stdCmd = "make clean run REDO=40 DHRYSTONE=no STOP_ON_ERROR=yes TRACE=yess " val testCmd = stdCmd + (positionsToApply).map(_.testParam).mkString(" ") println(testCmd) val str = doCmd(testCmd) - assert(!str.contains("FAIL") && !str.contains("Broken pipe")) + assert(str.contains("REGRESSION SUCCESS") && !str.contains("Broken pipe")) // val intFind = "(\\d+\\.?)+".r // val dmips = intFind.findFirstIn("DMIPS per Mhz\\: (\\d+.?)+".r.findAllIn(str).toList.last).get.toDouble } @@ -556,8 +568,8 @@ class TestIndividualFeatures extends FunSuite { // dimensions.foreach(d => d.positions.foreach(p => p.dimension = d)) -// val testId : Option[mutable.HashSet[Int]] = None -// val seed = Random.nextLong() + val testId : Option[mutable.HashSet[Int]] = None + val seed = Random.nextLong() // val testId = Some(mutable.HashSet(18,34,77,85,118,129,132,134,152,167,175,188,191,198,199)) //37/29 sp_flop_rv32i_O3 //val testId = Some(mutable.HashSet(18)) @@ -565,13 +577,6 @@ class TestIndividualFeatures extends FunSuite { // val seed = -2412372746600605141l -//// val testId = Some(mutable.HashSet[Int](0,28,45,93)) - val testId = Some(mutable.HashSet[Int](69, 43)) -//val testId = Some(mutable.HashSet[Int]( 43)) - val seed = -8485282932516819277l - - - val rand = new Random(seed) test("Info"){ @@ -589,6 +594,7 @@ class TestIndividualFeatures extends FunSuite { val testSeed = rand.nextInt() if(testId.isEmpty || testId.get.contains(i)) doTest(positions," random_" + i + "_", testSeed) + Hack.dCounter += 1 } // println(s"${usedPositions.size}/$positionsCount positions")