diff --git a/README.md b/README.md index c11922f30..30c814edf 100644 --- a/README.md +++ b/README.md @@ -1295,6 +1295,42 @@ Write Address 0x04 -> The OpenOCD port is here: +#### EmbeddedRiscvJtag + +VexRiscv also support the official RISC-V debug specification (Thanks Efinix for the funding !). + +To enable it, you need to add the EmbeddedRiscvJtag to the plugin list : + +```scala +new EmbeddedRiscvJtag( + p = DebugTransportModuleParameter( + addressWidth = 7, + version = 1, + idle = 7 + ), + withTunneling = false, + withTap = true +) +``` + +And turn on the withPrivilegedDebug option in the CsrPlugin config. + +Here is an example of openocd tcl script to connect : + +```tcl +# ADD HERE YOUR JTAG ADAPTER SETTINGS + +set _CHIPNAME riscv +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10002FFF + +set _TARGETNAME $_CHIPNAME.cpu + +target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME + +init +halt +``` + #### YamlPlugin This plugin offers a service to other plugins to generate a useful Yaml file describing the CPU configuration. It contains, for instance, the sequence of instructions required diff --git a/build.sbt b/build.sbt index d749f35b2..766b2b01f 100644 --- a/build.sbt +++ b/build.sbt @@ -1,4 +1,4 @@ -val spinalVersion = "1.9.3" +val spinalVersion = "1.9.4" lazy val root = (project in file(".")). settings( diff --git a/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala b/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala index 6fffb6a5b..854828f36 100644 --- a/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala +++ b/src/main/scala/vexriscv/demo/smp/VexRiscvSmpCluster.scala @@ -14,6 +14,7 @@ import spinal.lib.com.jtag.xilinx.Bscane2BmbMasterGenerator import spinal.lib.generator._ import spinal.core.fiber._ import spinal.idslplugin.PostInitCallback +import spinal.lib.cpu.riscv.debug.{DebugModule, DebugModuleCpuConfig, DebugModuleParameter, DebugTransportModuleParameter, DebugTransportModuleTunneled} import spinal.lib.misc.plic.PlicMapping import spinal.lib.system.debugger.SystemDebuggerConfig import vexriscv.ip.{DataCacheAck, DataCacheConfig, DataCacheMemBus, InstructionCache, InstructionCacheConfig} @@ -30,7 +31,9 @@ case class VexRiscvSmpClusterParameter(cpuConfigs : Seq[VexRiscvConfig], withExclusiveAndInvalidation : Boolean, forcePeripheralWidth : Boolean = true, outOfOrderDecoder : Boolean = true, - fpu : Boolean = false) + fpu : Boolean = false, + privilegedDebug : Boolean = false, + hardwareBreakpoints : Int = 0) class VexRiscvSmpClusterBase(p : VexRiscvSmpClusterParameter) extends Area with PostInitCallback{ val cpuCount = p.cpuConfigs.size @@ -52,10 +55,12 @@ class VexRiscvSmpClusterBase(p : VexRiscvSmpClusterParameter) extends Area with implicit val interconnect = BmbInterconnectGenerator() - val debugBridge = debugCd.outputClockDomain on JtagInstructionDebuggerGenerator(p.jtagHeaderIgnoreWidth) - debugBridge.jtagClockDomain.load(ClockDomain.external("jtag", withReset = false)) + val customDebug = !p.privilegedDebug generate new Area { + val debugBridge = debugCd.outputClockDomain on JtagInstructionDebuggerGenerator(p.jtagHeaderIgnoreWidth) + debugBridge.jtagClockDomain.load(ClockDomain.external("jtag", withReset = false)) - val debugPort = Handle(debugBridge.logic.jtagBridge.io.ctrl.toIo) + val debugPort = Handle(debugBridge.logic.jtagBridge.io.ctrl.toIo).setName("debugPort") + } val dBusCoherent = BmbBridgeGenerator() val dBusNonCoherent = BmbBridgeGenerator() @@ -80,12 +85,66 @@ class VexRiscvSmpClusterBase(p : VexRiscvSmpClusterParameter) extends Area with interconnect.addConnection( cpu.dBus -> List(dBusCoherent.bmb) ) - cpu.enableDebugBmb( - debugCd = debugCd.outputClockDomain, - resetCd = systemCd, - mapping = SizeMapping(cpuId*0x1000, 0x1000) + + cpu.hardwareBreakpointCount.load(p.hardwareBreakpoints) + if(!p.privilegedDebug) { + cpu.enableDebugBmb( + debugCd = debugCd.outputClockDomain, + resetCd = systemCd, + mapping = SizeMapping(cpuId * 0x1000, 0x1000) + ) + interconnect.addConnection(customDebug.debugBridge.bmb, cpu.debugBmb) + } else { + cpu.enableRiscvDebug(debugCd.outputClockDomain, systemCd) + } + } + + val privilegedDebug = p.privilegedDebug generate new Area{ + val jtagCd = ClockDomain.external("jtag", withReset = false) + + val systemReset = Handle(Bool()) + systemCd.relaxedReset(systemReset, ResetSensitivity.HIGH) + + val p = DebugTransportModuleParameter( + addressWidth = 7, + version = 1, + idle = 7 ) - interconnect.addConnection(debugBridge.bmb, cpu.debugBmb) + + val logic = hardFork(debugCd.outputClockDomain on new Area { + val XLEN = 32 + + val dm = DebugModule( + DebugModuleParameter( + version = p.version + 1, + harts = cpuCount, + progBufSize = 2, + datacount = XLEN / 32 + cores.exists(_.cpu.config.get.FLEN == 64).toInt, + hartsConfig = cores.map(c => DebugModuleCpuConfig( + xlen = XLEN, + flen = c.cpu.config.get.FLEN, + withFpuRegAccess = c.cpu.config.get.FLEN == 64 + )) + ) + ) + systemReset := dm.io.ndmreset + for ((cpu, i) <- cores.zipWithIndex) { + val privBus = cpu.cpu.debugRiscv + privBus <> dm.io.harts(i) + privBus.dmToHart.removeAssignments() <-< dm.io.harts(i).dmToHart + } + + val clintStop = (cores.map(e => e.cpu.logic.cpu.service(classOf[CsrPlugin]).stoptime).andR) + + val tunnel = DebugTransportModuleTunneled( + p = p, + jtagCd = jtagCd, + debugCd = ClockDomain.current + ) + dm.io.ctrl <> tunnel.io.bus + + val debugPort = Handle(tunnel.io.instruction.toIo).setName("debugPort") + }) } } @@ -157,9 +216,10 @@ class VexRiscvSmpClusterWithPeripherals(p : VexRiscvSmpClusterParameter) extends } clint.cpuCount.load(cpuCount) + if(p.privilegedDebug) hardFork(clint.logic.io.stop := privilegedDebug.logic.clintStop) } - +//python3 -m litex_boards.targets.digilent_nexys_video --cpu-type=vexriscv_smp --with-privileged-debug --sys-clk-freq 50000000 --cpu-count 1 --build --load object VexRiscvSmpClusterGen { def vexRiscvConfig(hartId : Int, ioRange : UInt => Bool = (x => x(31 downto 28) === 0xF), diff --git a/src/main/scala/vexriscv/demo/smp/VexRiscvSmpLitexCluster.scala b/src/main/scala/vexriscv/demo/smp/VexRiscvSmpLitexCluster.scala index 3454577b8..9a8aefe8f 100644 --- a/src/main/scala/vexriscv/demo/smp/VexRiscvSmpLitexCluster.scala +++ b/src/main/scala/vexriscv/demo/smp/VexRiscvSmpLitexCluster.scala @@ -7,6 +7,7 @@ import spinal.lib.bus.misc.{AddressMapping, DefaultMapping, SizeMapping} import spinal.lib.bus.wishbone.{WishboneConfig, WishboneToBmbGenerator} import spinal.lib.generator.GeneratorComponent import spinal.lib.sim.SparseMemory +import vexriscv.demo.smp.VexRiscvLitexSmpClusterCmdGen.exposeTime import vexriscv.demo.smp.VexRiscvSmpClusterGen.vexRiscvConfig import vexriscv.ip.fpu.{FpuCore, FpuParameter} import vexriscv.plugin.{AesPlugin, DBusCachedPlugin, FpuPlugin} @@ -17,7 +18,8 @@ case class VexRiscvLitexSmpClusterParameter( cluster : VexRiscvSmpClusterParamet liteDramMapping : AddressMapping, coherentDma : Boolean, wishboneMemory : Boolean, - cpuPerFpu : Int) + cpuPerFpu : Int, + exposeTime : Boolean) class VexRiscvLitexSmpCluster(p : VexRiscvLitexSmpClusterParameter) extends VexRiscvSmpClusterWithPeripherals(p.cluster) { @@ -97,10 +99,13 @@ class VexRiscvLitexSmpCluster(p : VexRiscvLitexSmpClusterParameter) extends VexR interconnect.setPipelining(iBridge.bmb)(cmdHalfRate = true) interconnect.setPipelining(dBridge.bmb)(cmdReady = true) } + + val clint_time = p.exposeTime generate hardFork(clint.logic.io.time.toIo) } object VexRiscvLitexSmpClusterCmdGen extends App { + Handle.loadHandleAsync = true var cpuCount = 1 var iBusWidth = 64 var dBusWidth = 64 @@ -108,6 +113,8 @@ object VexRiscvLitexSmpClusterCmdGen extends App { var dCacheSize = 8192 var iCacheWays = 2 var dCacheWays = 2 + var privilegedDebug = false + var hardwareBreakpoints = 0 var liteDramWidth = 128 var coherentDma = false var wishboneMemory = false @@ -121,9 +128,10 @@ object VexRiscvLitexSmpClusterCmdGen extends App { var iTlbSize = 4 var dTlbSize = 4 var wishboneForce32b = false + var exposeTime = false assert(new scopt.OptionParser[Unit]("VexRiscvLitexSmpClusterCmdGen") { help("help").text("prints this usage text") - opt[Unit]("coherent-dma") action { (v, c) => coherentDma = true } + opt[Unit] ("coherent-dma") action { (v, c) => coherentDma = true } opt[String]("cpu-count") action { (v, c) => cpuCount = v.toInt } opt[String]("ibus-width") action { (v, c) => iBusWidth = v.toInt } opt[String]("dbus-width") action { (v, c) => dBusWidth = v.toInt } @@ -131,6 +139,8 @@ object VexRiscvLitexSmpClusterCmdGen extends App { opt[String]("dcache-size") action { (v, c) => dCacheSize = v.toInt } opt[String]("icache-ways") action { (v, c) => iCacheWays = v.toInt } opt[String]("dcache-ways") action { (v, c) => dCacheWays = v.toInt } + opt[Boolean]("privileged-debug") action { (v, c) => privilegedDebug = v } + opt[Int] ("hardware-breakpoints") action { (v, c) => hardwareBreakpoints = v } opt[String]("litedram-width") action { (v, c) => liteDramWidth = v.toInt } opt[String]("netlist-directory") action { (v, c) => netlistDirectory = v } opt[String]("netlist-name") action { (v, c) => netlistName = v } @@ -143,6 +153,7 @@ object VexRiscvLitexSmpClusterCmdGen extends App { opt[String]("rvc") action { (v, c) => rvc = v.toBoolean } opt[String]("itlb-size") action { (v, c) => iTlbSize = v.toInt } opt[String]("dtlb-size") action { (v, c) => dTlbSize = v.toInt } + opt[String]("expose-time") action { (v, c) => exposeTime = v.toBoolean } }.parse(args, Unit).nonEmpty) val coherency = coherentDma || cpuCount > 1 @@ -160,6 +171,7 @@ object VexRiscvLitexSmpClusterCmdGen extends App { iCacheWays = iCacheWays, dCacheWays = dCacheWays, coherency = coherency, + privilegedDebug = privilegedDebug, iBusRelax = true, earlyBranch = true, withFloat = fpu, @@ -168,8 +180,8 @@ object VexRiscvLitexSmpClusterCmdGen extends App { loadStoreWidth = if(fpu) 64 else 32, rvc = rvc, injectorStage = rvc, - iTlbSize = iTlbSize, - dTlbSize = dTlbSize + iTlbSize = iTlbSize, + dTlbSize = dTlbSize ) if(aesInstruction) c.add(new AesPlugin) c @@ -178,13 +190,16 @@ object VexRiscvLitexSmpClusterCmdGen extends App { forcePeripheralWidth = !wishboneMemory || wishboneForce32b, outOfOrderDecoder = outOfOrderDecoder, fpu = fpu, - jtagHeaderIgnoreWidth = 0 + jtagHeaderIgnoreWidth = 0, + privilegedDebug = privilegedDebug, + hardwareBreakpoints = hardwareBreakpoints ), liteDram = LiteDramNativeParameter(addressWidth = 32, dataWidth = liteDramWidth), liteDramMapping = SizeMapping(0x40000000l, 0x40000000l), coherentDma = coherentDma, wishboneMemory = wishboneMemory, - cpuPerFpu = cpuPerFpu + cpuPerFpu = cpuPerFpu, + exposeTime = exposeTime ) def dutGen = { @@ -260,7 +275,8 @@ object VexRiscvLitexSmpClusterOpenSbi extends App{ liteDramMapping = SizeMapping(0x80000000l, 0x70000000l), coherentDma = false, wishboneMemory = false, - cpuPerFpu = 4 + cpuPerFpu = 4, + exposeTime = false ) def dutGen = { diff --git a/src/test/scala/vexriscv/experimental/PlicCost.scala b/src/test/scala/vexriscv/experimental/PlicCost.scala index 79d5c663a..df008423c 100644 --- a/src/test/scala/vexriscv/experimental/PlicCost.scala +++ b/src/test/scala/vexriscv/experimental/PlicCost.scala @@ -33,6 +33,7 @@ class PlicBench(inputCount : Int) extends Component{ val targets = Seq( PlicTarget( + id = 0, gateways = gateways, priorityWidth = priorityWidth )