Add official RISC-V debug support (WIP, but can already load / step / run code via openocd telnet)
This commit is contained in:
parent
959e48a353
commit
4cd3f65296
38
README.md
38
README.md
|
@ -45,6 +45,7 @@
|
|||
- [MmuPlugin](#mmuplugin)
|
||||
- [PmpPlugin](#pmpplugin)
|
||||
- [DebugPlugin](#debugplugin)
|
||||
- [EmbeddedRiscvJtag](#embeddedRiscvJtag)
|
||||
- [YamlPlugin](#yamlplugin)
|
||||
- [FpuPlugin](#fpuplugin)
|
||||
- [AesPlugin](#aesplugin)
|
||||
|
@ -773,6 +774,7 @@ This chapter describes the currently implemented plugins.
|
|||
- [StaticMemoryTranslatorPlugin](#staticmemorytranslatorplugin)
|
||||
- [MemoryTranslatorPlugin](#memorytranslatorplugin)
|
||||
- [DebugPlugin](#debugplugin)
|
||||
- [EmbeddedRiscvJtag](#embeddedRiscvJtag)
|
||||
- [YamlPlugin](#yamlplugin)
|
||||
- [FpuPlugin](#fpuplugin)
|
||||
|
||||
|
@ -1180,6 +1182,42 @@ Write Address 0x04 ->
|
|||
|
||||
The OpenOCD port is here: <https://github.com/SpinalHDL/openocd_riscv>
|
||||
|
||||
#### 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
|
||||
|
|
|
@ -238,5 +238,13 @@ object Riscv{
|
|||
val FFLAGS = 0x1
|
||||
val FRM = 0x2
|
||||
val FCSR = 0x3
|
||||
|
||||
val DCSR = 0x7B0
|
||||
val DPC = 0x7B1
|
||||
val TSELECT = 0x7A0
|
||||
val TDATA1 = 0x7A1
|
||||
val TDATA2 = 0x7A2
|
||||
val TINFO = 0x7a4
|
||||
val TCONTROL = 0x7A5
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import spinal.core._
|
|||
import spinal.lib._
|
||||
import vexriscv.ip._
|
||||
import spinal.lib.bus.avalon.AvalonMM
|
||||
import spinal.lib.cpu.riscv.debug.DebugTransportModuleParameter
|
||||
import spinal.lib.eda.altera.{InterruptReceiverTag, ResetEmitterTag}
|
||||
import vexriscv.demo.smp.VexRiscvSmpClusterGen
|
||||
import vexriscv.ip.fpu.FpuParameter
|
||||
|
@ -141,14 +142,28 @@ object TestsWorkspace {
|
|||
withFloat = true,
|
||||
withDouble = true,
|
||||
externalFpu = false,
|
||||
simHalt = true
|
||||
simHalt = true,
|
||||
privilegedDebug = true
|
||||
)
|
||||
|
||||
config.plugins += new EmbeddedRiscvJtag(
|
||||
p = DebugTransportModuleParameter(
|
||||
addressWidth = 7,
|
||||
version = 1,
|
||||
idle = 7
|
||||
),
|
||||
withTunneling = false,
|
||||
withTap = true
|
||||
)
|
||||
|
||||
// l.foreach{
|
||||
// case p : EmbeddedRiscvJtag => p.debugCd.load(ClockDomain.current.copy(reset = Bool().setName("debug_reset")))
|
||||
// case _ =>
|
||||
// }
|
||||
|
||||
println("Args :")
|
||||
println(config.getRegressionArgs().mkString(" "))
|
||||
|
||||
|
||||
val toplevel = new VexRiscv(config)
|
||||
// val toplevel = new VexRiscv(configLight)
|
||||
// val toplevel = new VexRiscv(configTest)
|
||||
|
|
|
@ -194,14 +194,15 @@ object VexRiscvSmpClusterGen {
|
|||
withDataCache : Boolean = true,
|
||||
withInstructionCache : Boolean = true,
|
||||
forceMisa : Boolean = false,
|
||||
forceMscratch : Boolean = false
|
||||
forceMscratch : Boolean = false,
|
||||
privilegedDebug : Boolean = false
|
||||
) = {
|
||||
assert(iCacheSize/iCacheWays <= 4096, "Instruction cache ways can't be bigger than 4096 bytes")
|
||||
assert(dCacheSize/dCacheWays <= 4096, "Data cache ways can't be bigger than 4096 bytes")
|
||||
assert(!(withDouble && !withFloat))
|
||||
|
||||
val csrConfig = if(withSupervisor){
|
||||
CsrPluginConfig.openSbi(mhartid = hartId, misa = Riscv.misaToInt(s"ima${if(withFloat) "f" else ""}${if(withDouble) "d" else ""}s")).copy(utimeAccess = CsrAccess.READ_ONLY)
|
||||
CsrPluginConfig.openSbi(mhartid = hartId, misa = Riscv.misaToInt(s"ima${if(withFloat) "f" else ""}${if(withDouble) "d" else ""}s")).copy(utimeAccess = CsrAccess.READ_ONLY, withPrivilegedDebug = privilegedDebug)
|
||||
} else {
|
||||
CsrPluginConfig(
|
||||
catchIllegalAccess = true,
|
||||
|
@ -223,7 +224,8 @@ object VexRiscvSmpClusterGen {
|
|||
ebreakGen = true,
|
||||
wfiGenAsWait = false,
|
||||
wfiGenAsNop = true,
|
||||
ucycleAccess = CsrAccess.NONE
|
||||
ucycleAccess = CsrAccess.NONE,
|
||||
withPrivilegedDebug = privilegedDebug
|
||||
)
|
||||
}
|
||||
val config = VexRiscvConfig(
|
||||
|
|
|
@ -9,6 +9,8 @@ import vexriscv.plugin.IntAluPlugin.{ALU_BITWISE_CTRL, ALU_CTRL, AluBitwiseCtrlE
|
|||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.collection.mutable
|
||||
import spinal.core.sim._
|
||||
import spinal.lib.cpu.riscv.debug._
|
||||
import spinal.lib.fsm.{State, StateMachine}
|
||||
|
||||
/**
|
||||
* Created by spinalvm on 21.03.17.
|
||||
|
@ -78,7 +80,9 @@ case class CsrPluginConfig(
|
|||
pipelinedInterrupt : Boolean = true,
|
||||
csrOhDecoder : Boolean = true,
|
||||
deterministicInteruptionEntry : Boolean = false, //Only used for simulatation purposes
|
||||
wfiOutput : Boolean = false
|
||||
wfiOutput : Boolean = false,
|
||||
withPrivilegedDebug : Boolean = false, //For the official RISC-V debug spec implementation
|
||||
debugTriggers : Int = 2
|
||||
){
|
||||
assert(!ucycleAccess.canWrite)
|
||||
def privilegeGen = userGen || supervisorGen
|
||||
|
@ -465,6 +469,10 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep
|
|||
var externalMhartId : UInt = null
|
||||
var utime : UInt = null
|
||||
|
||||
var debugBus : DebugHartBus = null
|
||||
var debugMode : Bool = null
|
||||
var injectionPort : Stream[Bits] = null
|
||||
|
||||
override def askWake(): Unit = thirdPartyWake := True
|
||||
|
||||
override def isContextSwitching = contextSwitching
|
||||
|
@ -622,6 +630,11 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep
|
|||
decoderService.addDefault(IS_SFENCE_VMA, False)
|
||||
decoderService.add(SFENCE_VMA, List(IS_SFENCE_VMA -> True))
|
||||
}
|
||||
|
||||
|
||||
injectionPort = withPrivilegedDebug generate pipeline.service(classOf[IBusFetcher]).getInjectionPort()
|
||||
debugMode = withPrivilegedDebug generate Bool().setName("debugMode")
|
||||
debugBus = withPrivilegedDebug generate slave(DebugHartBus()).setName("debugBus")
|
||||
}
|
||||
|
||||
def inhibateInterrupts() : Unit = allowInterrupts := False
|
||||
|
@ -657,11 +670,244 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep
|
|||
val base = UInt(xlen-2 bits)
|
||||
}
|
||||
|
||||
val trapEvent = False
|
||||
|
||||
val privilegeReg = privilegeGen generate RegInit(U"11")
|
||||
privilege := (if(privilegeGen) privilegeReg else U"11")
|
||||
|
||||
when(forceMachineWire) { privilege := 3 }
|
||||
|
||||
val debug = withPrivilegedDebug generate pipeline plug new Area{
|
||||
val iBusFetcher = service(classOf[IBusFetcher])
|
||||
def bus = debugBus
|
||||
|
||||
|
||||
val running = RegInit(True)
|
||||
debugMode := !running
|
||||
|
||||
|
||||
when(!running) {
|
||||
iBusFetcher.haltIt()
|
||||
}
|
||||
|
||||
bus.resume.rsp.valid := False
|
||||
|
||||
bus.running := running
|
||||
bus.halted := !running
|
||||
bus.unavailable := RegNext(ClockDomain.current.isResetActive)
|
||||
val enterHalt = running.getAheadValue().fall(False)
|
||||
|
||||
val doHalt = RegInit(False) setWhen(bus.haltReq && bus.running && !debugMode) clearWhen(enterHalt)
|
||||
val forceResume = False
|
||||
val doResume = forceResume || bus.resume.isPending(1)
|
||||
|
||||
// Pipeline execution timeout used to trigger some redo
|
||||
val timeout = Timeout(3)
|
||||
when(pipeline.stages.map(_.arbitration.isValid).orR){
|
||||
timeout.clear()
|
||||
}
|
||||
|
||||
val inject = new Area{
|
||||
val cmd = bus.dmToHart.translateWith(bus.dmToHart.data).takeWhen(bus.dmToHart.op === DebugDmToHartOp.EXECUTE)
|
||||
injectionPort << cmd.toStream.stage
|
||||
|
||||
|
||||
val pending = RegInit(False) setWhen(cmd.valid) clearWhen(bus.exception || bus.commit || bus.ebreak || bus.redo)
|
||||
when(cmd.valid){ timeout.clear() }
|
||||
bus.redo := pending && timeout.state
|
||||
}
|
||||
val dataCsrr = new Area{
|
||||
bus.hartToDm.valid := isWriting(DebugModule.CSR_DATA)
|
||||
bus.hartToDm.address := 0
|
||||
bus.hartToDm.data := execute.input(SRC1)
|
||||
}
|
||||
|
||||
val dataCsrw = new Area{
|
||||
val value = Reg(Bits(32 bits))
|
||||
|
||||
val fromDm = new Area{
|
||||
when(bus.dmToHart.valid && bus.dmToHart.op === DebugDmToHartOp.DATA){
|
||||
value := bus.dmToHart.data
|
||||
}
|
||||
}
|
||||
|
||||
val toHart = new Area{
|
||||
r(DebugModule.CSR_DATA, value)
|
||||
}
|
||||
}
|
||||
|
||||
val dpc = Reg(UInt(32 bits))
|
||||
val dcsr = new Area{
|
||||
rw(CSR.DPC, dpc)
|
||||
val prv = RegInit(U"11")
|
||||
val step = RegInit(False) //TODO
|
||||
val nmip = False
|
||||
val mprven = False
|
||||
val cause = RegInit(U"000")
|
||||
val stoptime = False
|
||||
val stopcount = False
|
||||
val stepie = RegInit(False) //TODO
|
||||
val ebreaku = userGen generate RegInit(False)
|
||||
val ebreaks = supervisorGen generate RegInit(False)
|
||||
val ebreakm = RegInit(False)
|
||||
val xdebugver = U(4, 4 bits)
|
||||
|
||||
val stepLogic = new StateMachine{
|
||||
val IDLE, SINGLE, WAIT, DELAY = new State()
|
||||
setEntry(IDLE)
|
||||
|
||||
val isCause = RegInit(False)
|
||||
|
||||
IDLE whenIsActive{
|
||||
when(step && bus.resume.rsp.valid){
|
||||
goto(SINGLE)
|
||||
}
|
||||
}
|
||||
SINGLE whenIsActive{
|
||||
when(iBusFetcher.incoming()) {
|
||||
iBusFetcher.haltIt()
|
||||
when(decode.arbitration.isValid) {
|
||||
goto(WAIT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WAIT whenIsActive{
|
||||
iBusFetcher.haltIt()
|
||||
when(pipeline.lastStageIsFiring || trapEvent){ //TODO
|
||||
doHalt := True
|
||||
isCause := True
|
||||
goto(DELAY)
|
||||
}
|
||||
//re resume the execution in case of timeout (ex cache miss)
|
||||
when(timeout.state){
|
||||
forceResume := True
|
||||
goto(DELAY)
|
||||
}
|
||||
}
|
||||
DELAY whenIsActive{
|
||||
iBusFetcher.haltIt()
|
||||
goto(IDLE)
|
||||
}
|
||||
|
||||
always{
|
||||
when(enterHalt){
|
||||
goto(IDLE)
|
||||
}
|
||||
}
|
||||
build()
|
||||
}
|
||||
|
||||
|
||||
r(CSR.DCSR, 3 -> nmip, 6 -> cause, 28 -> xdebugver, 4 -> mprven, 9 -> stoptime, 10 -> stopcount)
|
||||
rw(CSR.DCSR, 0 -> prv, 2 -> step, 11 -> stepie, 15 -> ebreakm)
|
||||
if(supervisorGen) rw(CSR.DCSR, 13 -> ebreaks)
|
||||
if(userGen) rw(CSR.DCSR, 12 -> ebreaku)
|
||||
|
||||
|
||||
when(debugMode) {
|
||||
pipeline.plugins.foreach{
|
||||
case p : PredictionInterface => p.inDebugNoFetch()
|
||||
case _ =>
|
||||
}
|
||||
if(pipeline.things.contains(DEBUG_BYPASS_CACHE)) pipeline(DEBUG_BYPASS_CACHE) := True
|
||||
}
|
||||
|
||||
val wakeService = serviceElse(classOf[IWake], null)
|
||||
if(wakeService != null) when(debugMode || step){
|
||||
wakeService.askWake()
|
||||
}
|
||||
}
|
||||
|
||||
//Very limited subset of the trigger spec
|
||||
val trigger = (debugTriggers > 0) generate new Area {
|
||||
val tselect = new Area{
|
||||
val index = Reg(UInt(log2Up(debugTriggers) bits))
|
||||
rw(CSR.TSELECT, index)
|
||||
|
||||
val outOfRange = if(isPow2(debugTriggers)) False else index < debugTriggers
|
||||
}
|
||||
|
||||
val tinfo = new Area{
|
||||
r(CSR.TINFO, 0 -> tselect.outOfRange, 2 -> !tselect.outOfRange)
|
||||
}
|
||||
|
||||
val decodeBreak = new Area {
|
||||
val enabled = False
|
||||
val timeout = Timeout(3).clearWhen(!enabled || stages.tail.map(_.arbitration.isValid).orR)
|
||||
when(enabled) {
|
||||
decode.arbitration.haltByOther := True
|
||||
when(timeout.state) {
|
||||
dpc := decode.input(PC)
|
||||
running := False
|
||||
dcsr.cause := 2
|
||||
dcsr.prv := privilege
|
||||
privilegeReg := 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val slots = for(slotId <- 0 until debugTriggers) yield new Area {
|
||||
val selected = tselect.index === slotId
|
||||
def csrw(csrId : Int, thats : (Int, Data)*): Unit ={
|
||||
onWrite(csrId){
|
||||
when(selected) {
|
||||
for((offset, data) <- thats){
|
||||
data.assignFromBits(writeData()(offset, widthOf(data) bits))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def csrr(csrId : Int, read : Bits, thats : (Int, Data)*): Unit ={
|
||||
when(selected) {
|
||||
for((offset, data) <- thats){
|
||||
read(offset, widthOf(data) bits) := data.asBits
|
||||
}
|
||||
}
|
||||
}
|
||||
def csrrw(csrId : Int, read : Bits, thats : (Int, Data)*) : Unit = {
|
||||
csrw(csrId, thats :_*)
|
||||
csrr(csrId, read, thats :_*)
|
||||
}
|
||||
|
||||
val tdata1 = new Area{
|
||||
val read = B(0, 32 bits)
|
||||
val tpe = Reg(UInt(4 bits)) init(2)
|
||||
val dmode = Reg(Bool()) init(False)
|
||||
|
||||
val execute = RegInit(False)
|
||||
val m, s, u = RegInit(False)
|
||||
val action = RegInit(U"0000")
|
||||
val privilegeHit = !debugMode && privilege.mux(
|
||||
0 -> u,
|
||||
1 -> s,
|
||||
3 -> m,
|
||||
default -> False
|
||||
)
|
||||
|
||||
csrrw(CSR.TDATA1, read, 2 -> execute , 3 -> u, 4-> s, 6 -> m, 32 - 4 -> tpe, 32 - 5 -> dmode, 12 -> action)
|
||||
|
||||
|
||||
//TODO action sizelo timing select sizehi maskmax
|
||||
}
|
||||
|
||||
val tdata2 = new Area{
|
||||
val value = Reg(PC)
|
||||
csrw(CSR.TDATA2, 0 -> value)
|
||||
|
||||
val execute = new Area{
|
||||
val enabled = !debugMode && tdata1.action === 1 && tdata1.execute && tdata1.privilegeHit
|
||||
val hit = enabled && value === decode.input(PC)
|
||||
decodeBreak.enabled.setWhen(hit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r(CSR.TDATA1, 0 -> slots.map(_.tdata1.read).read(tselect.index))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val machineCsr = pipeline plug new Area{
|
||||
//Define CSR registers
|
||||
// Status => MXR, SUM, TVM, TW, TSE ?
|
||||
|
@ -973,6 +1219,8 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep
|
|||
}
|
||||
|
||||
code.addTag(Verilator.public)
|
||||
|
||||
if(withPrivilegedDebug) valid setWhen(debug.doHalt)
|
||||
}
|
||||
|
||||
|
||||
|
@ -1012,6 +1260,11 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep
|
|||
val hadException = RegNext(exception) init(False) addTag(Verilator.public)
|
||||
pipelineLiberator.done.clearWhen(hadException)
|
||||
|
||||
if(withPrivilegedDebug) {
|
||||
debugBus.commit := debugMode && pipeline.stages.last.arbitration.isFiring
|
||||
debugBus.exception := debugMode && hadException
|
||||
debugBus.ebreak := False
|
||||
}
|
||||
|
||||
val targetPrivilege = CombInit(interrupt.targetPrivilege)
|
||||
if(exceptionPortCtrl != null) when(hadException) {
|
||||
|
@ -1019,8 +1272,17 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep
|
|||
}
|
||||
|
||||
val trapCause = CombInit(interrupt.code.resize(trapCodeWidth))
|
||||
val trapCauseEbreakDebug = False
|
||||
if(exceptionPortCtrl != null) when( hadException){
|
||||
trapCause := exceptionPortCtrl.exceptionContext.code.resized
|
||||
if(withPrivilegedDebug) {
|
||||
when(exceptionPortCtrl.exceptionContext.code === 3){
|
||||
trapCauseEbreakDebug setWhen(debugMode)
|
||||
trapCauseEbreakDebug setWhen(privilege === 3 && debug.dcsr.ebreakm)
|
||||
if (userGen) trapCauseEbreakDebug setWhen (privilege === 0 && debug.dcsr.ebreaku)
|
||||
if (supervisorGen) trapCauseEbreakDebug setWhen (privilege === 1 && debug.dcsr.ebreaks)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val xtvec = Xtvec().assignDontCare()
|
||||
|
@ -1029,38 +1291,61 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep
|
|||
is(3){ xtvec := machineCsr.mtvec }
|
||||
}
|
||||
|
||||
val trapEnterDebug = False
|
||||
if(withPrivilegedDebug) trapEnterDebug setWhen(debug.doHalt || trapCauseEbreakDebug || !hadException && debug.doHalt)
|
||||
when(hadException || interruptJump){
|
||||
trapEvent := True
|
||||
fetcher.haltIt() //Avoid having the fetch confused by the incomming privilege switch
|
||||
|
||||
jumpInterface.valid := True
|
||||
jumpInterface.payload := (if(!xtvecModeGen) xtvec.base @@ U"00" else (xtvec.mode === 0 || hadException) ? (xtvec.base @@ U"00") | ((xtvec.base + trapCause) @@ U"00") )
|
||||
lastStage.arbitration.flushNext := True
|
||||
|
||||
if(privilegeGen) privilegeReg := targetPrivilege
|
||||
when(!trapEnterDebug){
|
||||
if(privilegeGen) privilegeReg := targetPrivilege
|
||||
switch(targetPrivilege){
|
||||
if(supervisorGen) is(1) {
|
||||
sstatus.SIE := False
|
||||
sstatus.SPIE := sstatus.SIE
|
||||
sstatus.SPP := privilege(0 downto 0)
|
||||
scause.interrupt := !hadException
|
||||
scause.exceptionCode := trapCause
|
||||
sepc := mepcCaptureStage.input(PC)
|
||||
if (exceptionPortCtrl != null) when(hadException){
|
||||
stval := exceptionPortCtrl.exceptionContext.badAddr
|
||||
}
|
||||
}
|
||||
|
||||
switch(targetPrivilege){
|
||||
if(supervisorGen) is(1) {
|
||||
sstatus.SIE := False
|
||||
sstatus.SPIE := sstatus.SIE
|
||||
sstatus.SPP := privilege(0 downto 0)
|
||||
scause.interrupt := !hadException
|
||||
scause.exceptionCode := trapCause
|
||||
sepc := mepcCaptureStage.input(PC)
|
||||
if (exceptionPortCtrl != null) when(hadException){
|
||||
stval := exceptionPortCtrl.exceptionContext.badAddr
|
||||
is(3){
|
||||
mstatus.MIE := False
|
||||
mstatus.MPIE := mstatus.MIE
|
||||
mstatus.MPP := privilege
|
||||
mcause.interrupt := !hadException
|
||||
mcause.exceptionCode := trapCause
|
||||
mepc := mepcCaptureStage.input(PC)
|
||||
if(exceptionPortCtrl != null) when(hadException){
|
||||
mtval := exceptionPortCtrl.exceptionContext.badAddr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is(3){
|
||||
mstatus.MIE := False
|
||||
mstatus.MPIE := mstatus.MIE
|
||||
mstatus.MPP := privilege
|
||||
mcause.interrupt := !hadException
|
||||
mcause.exceptionCode := trapCause
|
||||
mepc := mepcCaptureStage.input(PC)
|
||||
if(exceptionPortCtrl != null) when(hadException){
|
||||
mtval := exceptionPortCtrl.exceptionContext.badAddr
|
||||
} otherwise {
|
||||
if(withPrivilegedDebug) {
|
||||
debug.running := False
|
||||
when(!debugMode) {
|
||||
debug.dpc := mepcCaptureStage.input(PC)
|
||||
debug.dcsr.cause := 3
|
||||
when(debug.dcsr.step) {
|
||||
debug.dcsr.cause := 4
|
||||
}
|
||||
when(trapCauseEbreakDebug) {
|
||||
debug.dcsr.cause := 1
|
||||
}
|
||||
debug.dcsr.prv := privilege
|
||||
} otherwise {
|
||||
debugBus.exception := !trapCauseEbreakDebug //TODO mask interrupt while in debug mode
|
||||
debugBus.ebreak := trapCauseEbreakDebug
|
||||
}
|
||||
privilegeReg := 3
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1097,9 +1382,21 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
contextSwitching := jumpInterface.valid
|
||||
|
||||
// Debug resume
|
||||
if(withPrivilegedDebug) {
|
||||
when(debug.doResume) {
|
||||
jumpInterface.valid := True
|
||||
jumpInterface.payload := debug.dpc
|
||||
|
||||
privilegeReg := debug.dcsr.prv
|
||||
debug.running := True
|
||||
debug.bus.resume.rsp.valid := True
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//CSR read/write instructions management
|
||||
decode plug new Area{
|
||||
import decode._
|
||||
|
@ -1200,12 +1497,6 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep
|
|||
memory.output(REGFILE_WRITE_DATA) := memory.input(PIPELINED_CSR_READ)
|
||||
}
|
||||
}
|
||||
//
|
||||
// Component.current.rework{
|
||||
// when(arbitration.isFiring && input(IS_CSR)) {
|
||||
// memory.input(REGFILE_WRITE_DATA).getDrivingReg := readData
|
||||
// }
|
||||
// }
|
||||
|
||||
//Translation of the csrMapping into real logic
|
||||
val csrAddress = input(INSTRUCTION)(csrRange)
|
||||
|
@ -1307,11 +1598,15 @@ class CsrPlugin(val config: CsrPluginConfig) extends Plugin[VexRiscv] with Excep
|
|||
|
||||
illegalAccess clearWhen(csrMapping.allowCsrSignal)
|
||||
|
||||
when(privilege < csrAddress(9 downto 8).asUInt){
|
||||
val forceFail = False
|
||||
forceFail setWhen(privilege < csrAddress(9 downto 8).asUInt)
|
||||
if(withPrivilegedDebug) forceFail setWhen(!debugMode && csrAddress >> 4 === 0x7B)
|
||||
when(forceFail){
|
||||
illegalAccess := True
|
||||
readInstruction := False
|
||||
writeInstruction := False
|
||||
}
|
||||
|
||||
illegalAccess clearWhen(!arbitration.isValid || !input(IS_CSR))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,7 +179,6 @@ case class DebugExtensionIo() extends Bundle with IMasterSlave{
|
|||
class DebugPlugin(var debugClockDomain : ClockDomain, hardwareBreakpointCount : Int = 0, BreakpointReadback : Boolean = false) extends Plugin[VexRiscv] {
|
||||
|
||||
var io : DebugExtensionIo = null
|
||||
val injectionAsks = ArrayBuffer[(Stage, Bool)]()
|
||||
var injectionPort : Stream[Bits] = null
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* Thanks Efinix for funding the official RISC-V debug implementation on VexRiscv !
|
||||
*/
|
||||
|
||||
|
||||
package vexriscv.plugin
|
||||
|
||||
import spinal.core._
|
||||
import spinal.lib._
|
||||
import spinal.lib.com.jtag._
|
||||
import spinal.lib.cpu.riscv.debug._
|
||||
import vexriscv._
|
||||
|
||||
|
||||
class EmbeddedRiscvJtag(var p : DebugTransportModuleParameter,
|
||||
var withTap : Boolean = true,
|
||||
var withTunneling : Boolean = false
|
||||
) extends Plugin[VexRiscv] with VexRiscvRegressionArg{
|
||||
|
||||
|
||||
override def getVexRiscvRegressionArgs() = List("DEBUG_PLUGIN=RISCV")
|
||||
|
||||
var jtag : Jtag = null
|
||||
var jtagInstruction : JtagTapInstructionCtrl = null
|
||||
var ndmreset : Bool = null
|
||||
|
||||
// val debugCd = Handle[ClockDomain].setName("debugCd")
|
||||
// val noTapCd = Handle[ClockDomain].setName("jtagCd")
|
||||
|
||||
override def setup(pipeline: VexRiscv): Unit = {
|
||||
jtag = withTap generate slave(Jtag()).setName("jtag")
|
||||
jtagInstruction = !withTap generate slave(JtagTapInstructionCtrl()).setName("jtagInstruction")
|
||||
ndmreset = Bool().setName("ndmreset")
|
||||
}
|
||||
|
||||
override def build(pipeline: VexRiscv): Unit = {
|
||||
val XLEN = 32
|
||||
val dm = DebugModule(
|
||||
DebugModuleParameter(
|
||||
version = p.version + 1,
|
||||
harts = 1,
|
||||
progBufSize = 2,
|
||||
datacount = XLEN/32,
|
||||
xlens = List(XLEN)
|
||||
)
|
||||
)
|
||||
|
||||
ndmreset := dm.io.ndmreset
|
||||
|
||||
val dmiDirect = if(withTap && !withTunneling) new Area {
|
||||
val logic = DebugTransportModuleJtagTap(
|
||||
p.copy(addressWidth = 7),
|
||||
debugCd = ClockDomain.current
|
||||
)
|
||||
dm.io.ctrl <> logic.io.bus
|
||||
logic.io.jtag <> jtag
|
||||
}
|
||||
val dmiTunneled = if(withTap && withTunneling) new Area {
|
||||
val logic = DebugTransportModuleJtagTapWithTunnel(
|
||||
p.copy(addressWidth = 7),
|
||||
debugCd = ClockDomain.current
|
||||
)
|
||||
dm.io.ctrl <> logic.io.bus
|
||||
logic.io.jtag <> jtag
|
||||
}
|
||||
|
||||
val privBus = pipeline.service(classOf[CsrPlugin]).debugBus.setAsDirectionLess()
|
||||
privBus <> dm.io.harts(0)
|
||||
privBus.dmToHart.removeAssignments() <-< dm.io.harts(0).dmToHart
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
make IBUS=CACHED IBUS_DATA_WIDTH=64 COMPRESSED=no DBUS=CACHED DBUS_LOAD_DATA_WIDTH=64 DBUS_STORE_DATA_WIDTH=64 LRSC=yes AMO=yes DBUS_EXCLUSIVE=yes DBUS_INVALIDATE=yes MUL=yes DIV=yes RVF=yes RVD=yes RISCV_JTAG=yes REDO=1 WITH_RISCV_REF=no DEBUG_PLUGIN_EXTERNAL=yes DEBUG_PLUGIN=no
|
||||
src/openocd -f ../VexRiscvOoo/src/main/tcl/openocd/naxriscv_sim.tcl -c "sleep 5000" -c "reg pc 0x80000000" -c "exit" -d3
|
||||
|
||||
mdw 0x1000 16
|
||||
mww 0x1000 0x12345678
|
||||
mdw 0x1000 16
|
||||
|
||||
load_image /media/data/open/VexRiscv/src/test/resources/hex/dhrystoneO3.hex 0 ihex
|
||||
mdw 0x80000000 16
|
||||
reg pc 0x80000000
|
||||
bp 0x80000114 4
|
||||
resume
|
||||
*/
|
|
@ -0,0 +1,188 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
/** Returns true on success, or false if there was an error */
|
||||
bool SetSocketBlockingEnabled(int fd, bool blocking)
|
||||
{
|
||||
if (fd < 0) return false;
|
||||
|
||||
#ifdef WIN32
|
||||
unsigned long mode = blocking ? 0 : 1;
|
||||
return (ioctlsocket(fd, FIONBIO, &mode) == 0) ? true : false;
|
||||
#else
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags < 0) return false;
|
||||
flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
|
||||
return (fcntl(fd, F_SETFL, flags) == 0) ? true : false;
|
||||
#endif
|
||||
}
|
||||
|
||||
class Jtag : public SimElement{
|
||||
public:
|
||||
CData *tms, *tdi, *tdo, *tck;
|
||||
enum State {reset};
|
||||
uint32_t state;
|
||||
|
||||
int serverSocket, clientHandle;
|
||||
struct sockaddr_in serverAddr;
|
||||
struct sockaddr_storage serverStorage;
|
||||
socklen_t addr_size;
|
||||
uint64_t tooglePeriod;
|
||||
// char buffer[1024];
|
||||
|
||||
uint32_t timer;
|
||||
Jtag(CData *tms, CData *tdi, CData *tdo, CData* tck,uint64_t period){
|
||||
this->tms = tms;
|
||||
this->tdi = tdi;
|
||||
this->tdo = tdo;
|
||||
this->tck = tck;
|
||||
this->tooglePeriod = period-1;
|
||||
*tms = 0;
|
||||
*tdi = 0;
|
||||
*tdo = 0;
|
||||
*tck = 0;
|
||||
state = 0;
|
||||
timer = 0;
|
||||
|
||||
|
||||
//---- Create the socket. The three arguments are: ----//
|
||||
// 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) //
|
||||
serverSocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
assert(serverSocket != -1);
|
||||
int flag = 1;
|
||||
setsockopt( serverSocket, /* socket affected */
|
||||
IPPROTO_TCP, /* set option at TCP level */
|
||||
TCP_NODELAY, /* name of option */
|
||||
(char *) &flag, /* the cast is historical
|
||||
cruft */
|
||||
sizeof(int)); /* length of option value */
|
||||
|
||||
/*int a = 0xFFF;
|
||||
if (setsockopt(serverSocket, SOL_SOCKET, SO_RCVBUF, &a, sizeof(int)) == -1) {
|
||||
fprintf(stderr, "Error setting socket opts: %s\n", strerror(errno));
|
||||
}
|
||||
a = 0xFFFFFF;
|
||||
if (setsockopt(serverSocket, SOL_SOCKET, SO_SNDBUF, &a, sizeof(int)) == -1) {
|
||||
fprintf(stderr, "Error setting socket opts: %s\n", strerror(errno));
|
||||
}*/
|
||||
|
||||
SetSocketBlockingEnabled(serverSocket,0);
|
||||
|
||||
|
||||
//---- Configure settings of the server address struct ----//
|
||||
// Address family = Internet //
|
||||
serverAddr.sin_family = AF_INET;
|
||||
serverAddr.sin_port = htons(7894);
|
||||
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);
|
||||
|
||||
//---- Bind the address struct to the socket ----//
|
||||
bind(serverSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));
|
||||
|
||||
//---- Listen on the socket, with 5 max connection requests queued ----//
|
||||
listen(serverSocket,1);
|
||||
|
||||
//---- Accept call creates a new socket for the incoming connection ----//
|
||||
addr_size = sizeof serverStorage;
|
||||
clientHandle = -1;
|
||||
|
||||
}
|
||||
void connectionReset(){
|
||||
printf("CONNECTION RESET\n");
|
||||
shutdown(clientHandle,SHUT_RDWR);
|
||||
clientHandle = -1;
|
||||
}
|
||||
|
||||
|
||||
virtual ~Jtag(){
|
||||
if(clientHandle != -1) {
|
||||
shutdown(clientHandle,SHUT_RDWR);
|
||||
usleep(100);
|
||||
}
|
||||
if(serverSocket != -1) {
|
||||
close(serverSocket);
|
||||
usleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t selfSleep = 0;
|
||||
uint32_t checkNewConnectionsTimer = 0;
|
||||
uint8_t rxBuffer[100];
|
||||
int32_t rxBufferSize = 0;
|
||||
int32_t rxBufferRemaining = 0;
|
||||
|
||||
// virtual void onReset(){}
|
||||
// virtual void postReset(){}
|
||||
// virtual void preCycle(){}
|
||||
// virtual void postCycle(){}
|
||||
virtual void postCycle(){
|
||||
if(timer != 0){
|
||||
timer -= 1;
|
||||
return;
|
||||
}
|
||||
checkNewConnectionsTimer++;
|
||||
if(checkNewConnectionsTimer == 5000){
|
||||
checkNewConnectionsTimer = 0;
|
||||
int newclientHandle = accept(serverSocket, (struct sockaddr *) &serverStorage, &addr_size);
|
||||
if(newclientHandle != -1){
|
||||
if(clientHandle != -1){
|
||||
connectionReset();
|
||||
}
|
||||
clientHandle = newclientHandle;
|
||||
printf("CONNECTED\n");
|
||||
}
|
||||
else{
|
||||
if(clientHandle == -1)
|
||||
selfSleep = 1000;
|
||||
}
|
||||
}
|
||||
if(selfSleep)
|
||||
selfSleep--;
|
||||
else{
|
||||
if(clientHandle != -1){
|
||||
uint8_t buffer;
|
||||
int n;
|
||||
|
||||
if(rxBufferRemaining == 0){
|
||||
if(ioctl(clientHandle,FIONREAD,&n) != 0)
|
||||
connectionReset();
|
||||
else if(n >= 1){
|
||||
rxBufferSize = read(clientHandle,&rxBuffer,100);
|
||||
if(rxBufferSize < 0){
|
||||
connectionReset();
|
||||
}else {
|
||||
rxBufferRemaining = rxBufferSize;
|
||||
}
|
||||
}else {
|
||||
selfSleep = 30;
|
||||
}
|
||||
}
|
||||
|
||||
if(rxBufferRemaining != 0){
|
||||
uint8_t buffer = rxBuffer[rxBufferSize - (rxBufferRemaining--)];
|
||||
*tms = (buffer & 1) != 0;
|
||||
*tdi = (buffer & 2) != 0;
|
||||
*tck = (buffer & 8) != 0;
|
||||
if(buffer & 4){
|
||||
buffer = (*tdo != 0);
|
||||
//printf("TDO=%d\n",buffer);
|
||||
if(-1 == send(clientHandle,&buffer,1,0))
|
||||
connectionReset();
|
||||
}else {
|
||||
|
||||
// printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
timer = tooglePeriod;
|
||||
}
|
||||
|
||||
};
|
|
@ -3071,6 +3071,8 @@ public:
|
|||
|
||||
#endif
|
||||
|
||||
#include "jtag.h"
|
||||
|
||||
void Workspace::fillSimELements(){
|
||||
#ifdef IBUS_SIMPLE
|
||||
simElements.push_back(new IBusSimple(this));
|
||||
|
@ -3121,6 +3123,9 @@ void Workspace::fillSimELements(){
|
|||
#ifdef DEBUG_PLUGIN_AVALON
|
||||
simElements.push_back(new DebugPluginAvalon(this));
|
||||
#endif
|
||||
#ifdef RISCV_JTAG
|
||||
simElements.push_back(new Jtag(&top->jtag_tms, &top->jtag_tdi, &top->jtag_tdo, &top->jtag_tck, 4));
|
||||
#endif
|
||||
}
|
||||
|
||||
mutex Workspace::staticMutex;
|
||||
|
@ -4126,16 +4131,7 @@ int main(int argc, char **argv, char **env) {
|
|||
|
||||
|
||||
|
||||
#ifdef RVF
|
||||
for(const string &name : riscvTestFloat){
|
||||
redo(REDO,RiscvTest(name).withRiscvRef()->bootAt(0x80000188u)->writeWord(0x80000184u, 0x00305073)->run();)
|
||||
}
|
||||
#endif
|
||||
#ifdef RVD
|
||||
for(const string &name : riscvTestDouble){
|
||||
redo(REDO,RiscvTest(name).withRiscvRef()->bootAt(0x80000188u)->writeWord(0x80000184u, 0x00305073)->run();)
|
||||
}
|
||||
#endif
|
||||
|
||||
//return 0;
|
||||
|
||||
//#ifdef LITEX
|
||||
|
@ -4365,6 +4361,17 @@ int main(int argc, char **argv, char **env) {
|
|||
redo(REDO,WorkspaceRegression("amo").withRiscvRef()->loadHex(string(REGRESSION_PATH) + "../raw/amo/build/amo.hex")->bootAt(0x00000000u)->run(10e3););
|
||||
#endif
|
||||
|
||||
#ifdef RVF
|
||||
for(const string &name : riscvTestFloat){
|
||||
redo(REDO,RiscvTest(name).withRiscvRef()->bootAt(0x80000188u)->writeWord(0x80000184u, 0x00305073)->run();)
|
||||
}
|
||||
#endif
|
||||
#ifdef RVD
|
||||
for(const string &name : riscvTestDouble){
|
||||
redo(REDO,RiscvTest(name).withRiscvRef()->bootAt(0x80000188u)->writeWord(0x80000184u, 0x00305073)->run();)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DHRYSTONE
|
||||
Dhrystone("dhrystoneO3_Stall","dhrystoneO3",true,true).run(1.5e6);
|
||||
#if defined(COMPRESSED)
|
||||
|
|
|
@ -30,6 +30,7 @@ AMO?=no
|
|||
NO_STALL?=no
|
||||
DEBUG_PLUGIN?=STD
|
||||
DEBUG_PLUGIN_EXTERNAL?=no
|
||||
RISCV_JTAG?=no
|
||||
RUN_HEX=no
|
||||
WITH_RISCV_REF=yes
|
||||
CUSTOM_SIMD_ADD?=no
|
||||
|
@ -302,6 +303,12 @@ ifeq ($(DEBUG_PLUGIN_EXTERNAL),yes)
|
|||
ADDCFLAGS += -CFLAGS -DDEBUG_PLUGIN_EXTERNAL
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(RISCV_JTAG),yes)
|
||||
ADDCFLAGS += -CFLAGS -DRISCV_JTAG
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(REF),yes)
|
||||
ADDCFLAGS += -CFLAGS -DREF
|
||||
endif
|
||||
|
|
Loading…
Reference in New Issue