1
0

debug: Breaking change until FESVR is updated as well.

* Replace v11 Debug Module with v13 module.
* Correct all instantiating interfaces.
* Rename "Debug Bus" to "DMI" (Debug
  Module Interface)
* Use Diplomacy interrupts for DebugInterrupt
* Seperate device for TLDebugROM
This commit is contained in:
Megan Wachs
2017-03-27 21:19:08 -07:00
parent 43804726ac
commit 42ca597478
10 changed files with 1477 additions and 1019 deletions

View File

@ -36,6 +36,7 @@ class BasePlatformConfig extends Config((site, here, up) => {
case PeripheryBusArithmetic => true
// Note that PLIC asserts that this is > 0.
case IncludeJtagDTM => false
case JtagDTMKey => new JtagDTMKeyDefault()
case ZeroConfig => ZeroConfig(base=0xa000000L, size=0x2000000L, beatBytes=8)
case ExtMem => MasterConfig(base=0x80000000L, size=0x10000000L, beatBytes=8, idBits=4)
case ExtBus => MasterConfig(base=0x60000000L, size=0x20000000L, beatBytes=8, idBits=4)

View File

@ -3,78 +3,227 @@
package rocketchip
import Chisel._
import uncore.devices._
import junctions._
import util._
import config._
import jtag._
import uncore.devices.{DMIConsts, DMIIO, DMIReq, DMIResp}
case object IncludeJtagDTM extends Field[Boolean]
/* JTAG-based Debug Transport Module
* and synchronization logic.
*
* This implements JTAG interface described
* in the RISC-V Debug Specification
*
* This Module is currently a
* wrapper around a JTAG implementation
* of the Debug Transport Module.
* This is black-boxed because
* Chisel doesn't currently support:
* - Negative Edge Clocking
* - Asynchronous Resets
* (The tristate requirements of JTAG are exported from the
* Chisel domain with the DRV_TDO signal).
*
* The 'TRST' input is used to asynchronously
* reset the Debug Transport Module and the
* DTM side of the synchronizer.
* This design requires that TRST be
* synchronized to TCK (for de-assert) outside
* of this module. Your top level code should ensure
* that TRST is asserted before the rocket-chip core
* comes out of reset.
* Note that TRST is an optional
* part of the JTAG protocol, but it is not
* optional for interfacing with this logic.
*
*/
case class JtagDTMConfig (
idcodeVersion : Int, // chosen by manuf.
idcodePartNum : Int, // Chosen by manuf.
idcodeManufId : Int, // Assigned by JEDEC
debugIdleCycles : Int)
class JtagDTMWithSync(implicit val p: Parameters) extends Module {
// io.DebugBusIO <-> Sync <-> DebugBusIO <-> UInt <-> DTM Black Box
case object JtagDTMKey extends Field[JtagDTMConfig]
val io = new Bundle {
val jtag = new JTAGIO(true).flip
val debug = new AsyncDebugBusIO
}
class JtagDTMKeyDefault extends JtagDTMConfig(
idcodeVersion = 0,
idcodePartNum = 0,
idcodeManufId = 0,
debugIdleCycles = 5) // Reasonable guess for synchronization.
val req_width = io.debug.req.mem(0).getWidth
val resp_width = io.debug.resp.mem(0).getWidth
val jtag_dtm = Module(new DebugTransportModuleJtag(req_width, resp_width))
jtag_dtm.io.jtag <> io.jtag
val io_debug_bus = Wire (new DebugBusIO)
io.debug <> ToAsyncDebugBus(io_debug_bus)
val dtm_req = jtag_dtm.io.dtm_req
val dtm_resp = jtag_dtm.io.dtm_resp
// Translate from straight 'bits' interface of the blackboxes
// into the Resp/Req data structures.
io_debug_bus.req.valid := dtm_req.valid
io_debug_bus.req.bits := new DebugBusReq(p(DMKey).nDebugBusAddrSize).fromBits(dtm_req.bits)
dtm_req.ready := io_debug_bus.req.ready
dtm_resp.valid := io_debug_bus.resp.valid
dtm_resp.bits := io_debug_bus.resp.bits.asUInt
io_debug_bus.resp.ready := dtm_resp.ready
object dtmJTAGAddrs {
def IDCODE = 0x1
def DTM_INFO = 0x10
def DMI_ACCESS = 0x11
}
class DebugTransportModuleJtag(reqSize : Int, respSize : Int)(implicit val p: Parameters) extends BlackBox {
val io = new Bundle {
val jtag = new JTAGIO(true).flip()
val dtm_req = new DecoupledIO(UInt(width = reqSize))
val dtm_resp = new DecoupledIO(UInt(width = respSize)).flip()
}
class DMIAccessUpdate(addrBits: Int) extends Bundle {
val addr = UInt(width = addrBits)
val data = UInt(width = DMIConsts.dmiDataSize)
val op = UInt(width = DMIConsts.dmiOpSize)
override def cloneType = new DMIAccessUpdate(addrBits).asInstanceOf[this.type]
}
class DMIAccessCapture(addrBits: Int) extends Bundle {
val addr = UInt(width = addrBits)
val data = UInt(width = DMIConsts.dmiDataSize)
val resp = UInt(width = DMIConsts.dmiRespSize)
override def cloneType = new DMIAccessCapture(addrBits).asInstanceOf[this.type]
}
class DTMInfo extends Bundle {
val reserved1 = UInt(15.W)
val dmireset = Bool()
val reserved0 = UInt(1.W)
val dmiIdleCycles = UInt(3.W)
val dmiStatus = UInt(2.W)
val debugAddrBits = UInt(6.W)
val debugVersion = UInt(4.W)
}
class DebugTransportModuleJTAG(debugAddrBits: Int, c: JtagDTMConfig)
(implicit val p: Parameters) extends Module {
val io = new Bundle {
val dmi = new DMIIO()(p)
val jtag = Flipped(new JTAGIO(hasTRSTn = false))
val jtag_reset = Bool(INPUT)
val fsmReset = Bool(OUTPUT)
}
//--------------------------------------------------------
// Reg and Wire Declarations
val dtmInfo = Wire(new DTMInfo)
val busyReg = RegInit(Bool(false))
val stickyBusyReg = RegInit(Bool(false))
val stickyNonzeroRespReg = RegInit(Bool(false))
val skipOpReg = Reg(init = Bool(false)) // Skip op because we're busy
val downgradeOpReg = Reg(init = Bool(false)) // downgrade op because prev. failed.
val busy = Wire(Bool())
val nonzeroResp = Wire(Bool())
val busyResp = Wire(new DMIAccessCapture(debugAddrBits))
val nonbusyResp = Wire(new DMIAccessCapture(debugAddrBits))
val dmiReqReg = Reg(new DMIReq(debugAddrBits))
val dmiReqValidReg = Reg(init = Bool(false));
val dmiStatus = Wire(UInt(width = 2))
//--------------------------------------------------------
// DTM Info Chain Declaration
dmiStatus := Cat(stickyNonzeroRespReg, stickyNonzeroRespReg | stickyBusyReg)
dtmInfo.debugVersion := 1.U // This implements version 1 of the spec.
dtmInfo.debugAddrBits := UInt(debugAddrBits)
dtmInfo.dmiStatus := dmiStatus
dtmInfo.dmiIdleCycles := UInt(c.debugIdleCycles)
dtmInfo.reserved0 := 0.U
dtmInfo.dmireset := false.B // This is write-only
dtmInfo.reserved1 := 0.U
val dtmInfoChain = Module (CaptureUpdateChain(gen = new DTMInfo()))
dtmInfoChain.io.capture.bits := dtmInfo
//--------------------------------------------------------
// Debug Access Chain Declaration
val dmiAccessChain = Module(CaptureUpdateChain(genCapture = new DMIAccessCapture(debugAddrBits),
genUpdate = new DMIAccessUpdate(debugAddrBits)))
//--------------------------------------------------------
// Debug Access Support
// Busy Register. We become busy when we first try to send a request.
// We stop being busy when we accept a response.
when (io.dmi.req.valid) {
busyReg := Bool(true)
}
when (io.dmi.resp.fire()) {
busyReg := Bool(false)
}
// We are busy during a given CAPTURE
// if we haven't received a valid response yet or if we
// were busy last time without a reset.
// busyReg will still be set when we check it,
// so the logic for checking busy looks ahead.
busy := (busyReg & !io.dmi.resp.valid) | stickyBusyReg;
// Downgrade/Skip. We make the decision to downgrade or skip
// during every CAPTURE_DR, and use the result in UPDATE_DR.
// The sticky versions are reset by write to dmiReset in DTM_INFO.
when (dmiAccessChain.io.update.valid) {
skipOpReg := Bool(false)
downgradeOpReg := Bool(false)
}
when (dmiAccessChain.io.capture.capture) {
skipOpReg := busy
downgradeOpReg := (!busy & nonzeroResp)
stickyBusyReg := busy
stickyNonzeroRespReg := nonzeroResp
}
when (dtmInfoChain.io.update.valid) {
when (dtmInfoChain.io.update.bits.dmireset) {
stickyNonzeroRespReg := Bool(false)
stickyBusyReg := Bool(false)
}
}
// Especially for the first request, we must consider dtmResp.valid,
// so that we don't consider junk in the FIFO to be an error response.
// The current specification says that any non-zero response is an error.
nonzeroResp := stickyNonzeroRespReg | (io.dmi.resp.valid & (io.dmi.resp.bits.resp != UInt(0)))
busyResp.addr := UInt(0)
busyResp.resp := UInt(0)
busyResp.data := UInt(0)
nonbusyResp.addr := dmiReqReg.addr
nonbusyResp.resp := io.dmi.resp.bits.resp
nonbusyResp.data := io.dmi.resp.bits.data
//--------------------------------------------------------
// Debug Access Chain Implementation
dmiAccessChain.io.capture.bits := Mux(busy, busyResp, nonbusyResp)
when (dmiAccessChain.io.update.valid) {
skipOpReg := Bool(false)
downgradeOpReg := Bool(false)
}
when (dmiAccessChain.io.capture.capture) {
skipOpReg := busy
downgradeOpReg := (!busy & nonzeroResp)
stickyBusyReg := busy
stickyNonzeroRespReg := nonzeroResp
}
//--------------------------------------------------------
// Drive Ready Valid Interface
when (dmiAccessChain.io.update.valid) {
when (skipOpReg) {
// Do Nothing
}.otherwise {
when (downgradeOpReg) {
dmiReqReg.addr := UInt(0)
dmiReqReg.data := UInt(0)
dmiReqReg.op := UInt(0)
}.otherwise {
dmiReqReg := dmiAccessChain.io.update.bits
}
dmiReqValidReg := Bool(true)
}
}.otherwise {
when (io.dmi.req.ready) {
dmiReqValidReg := Bool(false)
}
}
io.dmi.resp.ready := dmiAccessChain.io.capture.capture
io.dmi.req.valid := dmiReqValidReg
// This is a name-based, not type-based assignment. Do these still work?
io.dmi.req.bits := dmiReqReg
//--------------------------------------------------------
// Actual JTAG TAP
val tapIO = JtagTapGenerator(irLength = 5,
instructions = Map(dtmJTAGAddrs.DMI_ACCESS -> dmiAccessChain,
dtmJTAGAddrs.DTM_INFO -> dtmInfoChain),
idcode = Some((dtmJTAGAddrs.IDCODE, JtagIdcode(c.idcodeVersion, c.idcodePartNum, c.idcodeManufId))))
tapIO.jtag <> io.jtag
tapIO.control.jtag_reset := io.jtag_reset
//--------------------------------------------------------
// Reset Generation (this is fed back to us by the instantiating module,
// and is used to reset the debug registers).
io.fsmReset := tapIO.output.reset
}

View File

@ -8,55 +8,64 @@ import diplomacy._
import uncore.tilelink2._
import uncore.devices._
import util._
import junctions.JTAGIO
import jtag.JTAGIO
import coreplex._
/// Core with JTAG for debug only
// System with JTAG DTM Instantiated inside. JTAG interface is
// exported outside.
trait PeripheryJTAG extends HasTopLevelNetworks {
val module: PeripheryJTAGModule
trait PeripheryJTAGDTM extends HasTopLevelNetworks {
val module: PeripheryJTAGDTMModule
val coreplex: CoreplexRISCVPlatform
}
trait PeripheryJTAGBundle extends HasTopLevelNetworksBundle {
val outer: PeripheryJTAG
trait PeripheryJTAGDTMBundle extends HasTopLevelNetworksBundle {
val outer: PeripheryJTAGDTM
val jtag = new JTAGIO(hasTRSTn = false).flip
val jtag_reset = Bool(INPUT)
val jtag = new JTAGIO(true).flip
}
trait PeripheryJTAGModule extends HasTopLevelNetworksModule {
val outer: PeripheryJTAG
val io: PeripheryJTAGBundle
trait PeripheryJTAGDTMModule extends HasTopLevelNetworksModule {
val outer: PeripheryJTAGDTM
val io: PeripheryJTAGDTMBundle
val dtm = Module (new JtagDTMWithSync)
val dtm = Module (new DebugTransportModuleJTAG(p(DMKey).nDMIAddrSize, p(JtagDTMKey)))
dtm.io.jtag <> io.jtag
outer.coreplex.module.io.debug <> dtm.io.debug
dtm.clock := io.jtag.TCK
dtm.io.jtag_reset := io.jtag_reset
dtm.reset := dtm.io.fsmReset
outer.coreplex.module.io.debug.dmi <> dtm.io.dmi
outer.coreplex.module.io.debug.dmiClock := io.jtag.TCK
outer.coreplex.module.io.debug.dmiReset := ResetCatchAndSync(io.jtag.TCK, io.jtag_reset, "dmiResetCatch")
dtm.clock := io.jtag.TCK
dtm.reset := io.jtag.TRST
}
/// Core with DTM for debug only
// System with Debug Module Interface Only. Any sort of DTM
// can be connected outside. DMI Clock and Reset must be provided.
trait PeripheryDTM extends HasTopLevelNetworks {
val module: PeripheryDTMModule
trait PeripheryDMI extends HasTopLevelNetworks {
val module: PeripheryDMIModule
val coreplex: CoreplexRISCVPlatform
}
trait PeripheryDTMBundle extends HasTopLevelNetworksBundle {
val outer: PeripheryDTM
trait PeripheryDMIBundle extends HasTopLevelNetworksBundle {
val outer: PeripheryDMI
val debug = new DebugBusIO().flip
val debug = new ClockedDMIIO().flip
}
trait PeripheryDTMModule extends HasTopLevelNetworksModule {
val outer: PeripheryDTM
val io: PeripheryDTMBundle
trait PeripheryDMIModule extends HasTopLevelNetworksModule {
val outer: PeripheryDMI
val io: PeripheryDMIBundle
outer.coreplex.module.io.debug <> ToAsyncDebugBus(io.debug)
outer.coreplex.module.io.debug <> io.debug
}
/// Core with DTM or JTAG based on a parameter
// System with DMI or JTAG interface based on a parameter
trait PeripheryDebug extends HasTopLevelNetworks {
val module: PeripheryDebugModule
@ -66,21 +75,30 @@ trait PeripheryDebug extends HasTopLevelNetworks {
trait PeripheryDebugBundle extends HasTopLevelNetworksBundle {
val outer: PeripheryDebug
val debug = (!p(IncludeJtagDTM)).option(new DebugBusIO().flip)
val jtag = (p(IncludeJtagDTM)).option(new JTAGIO(true).flip)
val debug = (!p(IncludeJtagDTM)).option(new ClockedDMIIO().flip)
val jtag = (p(IncludeJtagDTM)).option(new JTAGIO(hasTRSTn = false).flip)
val jtag_reset = (p(IncludeJtagDTM)).option(Bool(INPUT))
}
trait PeripheryDebugModule extends HasTopLevelNetworksModule {
val outer: PeripheryDebug
val io: PeripheryDebugBundle
io.debug.foreach { dbg => outer.coreplex.module.io.debug <> ToAsyncDebugBus(dbg) }
io.jtag.foreach { jtag =>
val dtm = Module (new JtagDTMWithSync)
dtm.clock := jtag.TCK
dtm.reset := jtag.TRST
dtm.io.jtag <> jtag
outer.coreplex.module.io.debug <> dtm.io.debug
io.debug.foreach { dbg => outer.coreplex.module.io.debug <> dbg }
val dtm = if (io.jtag.isDefined) Some[DebugTransportModuleJTAG](Module (new DebugTransportModuleJTAG(p(DMKey).nDMIAddrSize, p(JtagDTMKey)))) else None
dtm.foreach { dtm =>
dtm.io.jtag <> io.jtag.get
dtm.clock := io.jtag.get.TCK
dtm.io.jtag_reset := io.jtag_reset.get
dtm.reset := dtm.io.fsmReset
outer.coreplex.module.io.debug.dmi <> dtm.io.dmi
outer.coreplex.module.io.debug.dmiClock := io.jtag.get.TCK
outer.coreplex.module.io.debug.dmiReset := ResetCatchAndSync(io.jtag.get.TCK, io.jtag_reset.get, "dmiResetCatch")
}
}

View File

@ -8,6 +8,7 @@ import junctions._
import diplomacy._
import coreplex._
import uncore.axi4._
import jtag.JTAGIO
class TestHarness()(implicit p: Parameters) extends Module {
val io = new Bundle {
@ -23,7 +24,7 @@ class TestHarness()(implicit p: Parameters) extends Module {
if (!p(IncludeJtagDTM)) {
val dtm = Module(new SimDTM).connect(clock, reset, dut.io.debug.get, io.success)
} else {
val jtag = Module(new JTAGVPI).connect(dut.io.jtag.get, reset, io.success)
val jtag = Module(new JTAGVPI).connect(dut.io.jtag.get, dut.io.jtag_reset.get, reset, io.success)
}
val mmio_sim = Module(LazyModule(new SimAXIMem(1, 4096)).module)
@ -62,14 +63,16 @@ class SimDTM(implicit p: Parameters) extends BlackBox {
val io = new Bundle {
val clk = Clock(INPUT)
val reset = Bool(INPUT)
val debug = new uncore.devices.DebugBusIO
val debug = new uncore.devices.DMIIO
val exit = UInt(OUTPUT, 32)
}
def connect(tbclk: Clock, tbreset: Bool, dutio: uncore.devices.DebugBusIO, tbsuccess: Bool) = {
def connect(tbclk: Clock, tbreset: Bool, dutio: uncore.devices.ClockedDMIIO, tbsuccess: Bool) = {
io.clk := tbclk
io.reset := tbreset
dutio <> io.debug
dutio.dmiClock := tbclk
dutio.dmiReset := tbreset
tbsuccess := io.exit === UInt(1)
when (io.exit >= UInt(2)) {
@ -81,23 +84,18 @@ class SimDTM(implicit p: Parameters) extends BlackBox {
class JTAGVPI(implicit val p: Parameters) extends BlackBox {
val io = new Bundle {
val jtag = new JTAGIO(false)
val jtag = new JTAGIO(hasTRSTn = false)
val enable = Bool(INPUT)
val init_done = Bool(INPUT)
}
def connect(dutio: JTAGIO, tbreset: Bool, tbsuccess: Bool) = {
def connect(dutio: JTAGIO, jtag_reset: Bool, tbreset: Bool, tbsuccess: Bool) = {
dutio <> io.jtag
// To be proper,
// TRST should really be synchronized
// with TCK. But this is a fairly
// accurate representation of how
// HW may drive this signal.
// Neither OpenOCD nor JtagVPI drive TRST.
dutio.TRSTn.foreach{ _:= false.B}
jtag_reset := tbreset
dutio.TRST := tbreset
io.enable := ~tbreset
io.enable := ~tbreset
io.init_done := ~tbreset
// Success is determined by the gdbserver