diff --git a/README.md b/README.md index 329dbaf..eec2dd0 100644 --- a/README.md +++ b/README.md @@ -283,7 +283,7 @@ Note that sometimes Eclipse needs to be restarted in order to be able to place n ## Briey SoC As a demonstration, a SoC named Briey is implemented in `src/main/scala/vexriscv/demo/Briey.scala`. This SoC is very similar to -the [Pinsec SoC](https://spinalhdl.github.io/SpinalDoc/spinal/lib/pinsec/hardware/): +the [Pinsec SoC](https://spinalhdl.github.io/SpinalDoc-RTD/SpinalHDL/Legacy/pinsec/hardware_toplevel.html#): ![Briey SoC](assets/brieySoc.png?raw=true "") @@ -296,7 +296,7 @@ sbt "runMain vexriscv.demo.Briey" To run the verilator simulation of the Briey SoC, which can then be connected to OpenOCD/GDB, first get these dependencies: ```sh -sudo apt-get install build-essential xorg-dev libudev-dev libts-dev libgl1-mesa-dev libglu1-mesa-dev libasound2-dev libpulse-dev libopenal-dev libogg-dev libvorbis-dev libaudiofile-dev libpng12-dev libfreetype6-dev libusb-dev libdbus-1-dev zlib1g-dev libdirectfb-dev libsdl2-dev +sudo apt-get install build-essential xorg-dev libudev-dev libgl1-mesa-dev libglu1-mesa-dev libasound2-dev libpulse-dev libopenal-dev libogg-dev libvorbis-dev libaudiofile-dev libpng12-dev libfreetype6-dev libusb-dev libdbus-1-dev zlib1g-dev libdirectfb-dev libsdl2-dev ``` Then go in `src/test/cpp/briey` and run the simulation with (UART TX is printed in the terminal, VGA is displayed in a GUI): @@ -412,7 +412,7 @@ Note that VexRiscv can run Linux on both cache full and cache less design. A prebuild GCC toolsuite can be found here: -- https://www.sifive.com/products/tools/ => SiFive GNU Embedded Toolchain +- https://www.sifive.com/software/ => Prebuilt RISC‑V GCC Toolchain and Emulator The VexRiscvSocSoftware makefiles are expecting to find this prebuild version in /opt/riscv/__contentOfThisPreBuild__ diff --git a/scripts/regression/regression.mk b/scripts/regression/regression.mk index b1899aa..20234cf 100644 --- a/scripts/regression/regression.mk +++ b/scripts/regression/regression.mk @@ -13,6 +13,7 @@ regression_random_linux: cd ../.. export VEXRISCV_REGRESSION_CONFIG_COUNT=3 export VEXRISCV_REGRESSION_CONFIG_LINUX_RATE=1.0 + export VEXRISCV_REGRESSION_CONFIG_SECURE_RATE = 0.0 export VEXRISCV_REGRESSION_FREERTOS_COUNT=1 export VEXRISCV_REGRESSION_ZEPHYR_COUNT=2 export VEXRISCV_REGRESSION_THREAD_COUNT=1 @@ -24,6 +25,7 @@ regression_random_machine_os: export VEXRISCV_REGRESSION_CONFIG_COUNT=15 export VEXRISCV_REGRESSION_CONFIG_LINUX_RATE=0.0 export VEXRISCV_REGRESSION_CONFIG_MACHINE_OS_RATE=1.0 + export VEXRISCV_REGRESSION_CONFIG_SECURE_RATE = 0.0 export VEXRISCV_REGRESSION_FREERTOS_COUNT=1 export VEXRISCV_REGRESSION_ZEPHYR_COUNT=2 export VEXRISCV_REGRESSION_THREAD_COUNT=1 @@ -34,6 +36,7 @@ regression_random_baremetal: export VEXRISCV_REGRESSION_CONFIG_COUNT=40 export VEXRISCV_REGRESSION_CONFIG_LINUX_RATE=0.0 export VEXRISCV_REGRESSION_CONFIG_MACHINE_OS_RATE=0.0 + export VEXRISCV_REGRESSION_CONFIG_SECURE_RATE = 0.0 export VEXRISCV_REGRESSION_FREERTOS_COUNT=1 export VEXRISCV_REGRESSION_ZEPHYR_COUNT=no export VEXRISCV_REGRESSION_THREAD_COUNT=1 diff --git a/src/main/scala/vexriscv/Riscv.scala b/src/main/scala/vexriscv/Riscv.scala index ee9be3d..23ab965 100644 --- a/src/main/scala/vexriscv/Riscv.scala +++ b/src/main/scala/vexriscv/Riscv.scala @@ -163,6 +163,7 @@ object Riscv{ def UCYCLEH = 0xC80 def UTIME = 0xC01 // rdtime def UTIMEH = 0xC81 - + def UINSTRET = 0xC02 // UR Machine instructions-retired counter. + def UINSTRETH = 0xC82 // UR Upper 32 bits of minstret, RV32I only. } } diff --git a/src/main/scala/vexriscv/Services.scala b/src/main/scala/vexriscv/Services.scala index 51dbe6b..d7453d1 100644 --- a/src/main/scala/vexriscv/Services.scala +++ b/src/main/scala/vexriscv/Services.scala @@ -72,6 +72,7 @@ case class MemoryTranslatorCmd() extends Bundle{ case class MemoryTranslatorRsp(p : MemoryTranslatorBusParameter) extends Bundle{ val physicalAddress = UInt(32 bits) val isIoAccess = Bool + val isPaging = Bool val allowRead, allowWrite, allowExecute = Bool val exception = Bool val refilling = Bool diff --git a/src/main/scala/vexriscv/demo/Briey.scala b/src/main/scala/vexriscv/demo/Briey.scala index 2bf5947..dd794a6 100644 --- a/src/main/scala/vexriscv/demo/Briey.scala +++ b/src/main/scala/vexriscv/demo/Briey.scala @@ -147,7 +147,8 @@ object BrieyConfig{ minstretAccess = CsrAccess.NONE, ecallGen = false, wfiGenAsWait = false, - ucycleAccess = CsrAccess.NONE + ucycleAccess = CsrAccess.NONE, + uinstretAccess = CsrAccess.NONE ) ), new YamlPlugin("cpu0.yaml") diff --git a/src/main/scala/vexriscv/demo/GenSecure.scala b/src/main/scala/vexriscv/demo/GenSecure.scala new file mode 100644 index 0000000..2835b44 --- /dev/null +++ b/src/main/scala/vexriscv/demo/GenSecure.scala @@ -0,0 +1,86 @@ +package vexriscv.demo + +import vexriscv.plugin._ +import vexriscv.ip.{DataCacheConfig, InstructionCacheConfig} +import vexriscv.{plugin, VexRiscv, VexRiscvConfig} +import spinal.core._ + +object GenSecure extends App { + def cpu() = new VexRiscv( + config = VexRiscvConfig( + plugins = List( + new IBusCachedPlugin( + resetVector = 0x80000000l, + prediction = STATIC, + config = InstructionCacheConfig( + cacheSize = 4096, + bytePerLine = 32, + wayCount = 1, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchIllegalAccess = true, + catchAccessFault = true, + asyncTagMemory = false, + twoCycleRam = true, + twoCycleCache = true + ) + ), + new DBusCachedPlugin( + config = new DataCacheConfig( + cacheSize = 4096, + bytePerLine = 32, + wayCount = 1, + addressWidth = 32, + cpuDataWidth = 32, + memDataWidth = 32, + catchAccessError = true, + catchIllegal = true, + catchUnaligned = true + ) + ), + new PmpPlugin( + regions = 16, + ioRange = _(31 downto 28) === 0xf + ), + new DecoderSimplePlugin( + catchIllegalInstruction = true + ), + new RegFilePlugin( + regFileReadyKind = plugin.SYNC, + zeroBoot = false + ), + new IntAluPlugin, + new SrcPlugin( + separatedAddSub = false, + executeInsertion = true + ), + new FullBarrelShifterPlugin, + new HazardSimplePlugin( + bypassExecute = true, + bypassMemory = true, + bypassWriteBack = true, + bypassWriteBackBuffer = true, + pessimisticUseSrc = false, + pessimisticWriteRegFile = false, + pessimisticAddressMatch = false + ), + new MulDivIterativePlugin( + genMul = true, + genDiv = true, + mulUnrollFactor = 1, + divUnrollFactor = 1 + ), + new CsrPlugin(CsrPluginConfig.secure(0x00000020l)), + new DebugPlugin(ClockDomain.current.clone(reset = Bool().setName("debugReset"))), + new BranchPlugin( + earlyBranch = false, + catchAddressMisaligned = true + ), + new YamlPlugin("cpu0.yaml") + ) + ) + ) + + SpinalVerilog(cpu()) +} diff --git a/src/main/scala/vexriscv/demo/Murax.scala b/src/main/scala/vexriscv/demo/Murax.scala index 6d98907..a0590ad 100644 --- a/src/main/scala/vexriscv/demo/Murax.scala +++ b/src/main/scala/vexriscv/demo/Murax.scala @@ -52,8 +52,8 @@ case class MuraxConfig(coreFrequency : HertzNumber, object MuraxConfig{ - def default : MuraxConfig = default(false) - def default(withXip : Boolean) = MuraxConfig( + def default : MuraxConfig = default(false, false) + def default(withXip : Boolean = false, bigEndian : Boolean = false) = MuraxConfig( coreFrequency = 12 MHz, onChipRamSize = 8 kB, onChipRamHexFile = null, @@ -75,12 +75,14 @@ object MuraxConfig{ cmdForkPersistence = withXip, //Required by the Xip controller prediction = NONE, catchAccessFault = false, - compressedGen = false + compressedGen = false, + bigEndian = bigEndian ), new DBusSimplePlugin( catchAddressMisaligned = false, catchAccessFault = false, - earlyInjection = false + earlyInjection = false, + bigEndian = bigEndian ), new CsrPlugin(CsrPluginConfig.smallest(mtvecInit = if(withXip) 0xE0040020l else 0x80000020l)), new DecoderSimplePlugin( @@ -214,9 +216,11 @@ case class Murax(config : MuraxConfig) extends Component{ dataWidth = 32 ) + val bigEndianDBus = config.cpuPlugins.exists(_ match{ case plugin : DBusSimplePlugin => plugin.bigEndian case _ => false}) + //Arbiter of the cpu dBus/iBus to drive the mainBus //Priority to dBus, !! cmd transactions can change on the fly !! - val mainBusArbiter = new MuraxMasterArbiter(pipelinedMemoryBusConfig) + val mainBusArbiter = new MuraxMasterArbiter(pipelinedMemoryBusConfig, bigEndianDBus) //Instanciate the CPU val cpu = new VexRiscv( @@ -258,7 +262,8 @@ case class Murax(config : MuraxConfig) extends Component{ val ram = new MuraxPipelinedMemoryBusRam( onChipRamSize = onChipRamSize, onChipRamHexFile = onChipRamHexFile, - pipelinedMemoryBusConfig = pipelinedMemoryBusConfig + pipelinedMemoryBusConfig = pipelinedMemoryBusConfig, + bigEndian = bigEndianDBus ) mainBusMapping += ram.io.bus -> (0x80000000l, onChipRamSize) diff --git a/src/main/scala/vexriscv/demo/MuraxUtiles.scala b/src/main/scala/vexriscv/demo/MuraxUtiles.scala index 1c45bc3..1e22157 100644 --- a/src/main/scala/vexriscv/demo/MuraxUtiles.scala +++ b/src/main/scala/vexriscv/demo/MuraxUtiles.scala @@ -10,10 +10,10 @@ import spinal.lib._ import spinal.lib.bus.simple._ import vexriscv.plugin.{DBusSimpleBus, IBusSimpleBus} -class MuraxMasterArbiter(pipelinedMemoryBusConfig : PipelinedMemoryBusConfig) extends Component{ +class MuraxMasterArbiter(pipelinedMemoryBusConfig : PipelinedMemoryBusConfig, bigEndian : Boolean = false) extends Component{ val io = new Bundle{ val iBus = slave(IBusSimpleBus(null)) - val dBus = slave(DBusSimpleBus()) + val dBus = slave(DBusSimpleBus(bigEndian)) val masterBus = master(PipelinedMemoryBus(pipelinedMemoryBusConfig)) } @@ -21,11 +21,7 @@ class MuraxMasterArbiter(pipelinedMemoryBusConfig : PipelinedMemoryBusConfig) ex io.masterBus.cmd.write := io.dBus.cmd.valid && io.dBus.cmd.wr io.masterBus.cmd.address := io.dBus.cmd.valid ? io.dBus.cmd.address | io.iBus.cmd.pc io.masterBus.cmd.data := io.dBus.cmd.data - io.masterBus.cmd.mask := io.dBus.cmd.size.mux( - 0 -> B"0001", - 1 -> B"0011", - default -> B"1111" - ) |<< io.dBus.cmd.address(1 downto 0) + io.masterBus.cmd.mask := io.dBus.genMask(io.dBus.cmd) io.iBus.cmd.ready := io.masterBus.cmd.ready && !io.dBus.cmd.valid io.dBus.cmd.ready := io.masterBus.cmd.ready @@ -53,7 +49,7 @@ class MuraxMasterArbiter(pipelinedMemoryBusConfig : PipelinedMemoryBusConfig) ex } -case class MuraxPipelinedMemoryBusRam(onChipRamSize : BigInt, onChipRamHexFile : String, pipelinedMemoryBusConfig : PipelinedMemoryBusConfig) extends Component{ +case class MuraxPipelinedMemoryBusRam(onChipRamSize : BigInt, onChipRamHexFile : String, pipelinedMemoryBusConfig : PipelinedMemoryBusConfig, bigEndian : Boolean = false) extends Component{ val io = new Bundle{ val bus = slave(PipelinedMemoryBus(pipelinedMemoryBusConfig)) } @@ -71,6 +67,14 @@ case class MuraxPipelinedMemoryBusRam(onChipRamSize : BigInt, onChipRamHexFile : if(onChipRamHexFile != null){ HexTools.initRam(ram, onChipRamHexFile, 0x80000000l) + if(bigEndian) + // HexTools.initRam (incorrectly) assumes little endian byte ordering + for((word, wordIndex) <- ram.initialContent.zipWithIndex) + ram.initialContent(wordIndex) = + ((word & 0xffl) << 24) | + ((word & 0xff00l) << 8) | + ((word & 0xff0000l) >> 8) | + ((word & 0xff000000l) >> 24) } } diff --git a/src/main/scala/vexriscv/demo/VexRiscvAhbLite3.scala b/src/main/scala/vexriscv/demo/VexRiscvAhbLite3.scala index fc3c3c2..f817fb3 100644 --- a/src/main/scala/vexriscv/demo/VexRiscvAhbLite3.scala +++ b/src/main/scala/vexriscv/demo/VexRiscvAhbLite3.scala @@ -126,7 +126,8 @@ object VexRiscvAhbLite3{ minstretAccess = CsrAccess.NONE, ecallGen = false, wfiGenAsWait = false, - ucycleAccess = CsrAccess.NONE + ucycleAccess = CsrAccess.NONE, + uinstretAccess = CsrAccess.NONE ) ), new YamlPlugin("cpu0.yaml") diff --git a/src/main/scala/vexriscv/demo/VexRiscvAvalonForSim.scala b/src/main/scala/vexriscv/demo/VexRiscvAvalonForSim.scala index 4245cd1..b2c3f69 100644 --- a/src/main/scala/vexriscv/demo/VexRiscvAvalonForSim.scala +++ b/src/main/scala/vexriscv/demo/VexRiscvAvalonForSim.scala @@ -124,7 +124,8 @@ object VexRiscvAvalonForSim{ minstretAccess = CsrAccess.NONE, ecallGen = false, wfiGenAsWait = false, - ucycleAccess = CsrAccess.NONE + ucycleAccess = CsrAccess.NONE, + uinstretAccess = CsrAccess.NONE ) ), new YamlPlugin("cpu0.yaml") diff --git a/src/main/scala/vexriscv/demo/VexRiscvAvalonWithIntegratedJtag.scala b/src/main/scala/vexriscv/demo/VexRiscvAvalonWithIntegratedJtag.scala index bba065e..063d945 100644 --- a/src/main/scala/vexriscv/demo/VexRiscvAvalonWithIntegratedJtag.scala +++ b/src/main/scala/vexriscv/demo/VexRiscvAvalonWithIntegratedJtag.scala @@ -121,7 +121,8 @@ object VexRiscvAvalonWithIntegratedJtag{ minstretAccess = CsrAccess.NONE, ecallGen = false, wfiGenAsWait = false, - ucycleAccess = CsrAccess.NONE + ucycleAccess = CsrAccess.NONE, + uinstretAccess = CsrAccess.NONE ) ), new YamlPlugin("cpu0.yaml") diff --git a/src/main/scala/vexriscv/demo/VexRiscvAxi4WithIntegratedJtag.scala b/src/main/scala/vexriscv/demo/VexRiscvAxi4WithIntegratedJtag.scala index b002c06..67556e9 100644 --- a/src/main/scala/vexriscv/demo/VexRiscvAxi4WithIntegratedJtag.scala +++ b/src/main/scala/vexriscv/demo/VexRiscvAxi4WithIntegratedJtag.scala @@ -122,7 +122,8 @@ object VexRiscvAxi4WithIntegratedJtag{ minstretAccess = CsrAccess.NONE, ecallGen = false, wfiGenAsWait = false, - ucycleAccess = CsrAccess.NONE + ucycleAccess = CsrAccess.NONE, + uinstretAccess = CsrAccess.NONE ) ), new YamlPlugin("cpu0.yaml") diff --git a/src/main/scala/vexriscv/ip/DataCache.scala b/src/main/scala/vexriscv/ip/DataCache.scala index eff1097..6a7cbb7 100644 --- a/src/main/scala/vexriscv/ip/DataCache.scala +++ b/src/main/scala/vexriscv/ip/DataCache.scala @@ -890,12 +890,16 @@ class DataCache(val p : DataCacheConfig, mmuParameter : MemoryTranslatorBusParam dataWriteCmd.way := waysHits } + val badPermissions = (!mmuRsp.allowWrite && request.wr) || (!mmuRsp.allowRead && (!request.wr || isAmo)) + val loadStoreFault = io.cpu.writeBack.isValid && (mmuRsp.exception || badPermissions) + 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 || isAmo)) else False) + io.cpu.writeBack.mmuException := loadStoreFault && (if(catchIllegal) mmuRsp.isPaging else False) io.cpu.writeBack.unalignedAccess := io.cpu.writeBack.isValid && unaligned io.cpu.writeBack.isWrite := request.wr + io.mem.cmd.valid := False io.mem.cmd.address := mmuRsp.physicalAddress(tagRange.high downto cpuWordRange.low) @@ U(0, cpuWordRange.low bits) io.mem.cmd.length := 0 @@ -1002,7 +1006,7 @@ class DataCache(val p : DataCacheConfig, mmuParameter : MemoryTranslatorBusParam if(catchAccessError) io.cpu.writeBack.accessError := !request.wr && isLast && io.mem.rsp.valid && io.mem.rsp.error } otherwise { io.cpu.writeBack.data := dataMux - if(catchAccessError) io.cpu.writeBack.accessError := (waysHits & B(tagsReadRsp.map(_.error))) =/= 0 + if(catchAccessError) io.cpu.writeBack.accessError := (waysHits & B(tagsReadRsp.map(_.error))) =/= 0 || (loadStoreFault && !mmuRsp.isPaging) } if(withLrSc) when(request.isLrsc && request.wr){ diff --git a/src/main/scala/vexriscv/ip/InstructionCache.scala b/src/main/scala/vexriscv/ip/InstructionCache.scala index 856433e..ae8a80d 100644 --- a/src/main/scala/vexriscv/ip/InstructionCache.scala +++ b/src/main/scala/vexriscv/ip/InstructionCache.scala @@ -447,9 +447,9 @@ class InstructionCache(p : InstructionCacheConfig, mmuParameter : MemoryTranslat val mmuRsp = io.cpu.fetch.mmuRsp io.cpu.fetch.cacheMiss := !hit.valid - io.cpu.fetch.error := hit.error + io.cpu.fetch.error := hit.error || (!mmuRsp.isPaging && (mmuRsp.exception || !mmuRsp.allowExecute)) io.cpu.fetch.mmuRefilling := mmuRsp.refilling - io.cpu.fetch.mmuException := !mmuRsp.refilling && (mmuRsp.exception || !mmuRsp.allowExecute) + io.cpu.fetch.mmuException := !mmuRsp.refilling && mmuRsp.isPaging && (mmuRsp.exception || !mmuRsp.allowExecute) }) } @@ -478,9 +478,9 @@ class InstructionCache(p : InstructionCacheConfig, mmuParameter : MemoryTranslat } io.cpu.decode.cacheMiss := !hit.valid - io.cpu.decode.error := hit.error + io.cpu.decode.error := hit.error || (!mmuRsp.isPaging && (mmuRsp.exception || !mmuRsp.allowExecute)) io.cpu.decode.mmuRefilling := mmuRsp.refilling - io.cpu.decode.mmuException := !mmuRsp.refilling && (mmuRsp.exception || !mmuRsp.allowExecute) + io.cpu.decode.mmuException := !mmuRsp.refilling && mmuRsp.isPaging && (mmuRsp.exception || !mmuRsp.allowExecute) io.cpu.decode.physicalAddress := mmuRsp.physicalAddress }) } diff --git a/src/main/scala/vexriscv/plugin/CsrPlugin.scala b/src/main/scala/vexriscv/plugin/CsrPlugin.scala index 7eee653..c980fa8 100644 --- a/src/main/scala/vexriscv/plugin/CsrPlugin.scala +++ b/src/main/scala/vexriscv/plugin/CsrPlugin.scala @@ -50,6 +50,7 @@ case class CsrPluginConfig( mcycleAccess : CsrAccess, minstretAccess : CsrAccess, ucycleAccess : CsrAccess, + uinstretAccess : CsrAccess = CsrAccess.NONE, wfiGenAsWait : Boolean, ecallGen : Boolean, xtvecModeGen : Boolean = false, @@ -144,6 +145,7 @@ object CsrPluginConfig{ mcycleAccess = CsrAccess.NONE, minstretAccess = CsrAccess.NONE, ucycleAccess = CsrAccess.NONE, + uinstretAccess = CsrAccess.NONE, wfiGenAsWait = true, ecallGen = true, xtvecModeGen = false, @@ -184,6 +186,7 @@ object CsrPluginConfig{ mcycleAccess = CsrAccess.READ_WRITE, minstretAccess = CsrAccess.READ_WRITE, ucycleAccess = CsrAccess.READ_ONLY, + uinstretAccess = CsrAccess.READ_ONLY, wfiGenAsWait = true, ecallGen = true, xtvecModeGen = false, @@ -224,7 +227,8 @@ object CsrPluginConfig{ minstretAccess = CsrAccess.READ_WRITE, ecallGen = true, wfiGenAsWait = true, - ucycleAccess = CsrAccess.READ_ONLY + ucycleAccess = CsrAccess.READ_ONLY, + uinstretAccess = CsrAccess.READ_ONLY ) def all2(mtvecInit : BigInt) : CsrPluginConfig = CsrPluginConfig( @@ -246,6 +250,7 @@ object CsrPluginConfig{ ecallGen = true, wfiGenAsWait = true, ucycleAccess = CsrAccess.READ_ONLY, + uinstretAccess = CsrAccess.READ_ONLY, supervisorGen = true, sscratchGen = true, stvecAccess = CsrAccess.READ_WRITE, @@ -277,7 +282,8 @@ object CsrPluginConfig{ minstretAccess = CsrAccess.NONE, ecallGen = false, wfiGenAsWait = false, - ucycleAccess = CsrAccess.NONE + ucycleAccess = CsrAccess.NONE, + uinstretAccess = CsrAccess.NONE ) def smallest(mtvecInit : BigInt) = CsrPluginConfig( @@ -298,7 +304,33 @@ object CsrPluginConfig{ minstretAccess = CsrAccess.NONE, ecallGen = false, wfiGenAsWait = false, - ucycleAccess = CsrAccess.NONE + ucycleAccess = CsrAccess.NONE, + uinstretAccess = CsrAccess.NONE + ) + + def secure(mtvecInit : BigInt) = CsrPluginConfig( + catchIllegalAccess = true, + mvendorid = 1, + marchid = 2, + mimpid = 3, + mhartid = 0, + misaExtensionsInit = 0x101064, // RV32GCFMU + misaAccess = CsrAccess.READ_WRITE, + mtvecAccess = CsrAccess.READ_WRITE, + mtvecInit = mtvecInit, + mepcAccess = CsrAccess.READ_WRITE, + mscratchGen = true, + mcauseAccess = CsrAccess.READ_WRITE, + mbadaddrAccess = CsrAccess.READ_WRITE, + mcycleAccess = CsrAccess.READ_WRITE, + minstretAccess = CsrAccess.READ_WRITE, + ucycleAccess = CsrAccess.READ_ONLY, + uinstretAccess = CsrAccess.READ_ONLY, + wfiGenAsWait = true, + ecallGen = true, + userGen = true, + medelegAccess = CsrAccess.READ_WRITE, + midelegAccess = CsrAccess.READ_WRITE ) } @@ -641,6 +673,8 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep //User CSR ucycleAccess(CSR.UCYCLE, mcycle(31 downto 0)) ucycleAccess(CSR.UCYCLEH, mcycle(63 downto 32)) + uinstretAccess(CSR.UINSTRET, minstret(31 downto 0)) + uinstretAccess(CSR.UINSTRETH, minstret(63 downto 32)) if(utimeAccess != CsrAccess.NONE) { utimeAccess(CSR.UTIME, utime(31 downto 0)) diff --git a/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala b/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala index 2d66a58..7a18c96 100644 --- a/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala +++ b/src/main/scala/vexriscv/plugin/DBusCachedPlugin.scala @@ -355,15 +355,14 @@ class DBusCachedPlugin(val config : DataCacheConfig, exceptionBus.valid := True exceptionBus.code := (input(MEMORY_WR) ? U(7) | U(5)).resized } - - if (catchUnaligned) when(cache.io.cpu.writeBack.unalignedAccess) { - exceptionBus.valid := True - exceptionBus.code := (input(MEMORY_WR) ? U(6) | U(4)).resized - } if(catchIllegal) when (cache.io.cpu.writeBack.mmuException) { exceptionBus.valid := True exceptionBus.code := (input(MEMORY_WR) ? U(15) | U(13)).resized } + if (catchUnaligned) when(cache.io.cpu.writeBack.unalignedAccess) { + exceptionBus.valid := True + exceptionBus.code := (input(MEMORY_WR) ? U(6) | U(4)).resized + } when(cache.io.cpu.redo) { redoBranch.valid := True diff --git a/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala b/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala index e273195..1fe09ed 100644 --- a/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/DBusSimplePlugin.scala @@ -87,7 +87,7 @@ object DBusSimpleBus{ ) } -case class DBusSimpleBus() extends Bundle with IMasterSlave{ +case class DBusSimpleBus(bigEndian : Boolean = false) extends Bundle with IMasterSlave{ val cmd = Stream(DBusSimpleCmd()) val rsp = DBusSimpleRsp() @@ -97,12 +97,27 @@ case class DBusSimpleBus() extends Bundle with IMasterSlave{ } def cmdS2mPipe() : DBusSimpleBus = { - val s = DBusSimpleBus() + val s = DBusSimpleBus(bigEndian) s.cmd << this.cmd.s2mPipe() this.rsp := s.rsp s } + def genMask(cmd : DBusSimpleCmd) = { + if(bigEndian) + cmd.size.mux( + U(0) -> B"1000", + U(1) -> B"1100", + default -> B"1111" + ) |>> cmd.address(1 downto 0) + else + cmd.size.mux( + U(0) -> B"0001", + U(1) -> B"0011", + default -> B"1111" + ) |<< cmd.address(1 downto 0) + } + def toAxi4Shared(stageCmd : Boolean = false, pendingWritesMax : Int = 7): Axi4Shared = { val axi = Axi4Shared(DBusSimpleBus.getAxi4Config()) @@ -127,11 +142,7 @@ case class DBusSimpleBus() extends Bundle with IMasterSlave{ axi.writeData.arbitrationFrom(dataStage) axi.writeData.last := True axi.writeData.data := dataStage.data - axi.writeData.strb := (dataStage.size.mux( - U(0) -> B"0001", - U(1) -> B"0011", - default -> B"1111" - ) << dataStage.address(1 downto 0)).resized + axi.writeData.strb := genMask(dataStage).resized rsp.ready := axi.r.valid @@ -155,11 +166,7 @@ case class DBusSimpleBus() extends Bundle with IMasterSlave{ mm.write := cmdStage.valid && cmdStage.wr mm.address := (cmdStage.address >> 2) @@ U"00" mm.writeData := 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 + mm.byteEnable := genMask(cmdStage).resized cmdStage.ready := mm.waitRequestn @@ -178,11 +185,7 @@ case class DBusSimpleBus() extends Bundle with IMasterSlave{ bus.ADR := cmdStage.address >> 2 bus.CTI :=B"000" bus.BTE := "00" - bus.SEL := (cmdStage.size.mux ( - U(0) -> B"0001", - U(1) -> B"0011", - default -> B"1111" - ) << cmdStage.address(1 downto 0)).resized + bus.SEL := genMask(cmdStage).resized when(!cmdStage.wr) { bus.SEL := "1111" } @@ -206,11 +209,7 @@ case class DBusSimpleBus() extends Bundle with IMasterSlave{ bus.cmd.write := cmd.wr bus.cmd.address := cmd.address.resized bus.cmd.data := cmd.data - bus.cmd.mask := cmd.size.mux( - 0 -> B"0001", - 1 -> B"0011", - default -> B"1111" - ) |<< cmd.address(1 downto 0) + bus.cmd.mask := genMask(cmd) cmd.ready := bus.cmd.ready rsp.ready := bus.rsp.valid @@ -262,11 +261,7 @@ case class DBusSimpleBus() extends Bundle with IMasterSlave{ 1 -> U"01", default -> U"11" ) - bus.cmd.mask := cmd.size.mux( - 0 -> B"0001", - 1 -> B"0011", - default -> B"1111" - ) |<< cmd.address(1 downto 0) + bus.cmd.mask := genMask(cmd) cmd.ready := bus.cmd.ready @@ -286,6 +281,7 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false, emitCmdInMemoryStage : Boolean = false, onlyLoadWords : Boolean = false, withLrSc : Boolean = false, + val bigEndian : Boolean = false, memoryTranslatorPortConfig : Any = null) extends Plugin[VexRiscv] with DBusAccessService { var dBus : DBusSimpleBus = null @@ -391,7 +387,7 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false, object MMU_RSP extends Stageable(MemoryTranslatorRsp(mmuBus.p)) - dBus = master(DBusSimpleBus()).setName("dBus") + dBus = master(DBusSimpleBus(bigEndian)).setName("dBus") decode plug new Area { @@ -434,11 +430,7 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false, insert(MEMORY_ADDRESS_LOW) := dBus.cmd.address(1 downto 0) //formal - val formalMask = dBus.cmd.size.mux( - U(0) -> B"0001", - U(1) -> B"0011", - default -> B"1111" - ) |<< dBus.cmd.address(1 downto 0) + val formalMask = dBus.genMask(dBus.cmd) insert(FORMAL_MEM_ADDR) := dBus.cmd.address & U"xFFFFFFFC" insert(FORMAL_MEM_WMASK) := (dBus.cmd.valid && dBus.cmd.wr) ? formalMask | B"0000" @@ -536,17 +528,32 @@ class DBusSimplePlugin(catchAddressMisaligned : Boolean = false, val rspShifted = MEMORY_READ_DATA() rspShifted := input(MEMORY_READ_DATA) - switch(input(MEMORY_ADDRESS_LOW)){ - is(1){rspShifted(7 downto 0) := input(MEMORY_READ_DATA)(15 downto 8)} - is(2){rspShifted(15 downto 0) := input(MEMORY_READ_DATA)(31 downto 16)} - is(3){rspShifted(7 downto 0) := input(MEMORY_READ_DATA)(31 downto 24)} - } + if(bigEndian) + switch(input(MEMORY_ADDRESS_LOW)){ + is(1){rspShifted(31 downto 24) := input(MEMORY_READ_DATA)(23 downto 16)} + is(2){rspShifted(31 downto 16) := input(MEMORY_READ_DATA)(15 downto 0)} + is(3){rspShifted(31 downto 24) := input(MEMORY_READ_DATA)(7 downto 0)} + } + else + switch(input(MEMORY_ADDRESS_LOW)){ + is(1){rspShifted(7 downto 0) := input(MEMORY_READ_DATA)(15 downto 8)} + is(2){rspShifted(15 downto 0) := input(MEMORY_READ_DATA)(31 downto 16)} + is(3){rspShifted(7 downto 0) := input(MEMORY_READ_DATA)(31 downto 24)} + } - val rspFormated = input(INSTRUCTION)(13 downto 12).mux( - 0 -> B((31 downto 8) -> (rspShifted(7) && !input(INSTRUCTION)(14)),(7 downto 0) -> rspShifted(7 downto 0)), - 1 -> B((31 downto 16) -> (rspShifted(15) && ! input(INSTRUCTION)(14)),(15 downto 0) -> rspShifted(15 downto 0)), - default -> rspShifted //W - ) + val rspFormated = + if(bigEndian) + input(INSTRUCTION)(13 downto 12).mux( + 0 -> B((31 downto 8) -> (rspShifted(31) && !input(INSTRUCTION)(14)),(7 downto 0) -> rspShifted(31 downto 24)), + 1 -> B((31 downto 16) -> (rspShifted(31) && ! input(INSTRUCTION)(14)),(15 downto 0) -> rspShifted(31 downto 16)), + default -> rspShifted //W + ) + else + input(INSTRUCTION)(13 downto 12).mux( + 0 -> B((31 downto 8) -> (rspShifted(7) && !input(INSTRUCTION)(14)),(7 downto 0) -> rspShifted(7 downto 0)), + 1 -> B((31 downto 16) -> (rspShifted(15) && ! input(INSTRUCTION)(14)),(15 downto 0) -> rspShifted(15 downto 0)), + default -> rspShifted //W + ) when(arbitration.isValid && input(MEMORY_ENABLE)) { output(REGFILE_WRITE_DATA) := (if(!onlyLoadWords) rspFormated else input(MEMORY_READ_DATA)) diff --git a/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala b/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala index 6af134c..8f36e4c 100644 --- a/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala +++ b/src/main/scala/vexriscv/plugin/IBusSimplePlugin.scala @@ -234,7 +234,8 @@ class IBusSimplePlugin( resetVector : BigInt, val singleInstructionPipeline : Boolean = false, val memoryTranslatorPortConfig : Any = null, relaxPredictorAddress : Boolean = true, - predictionBuffer : Boolean = true + predictionBuffer : Boolean = true, + bigEndian : Boolean = false ) extends IBusFetcherImpl( resetVector = resetVector, keepPcPlus4 = keepPcPlus4, @@ -371,6 +372,11 @@ class IBusSimplePlugin( resetVector : BigInt, fetchRsp.pc := stages.last.output.payload fetchRsp.rsp := rspBuffer.output.payload fetchRsp.rsp.error.clearWhen(!rspBuffer.output.valid) //Avoid interference with instruction injection from the debug plugin + if(bigEndian){ + // instructions are stored in little endian byteorder + fetchRsp.rsp.inst.allowOverride + fetchRsp.rsp.inst := EndiannessSwap(rspBuffer.output.payload.inst) + } val join = Stream(FetchRsp()) val exceptionDetected = False diff --git a/src/main/scala/vexriscv/plugin/MmuPlugin.scala b/src/main/scala/vexriscv/plugin/MmuPlugin.scala index 6e1ab34..3f6635e 100644 --- a/src/main/scala/vexriscv/plugin/MmuPlugin.scala +++ b/src/main/scala/vexriscv/plugin/MmuPlugin.scala @@ -151,6 +151,7 @@ class MmuPlugin(ioRange : UInt => Bool, port.bus.rsp.allowExecute := cacheLine.allowExecute port.bus.rsp.exception := !dirty && cacheHit && (cacheLine.exception || cacheLine.allowUser && privilegeService.isSupervisor() && !csr.status.sum || !cacheLine.allowUser && privilegeService.isUser()) port.bus.rsp.refilling := dirty || !cacheHit + port.bus.rsp.isPaging := True } otherwise { port.bus.rsp.physicalAddress := port.bus.cmd.last.virtualAddress port.bus.rsp.allowRead := True @@ -158,6 +159,7 @@ class MmuPlugin(ioRange : UInt => Bool, port.bus.rsp.allowExecute := True port.bus.rsp.exception := False port.bus.rsp.refilling := False + port.bus.rsp.isPaging := False } port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress) diff --git a/src/main/scala/vexriscv/plugin/PmpPlugin.scala b/src/main/scala/vexriscv/plugin/PmpPlugin.scala new file mode 100644 index 0000000..e6cb396 --- /dev/null +++ b/src/main/scala/vexriscv/plugin/PmpPlugin.scala @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020 Samuel Lindemer + * + * SPDX-License-Identifier: MIT + */ + +package vexriscv.plugin + +import vexriscv.{VexRiscv, _} +import spinal.core._ +import spinal.lib._ +import scala.collection.mutable.ArrayBuffer + +/* Each 32-bit pmpcfg# register contains four 8-bit configuration sections. + * These section numbers contain flags which apply to regions defined by the + * corresponding pmpaddr# register. + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | pmp3cfg | pmp2cfg | pmp1cfg | pmp0cfg | pmpcfg0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | pmp7cfg | pmp6cfg | pmp5cfg | pmp4cfg | pmpcfg2 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * 7 6 5 4 3 2 1 0 + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * | L | 0 | A | X | W | R | pmp#cfg + * +-------+-------+-------+-------+-------+-------+-------+-------+ + * + * L: locks configuration until system reset (including M-mode) + * 0: hardwired to zero + * A: 0 = OFF (null region / disabled) + * 1 = TOR (top of range) + * 2 = NA4 (naturally aligned four-byte region) + * 3 = NAPOT (naturally aligned power-of-two region, > 7 bytes) + * X: execute + * W: write + * R: read + * + * TOR: Each 32-bit pmpaddr# register defines the upper bound of the pmp region + * right-shifted by two bits. The lower bound of the region is the previous + * pmpaddr# register. In the case of pmpaddr0, the lower bound is address 0x0. + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | address[33:2] | pmpaddr# + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * NAPOT: Each 32-bit pmpaddr# register defines the region address and the size + * of the pmp region. The number of concurrent 1s begging at the LSB indicates + * the size of the region as a power of two (e.g. 0x...0 = 8-byte, 0x...1 = + * 16-byte, 0x...11 = 32-byte, etc.). + * + * 3 2 1 + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | address[33:2] |0|1|1|1|1| pmpaddr# + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * NA4: This is essentially an edge case of NAPOT where the entire pmpaddr# + * register defines a 4-byte wide region. + */ + +case class PmpRegister(previous : PmpRegister) extends Area { + + def OFF = 0 + def TOR = 1 + def NA4 = 2 + def NAPOT = 3 + + val state = new Area { + val r, w, x = Reg(Bool) + val l = RegInit(False) + val a = Reg(UInt(2 bits)) init(0) + val addr = Reg(UInt(32 bits)) + } + + // CSR writes connect to these signals rather than the internal state + // registers. This makes locking and WARL possible. + val csr = new Area { + val r, w, x = Bool + val l = Bool + val a = UInt(2 bits) + val addr = UInt(32 bits) + } + + // Last valid assignment wins; nothing happens if a user-initiated write did + // not occur on this clock cycle. + csr.r := state.r + csr.w := state.w + csr.x := state.x + csr.l := state.l + csr.a := state.a + csr.addr := state.addr + + // Computed PMP region bounds + val region = new Area { + val valid, locked = Bool + val start, end = UInt(32 bits) + } + + when(~state.l) { + state.r := csr.r + state.w := csr.w + state.x := csr.x + state.l := csr.l + state.a := csr.a + state.addr := csr.addr + + if (csr.l == True & csr.a == TOR) { + previous.state.l := True + } + } + + val shifted = state.addr |<< 2 + val mask = state.addr & ~(state.addr + 1) + val masked = (state.addr & ~mask) |<< 2 + + // PMP changes take effect two clock cycles after the initial CSR write (i.e., + // settings propagate from csr -> state -> region). + region.locked := state.l + region.valid := True + + switch(csr.a) { + is(TOR) { + if (previous == null) region.start := 0 + else region.start := previous.region.end + region.end := shifted + } + is(NA4) { + region.start := shifted + region.end := shifted + 4 + } + is(NAPOT) { + region.start := masked + region.end := masked + ((mask + 1) |<< 3) + } + default { + region.start := 0 + region.end := shifted + region.valid := False + } + } +} + +case class ProtectedMemoryTranslatorPort(bus : MemoryTranslatorBus) + +class PmpPlugin(regions : Int, ioRange : UInt => Bool) extends Plugin[VexRiscv] with MemoryTranslator { + + // Each pmpcfg# CSR configures four regions. + assert((regions % 4) == 0) + + val pmps = ArrayBuffer[PmpRegister]() + val portsInfo = ArrayBuffer[ProtectedMemoryTranslatorPort]() + + override def newTranslationPort(priority : Int, args : Any): MemoryTranslatorBus = { + val port = ProtectedMemoryTranslatorPort(MemoryTranslatorBus(new MemoryTranslatorBusParameter(0, 0))) + portsInfo += port + port.bus + } + + override def build(pipeline: VexRiscv): Unit = { + import pipeline.config._ + import pipeline._ + import Riscv._ + + val csrService = pipeline.service(classOf[CsrInterface]) + val privilegeService = pipeline.service(classOf[PrivilegeService]) + + val core = pipeline plug new Area { + + // Instantiate pmpaddr0 ... pmpaddr# CSRs. + for (i <- 0 until regions) { + if (i == 0) { + pmps += PmpRegister(null) + } else { + pmps += PmpRegister(pmps.last) + } + csrService.r(0x3b0 + i, pmps(i).state.addr) + csrService.w(0x3b0 + i, pmps(i).csr.addr) + } + + // Instantiate pmpcfg0 ... pmpcfg# CSRs. + for (i <- 0 until (regions / 4)) { + csrService.r(0x3a0 + i, + 31 -> pmps((i * 4) + 3).state.l, 23 -> pmps((i * 4) + 2).state.l, + 15 -> pmps((i * 4) + 1).state.l, 7 -> pmps((i * 4) ).state.l, + 27 -> pmps((i * 4) + 3).state.a, 26 -> pmps((i * 4) + 3).state.x, + 25 -> pmps((i * 4) + 3).state.w, 24 -> pmps((i * 4) + 3).state.r, + 19 -> pmps((i * 4) + 2).state.a, 18 -> pmps((i * 4) + 2).state.x, + 17 -> pmps((i * 4) + 2).state.w, 16 -> pmps((i * 4) + 2).state.r, + 11 -> pmps((i * 4) + 1).state.a, 10 -> pmps((i * 4) + 1).state.x, + 9 -> pmps((i * 4) + 1).state.w, 8 -> pmps((i * 4) + 1).state.r, + 3 -> pmps((i * 4) ).state.a, 2 -> pmps((i * 4) ).state.x, + 1 -> pmps((i * 4) ).state.w, 0 -> pmps((i * 4) ).state.r + ) + csrService.w(0x3a0 + i, + 31 -> pmps((i * 4) + 3).csr.l, 23 -> pmps((i * 4) + 2).csr.l, + 15 -> pmps((i * 4) + 1).csr.l, 7 -> pmps((i * 4) ).csr.l, + 27 -> pmps((i * 4) + 3).csr.a, 26 -> pmps((i * 4) + 3).csr.x, + 25 -> pmps((i * 4) + 3).csr.w, 24 -> pmps((i * 4) + 3).csr.r, + 19 -> pmps((i * 4) + 2).csr.a, 18 -> pmps((i * 4) + 2).csr.x, + 17 -> pmps((i * 4) + 2).csr.w, 16 -> pmps((i * 4) + 2).csr.r, + 11 -> pmps((i * 4) + 1).csr.a, 10 -> pmps((i * 4) + 1).csr.x, + 9 -> pmps((i * 4) + 1).csr.w, 8 -> pmps((i * 4) + 1).csr.r, + 3 -> pmps((i * 4) ).csr.a, 2 -> pmps((i * 4) ).csr.x, + 1 -> pmps((i * 4) ).csr.w, 0 -> pmps((i * 4) ).csr.r + ) + } + + // Connect memory ports to PMP logic. + val ports = for ((port, portId) <- portsInfo.zipWithIndex) yield new Area { + + val address = port.bus.cmd(0).virtualAddress + port.bus.rsp.physicalAddress := address + + // Only the first matching PMP region applies. + val hits = pmps.map(pmp => pmp.region.valid & + pmp.region.start <= address & + pmp.region.end > address & + (pmp.region.locked | ~privilegeService.isMachine())) + + // M-mode has full access by default, others have none. + when(CountOne(hits) === 0) { + port.bus.rsp.allowRead := privilegeService.isMachine() + port.bus.rsp.allowWrite := privilegeService.isMachine() + port.bus.rsp.allowExecute := privilegeService.isMachine() + } otherwise { + port.bus.rsp.allowRead := MuxOH(OHMasking.first(hits), pmps.map(_.state.r)) + port.bus.rsp.allowWrite := MuxOH(OHMasking.first(hits), pmps.map(_.state.w)) + port.bus.rsp.allowExecute := MuxOH(OHMasking.first(hits), pmps.map(_.state.x)) + } + + port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress) + port.bus.rsp.isPaging := False + port.bus.rsp.exception := False + port.bus.rsp.refilling := False + port.bus.busy := False + + } + } + } +} + diff --git a/src/main/scala/vexriscv/plugin/RegFilePlugin.scala b/src/main/scala/vexriscv/plugin/RegFilePlugin.scala index 0b7bcd2..94a3f32 100644 --- a/src/main/scala/vexriscv/plugin/RegFilePlugin.scala +++ b/src/main/scala/vexriscv/plugin/RegFilePlugin.scala @@ -18,6 +18,7 @@ class RegFilePlugin(regFileReadyKind : RegFileReadKind, writeRfInMemoryStage : Boolean = false, readInExecute : Boolean = false, syncUpdateOnStall : Boolean = true, + rv32e : Boolean = false, withShadow : Boolean = false //shadow registers aren't transition hazard free ) extends Plugin[VexRiscv] with RegFileService{ import Riscv._ @@ -39,8 +40,11 @@ class RegFilePlugin(regFileReadyKind : RegFileReadKind, val readStage = if(readInExecute) execute else decode val writeStage = if(writeRfInMemoryStage) memory else stages.last + val numRegisters = if(rv32e) 16 else 32 + def clipRange(that : Range) = if(rv32e) that.tail else that + val global = pipeline plug new Area{ - val regFileSize = if(withShadow) 64 else 32 + val regFileSize = if(withShadow) numRegisters * 2 else numRegisters val regFile = Mem(Bits(32 bits),regFileSize) addAttribute(Verilator.public) if(zeroBoot) regFile.init(List.fill(regFileSize)(B(0, 32 bits))) @@ -59,6 +63,9 @@ class RegFilePlugin(regFileReadyKind : RegFileReadKind, when(decode.input(INSTRUCTION)(rdRange) === 0) { decode.input(REGFILE_WRITE_VALID) := False } + if(rv32e) when(decode.input(INSTRUCTION)(rdRange.head)) { + decode.input(REGFILE_WRITE_VALID) := False + } //Read register file readStage plug new Area{ @@ -72,8 +79,8 @@ class RegFilePlugin(regFileReadyKind : RegFileReadKind, } def shadowPrefix(that : Bits) = if(withShadow) global.shadow.read ## that else that - val regFileReadAddress1 = U(shadowPrefix(srcInstruction(Riscv.rs1Range))) - val regFileReadAddress2 = U(shadowPrefix(srcInstruction(Riscv.rs2Range))) + val regFileReadAddress1 = U(shadowPrefix(srcInstruction(clipRange(Riscv.rs1Range)))) + val regFileReadAddress2 = U(shadowPrefix(srcInstruction(clipRange(Riscv.rs2Range)))) val (rs1Data,rs2Data) = regFileReadyKind match{ case `ASYNC` => (global.regFile.readAsync(regFileReadAddress1),global.regFile.readAsync(regFileReadAddress2)) @@ -93,7 +100,7 @@ class RegFilePlugin(regFileReadyKind : RegFileReadKind, def shadowPrefix(that : Bits) = if(withShadow) global.shadow.write ## that else that val regFileWrite = global.regFile.writePort.addAttribute(Verilator.public).setName("lastStageRegFileWrite") regFileWrite.valid := output(REGFILE_WRITE_VALID) && arbitration.isFiring - regFileWrite.address := U(shadowPrefix(output(INSTRUCTION)(rdRange))) + regFileWrite.address := U(shadowPrefix(output(INSTRUCTION)(clipRange(rdRange)))) regFileWrite.data := output(REGFILE_WRITE_DATA) //Ensure no boot glitches modify X0 diff --git a/src/main/scala/vexriscv/plugin/StaticMemoryTranslatorPlugin.scala b/src/main/scala/vexriscv/plugin/StaticMemoryTranslatorPlugin.scala index bc910c0..cafd8de 100644 --- a/src/main/scala/vexriscv/plugin/StaticMemoryTranslatorPlugin.scala +++ b/src/main/scala/vexriscv/plugin/StaticMemoryTranslatorPlugin.scala @@ -31,6 +31,7 @@ class StaticMemoryTranslatorPlugin(ioRange : UInt => Bool) extends Plugin[VexRis port.bus.rsp.allowWrite := True port.bus.rsp.allowExecute := True port.bus.rsp.isIoAccess := ioRange(port.bus.rsp.physicalAddress) + port.bus.rsp.isPaging := False port.bus.rsp.exception := False port.bus.rsp.refilling := False port.bus.busy := False diff --git a/src/test/cpp/raw/pmp/build/pmp.asm b/src/test/cpp/raw/pmp/build/pmp.asm new file mode 100644 index 0000000..4508ee6 --- /dev/null +++ b/src/test/cpp/raw/pmp/build/pmp.asm @@ -0,0 +1,203 @@ + +build/pmp.elf: file format elf32-littleriscv + + +Disassembly of section .crt_section: + +80000000 <_start>: +80000000: 00000097 auipc ra,0x0 +80000004: 01008093 addi ra,ra,16 # 80000010 +80000008: 30509073 csrw mtvec,ra +8000000c: 00c0006f j 80000018 + +80000010 : +80000010: 341f1073 csrw mepc,t5 +80000014: 30200073 mret + +80000018 : +80000018: 00000e13 li t3,0 +8000001c: 00000f17 auipc t5,0x0 +80000020: 27cf0f13 addi t5,t5,636 # 80000298 +80000024: 800000b7 lui ra,0x80000 +80000028: 80008237 lui tp,0x80008 +8000002c: deadc137 lui sp,0xdeadc +80000030: eef10113 addi sp,sp,-273 # deadbeef +80000034: 0020a023 sw sp,0(ra) # 80000000 +80000038: 00222023 sw sp,0(tp) # 80008000 +8000003c: 0000a183 lw gp,0(ra) +80000040: 24311c63 bne sp,gp,80000298 +80000044: 00022183 lw gp,0(tp) # 0 <_start-0x80000000> +80000048: 24311863 bne sp,gp,80000298 +8000004c: 071202b7 lui t0,0x7120 +80000050: 3a029073 csrw pmpcfg0,t0 +80000054: 3a002373 csrr t1,pmpcfg0 +80000058: 24629063 bne t0,t1,80000298 +8000005c: 191f02b7 lui t0,0x191f0 +80000060: 30428293 addi t0,t0,772 # 191f0304 <_start-0x66e0fcfc> +80000064: 3a129073 csrw pmpcfg1,t0 +80000068: 000f02b7 lui t0,0xf0 +8000006c: 50628293 addi t0,t0,1286 # f0506 <_start-0x7ff0fafa> +80000070: 3a229073 csrw pmpcfg2,t0 +80000074: 0f1e22b7 lui t0,0xf1e2 +80000078: 90028293 addi t0,t0,-1792 # f1e1900 <_start-0x70e1e700> +8000007c: 3a329073 csrw pmpcfg3,t0 +80000080: 200002b7 lui t0,0x20000 +80000084: 3b029073 csrw pmpaddr0,t0 +80000088: 3b002373 csrr t1,pmpaddr0 +8000008c: 20629663 bne t0,t1,80000298 +80000090: fff00293 li t0,-1 +80000094: 3b129073 csrw pmpaddr1,t0 +80000098: 200022b7 lui t0,0x20002 +8000009c: 3b229073 csrw pmpaddr2,t0 +800000a0: 200042b7 lui t0,0x20004 +800000a4: fff28293 addi t0,t0,-1 # 20003fff <_start-0x5fffc001> +800000a8: 3b329073 csrw pmpaddr3,t0 +800000ac: 200042b7 lui t0,0x20004 +800000b0: fff28293 addi t0,t0,-1 # 20003fff <_start-0x5fffc001> +800000b4: 3b429073 csrw pmpaddr4,t0 +800000b8: 200042b7 lui t0,0x20004 +800000bc: fff28293 addi t0,t0,-1 # 20003fff <_start-0x5fffc001> +800000c0: 3b529073 csrw pmpaddr5,t0 +800000c4: 200022b7 lui t0,0x20002 +800000c8: fff28293 addi t0,t0,-1 # 20001fff <_start-0x5fffe001> +800000cc: 3b629073 csrw pmpaddr6,t0 +800000d0: 200062b7 lui t0,0x20006 +800000d4: fff28293 addi t0,t0,-1 # 20005fff <_start-0x5fffa001> +800000d8: 3b729073 csrw pmpaddr7,t0 +800000dc: 2000c2b7 lui t0,0x2000c +800000e0: 3b829073 csrw pmpaddr8,t0 +800000e4: 2000d2b7 lui t0,0x2000d +800000e8: 3b929073 csrw pmpaddr9,t0 +800000ec: fff00293 li t0,-1 +800000f0: 3ba29073 csrw pmpaddr10,t0 +800000f4: 00000293 li t0,0 +800000f8: 3bb29073 csrw pmpaddr11,t0 +800000fc: 00000293 li t0,0 +80000100: 3bc29073 csrw pmpaddr12,t0 +80000104: 00000293 li t0,0 +80000108: 3bd29073 csrw pmpaddr13,t0 +8000010c: 00000293 li t0,0 +80000110: 3be29073 csrw pmpaddr14,t0 +80000114: 00000293 li t0,0 +80000118: 3bf29073 csrw pmpaddr15,t0 +8000011c: 00c10137 lui sp,0xc10 +80000120: fee10113 addi sp,sp,-18 # c0ffee <_start-0x7f3f0012> +80000124: 0020a023 sw sp,0(ra) +80000128: 00222023 sw sp,0(tp) # 0 <_start-0x80000000> +8000012c: 0000a183 lw gp,0(ra) +80000130: 16311463 bne sp,gp,80000298 +80000134: 00000193 li gp,0 +80000138: 00022183 lw gp,0(tp) # 0 <_start-0x80000000> +8000013c: 14311e63 bne sp,gp,80000298 + +80000140 : +80000140: 00100e13 li t3,1 +80000144: 00000f17 auipc t5,0x0 +80000148: 154f0f13 addi t5,t5,340 # 80000298 +8000014c: 079212b7 lui t0,0x7921 +80000150: 80828293 addi t0,t0,-2040 # 7920808 <_start-0x786df7f8> +80000154: 3a029073 csrw pmpcfg0,t0 +80000158: 3a002373 csrr t1,pmpcfg0 +8000015c: 12629e63 bne t0,t1,80000298 +80000160: 800080b7 lui ra,0x80008 +80000164: deadc137 lui sp,0xdeadc +80000168: eef10113 addi sp,sp,-273 # deadbeef +8000016c: 0020a023 sw sp,0(ra) # 80008000 +80000170: 00000f17 auipc t5,0x0 +80000174: 010f0f13 addi t5,t5,16 # 80000180 +80000178: 0000a183 lw gp,0(ra) +8000017c: 11c0006f j 80000298 + +80000180 : +80000180: 00200e13 li t3,2 +80000184: 00000f17 auipc t5,0x0 +80000188: 114f0f13 addi t5,t5,276 # 80000298 +8000018c: 071202b7 lui t0,0x7120 +80000190: 3a029073 csrw pmpcfg0,t0 +80000194: 3a002373 csrr t1,pmpcfg0 +80000198: 3b205073 csrwi pmpaddr2,0 +8000019c: 3b202373 csrr t1,pmpaddr2 +800001a0: 0e030c63 beqz t1,80000298 +800001a4: 0e628a63 beq t0,t1,80000298 +800001a8: 800080b7 lui ra,0x80008 +800001ac: deadc137 lui sp,0xdeadc +800001b0: eef10113 addi sp,sp,-273 # deadbeef +800001b4: 0020a023 sw sp,0(ra) # 80008000 +800001b8: 00000f17 auipc t5,0x0 +800001bc: 010f0f13 addi t5,t5,16 # 800001c8 +800001c0: 0000a183 lw gp,0(ra) +800001c4: 0d40006f j 80000298 + +800001c8 : +800001c8: 00300e13 li t3,3 +800001cc: 00000f17 auipc t5,0x0 +800001d0: 0ccf0f13 addi t5,t5,204 # 80000298 +800001d4: 00000117 auipc sp,0x0 +800001d8: 01010113 addi sp,sp,16 # 800001e4 +800001dc: 34111073 csrw mepc,sp +800001e0: 30200073 mret + +800001e4 : +800001e4: 00400e13 li t3,4 +800001e8: 00000f17 auipc t5,0x0 +800001ec: 0b0f0f13 addi t5,t5,176 # 80000298 +800001f0: deadc137 lui sp,0xdeadc +800001f4: eef10113 addi sp,sp,-273 # deadbeef +800001f8: 800080b7 lui ra,0x80008 +800001fc: 0020a023 sw sp,0(ra) # 80008000 +80000200: 00000f17 auipc t5,0x0 +80000204: 010f0f13 addi t5,t5,16 # 80000210 +80000208: 0000a183 lw gp,0(ra) +8000020c: 08c0006f j 80000298 + +80000210 : +80000210: 00500e13 li t3,5 +80000214: deadc137 lui sp,0xdeadc +80000218: eef10113 addi sp,sp,-273 # deadbeef +8000021c: 800000b7 lui ra,0x80000 +80000220: 0020a023 sw sp,0(ra) # 80000000 +80000224: 0000a183 lw gp,0(ra) +80000228: 06311863 bne sp,gp,80000298 + +8000022c : +8000022c: 00600e13 li t3,6 +80000230: 800100b7 lui ra,0x80010 +80000234: 0000a183 lw gp,0(ra) # 80010000 +80000238: 00000f17 auipc t5,0x0 +8000023c: 06cf0f13 addi t5,t5,108 # 800002a4 +80000240: 0030a023 sw gp,0(ra) +80000244: 0540006f j 80000298 + +80000248 : +80000248: 00700e13 li t3,7 +8000024c: 00000f17 auipc t5,0x0 +80000250: 04cf0f13 addi t5,t5,76 # 80000298 +80000254: deadc137 lui sp,0xdeadc +80000258: eef10113 addi sp,sp,-273 # deadbeef +8000025c: 800300b7 lui ra,0x80030 +80000260: ff808093 addi ra,ra,-8 # 8002fff8 +80000264: 00222023 sw sp,0(tp) # 0 <_start-0x80000000> +80000268: 00000f17 auipc t5,0x0 +8000026c: fa8f0f13 addi t5,t5,-88 # 80000210 +80000270: 00022183 lw gp,0(tp) # 0 <_start-0x80000000> +80000274: 0240006f j 80000298 + +80000278 : +80000278: 00800e13 li t3,8 +8000027c: 800400b7 lui ra,0x80040 +80000280: ff808093 addi ra,ra,-8 # 8003fff8 +80000284: 0000a183 lw gp,0(ra) +80000288: 00000f17 auipc t5,0x0 +8000028c: 01cf0f13 addi t5,t5,28 # 800002a4 +80000290: 0030a023 sw gp,0(ra) +80000294: 0040006f j 80000298 + +80000298 : +80000298: f0100137 lui sp,0xf0100 +8000029c: f2410113 addi sp,sp,-220 # f00fff24 +800002a0: 01c12023 sw t3,0(sp) + +800002a4 : +800002a4: f0100137 lui sp,0xf0100 +800002a8: f2010113 addi sp,sp,-224 # f00fff20 +800002ac: 00012023 sw zero,0(sp) diff --git a/src/test/cpp/raw/pmp/build/pmp.elf b/src/test/cpp/raw/pmp/build/pmp.elf new file mode 100755 index 0000000..b844a20 Binary files /dev/null and b/src/test/cpp/raw/pmp/build/pmp.elf differ diff --git a/src/test/cpp/raw/pmp/build/pmp.hex b/src/test/cpp/raw/pmp/build/pmp.hex new file mode 100644 index 0000000..8197935 --- /dev/null +++ b/src/test/cpp/raw/pmp/build/pmp.hex @@ -0,0 +1,46 @@ +:0200000480007A +:100000009700000093800001739050306F00C00093 +:1000100073101F3473002030130E0000170F000000 +:10002000130FCF27B70000803782008037C1ADDEC5 +:100030001301F1EE23A020002320220083A1000061 +:10004000631C31248321020063183124B702120794 +:100050007390023A7323003A63906224B7021F1927 +:10006000938242307390123AB7020F00938262502B +:100070007390223AB7221E0F938202907390323A05 +:10008000B70200207390023B7323003B639662200B +:100090009302F0FF7390123BB72200207390223B33 +:1000A000B74200209382F2FF7390323BB7420020A8 +:1000B0009382F2FF7390423BB74200209382F2FF9B +:1000C0007390523BB72200209382F2FF7390623B01 +:1000D000B76200209382F2FF7390723BB7C2002098 +:1000E0007390823BB7D200207390923B9302F0FF53 +:1000F0007390A23B930200007390B23B9302000006 +:100100007390C23B930200007390D23B93020000B5 +:100110007390E23B930200007390F23B3701C10001 +:100120001301E1FE23A020002320220083A1000070 +:10013000631431169301000083210200631E311401 +:10014000130E1000170F0000130F4F15B712920770 +:10015000938282807390023A7323003A639E621204 +:10016000B780008037C1ADDE1301F1EE23A020007F +:10017000170F0000130F0F0183A100006F00C011C3 +:10018000130E2000170F0000130F4F11B7021207B4 +:100190007390023A7323003A7350203B7323203B41 +:1001A000630C030E638A620EB780008037C1ADDE38 +:1001B0001301F1EE23A02000170F0000130F0F0111 +:1001C00083A100006F00400D130E3000170F0000D8 +:1001D000130FCF0C1701000013010101731011342C +:1001E00073002030130E4000170F0000130F0F0B89 +:1001F00037C1ADDE1301F1EEB780008023A02000EF +:10020000170F0000130F0F0183A100006F00C0083B +:10021000130E500037C1ADDE1301F1EEB7000080C0 +:1002200023A0200083A1000063183106130E600094 +:10023000B700018083A10000170F0000130FCF0645 +:1002400023A030006F004005130E7000170F000050 +:10025000130FCF0437C1ADDE1301F1EEB7000380F9 +:10026000938080FF23202200170F0000130F8FFAC6 +:10027000832102006F004002130E8000B70004804B +:10028000938080FF83A10000170F0000130FCF01A0 +:1002900023A030006F004000370110F0130141F23D +:1002A0002320C101370110F0130101F223200100C6 +:040000058000000077 +:00000001FF diff --git a/src/test/cpp/raw/pmp/build/pmp.map b/src/test/cpp/raw/pmp/build/pmp.map new file mode 100644 index 0000000..2a01820 --- /dev/null +++ b/src/test/cpp/raw/pmp/build/pmp.map @@ -0,0 +1,35 @@ + +Memory Configuration + +Name Origin Length Attributes +onChipRam 0x0000000080000000 0x0000000000020000 w !xr +*default* 0x0000000000000000 0xffffffffffffffff + +Linker script and memory map + +LOAD build/src/crt.o +LOAD /opt/riscv/lib/gcc/riscv64-unknown-elf/10.2.0/libgcc.a +START GROUP +LOAD /opt/riscv/lib/gcc/riscv64-unknown-elf/10.2.0/../../../../riscv64-unknown-elf/lib/libc.a +LOAD /opt/riscv/lib/gcc/riscv64-unknown-elf/10.2.0/../../../../riscv64-unknown-elf/lib/libgloss.a +END GROUP +LOAD /opt/riscv/lib/gcc/riscv64-unknown-elf/10.2.0/libgcc.a + +.crt_section 0x0000000080000000 0x2b0 + 0x0000000080000000 . = ALIGN (0x4) + *crt.o(.text) + .text 0x0000000080000000 0x2b0 build/src/crt.o + 0x0000000080000000 _start + 0x0000000080000010 trap +OUTPUT(build/pmp.elf elf32-littleriscv) + +.data 0x00000000800002b0 0x0 + .data 0x00000000800002b0 0x0 build/src/crt.o + +.bss 0x00000000800002b0 0x0 + .bss 0x00000000800002b0 0x0 build/src/crt.o + +.riscv.attributes + 0x0000000000000000 0x1e + .riscv.attributes + 0x0000000000000000 0x1e build/src/crt.o diff --git a/src/test/cpp/raw/pmp/makefile b/src/test/cpp/raw/pmp/makefile new file mode 100644 index 0000000..0069df4 --- /dev/null +++ b/src/test/cpp/raw/pmp/makefile @@ -0,0 +1,3 @@ +PROJ_NAME=pmp + +include ../common/asm.mk diff --git a/src/test/cpp/raw/pmp/src/crt.S b/src/test/cpp/raw/pmp/src/crt.S new file mode 100644 index 0000000..76ee02f --- /dev/null +++ b/src/test/cpp/raw/pmp/src/crt.S @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2020 Samuel Lindemer + * + * SPDX-License-Identifier: MIT + */ + +#define TEST_ID x28 +#define TRAP_RA x30 + +#define PMPCFG0 0x07120000 +#define PMPCFG0_ 0x07920808 // locked +#define PMPCFG1 0x191f0304 +#define PMPCFG2 0x000f0506 +#define PMPCFG3 0x0f1e1900 + +#define PMPADDR0 0x20000000 // OFF +#define PMPADDR1 0xffffffff // OFF +#define PMPADDR2 0x20002000 // NA4 W +#define PMPADDR3 0x20003fff // OFF RWX +#define PMPADDR4 0x20003fff // OFF X +#define PMPADDR5 0x20003fff // OFF RW +#define PMPADDR6 0x20001fff // NAPOT RWX +#define PMPADDR7 0x20005fff // NAPOT R +#define PMPADDR8 0x2000c000 // TOR W +#define PMPADDR9 0x2000d000 // TOR R +#define PMPADDR10 0xffffffff // TOR RWX +#define PMPADDR11 0x00000000 // OFF +#define PMPADDR12 0x00000000 // OFF +#define PMPADDR13 0x00000000 // NAPOT R +#define PMPADDR14 0x00000000 // NAPOT WX +#define PMPADDR15 0x00000000 // TOR RWX + +.global _start +_start: + la x1, trap + csrw mtvec, x1 + j test0 + +.global trap +trap: + csrw mepc, TRAP_RA + mret + +// configure PMP, attempt read/write from machine mode +test0: + li TEST_ID, 0 + la TRAP_RA, fail + + li x1, 0x80000000 + li x4, 0x80008000 + li x2, 0xdeadbeef + sw x2, 0x0(x1) + sw x2, 0x0(x4) + lw x3, 0x0(x1) + bne x2, x3, fail + lw x3, 0x0(x4) + bne x2, x3, fail + + li x5, PMPCFG0 + csrw pmpcfg0, x5 + csrr x6, pmpcfg0 + bne x5, x6, fail + li x5, PMPCFG1 + csrw pmpcfg1, x5 + li x5, PMPCFG2 + csrw pmpcfg2, x5 + li x5, PMPCFG3 + csrw pmpcfg3, x5 + li x5, PMPADDR0 + csrw pmpaddr0, x5 + csrr x6, pmpaddr0 + bne x5, x6, fail + li x5, PMPADDR1 + csrw pmpaddr1, x5 + li x5, PMPADDR2 + csrw pmpaddr2, x5 + li x5, PMPADDR3 + csrw pmpaddr3, x5 + li x5, PMPADDR4 + csrw pmpaddr4, x5 + li x5, PMPADDR5 + csrw pmpaddr5, x5 + li x5, PMPADDR6 + csrw pmpaddr6, x5 + li x5, PMPADDR7 + csrw pmpaddr7, x5 + li x5, PMPADDR8 + csrw pmpaddr8, x5 + li x5, PMPADDR9 + csrw pmpaddr9, x5 + li x5, PMPADDR10 + csrw pmpaddr10, x5 + li x5, PMPADDR11 + csrw pmpaddr11, x5 + li x5, PMPADDR12 + csrw pmpaddr12, x5 + li x5, PMPADDR13 + csrw pmpaddr13, x5 + li x5, PMPADDR14 + csrw pmpaddr14, x5 + li x5, PMPADDR15 + csrw pmpaddr15, x5 + + li x2, 0xc0ffee + sw x2, 0x0(x1) + sw x2, 0x0(x4) + lw x3, 0x0(x1) + bne x2, x3, fail + li x3, 0x0 + lw x3, 0x0(x4) + bne x2, x3, fail + +// lock region 2, attempt read/write from machine mode +test1: + li TEST_ID, 1 + la TRAP_RA, fail + li x5, PMPCFG0_ + csrw pmpcfg0, x5 // lock region 2 + csrr x6, pmpcfg0 + bne x5, x6, fail + li x1, 0x80008000 + li x2, 0xdeadbeef + sw x2, 0x0(x1) // should be OK (write 0x80008000) + la TRAP_RA, test2 + lw x3, 0x0(x1) // should fault (read 0x80008000) + j fail + +// "unlock" region 2, attempt read/write from machine mode +test2: + li TEST_ID, 2 + la TRAP_RA, fail + li x5, PMPCFG0 + csrw pmpcfg0, x5 // "unlock" region 2 + csrr x6, pmpcfg0 + csrwi pmpaddr2, 0x0 + csrr x6, pmpaddr2 + beqz x6, fail + beq x5, x6, fail + li x1, 0x80008000 + li x2, 0xdeadbeef + sw x2, 0x0(x1) // should still be OK (write 0x80008000) + la TRAP_RA, test3 + lw x3, 0x0(x1) // should still fault (read 0x80008000) + j fail + +// jump into user mode +test3: + li TEST_ID, 3 + la TRAP_RA, fail + la x2, test4 + csrw mepc, x2 + mret + +// attempt to read/write region 2 from user mode +test4: + li TEST_ID, 4 + la TRAP_RA, fail + li x2, 0xdeadbeef + li x1, 0x80008000 + sw x2, 0x0(x1) // should be OK (write 0x80008000) + la TRAP_RA, test5 + lw x3, 0x0(x1) // should fault (read 0x80008000) + j fail + +// attempt to read/write other regions from user mode +test5: + li TEST_ID, 5 + li x2, 0xdeadbeef + li x1, 0x80000000 + sw x2, 0x0(x1) + lw x3, 0x0(x1) + bne x2, x3, fail // should be OK (read/write 0x80000000) + +test6: + li TEST_ID, 6 + li x1, 0x80010000 + lw x3, 0x0(x1) // should be OK (read 0x80010000) + la TRAP_RA, pass + sw x3, 0x0(x1) // should fault (write 0x80010000) + j fail + +test7: + li TEST_ID, 7 + la TRAP_RA, fail + li x2, 0xdeadbeef + li x1, 0x8002fff8 + sw x2, 0x0(x4) // should be OK (write 0x8002fff8) + la TRAP_RA, test5 + lw x3, 0x0(x4) // should fault (read 0x8002fff8) + j fail + +test8: + li TEST_ID, 8 + li x1, 0x8003fff8 + lw x3, 0x0(x1) // should be OK (read 0x8003fff8) + la TRAP_RA, pass + sw x3, 0x0(x1) // should fault (write 0x8003fff8) + j fail + +fail: + li x2, 0xf00fff24 + sw TEST_ID, 0(x2) + +pass: + li x2, 0xf00fff20 + sw x0, 0(x2) diff --git a/src/test/cpp/raw/pmp/src/ld b/src/test/cpp/raw/pmp/src/ld new file mode 100644 index 0000000..93d8de8 --- /dev/null +++ b/src/test/cpp/raw/pmp/src/ld @@ -0,0 +1,16 @@ +OUTPUT_ARCH( "riscv" ) + +MEMORY { + onChipRam (W!RX)/*(RX)*/ : ORIGIN = 0x80000000, LENGTH = 128K +} + +SECTIONS +{ + + .crt_section : + { + . = ALIGN(4); + *crt.o(.text) + } > onChipRam + +} diff --git a/src/test/cpp/regression/main.cpp b/src/test/cpp/regression/main.cpp index bc960fb..c3742b7 100644 --- a/src/test/cpp/regression/main.cpp +++ b/src/test/cpp/regression/main.cpp @@ -4103,6 +4103,10 @@ int main(int argc, char **argv, char **env) { redo(REDO,WorkspaceRegression("lrsc").withRiscvRef()->loadHex(string(REGRESSION_PATH) + "../raw/lrsc/build/lrsc.hex")->bootAt(0x00000000u)->run(10e3);); #endif + #ifdef PMP + redo(REDO,WorkspaceRegression("pmp").loadHex(string(REGRESSION_PATH) + "../raw/pmp/build/pmp.hex")->bootAt(0x80000000u)->run(10e3);); + #endif + #ifdef AMO redo(REDO,WorkspaceRegression("amo").withRiscvRef()->loadHex(string(REGRESSION_PATH) + "../raw/amo/build/amo.hex")->bootAt(0x00000000u)->run(10e3);); #endif diff --git a/src/test/cpp/regression/makefile b/src/test/cpp/regression/makefile index c7dcf5f..48c5c4e 100644 --- a/src/test/cpp/regression/makefile +++ b/src/test/cpp/regression/makefile @@ -20,6 +20,7 @@ FENCEI?=no MMU?=yes DBUS_EXCLUSIVE?=no DBUS_INVALIDATE?=no +PMP?=no SEED?=no LRSC?=no AMO?=no @@ -251,6 +252,10 @@ ifeq ($(DBUS_INVALIDATE),yes) ADDCFLAGS += -CFLAGS -DDBUS_INVALIDATE endif +ifeq ($(PMP),yes) + ADDCFLAGS += -CFLAGS -DPMP +endif + ifeq ($(MUL),yes) ADDCFLAGS += -CFLAGS -DMUL endif diff --git a/src/test/scala/vexriscv/TestIndividualFeatures.scala b/src/test/scala/vexriscv/TestIndividualFeatures.scala index 628e057..0cf5ce6 100644 --- a/src/test/scala/vexriscv/TestIndividualFeatures.scala +++ b/src/test/scala/vexriscv/TestIndividualFeatures.scala @@ -46,8 +46,10 @@ abstract class VexRiscvPosition(name: String) extends ConfigPosition[VexRiscvCo class VexRiscvUniverse extends ConfigUniverse object VexRiscvUniverse{ + val CACHE_ALL = new VexRiscvUniverse val CATCH_ALL = new VexRiscvUniverse val MMU = new VexRiscvUniverse + val PMP = new VexRiscvUniverse val FORCE_MULDIV = new VexRiscvUniverse val SUPERVISOR = new VexRiscvUniverse val NO_WRITEBACK = new VexRiscvUniverse @@ -321,12 +323,11 @@ class IBusDimension(rvcRate : Double) extends VexRiscvDimension("IBus") { override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = { val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL) - val noMemory = universes.contains(VexRiscvUniverse.NO_MEMORY) - val noWriteBack = universes.contains(VexRiscvUniverse.NO_WRITEBACK) + val cacheAll = universes.contains(VexRiscvUniverse.CACHE_ALL) - - if(r.nextDouble() < 0.5){ + if(r.nextDouble() < 0.5 && !cacheAll){ val mmuConfig = if(universes.contains(VexRiscvUniverse.MMU)) MmuPortConfig( portTlbSize = 4) else null + val latency = r.nextInt(5) + 1 val compressed = r.nextDouble() < rvcRate val injectorStage = r.nextBoolean() || latency == 1 @@ -414,11 +415,11 @@ class DBusDimension extends VexRiscvDimension("DBus") { override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = { val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL) + val cacheAll = universes.contains(VexRiscvUniverse.CACHE_ALL) val noMemory = universes.contains(VexRiscvUniverse.NO_MEMORY) val noWriteBack = universes.contains(VexRiscvUniverse.NO_WRITEBACK) - - if(r.nextDouble() < 0.4 || noMemory){ + if((r.nextDouble() < 0.4 || noMemory) && !cacheAll){ val mmuConfig = if(universes.contains(VexRiscvUniverse.MMU)) MmuPortConfig( portTlbSize = 4, latency = 0) else null val withLrSc = catchAll val earlyInjection = r.nextBoolean() && !universes.contains(VexRiscvUniverse.NO_WRITEBACK) @@ -490,12 +491,12 @@ class DBusDimension extends VexRiscvDimension("DBus") { } -class MmuDimension extends VexRiscvDimension("DBus") { +class MmuPmpDimension extends VexRiscvDimension("DBus") { override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = { if(universes.contains(VexRiscvUniverse.MMU)) { new VexRiscvPosition("WithMmu") { - override def testParam = "MMU=yes" + override def testParam = "MMU=yes PMP=no" override def applyOn(config: VexRiscvConfig): Unit = { config.plugins += new MmuPlugin( @@ -503,9 +504,20 @@ class MmuDimension extends VexRiscvDimension("DBus") { ) } } + } else if (universes.contains(VexRiscvUniverse.PMP)) { + new VexRiscvPosition("WithPmp") { + override def testParam = "MMU=no PMP=yes" + + override def applyOn(config: VexRiscvConfig): Unit = { + config.plugins += new PmpPlugin( + regions = 16, + ioRange = _ (31 downto 28) === 0xF + ) + } + } } else { - new VexRiscvPosition("NoMmu") { - override def testParam = "MMU=no" + new VexRiscvPosition("NoMemProtect") { + override def testParam = "MMU=no PMP=no" override def applyOn(config: VexRiscvConfig): Unit = { config.plugins += new StaticMemoryTranslatorPlugin( @@ -524,6 +536,7 @@ trait CatchAllPosition class CsrDimension(freertos : String, zephyr : String, linux : String) extends VexRiscvDimension("Csr") { override def randomPositionImpl(universes: Seq[ConfigUniverse], r: Random) = { + val pmp = universes.contains(VexRiscvUniverse.PMP) val catchAll = universes.contains(VexRiscvUniverse.CATCH_ALL) val supervisor = universes.contains(VexRiscvUniverse.SUPERVISOR) if(supervisor){ @@ -531,10 +544,15 @@ class CsrDimension(freertos : String, zephyr : String, linux : String) extends V override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.linuxFull(0x80000020l)) override def testParam = s"FREERTOS=$freertos ZEPHYR=$zephyr LINUX_REGRESSION=$linux SUPERVISOR=yes" } + } else if(pmp){ + new VexRiscvPosition("Secure") with CatchAllPosition{ + override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.secure(0x80000020l)) + override def testParam = s"CSR=yes CSR_SKIP_TEST=yes FREERTOS=$freertos ZEPHYR=$zephyr" + } } else if(catchAll){ new VexRiscvPosition("MachineOs") with CatchAllPosition{ override def applyOn(config: VexRiscvConfig): Unit = config.plugins += new CsrPlugin(CsrPluginConfig.all(0x80000020l)) - override def testParam = s"CSR=yes FREERTOS=$freertos ZEPHYR=$zephyr" + override def testParam = s"CSR=yes CSR_SKIP_TEST=yes FREERTOS=$freertos ZEPHYR=$zephyr" } } else if(r.nextDouble() < 0.3){ new VexRiscvPosition("AllNoException") with CatchAllPosition{ @@ -667,6 +685,7 @@ class TestIndividualFeatures extends MultithreadedFunSuite(sys.env.getOrElse("VE val rvcRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_RVC_RATE", "0.5").toDouble val linuxRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_LINUX_RATE", "0.3").toDouble val machineOsRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_MACHINE_OS_RATE", "0.5").toDouble + val secureRate = sys.env.getOrElse("VEXRISCV_REGRESSION_CONFIG_SECURE_RATE", "0.2").toDouble val linuxRegression = sys.env.getOrElse("VEXRISCV_REGRESSION_LINUX_REGRESSION", "yes") val coremarkRegression = sys.env.getOrElse("VEXRISCV_REGRESSION_COREMARK", "yes") val zephyrCount = sys.env.getOrElse("VEXRISCV_REGRESSION_ZEPHYR_COUNT", "4") @@ -689,7 +708,7 @@ class TestIndividualFeatures extends MultithreadedFunSuite(sys.env.getOrElse("VE new CsrDimension(/*sys.env.getOrElse("VEXRISCV_REGRESSION_FREERTOS_COUNT", "1")*/ "0", zephyrCount, linuxRegression), //Freertos old port software is broken new DecoderDimension, new DebugDimension, - new MmuDimension + new MmuPmpDimension ) var clockCounter = 0l @@ -774,6 +793,13 @@ class TestIndividualFeatures extends MultithreadedFunSuite(sys.env.getOrElse("VE if(demwRate < rand.nextDouble()){ universe += VexRiscvUniverse.NO_WRITEBACK } + } else if (secureRate > rand.nextDouble()) { + universe += VexRiscvUniverse.CACHE_ALL + universe += VexRiscvUniverse.CATCH_ALL + universe += VexRiscvUniverse.PMP + if(demwRate < rand.nextDouble()){ + universe += VexRiscvUniverse.NO_WRITEBACK + } } else { if(machineOsRate > rand.nextDouble()) { universe += VexRiscvUniverse.CATCH_ALL @@ -804,4 +830,4 @@ class TestIndividualFeatures extends MultithreadedFunSuite(sys.env.getOrElse("VE val clockPerSecond = (clockCounter/time*1e-3).toLong println(s"Duration=${(time/60).toInt}mn clocks=${(clockCounter*1e-6).toLong}M clockPerSecond=${clockPerSecond}K") } -} \ No newline at end of file +}