From 1090111a6f10b748ca69633ac92571e678180b4f Mon Sep 17 00:00:00 2001 From: Dolu1990 Date: Fri, 15 Jun 2018 13:00:59 +0200 Subject: [PATCH] TestIndividual is now fully random --- README.md | 69 ++++++++++--------- src/main/scala/vexriscv/TestsWorkspace.scala | 59 ++++++++-------- .../vexriscv/plugin/IBusCachedPlugin.scala | 9 +-- src/test/python/tool/disasm.s | 2 +- .../vexriscv/TestIndividualFeatures.scala | 68 ++++++++++++------ 5 files changed, 120 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 7b75f86..b905cb7 100644 --- a/README.md +++ b/README.md @@ -332,17 +332,19 @@ You can find multiples software examples and demo there : https://github.com/Spi There is some measurements of Murax SoC timings and area : ``` -Murax interlocked stages (0.45 DMIPS/Mhz) -> - Artix 7 -> 305 Mhz 1004 LUT 1297 FF - Cyclone V -> 160 Mhz 744 ALMs - Cyclone IV -> 148 Mhz 1,522 LUT 1,255 FF - ICE40-HX -> 51 Mhz 2402 LC (icestorm) +Murax interlocked stages (0.45 DMIPS/Mhz, 8 bits GPIO) -> + Artix 7 -> 299 Mhz 984 LUT 1186 FF + Cyclone V -> 175 Mhz 710 ALMs + Cyclone IV -> 137 Mhz 1,436 LUT 1,193 FF + iCE40 -> 48 Mhz 2337 LC (icestorm) + iCE40Ultra -> 20 Mhz 2337 LC (icestorm) -MuraxFast bypassed stages (0.65 DMIPS/Mhz) -> - Artix 7 -> 312 Mhz 1240 LUT 1330 FF - Cyclone V -> 159 Mhz 884 ALMs - Cyclone IV -> 142 Mhz 1,755 LUT 1,289 FF - ICE40-HX -> 50 Mhz, 2787 LC (icestorm) +MuraxFast bypassed stages (0.65 DMIPS/Mhz, 8 bits GPIO) -> + Artix 7 -> 294 Mhz 1128 LUT 1219 FF + Cyclone V -> 165 Mhz 840 ALMs + Cyclone IV -> 141 Mhz 1,680 LUT 1,227 FF + iCE40 -> 48 Mhz 2702 LC (icestorm) + iCE40Ultra -> 22 Mhz 2702 LC (icestorm) ``` There is some scripts to generate the SoC and call the icestorm toolchain there : scripts/Murax/ @@ -414,14 +416,10 @@ val cpu = new VexRiscv( config = VexRiscvConfig( //Provide a list of plugins which will futher add their logic into the CPU plugins = List( - new PcManagerSimplePlugin( + new IBusSimplePlugin( resetVector = 0x00000000l, relaxedPcCalculation = true ), - new IBusSimplePlugin( - interfaceKeepData = false, - catchAccessFault = false - ), new DBusSimplePlugin( catchAddressMisaligned = false, catchAccessFault = false @@ -447,8 +445,7 @@ val cpu = new VexRiscv( ), new BranchPlugin( earlyBranch = false, - catchAddressMisaligned = false, - prediction = NONE + catchAddressMisaligned = false ), new YamlPlugin("cpu0.yaml") ) @@ -616,7 +613,6 @@ So again, if you generate the CPU without any plugin, it will only contain the 5 This chapter is describing plugins currently implemented. -- [PcManagerSimplePlugin](#pcmanagersimpleplugin) - [IBusSimplePlugin](#ibussimpleplugin) - [IBusCachedPlugin](#ibuscachedplugin) - [DecoderSimplePlugin](#decodersimpleplugin) @@ -649,24 +645,24 @@ This plugin implement the programme counter and over an jump service to all plug | relaxedPcCalculation | Boolean | By default jump have an asynchronous immediate effect on the program counter, which allow to reduce the branch penalties by one cycle but could reduce the FMax as it will combinatorialy drive the instruction bus address signal. To avoid this you can set this parameter to true, which will make the jump affecting the programm counter in a sequancial way, which will cut the combinatorial path but add one additional cycle of penalty when a jump occur. | -The jump interface implemented by this plugin allow all other plugin to request jumps. The stage argument specify from which stage the jump is asked, which will allow the PcManagerSimplePlugin plugin to manage priorities between jump requests. - -```scala -trait JumpService{ - def createJumpInterface(stage : Stage) : Flow[UInt] -} -``` This plugin operate into the prefetch stage. #### IBusSimplePlugin -This plugin fetch instruction via a very simple and neutral memory interface going outside the CPU. +This plugin implement the CPU frontend (instruction fetch) via a very simple and neutral memory interface going outside the CPU. | Parameters | type | description | | ------ | ----------- | ------ | -| interfaceKeepData | Boolean | Specify if the read response interface keep the data until the next one, or if it's only present a single cycle.| | catchAccessFault | Boolean | If an the read response specify an read error and this parameter is true, it will generate an CPU exception trap | +| resetVector | BigInt | Address of the program counter after the reset | +| relaxedPcCalculation | Boolean | By default jump have an asynchronous immediate effect on the program counter, which allow to reduce the branch penalties by one cycle but could reduce the FMax as it will combinatorialy drive the instruction bus address signal. To avoid this you can set this parameter to true, which will make the jump affecting the programm counter in a sequancial way, which will cut the combinatorial path but add one additional cycle of penalty when a jump occur. | +| relaxedBusCmdValid | Boolean | Same than relaxedPcCalculation, but for the iBus.cmd.valid pin. | +| compressedGen | Boolean | Enable RVC support | +| busLatencyMin | Int | Specify the minimal latency between the iBus.cmd and iBus.rsp, which will add the corresponding number of stages into the frontend to keep the IPC to 1.| +| injectorStage | Boolean | Add a stage between the frontend and the decode stage of the CPU to improve FMax. (busLatencyMin + injectorStage) should be at least two. | +| prediction | BranchPrediction | Can be set to NONE/STATIC/DYNAMIC/DYNAMIC_TARGET to specify the branch predictor implementation, see bellow for more descriptions | +| historyRamSizeLog2 | Int | Specify the number of entries in the direct mapped prediction cache of DYNAMIC/DYNAMIC_TARGET implementation. 2 pow historyRamSizeLog2 entries | There is the SimpleBus interface definition @@ -700,7 +696,14 @@ There is at least one cycle latency between que cmd and the rsp. the rsp.ready f Note that bridges are implemented to convert this interface into AXI4 and Avalon -This plugin fit in the fetch stage +The jump interface implemented by this plugin allow all other plugin to request jumps. The stage argument specify from which stage the jump is asked, which will allow the PcManagerSimplePlugin plugin to manage priorities between jump requests. + +```scala +trait JumpService{ + def createJumpInterface(stage : Stage) : Flow[UInt] +} +``` + #### IBusCachedPlugin @@ -719,6 +722,11 @@ Simple and light multi way instruction cache. | catchIllegalAccess | Boolean | Catch when an memory access is done on non valid memory address (MMU) | | catchAccessFault | Boolean | Catch when the memeory bus is responding with an error | | catchMemoryTranslationMiss | Boolean | Catch when the MMU miss a TLB | +| resetVector | BigInt | Address of the program counter after the reset | +| relaxedPcCalculation | Boolean | By default jump have an asynchronous immediate effect on the program counter, which allow to reduce the branch penalties by one cycle but could reduce the FMax as it will combinatorialy drive the instruction bus address signal. To avoid this you can set this parameter to true, which will make the jump affecting the programm counter in a sequancial way, which will cut the combinatorial path but add one additional cycle of penalty when a jump occur. | +| compressedGen | Boolean | Enable RVC support | +| prediction | BranchPrediction | Can be set to NONE/STATIC/DYNAMIC/DYNAMIC_TARGET to specify the branch predictor implementation, see bellow for more descriptions | +| historyRamSizeLog2 | Int | Specify the number of entries in the direct mapped prediction cache of DYNAMIC/DYNAMIC_TARGET implementation. 2 pow historyRamSizeLog2 entries | Note : If you enable the twoCycleRam and and the wayCount is bigger than one, then the register file plugin should be configured to read the regFile in a asyncronus manner. @@ -813,18 +821,15 @@ Implement SLL/SRL/SRA instructions by using an full barriel shifter, so it execu #### BranchPlugin -This plugin implement all branch/jump instructions (JAL/JALR/BEQ/BNE/BLT/BGE/BLTU/BGEU) with some optional branch prediction. Each of those branch prediction could have been implemented into separated plugins. +This plugin implement all branch/jump instructions (JAL/JALR/BEQ/BNE/BLT/BGE/BLTU/BGEU) with primitives used by the cpu frontend plugins to implement branch prediction. The prediction implementation is set in the frontend plugins (IBusX) | Parameters | type | description | | ------ | ----------- | ------ | | earlyBranch | Boolean | By default the branch is done in the Memory stage to relax timings, but if this option is set it's done in the Execute stage| | catchAddressMisaligned | Boolean | If a jump/branch is done in an unaligned PC address, it will fire an trap exception | -| prediction | BranchPrediction | Can be set to NONE/STATIC/DYNAMIC/DYNAMIC_TARGET to specify the branch predictor implementation, see bellow for more descriptions | -| historyRamSizeLog2 | Int | Specify the number of entries in the direct mapped prediction cache of DYNAMIC/DYNAMIC_TARGET implementation. 2 pow historyRamSizeLog2 entries | Each miss predicted jumps will produce between 2 and 4 cycles penalty depending the `earlyBranch` and the `PcManagerSimplePlugin.relaxedPcCalculation` configurations - ##### Prediction NONE No prediction, each PC changes due to a jump/branch will produce a penalty. diff --git a/src/main/scala/vexriscv/TestsWorkspace.scala b/src/main/scala/vexriscv/TestsWorkspace.scala index a50a528..6cb0d42 100644 --- a/src/main/scala/vexriscv/TestsWorkspace.scala +++ b/src/main/scala/vexriscv/TestsWorkspace.scala @@ -31,38 +31,39 @@ object TestsWorkspace { SpinalConfig(mergeAsyncProcess = false).generateVerilog { val configFull = VexRiscvConfig( plugins = List( - new IBusSimplePlugin( - resetVector = 0x80000000l, - relaxedPcCalculation = true, - relaxedBusCmdValid = false, - prediction = DYNAMIC_TARGET, - historyRamSizeLog2 = 10, - catchAccessFault = true, - compressedGen = false, - busLatencyMin = 1 - ), -// new IBusCachedPlugin( +// new IBusSimplePlugin( // resetVector = 0x80000000l, -// compressedGen = true, +// relaxedPcCalculation = true, +// relaxedBusCmdValid = false, // prediction = DYNAMIC_TARGET, -// config = InstructionCacheConfig( -// cacheSize = 1024*16, -// bytePerLine = 32, -// wayCount = 1, -// addressWidth = 32, -// cpuDataWidth = 32, -// memDataWidth = 32, -// catchIllegalAccess = false, -// catchAccessFault = true, -// catchMemoryTranslationMiss = false, -// asyncTagMemory = false, -// twoCycleRam = false, -// twoCycleCache = true -// ), -// memoryTranslatorPortConfig = MemoryTranslatorPortConfig( -// portTlbSize = 4 -// ) +// historyRamSizeLog2 = 10, +// catchAccessFault = true, +// compressedGen = false, +// busLatencyMin = 1 // ), + new IBusCachedPlugin( + resetVector = 0x80000000l, + compressedGen = true, + prediction = DYNAMIC_TARGET, + injectorStage = true, + config = InstructionCacheConfig( + cacheSize = 1024*16, + bytePerLine = 32, + wayCount = 1, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchIllegalAccess = true, + catchAccessFault = true, + catchMemoryTranslationMiss = true, + asyncTagMemory = false, + twoCycleRam = false, + twoCycleCache = true + ), + memoryTranslatorPortConfig = MemoryTranslatorPortConfig( + portTlbSize = 4 + ) + ), // new DBusSimplePlugin( // catchAddressMisaligned = true, // catchAccessFault = true, diff --git a/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala b/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala index 4153389..c120c10 100644 --- a/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala +++ b/src/main/scala/vexriscv/plugin/IBusCachedPlugin.scala @@ -1,6 +1,6 @@ package vexriscv.plugin -import vexriscv._ +import vexriscv.{plugin, _} import vexriscv.ip._ import spinal.core._ import spinal.lib._ @@ -16,7 +16,8 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l, compressedGen : Boolean = false, keepPcPlus4 : Boolean = false, config : InstructionCacheConfig, - memoryTranslatorPortConfig : Any = null) extends IBusFetcherImpl( + memoryTranslatorPortConfig : Any = null, + injectorStage : Boolean = false) extends IBusFetcherImpl( catchAccessFault = config.catchAccessFault, resetVector = resetVector, keepPcPlus4 = keepPcPlus4, @@ -27,7 +28,7 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l, relaxedPcCalculation = relaxedPcCalculation, prediction = prediction, historyRamSizeLog2 = historyRamSizeLog2, - injectorStage = !config.twoCycleCache){ + injectorStage = !config.twoCycleCache || injectorStage){ import config._ var iBus : InstructionCacheMemBus = null @@ -133,7 +134,7 @@ class IBusCachedPlugin(resetVector : BigInt = 0x80000000l, cache.io.cpu.decode.pc := iBusRsp.inputPipeline(1).payload cache.io.cpu.decode.isUser := (if (privilegeService != null) privilegeService.isUser(decode) else False) - if((!twoCycleRam || wayCount == 1) && !compressedGen){ + if((!twoCycleRam || wayCount == 1) && !compressedGen && !injectorStage){ decode.insert(INSTRUCTION_ANTICIPATED) := Mux(decode.arbitration.isStuck, decode.input(INSTRUCTION), cache.io.cpu.fetch.data) } } diff --git a/src/test/python/tool/disasm.s b/src/test/python/tool/disasm.s index 2c71b28..257b105 100644 --- a/src/test/python/tool/disasm.s +++ b/src/test/python/tool/disasm.s @@ -1 +1 @@ -.word 0x7912a23 +.word 0x3c12083 diff --git a/src/test/scala/vexriscv/TestIndividualFeatures.scala b/src/test/scala/vexriscv/TestIndividualFeatures.scala index 247a58e..d3ef003 100644 --- a/src/test/scala/vexriscv/TestIndividualFeatures.scala +++ b/src/test/scala/vexriscv/TestIndividualFeatures.scala @@ -8,6 +8,7 @@ import vexriscv.demo._ import vexriscv.ip.{DataCacheConfig, InstructionCacheConfig} import vexriscv.plugin._ +import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.sys.process._ import scala.util.Random @@ -20,6 +21,16 @@ abstract class ConfigDimension[T](val name: String) { abstract class VexRiscvDimension(name: String) extends ConfigDimension[VexRiscvPosition](name) +abstract class ConfigPosition[T](val name: String) { + def applyOn(config: T): Unit + var dimension : ConfigDimension[_] = null + def isCompatibleWith(positions : Seq[ConfigPosition[T]]) : Boolean = true +} + +abstract class VexRiscvPosition(name: String) extends ConfigPosition[VexRiscvConfig](name){ + def testParam : String = "" +} + class ShiftDimension extends VexRiscvDimension("Shift") { override val positions = List( new VexRiscvPosition("FullLate") { @@ -256,14 +267,16 @@ class IBusDimension extends VexRiscvDimension("IBus") { wayCount <- List(1, 4); cacheSize <- List(512, 4096); compressed <- List(false, true); + injectorStage <- List(false, true); relaxedPcCalculation <- List(false, true); - if !(!twoCycleCache && twoCycleRam ) && !(prediction != NONE && (wayCount == 1 || cacheSize == 4096 ))) yield new VexRiscvPosition("Cached" + (if(twoCycleCache) "2cc" else "") + (if(twoCycleRam) "2cr" else "") + "S" + cacheSize + "W" + wayCount + (if(relaxedPcCalculation) "Relax" else "") + (if(compressed) "Rvc" else "") + prediction.getClass.getTypeName().replace("$","")) with InstructionAnticipatedPosition{ + if !(!twoCycleCache && twoCycleRam ) && !(prediction != NONE && (wayCount == 1 || cacheSize == 4096 ))) yield 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("$","")) with InstructionAnticipatedPosition{ override def testParam = "IBUS=CACHED" + (if(compressed) " COMPRESSED=yes" else "") override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new IBusCachedPlugin( resetVector = 0x80000000l, compressedGen = compressed, prediction = prediction, relaxedPcCalculation = relaxedPcCalculation, + injectorStage = injectorStage, config = InstructionCacheConfig( cacheSize = cacheSize, bytePerLine = 32, @@ -335,15 +348,24 @@ class DBusDimension extends VexRiscvDimension("DBus") { -abstract class ConfigPosition[T](val name: String) { - def applyOn(config: T): Unit - var dimension : ConfigDimension[_] = null - def isCompatibleWith(positions : Seq[ConfigPosition[T]]) : Boolean = true +trait CatchAllPosition + +class CsrDimension extends VexRiscvDimension("Src") { + override val positions = List( + new VexRiscvPosition("None") { + override def applyOn(config: VexRiscvConfig): Unit = {} + override def testParam = "CSR=no" + }, + new VexRiscvPosition("All") with CatchAllPosition{ + override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.all) + override def testParam = "CSR=no" + } + ) } -abstract class VexRiscvPosition(name: String) extends ConfigPosition[VexRiscvConfig](name){ - def testParam : String = "" -} + + + class TestIndividualFeatures extends FunSuite { def doCmd(cmd: String): String = { @@ -366,14 +388,15 @@ class TestIndividualFeatures extends FunSuite { val dimensions = List( + new IBusDimension, + new DBusDimension, new MulDivDimension, new ShiftDimension, new BranchDimension, new HazardDimension, new RegFileDimension, new SrcDimension, - new IBusDimension, - new DBusDimension + new CsrDimension ) @@ -382,8 +405,11 @@ class TestIndividualFeatures extends FunSuite { case Nil => List(stack) } + val usedPositions = mutable.HashSet[VexRiscvPosition](); + val positionsCount = dimensions.map(d => d.positions.length).sum def doTest(positionsToApply : List[VexRiscvPosition], prefix : String = ""): Unit ={ + usedPositions ++= positionsToApply def gen = { SpinalVerilog{ val config = VexRiscvConfig( @@ -404,7 +430,7 @@ class TestIndividualFeatures extends FunSuite { gen } test(prefix + name + "_test") { - val testCmd = "make clean run REDO=10 CSR=no MMU=no DEBUG_PLUGIN=no " + (positionsToApply).map(_.testParam).mkString(" ") + val testCmd = "make clean run REDO=5 MMU=no DEBUG_PLUGIN=no " + (positionsToApply).map(_.testParam).mkString(" ") val str = doCmd(testCmd) assert(!str.contains("FAIL")) val intFind = "(\\d+\\.?)+".r @@ -414,7 +440,8 @@ class TestIndividualFeatures extends FunSuite { dimensions.foreach(d => d.positions.foreach(_.dimension = d)) - for(i <- 0 until 40){ + + for(i <- 0 until 200){ var positions : List[VexRiscvPosition] = null do{ positions = dimensions.map(d => d.positions(Random.nextInt(d.positions.size))) @@ -422,14 +449,13 @@ class TestIndividualFeatures extends FunSuite { doTest(positions," random_" + i + "_") } - for (dimension <- dimensions) { - for (position <- dimension.positions/* if position.name.contains("Cached")*/) { - for(defaults <- genDefaultsPositions(dimensions.filter(_ != dimension))){ - doTest(position :: defaults) - } - } - } - - + println(s"${usedPositions.size}/$positionsCount positions") +// for (dimension <- dimensions) { +// for (position <- dimension.positions/* if position.name.contains("Cached")*/) { +// for(defaults <- genDefaultsPositions(dimensions.filter(_ != dimension))){ +// doTest(position :: defaults) +// } +// } +// } }