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:
parent
43804726ac
commit
42ca597478
@ -22,7 +22,7 @@ class BaseCoreplexConfig extends Config ((site, here, up) => {
|
||||
case BuildCore => (p: Parameters) => new Rocket()(p)
|
||||
case RocketCrossing => Synchronous
|
||||
case RocketTilesKey => Nil
|
||||
case DMKey => new DefaultDebugModuleConfig(site(NTiles), site(XLen))
|
||||
case DMKey => new DefaultDebugModuleConfig(site(XLen))
|
||||
case NTiles => site(RocketTilesKey).size
|
||||
case CBusConfig => TLBusConfig(beatBytes = site(XLen)/8)
|
||||
case L1toL2Config => TLBusConfig(beatBytes = site(XLen)/8) // increase for more PCIe bandwidth
|
||||
|
@ -15,10 +15,12 @@ trait CoreplexRISCVPlatform extends CoreplexNetwork {
|
||||
val module: CoreplexRISCVPlatformModule
|
||||
|
||||
val debug = LazyModule(new TLDebugModule())
|
||||
val debug_rom = LazyModule(new TLDebugModuleROM())
|
||||
val plic = LazyModule(new TLPLIC(maxPriorities = 7))
|
||||
val clint = LazyModule(new CoreplexLocalInterrupter)
|
||||
|
||||
debug.node := TLFragmenter(cbus_beatBytes, cbus_lineBytes)(cbus.node)
|
||||
debug_rom.node := TLFragmenter(cbus_beatBytes, cbus_lineBytes)(cbus.node)
|
||||
plic.node := TLFragmenter(cbus_beatBytes, cbus_lineBytes)(cbus.node)
|
||||
clint.node := TLFragmenter(cbus_beatBytes, cbus_lineBytes)(cbus.node)
|
||||
|
||||
@ -32,7 +34,7 @@ trait CoreplexRISCVPlatform extends CoreplexNetwork {
|
||||
trait CoreplexRISCVPlatformBundle extends CoreplexNetworkBundle {
|
||||
val outer: CoreplexRISCVPlatform
|
||||
|
||||
val debug = new AsyncDebugBusIO().flip
|
||||
val debug = new ClockedDMIIO().flip
|
||||
val rtcToggle = Bool(INPUT)
|
||||
val resetVector = UInt(INPUT, p(XLen))
|
||||
}
|
||||
@ -41,8 +43,14 @@ trait CoreplexRISCVPlatformModule extends CoreplexNetworkModule {
|
||||
val outer: CoreplexRISCVPlatform
|
||||
val io: CoreplexRISCVPlatformBundle
|
||||
|
||||
// Synchronize the debug bus into the coreplex
|
||||
outer.debug.module.io.db <> FromAsyncDebugBus(io.debug)
|
||||
outer.debug.module.io.dmi <> io.debug
|
||||
// TODO in inheriting traits: Set this to something meaningful, e.g. "component is in reset or powered down"
|
||||
val nDebugComponents = outer.debug.intnode.bundleOut.size
|
||||
outer.debug.module.io.ctrl.debugUnavail := Vec.fill(nDebugComponents){Bool(false)}
|
||||
// TODO in inheriting traits: Use these values in your power and reset controls.
|
||||
// TODO Or move these signals to Coreplex Top Level
|
||||
// ... := outer.debug.module.io.ctrl.dmactive
|
||||
// ... := outer.debug.module.io.ctrl.ndreset
|
||||
|
||||
// Synchronize the rtc into the coreplex
|
||||
val rtcSync = ShiftRegister(io.rtcToggle, 3)
|
||||
|
@ -33,11 +33,8 @@ trait HasRocketTiles extends CoreplexRISCVPlatform {
|
||||
case SharedMemoryTLEdge => l1tol2.node.edgesIn(0)
|
||||
}
|
||||
|
||||
// Hack debug interrupt into a node (future debug module should use diplomacy)
|
||||
val debugNode = IntInternalInputNode(IntSourcePortSimple())
|
||||
|
||||
val intBar = LazyModule(new IntXbar)
|
||||
intBar.intnode := debugNode
|
||||
intBar.intnode := debug.intnode // Debug Interrupt
|
||||
intBar.intnode := clint.intnode // msip+mtip
|
||||
intBar.intnode := plic.intnode // meip
|
||||
if (c.core.useVM) intBar.intnode := plic.intnode // seip
|
||||
@ -56,7 +53,6 @@ trait HasRocketTiles extends CoreplexRISCVPlatform {
|
||||
// leave clock as default (simpler for hierarchical PnR)
|
||||
wrapper.module.io.hartid := UInt(i)
|
||||
wrapper.module.io.resetVector := io.resetVector
|
||||
debugNode.bundleOut(0)(0) := debug.module.io.debugInterrupts(i)
|
||||
}
|
||||
}
|
||||
case Asynchronous(depth, sync) => {
|
||||
@ -75,7 +71,6 @@ trait HasRocketTiles extends CoreplexRISCVPlatform {
|
||||
wrapper.module.reset := io.tcrs(i).reset
|
||||
wrapper.module.io.hartid := UInt(i)
|
||||
wrapper.module.io.resetVector := io.resetVector
|
||||
debugNode.bundleOut(0)(0) := debug.module.io.debugInterrupts(i)
|
||||
}
|
||||
}
|
||||
case Rational => {
|
||||
@ -94,7 +89,6 @@ trait HasRocketTiles extends CoreplexRISCVPlatform {
|
||||
wrapper.module.reset := io.tcrs(i).reset
|
||||
wrapper.module.io.hartid := UInt(i)
|
||||
wrapper.module.io.resetVector := io.resetVector
|
||||
debugNode.bundleOut(0)(0) := debug.module.io.debugInterrupts(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -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.reset := io.jtag.TRST
|
||||
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")
|
||||
|
||||
}
|
||||
|
||||
/// 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")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,22 +84,17 @@ 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.init_done := ~tbreset
|
||||
|
||||
|
@ -1,887 +0,0 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package uncore.devices
|
||||
|
||||
import Chisel._
|
||||
import junctions._
|
||||
import util._
|
||||
import regmapper._
|
||||
import tile.XLen
|
||||
import uncore.tilelink2._
|
||||
import config._
|
||||
|
||||
// *****************************************
|
||||
// Constants which are interesting even
|
||||
// outside of this module
|
||||
// *****************************************
|
||||
|
||||
object DbRegAddrs{
|
||||
|
||||
def DMRAMBASE = UInt(0x0)
|
||||
def DMCONTROL = UInt(0x10)
|
||||
|
||||
def DMINFO = UInt(0x11)
|
||||
def AUTHDATA0 = UInt(0x12)
|
||||
def AUTHDATA1 = UInt(0x13)
|
||||
def SERDATA = UInt(0x14)
|
||||
def SERSTATUS = UInt(0x15)
|
||||
def SBUSADDRESS0 = UInt(0x16)
|
||||
def SBUSADDRESS1 = UInt(0x17)
|
||||
def SBDATA0 = UInt(0x18)
|
||||
def SBDATA1 = UInt(0x19)
|
||||
//1a
|
||||
def HALTSUM = UInt(0x1B)
|
||||
//1c - 3b are the halt notification registers.
|
||||
def SBADDRESS2 = UInt(0x3d)
|
||||
// 3c
|
||||
def SBDATA2 = UInt(0x3e)
|
||||
def SBDATA3 = UInt(0x3f)
|
||||
}
|
||||
|
||||
/** Constant values used by both Debug Bus Response & Request
|
||||
*/
|
||||
|
||||
object DbBusConsts{
|
||||
|
||||
def dbDataSize = 34
|
||||
|
||||
def dbRamWordBits = 32
|
||||
|
||||
def dbOpSize = 2
|
||||
def db_OP_NONE = UInt("b00")
|
||||
def db_OP_READ = UInt("b01")
|
||||
def db_OP_READ_WRITE = UInt("b10")
|
||||
def db_OP_READ_COND_WRITE = UInt("b11")
|
||||
|
||||
def dbRespSize = 2
|
||||
def db_RESP_SUCCESS = UInt("b00")
|
||||
def db_RESP_FAILURE = UInt("b01")
|
||||
def db_RESP_HW_FAILURE = UInt("b10")
|
||||
// This is used outside this block
|
||||
// to indicate 'busy'.
|
||||
def db_RESP_RESERVED = UInt("b11")
|
||||
|
||||
}
|
||||
|
||||
object DsbBusConsts {
|
||||
|
||||
def sbAddrWidth = 12
|
||||
def sbIdWidth = 10
|
||||
|
||||
//These are the default ROM contents, which support RV32 and RV64.
|
||||
// See $RISCV/riscv-tools/riscv-isa-sim/debug_rom/debug_rom.h/S
|
||||
// The code assumes 64 bytes of Debug RAM.
|
||||
|
||||
def xlenAnyRomContents : Array[Byte] = Array(
|
||||
0x6f, 0x00, 0xc0, 0x04, 0x6f, 0x00, 0xc0, 0x00, 0x13, 0x04, 0xf0, 0xff,
|
||||
0x6f, 0x00, 0x80, 0x00, 0x13, 0x04, 0x00, 0x00, 0x0f, 0x00, 0xf0, 0x0f,
|
||||
0xf3, 0x24, 0x00, 0xf1, 0x63, 0xc6, 0x04, 0x00, 0x83, 0x24, 0xc0, 0x43,
|
||||
0x6f, 0x00, 0x80, 0x00, 0x83, 0x34, 0x80, 0x43, 0x23, 0x2e, 0x80, 0x42,
|
||||
0x73, 0x24, 0x40, 0xf1, 0x23, 0x20, 0x80, 0x10, 0x73, 0x24, 0x00, 0x7b,
|
||||
0x13, 0x74, 0x84, 0x00, 0x63, 0x12, 0x04, 0x04, 0x73, 0x24, 0x20, 0x7b,
|
||||
0x73, 0x00, 0x20, 0x7b, 0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x00, 0x7b,
|
||||
0x13, 0x74, 0x04, 0x1c, 0x13, 0x04, 0x04, 0xf4, 0x63, 0x1e, 0x04, 0x00,
|
||||
0x73, 0x24, 0x00, 0xf1, 0x63, 0x46, 0x04, 0x00, 0x23, 0x2e, 0x90, 0x42,
|
||||
0x67, 0x00, 0x00, 0x40, 0x23, 0x3c, 0x90, 0x42, 0x67, 0x00, 0x00, 0x40,
|
||||
0x73, 0x24, 0x40, 0xf1, 0x23, 0x26, 0x80, 0x10, 0x73, 0x60, 0x04, 0x7b,
|
||||
0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x02, 0xe3, 0x0c, 0x04, 0xfe,
|
||||
0x6f, 0xf0, 0x1f, 0xfd).map(_.toByte)
|
||||
|
||||
// These ROM contents support only RV32
|
||||
// See $RISCV/riscv-tools/riscv-isa-sim/debug_rom/debug_rom.h/S
|
||||
// The code assumes only 28 bytes of Debug RAM.
|
||||
|
||||
def xlen32OnlyRomContents : Array[Byte] = Array(
|
||||
0x6f, 0x00, 0xc0, 0x03, 0x6f, 0x00, 0xc0, 0x00, 0x13, 0x04, 0xf0, 0xff,
|
||||
0x6f, 0x00, 0x80, 0x00, 0x13, 0x04, 0x00, 0x00, 0x0f, 0x00, 0xf0, 0x0f,
|
||||
0x83, 0x24, 0x80, 0x41, 0x23, 0x2c, 0x80, 0x40, 0x73, 0x24, 0x40, 0xf1,
|
||||
0x23, 0x20, 0x80, 0x10, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x84, 0x00,
|
||||
0x63, 0x1a, 0x04, 0x02, 0x73, 0x24, 0x20, 0x7b, 0x73, 0x00, 0x20, 0x7b,
|
||||
0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x1c,
|
||||
0x13, 0x04, 0x04, 0xf4, 0x63, 0x16, 0x04, 0x00, 0x23, 0x2c, 0x90, 0x40,
|
||||
0x67, 0x00, 0x00, 0x40, 0x73, 0x24, 0x40, 0xf1, 0x23, 0x26, 0x80, 0x10,
|
||||
0x73, 0x60, 0x04, 0x7b, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x02,
|
||||
0xe3, 0x0c, 0x04, 0xfe, 0x6f, 0xf0, 0x1f, 0xfe).map(_.toByte)
|
||||
|
||||
// These ROM contents support only RV64
|
||||
// See $RISCV/riscv-tools/riscv-isa-sim/debug_rom/debug_rom.h/S
|
||||
// The code assumes 64 bytes of Debug RAM.
|
||||
|
||||
def xlen64OnlyRomContents : Array[Byte] = Array(
|
||||
0x6f, 0x00, 0xc0, 0x03, 0x6f, 0x00, 0xc0, 0x00, 0x13, 0x04, 0xf0, 0xff,
|
||||
0x6f, 0x00, 0x80, 0x00, 0x13, 0x04, 0x00, 0x00, 0x0f, 0x00, 0xf0, 0x0f,
|
||||
0x83, 0x34, 0x80, 0x43, 0x23, 0x2e, 0x80, 0x42, 0x73, 0x24, 0x40, 0xf1,
|
||||
0x23, 0x20, 0x80, 0x10, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x84, 0x00,
|
||||
0x63, 0x1a, 0x04, 0x02, 0x73, 0x24, 0x20, 0x7b, 0x73, 0x00, 0x20, 0x7b,
|
||||
0x73, 0x10, 0x24, 0x7b, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x1c,
|
||||
0x13, 0x04, 0x04, 0xf4, 0x63, 0x16, 0x04, 0x00, 0x23, 0x3c, 0x90, 0x42,
|
||||
0x67, 0x00, 0x00, 0x40, 0x73, 0x24, 0x40, 0xf1, 0x23, 0x26, 0x80, 0x10,
|
||||
0x73, 0x60, 0x04, 0x7b, 0x73, 0x24, 0x00, 0x7b, 0x13, 0x74, 0x04, 0x02,
|
||||
0xe3, 0x0c, 0x04, 0xfe, 0x6f, 0xf0, 0x1f, 0xfe).map(_.toByte)
|
||||
}
|
||||
|
||||
|
||||
|
||||
object DsbRegAddrs{
|
||||
|
||||
def CLEARDEBINT = 0x100
|
||||
def SETHALTNOT = 0x10C
|
||||
def SERINFO = 0x110
|
||||
def SERBASE = 0x114
|
||||
// For each serial, there are
|
||||
// 3 registers starting here:
|
||||
// SERSEND0
|
||||
// SERRECEIVE0
|
||||
// SERSTATUS0
|
||||
// ...
|
||||
// SERSTATUS7
|
||||
def SERTX_OFFSET = 0
|
||||
def SERRX_OFFSET = 4
|
||||
def SERSTAT_OFFSET = 8
|
||||
def RAMBASE = 0x400
|
||||
def ROMBASE = 0x800
|
||||
|
||||
}
|
||||
|
||||
|
||||
// *****************************************
|
||||
// Configuration & Parameters for this Module
|
||||
//
|
||||
// *****************************************
|
||||
|
||||
/** Enumerations used both in the hardware
|
||||
* and in the configuration specification.
|
||||
*/
|
||||
|
||||
object DebugModuleAuthType extends scala.Enumeration {
|
||||
type DebugModuleAuthType = Value
|
||||
val None, Password, ChallengeResponse, Reserved = Value
|
||||
}
|
||||
import DebugModuleAuthType._
|
||||
|
||||
object DebugModuleAccessType extends scala.Enumeration {
|
||||
type DebugModuleAccessType = Value
|
||||
val Access8Bit, Access16Bit, Access32Bit, Access64Bit, Access128Bit = Value
|
||||
}
|
||||
import DebugModuleAccessType._
|
||||
|
||||
|
||||
/** Parameters exposed to the top-level design, set based on
|
||||
* external requirements, etc.
|
||||
*
|
||||
* This object checks that the parameters conform to the
|
||||
* full specification. The implementation which receives this
|
||||
* object can perform more checks on what that implementation
|
||||
* actually supports.
|
||||
* nComponents : The number of components to support debugging.
|
||||
* nDebugBusAddrSize : Size of the Debug Bus Address
|
||||
* nDebugRam Bytes: Size of the Debug RAM (depends on the XLEN of the machine).
|
||||
* debugRomContents: Optional Sequence of bytes which form the Debug ROM contents.
|
||||
* hasBusMaster: Whether or not a bus master should be included
|
||||
* The size of the accesses supported by the Bus Master.
|
||||
* nSerialPorts : Number of serial ports to instantiate
|
||||
* authType : The Authorization Type
|
||||
* Number of cycles to assert ndreset when pulsed.
|
||||
**/
|
||||
|
||||
|
||||
case class DebugModuleConfig (
|
||||
nComponents : Int,
|
||||
nDebugBusAddrSize : Int,
|
||||
nDebugRamBytes : Int,
|
||||
debugRomContents : Option[Seq[Byte]],
|
||||
hasBusMaster : Boolean,
|
||||
hasAccess128 : Boolean,
|
||||
hasAccess64 : Boolean,
|
||||
hasAccess32 : Boolean,
|
||||
hasAccess16 : Boolean,
|
||||
hasAccess8 : Boolean,
|
||||
nSerialPorts : Int,
|
||||
authType : DebugModuleAuthType,
|
||||
nNDResetCycles : Int
|
||||
) {
|
||||
|
||||
if (hasBusMaster == false){
|
||||
require (hasAccess128 == false)
|
||||
require (hasAccess64 == false)
|
||||
require (hasAccess32 == false)
|
||||
require (hasAccess16 == false)
|
||||
require (hasAccess8 == false)
|
||||
}
|
||||
|
||||
require (nSerialPorts <= 8)
|
||||
|
||||
require ((nDebugBusAddrSize >= 5) && (nDebugBusAddrSize <= 7))
|
||||
|
||||
private val maxComponents = nDebugBusAddrSize match {
|
||||
case 5 => (32*4)
|
||||
case 6 => (32*32)
|
||||
case 7 => (32*32)
|
||||
}
|
||||
require (nComponents > 0 && nComponents <= maxComponents)
|
||||
|
||||
private val maxRam = nDebugBusAddrSize match {
|
||||
case 5 => (4 * 16)
|
||||
case 6 => (4 * 16)
|
||||
case 7 => (4 * 64)
|
||||
}
|
||||
|
||||
require (nDebugRamBytes > 0 && nDebugRamBytes <= maxRam)
|
||||
|
||||
val hasHaltSum = (nComponents > 64) || (nSerialPorts > 0)
|
||||
|
||||
val hasDebugRom = debugRomContents.nonEmpty
|
||||
|
||||
if (hasDebugRom) {
|
||||
require (debugRomContents.get.size > 0)
|
||||
require (debugRomContents.get.size <= 512)
|
||||
}
|
||||
|
||||
require (nNDResetCycles > 0)
|
||||
|
||||
}
|
||||
|
||||
class DefaultDebugModuleConfig (val ncomponents : Int, val xlen:Int)
|
||||
extends DebugModuleConfig(
|
||||
nComponents = ncomponents,
|
||||
nDebugBusAddrSize = 5,
|
||||
// While smaller numbers are theoretically
|
||||
// possible as noted in the Spec,
|
||||
// the ROM image would need to be
|
||||
// adjusted accordingly.
|
||||
nDebugRamBytes = xlen match{
|
||||
case 32 => 28
|
||||
case 64 => 64
|
||||
case 128 => 64
|
||||
},
|
||||
debugRomContents = xlen match {
|
||||
case 32 => Some(DsbBusConsts.xlen32OnlyRomContents)
|
||||
case 64 => Some(DsbBusConsts.xlen64OnlyRomContents)
|
||||
},
|
||||
hasBusMaster = false,
|
||||
hasAccess128 = false,
|
||||
hasAccess64 = false,
|
||||
hasAccess32 = false,
|
||||
hasAccess16 = false,
|
||||
hasAccess8 = false,
|
||||
nSerialPorts = 0,
|
||||
authType = DebugModuleAuthType.None,
|
||||
nNDResetCycles = 1)
|
||||
|
||||
case object DMKey extends Field[DebugModuleConfig]
|
||||
|
||||
// *****************************************
|
||||
// Module Interfaces
|
||||
//
|
||||
// *****************************************
|
||||
|
||||
|
||||
/** Structure to define the contents of a Debug Bus Request
|
||||
*/
|
||||
|
||||
class DebugBusReq(addrBits : Int) extends Bundle {
|
||||
val addr = UInt(width = addrBits)
|
||||
val data = UInt(width = DbBusConsts.dbDataSize)
|
||||
val op = UInt(width = DbBusConsts.dbOpSize)
|
||||
|
||||
override def cloneType = new DebugBusReq(addrBits).asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
|
||||
/** Structure to define the contents of a Debug Bus Response
|
||||
*/
|
||||
class DebugBusResp( ) extends Bundle {
|
||||
val data = UInt(width = DbBusConsts.dbDataSize)
|
||||
val resp = UInt(width = DbBusConsts.dbRespSize)
|
||||
|
||||
}
|
||||
|
||||
/** Structure to define the top-level DebugBus interface
|
||||
* of DebugModule.
|
||||
* DebugModule is the consumer of this interface.
|
||||
* Therefore it has the 'flipped' version of this.
|
||||
*/
|
||||
|
||||
class DebugBusIO(implicit val p: Parameters) extends ParameterizedBundle()(p) {
|
||||
val req = new DecoupledIO(new DebugBusReq(p(DMKey).nDebugBusAddrSize))
|
||||
val resp = new DecoupledIO(new DebugBusResp).flip()
|
||||
}
|
||||
|
||||
class AsyncDebugBusIO(implicit val p: Parameters) extends ParameterizedBundle()(p) {
|
||||
val req = new AsyncBundle(1, new DebugBusReq(p(DMKey).nDebugBusAddrSize))
|
||||
val resp = new AsyncBundle(1, new DebugBusResp).flip
|
||||
}
|
||||
|
||||
object FromAsyncDebugBus
|
||||
{
|
||||
def apply(x: AsyncDebugBusIO) = {
|
||||
val out = Wire(new DebugBusIO()(x.p))
|
||||
out.req <> FromAsyncBundle(x.req)
|
||||
x.resp <> ToAsyncBundle(out.resp, 1)
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
object ToAsyncDebugBus
|
||||
{
|
||||
def apply(x: DebugBusIO) = {
|
||||
val out = Wire(new AsyncDebugBusIO()(x.p))
|
||||
out.req <> ToAsyncBundle(x.req, 1)
|
||||
x.resp <> FromAsyncBundle(out.resp)
|
||||
out
|
||||
}
|
||||
}
|
||||
|
||||
trait HasDebugModuleParameters {
|
||||
implicit val p: Parameters
|
||||
val cfg = p(DMKey)
|
||||
}
|
||||
|
||||
/** Debug Module I/O, with the exclusion of the RegisterRouter
|
||||
* Access interface.
|
||||
*/
|
||||
|
||||
trait DebugModuleBundle extends Bundle with HasDebugModuleParameters {
|
||||
val db = new DebugBusIO()(p).flip()
|
||||
val debugInterrupts = Vec(cfg.nComponents, Bool()).asOutput
|
||||
val ndreset = Bool(OUTPUT)
|
||||
val fullreset = Bool(OUTPUT)
|
||||
}
|
||||
|
||||
|
||||
// *****************************************
|
||||
// The Module
|
||||
//
|
||||
// *****************************************
|
||||
|
||||
/** Parameterized version of the Debug Module defined in the
|
||||
* RISC-V Debug Specification
|
||||
*
|
||||
* DebugModule is a slave to two masters:
|
||||
* The Debug Bus -- implemented as a generic Decoupled IO with request
|
||||
* and response channels
|
||||
* The System Bus -- implemented as generic RegisterRouter
|
||||
*
|
||||
* DebugModule is responsible for holding registers, RAM, and ROM
|
||||
* to support debug interactions, as well as driving interrupts
|
||||
* to a configurable number of components in the system.
|
||||
* It is also responsible for some reset lines.
|
||||
*/
|
||||
|
||||
trait DebugModule extends Module with HasDebugModuleParameters with HasRegMap {
|
||||
|
||||
val io: DebugModuleBundle
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Import constants for shorter variable names
|
||||
//--------------------------------------------------------------
|
||||
|
||||
import DbRegAddrs._
|
||||
import DsbRegAddrs._
|
||||
import DsbBusConsts._
|
||||
import DbBusConsts._
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Sanity Check Configuration For this implementation.
|
||||
//--------------------------------------------------------------
|
||||
|
||||
require (cfg.nComponents <= 128)
|
||||
require (cfg.nSerialPorts == 0)
|
||||
require (cfg.hasBusMaster == false)
|
||||
require (cfg.nDebugRamBytes <= 64)
|
||||
require (cfg.authType == DebugModuleAuthType.None)
|
||||
require((DbBusConsts.dbRamWordBits % 8) == 0)
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Private Classes (Register Fields)
|
||||
//--------------------------------------------------------------
|
||||
|
||||
class RAMFields() extends Bundle {
|
||||
val interrupt = Bool()
|
||||
val haltnot = Bool()
|
||||
val data = Bits(width = 32)
|
||||
|
||||
override def cloneType = new RAMFields().asInstanceOf[this.type]
|
||||
}
|
||||
|
||||
class CONTROLFields() extends Bundle {
|
||||
val interrupt = Bool()
|
||||
val haltnot = Bool()
|
||||
val reserved0 = Bits(width = 31-22 + 1)
|
||||
val buserror = Bits(width = 3)
|
||||
val serial = Bits(width = 3)
|
||||
val autoincrement = Bool()
|
||||
val access = UInt(width = 3)
|
||||
val hartid = Bits(width = 10)
|
||||
val ndreset = Bool()
|
||||
val fullreset = Bool()
|
||||
|
||||
override def cloneType = new CONTROLFields().asInstanceOf[this.type]
|
||||
|
||||
}
|
||||
|
||||
class DMINFOFields() extends Bundle {
|
||||
val reserved0 = Bits(width = 2)
|
||||
val abussize = UInt(width = 7)
|
||||
val serialcount = UInt(width = 4)
|
||||
val access128 = Bool()
|
||||
val access64 = Bool()
|
||||
val access32 = Bool()
|
||||
val access16 = Bool()
|
||||
val accesss8 = Bool()
|
||||
val dramsize = UInt(width = 6)
|
||||
val haltsum = Bool()
|
||||
val reserved1 = Bits(width = 3)
|
||||
val authenticated = Bool()
|
||||
val authbusy = Bool()
|
||||
val authtype = UInt(width = 2)
|
||||
val version = UInt(width = 2)
|
||||
|
||||
override def cloneType = new DMINFOFields().asInstanceOf[this.type]
|
||||
|
||||
}
|
||||
|
||||
class HALTSUMFields() extends Bundle {
|
||||
val serialfull = Bool()
|
||||
val serialvalid = Bool()
|
||||
val acks = Bits(width = 32)
|
||||
|
||||
override def cloneType = new HALTSUMFields().asInstanceOf[this.type]
|
||||
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Register & Wire Declarations
|
||||
//--------------------------------------------------------------
|
||||
|
||||
// --- Debug Bus Registers
|
||||
val CONTROLReset = Wire(new CONTROLFields())
|
||||
val CONTROLWrEn = Wire(Bool())
|
||||
val CONTROLReg = Reg(new CONTROLFields())
|
||||
val CONTROLWrData = Wire (new CONTROLFields())
|
||||
val CONTROLRdData = Wire (new CONTROLFields())
|
||||
val ndresetCtrReg = Reg(UInt(cfg.nNDResetCycles))
|
||||
|
||||
val DMINFORdData = Wire (new DMINFOFields())
|
||||
|
||||
val HALTSUMRdData = Wire (new HALTSUMFields())
|
||||
|
||||
val RAMWrData = Wire (new RAMFields())
|
||||
val RAMRdData = Wire (new RAMFields())
|
||||
|
||||
// --- System Bus Registers
|
||||
|
||||
val SETHALTNOTWrEn = Wire(Bool())
|
||||
val SETHALTNOTWrData = Wire(UInt(width = sbIdWidth))
|
||||
val CLEARDEBINTWrEn = Wire(Bool())
|
||||
val CLEARDEBINTWrData = Wire(UInt(width = sbIdWidth))
|
||||
|
||||
// --- Interrupt & Halt Notification Registers
|
||||
|
||||
val interruptRegs = Reg(init=Vec.fill(cfg.nComponents){Bool(false)})
|
||||
|
||||
val haltnotRegs = Reg(init=Vec.fill(cfg.nComponents){Bool(false)})
|
||||
val numHaltnotStatus = ((cfg.nComponents - 1) / 32) + 1
|
||||
|
||||
val haltnotStatus = Wire(Vec(numHaltnotStatus, Bits(width = 32)))
|
||||
val rdHaltnotStatus = Wire(Bits(width = 32))
|
||||
|
||||
val haltnotSummary = Cat(haltnotStatus.map(_.orR).reverse)
|
||||
|
||||
// --- Debug RAM
|
||||
|
||||
val ramDataWidth = DbBusConsts.dbRamWordBits
|
||||
val ramDataBytes = ramDataWidth / 8;
|
||||
val ramAddrWidth = log2Up(cfg.nDebugRamBytes / ramDataBytes)
|
||||
|
||||
val ramMem = Reg(init = Vec.fill(cfg.nDebugRamBytes){UInt(0, width = 8)})
|
||||
|
||||
val dbRamAddr = Wire(UInt(width=ramAddrWidth))
|
||||
val dbRamAddrValid = Wire(Bool())
|
||||
val dbRamRdData = Wire (UInt(width=ramDataWidth))
|
||||
val dbRamWrData = Wire(UInt(width=ramDataWidth))
|
||||
val dbRamWrEn = Wire(Bool())
|
||||
val dbRamRdEn = Wire(Bool())
|
||||
val dbRamWrEnFinal = Wire(Bool())
|
||||
val dbRamRdEnFinal = Wire(Bool())
|
||||
require((cfg.nDebugRamBytes % ramDataBytes) == 0)
|
||||
val dbRamDataOffset = log2Up(ramDataBytes)
|
||||
|
||||
|
||||
// --- Debug Bus Accesses
|
||||
|
||||
val dbRdEn = Wire(Bool())
|
||||
val dbWrEn = Wire(Bool())
|
||||
val dbRdData = Wire(UInt(width = DbBusConsts.dbDataSize))
|
||||
|
||||
val s_DB_READY :: s_DB_RESP :: Nil = Enum(Bits(), 2)
|
||||
val dbStateReg = Reg(init = s_DB_READY)
|
||||
|
||||
val dbResult = Wire(io.db.resp.bits)
|
||||
|
||||
val dbReq = Wire(io.db.req.bits)
|
||||
val dbRespReg = Reg(io.db.resp.bits)
|
||||
|
||||
val rdCondWrFailure = Wire(Bool())
|
||||
val dbWrNeeded = Wire(Bool())
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Interrupt Registers
|
||||
//--------------------------------------------------------------
|
||||
|
||||
for (component <- 0 until cfg.nComponents) {
|
||||
io.debugInterrupts(component) := interruptRegs(component)
|
||||
}
|
||||
|
||||
// Interrupt Registers are written by write to CONTROL or debugRAM addresses
|
||||
// for Debug Bus, and cleared by writes to CLEARDEBINT by System Bus.
|
||||
// It is "unspecified" what should happen if both
|
||||
// SET and CLEAR happen at the same time. In this
|
||||
// implementation, the SET wins.
|
||||
|
||||
for (component <- 0 until cfg.nComponents) {
|
||||
when (CONTROLWrEn) {
|
||||
when (CONTROLWrData.hartid === UInt(component)) {
|
||||
interruptRegs(component) := interruptRegs(component) | CONTROLWrData.interrupt
|
||||
}
|
||||
}.elsewhen (dbRamWrEn) {
|
||||
when (CONTROLReg.hartid === UInt(component)){
|
||||
interruptRegs(component) := interruptRegs(component) | RAMWrData.interrupt
|
||||
}
|
||||
}.elsewhen (CLEARDEBINTWrEn){
|
||||
when (CLEARDEBINTWrData === UInt(component, width = sbIdWidth)) {
|
||||
interruptRegs(component) := Bool(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Halt Notification Registers
|
||||
//--------------------------------------------------------------
|
||||
|
||||
// Halt Notifications Registers are cleared by zero write to CONTROL or debugRAM addresses
|
||||
// for Debug Bus, and set by write to SETHALTNOT by System Bus.
|
||||
// It is "unspecified" what should happen if both
|
||||
// SET and CLEAR happen at the same time. In this
|
||||
// implementation, the SET wins.
|
||||
|
||||
for (component <- 0 until cfg.nComponents) {
|
||||
when (SETHALTNOTWrEn){
|
||||
when (SETHALTNOTWrData === UInt(component, width = sbIdWidth)) {
|
||||
haltnotRegs(component) := Bool(true)
|
||||
}
|
||||
} .elsewhen (CONTROLWrEn) {
|
||||
when (CONTROLWrData.hartid === UInt(component)) {
|
||||
haltnotRegs(component) := haltnotRegs(component) & CONTROLWrData.haltnot
|
||||
}
|
||||
}.elsewhen (dbRamWrEn) {
|
||||
when (CONTROLReg.hartid === UInt(component)){
|
||||
haltnotRegs(component) := haltnotRegs(component) & RAMWrData.haltnot
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ii <- 0 until numHaltnotStatus) {
|
||||
haltnotStatus(ii) := Cat(haltnotRegs.slice(ii * 32, (ii + 1) * 32).reverse)
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Other Registers
|
||||
//--------------------------------------------------------------
|
||||
|
||||
CONTROLReset.interrupt := Bool(false)
|
||||
CONTROLReset.haltnot := Bool(false)
|
||||
CONTROLReset.reserved0 := Bits(0)
|
||||
CONTROLReset.buserror := Bits(0)
|
||||
CONTROLReset.serial := Bits(0)
|
||||
CONTROLReset.autoincrement := Bool(false)
|
||||
CONTROLReset.access := UInt(DebugModuleAccessType.Access32Bit.id)
|
||||
CONTROLReset.hartid := Bits(0)
|
||||
CONTROLReset.ndreset := Bool(false)
|
||||
CONTROLReset.fullreset := Bool(false)
|
||||
|
||||
// Because this version of DebugModule doesn't
|
||||
// support authentication, this entire register is
|
||||
// Read-Only constant wires.
|
||||
DMINFORdData.reserved0 := Bits(0)
|
||||
DMINFORdData.abussize := UInt(0) // Not Implemented.
|
||||
DMINFORdData.serialcount := UInt(cfg.nSerialPorts)
|
||||
DMINFORdData.access128 := Bool(cfg.hasAccess128)
|
||||
DMINFORdData.access64 := Bool(cfg.hasAccess64)
|
||||
DMINFORdData.access32 := Bool(cfg.hasAccess32)
|
||||
DMINFORdData.access16 := Bool(cfg.hasAccess16)
|
||||
DMINFORdData.accesss8 := Bool(cfg.hasAccess8)
|
||||
DMINFORdData.dramsize := Bits((cfg.nDebugRamBytes >> 2) - 1) // Size in 32-bit words minus 1.
|
||||
DMINFORdData.haltsum := Bool(cfg.hasHaltSum)
|
||||
DMINFORdData.reserved1 := Bits(0)
|
||||
DMINFORdData.authenticated := Bool(true) // Not Implemented.
|
||||
DMINFORdData.authbusy := Bool(false) // Not Implemented.
|
||||
DMINFORdData.authtype := UInt(cfg.authType.id)
|
||||
DMINFORdData.version := UInt(1) // Conforms to RISC-V Debug Spec
|
||||
|
||||
HALTSUMRdData.serialfull := Bool(false) // Not Implemented
|
||||
HALTSUMRdData.serialvalid := Bool(false) // Not Implemented
|
||||
HALTSUMRdData.acks := haltnotSummary
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Debug RAM Access (Debug Bus ... System Bus can override)
|
||||
//--------------------------------------------------------------
|
||||
|
||||
dbReq := io.db.req.bits
|
||||
// Debug Bus RAM Access
|
||||
// From Specification: Debug RAM is 0x00 - 0x0F
|
||||
// 0x40 - 0x6F Not Implemented
|
||||
dbRamAddr := dbReq.addr( ramAddrWidth-1 , 0)
|
||||
dbRamWrData := dbReq.data
|
||||
dbRamAddrValid := (dbReq.addr(3,0) <= UInt((cfg.nDebugRamBytes/ramDataBytes)))
|
||||
|
||||
val dbRamRdDataFields = List.tabulate(cfg.nDebugRamBytes / ramDataBytes) { ii =>
|
||||
val slice = ramMem.slice(ii * ramDataBytes, (ii+1)*ramDataBytes)
|
||||
slice.reduce[UInt]{ case (x: UInt, y: UInt) => Cat(y, x)}
|
||||
}
|
||||
|
||||
dbRamRdData := dbRamRdDataFields(dbRamAddr)
|
||||
|
||||
when (dbRamWrEnFinal) {
|
||||
for (ii <- 0 until ramDataBytes) {
|
||||
ramMem((dbRamAddr << UInt(dbRamDataOffset)) + UInt(ii)) := dbRamWrData((8*(ii+1)-1), (8*ii))
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Debug Bus Access
|
||||
//--------------------------------------------------------------
|
||||
|
||||
// 0x00 - 0x0F Debug RAM
|
||||
// 0x10 - 0x1B Registers
|
||||
// 0x1C - 0x3B Halt Notification Registers
|
||||
// 0x3C - 0x3F Registers
|
||||
// 0x40 - 0x6F Debug RAM
|
||||
|
||||
|
||||
// -----------------------------------------
|
||||
// DB Access Write Decoder
|
||||
|
||||
CONTROLWrData := new CONTROLFields().fromBits(dbReq.data)
|
||||
RAMWrData := new RAMFields().fromBits(dbReq.data)
|
||||
|
||||
dbRamWrEn := Bool(false)
|
||||
dbRamWrEnFinal := Bool(false)
|
||||
CONTROLWrEn := Bool(false)
|
||||
when ((dbReq.addr >> 4) === Bits(0)) { // 0x00 - 0x0F Debug RAM
|
||||
dbRamWrEn := dbWrEn
|
||||
when (dbRamAddrValid) {
|
||||
dbRamWrEnFinal := dbWrEn
|
||||
}
|
||||
}.elsewhen (dbReq.addr === DMCONTROL) {
|
||||
CONTROLWrEn := dbWrEn
|
||||
}.otherwise {
|
||||
//Other registers/RAM are Not Implemented.
|
||||
}
|
||||
|
||||
when (reset) {
|
||||
CONTROLReg := CONTROLReset
|
||||
ndresetCtrReg := UInt(0)
|
||||
}.elsewhen (CONTROLWrEn) {
|
||||
// interrupt handled in other logic
|
||||
// haltnot handled in other logic
|
||||
if (cfg.hasBusMaster){
|
||||
// buserror is set 'until 0 is written to any bit in this field'.
|
||||
CONTROLReg.buserror := Mux(CONTROLWrData.buserror.andR, CONTROLReg.buserror, UInt(0))
|
||||
CONTROLReg.autoincrement := CONTROLWrData.autoincrement
|
||||
CONTROLReg.access := CONTROLWrData.access
|
||||
}
|
||||
if (cfg.nSerialPorts > 0){
|
||||
CONTROLReg.serial := CONTROLWrData.serial
|
||||
}
|
||||
CONTROLReg.hartid := CONTROLWrData.hartid
|
||||
CONTROLReg.fullreset := CONTROLReg.fullreset | CONTROLWrData.fullreset
|
||||
when (CONTROLWrData.ndreset){
|
||||
ndresetCtrReg := UInt(cfg.nNDResetCycles)
|
||||
}.otherwise {
|
||||
ndresetCtrReg := Mux(ndresetCtrReg === UInt(0) , UInt(0), ndresetCtrReg - UInt(1))
|
||||
}
|
||||
}.otherwise {
|
||||
ndresetCtrReg := Mux(ndresetCtrReg === UInt(0) , UInt(0), ndresetCtrReg - UInt(1))
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
// DB Access Read Mux
|
||||
|
||||
CONTROLRdData := CONTROLReg;
|
||||
CONTROLRdData.interrupt := interruptRegs(CONTROLReg.hartid)
|
||||
CONTROLRdData.haltnot := haltnotRegs(CONTROLReg.hartid)
|
||||
CONTROLRdData.ndreset := ndresetCtrReg.orR
|
||||
|
||||
RAMRdData.interrupt := interruptRegs(CONTROLReg.hartid)
|
||||
RAMRdData.haltnot := haltnotRegs(CONTROLReg.hartid)
|
||||
RAMRdData.data := dbRamRdData
|
||||
|
||||
dbRdData := UInt(0)
|
||||
|
||||
// Higher numbers of numHaltnotStatus Not Implemented.
|
||||
// This logic assumes only up to 128 components.
|
||||
rdHaltnotStatus := Bits(0)
|
||||
for (ii <- 0 until numHaltnotStatus) {
|
||||
when (dbReq.addr(1, 0) === UInt(ii)) {
|
||||
rdHaltnotStatus := haltnotStatus(ii)
|
||||
}
|
||||
}
|
||||
|
||||
dbRamRdEn := Bool(false)
|
||||
dbRamRdEnFinal := Bool(false)
|
||||
when ((dbReq.addr >> 4) === Bits(0)) { // 0x00 - 0x0F Debug RAM
|
||||
dbRamRdEn := dbRdEn
|
||||
when (dbRamAddrValid) {
|
||||
dbRdData := RAMRdData.asUInt
|
||||
dbRamRdEnFinal := dbRdEn
|
||||
}
|
||||
}.elsewhen (dbReq.addr === DMCONTROL) {
|
||||
dbRdData := CONTROLRdData.asUInt
|
||||
}.elsewhen (dbReq.addr === DMINFO) {
|
||||
dbRdData := DMINFORdData.asUInt
|
||||
}.elsewhen (dbReq.addr === HALTSUM) {
|
||||
if (cfg.hasHaltSum){
|
||||
dbRdData := HALTSUMRdData.asUInt
|
||||
} else {
|
||||
dbRdData := UInt(0)
|
||||
}
|
||||
}.elsewhen ((dbReq.addr >> 2) === UInt(7)) { // 0x1C - 0x1F Haltnot
|
||||
dbRdData := rdHaltnotStatus
|
||||
} .otherwise {
|
||||
//These Registers are not implemented in this version of DebugModule:
|
||||
// AUTHDATA0
|
||||
// AUTHDATA1
|
||||
// SERDATA
|
||||
// SERSTATUS
|
||||
// SBUSADDRESS0
|
||||
// SBUSADDRESS1
|
||||
// SBDATA0
|
||||
// SBDATA1
|
||||
// SBADDRESS2
|
||||
// SBDATA2
|
||||
// SBDATA3
|
||||
// 0x20 - 0x3B haltnot
|
||||
// Upper bytes of Debug RAM.
|
||||
dbRdData := UInt(0)
|
||||
}
|
||||
|
||||
// Conditional write fails if MSB is set of the read data.
|
||||
rdCondWrFailure := dbRdData(dbDataSize - 1 ) &&
|
||||
(dbReq.op === db_OP_READ_COND_WRITE)
|
||||
|
||||
dbWrNeeded := (dbReq.op === db_OP_READ_WRITE) ||
|
||||
((dbReq.op === db_OP_READ_COND_WRITE) && ~rdCondWrFailure)
|
||||
|
||||
// This is only relevant at end of s_DB_READ.
|
||||
dbResult.resp := Mux(rdCondWrFailure,
|
||||
db_RESP_FAILURE,
|
||||
db_RESP_SUCCESS)
|
||||
dbResult.data := dbRdData
|
||||
|
||||
// -----------------------------------------
|
||||
// DB Access State Machine Decode (Combo)
|
||||
io.db.req.ready := (dbStateReg === s_DB_READY) ||
|
||||
(dbStateReg === s_DB_RESP && io.db.resp.fire())
|
||||
|
||||
io.db.resp.valid := (dbStateReg === s_DB_RESP)
|
||||
io.db.resp.bits := dbRespReg
|
||||
|
||||
dbRdEn := io.db.req.fire()
|
||||
dbWrEn := dbWrNeeded && io.db.req.fire()
|
||||
|
||||
// -----------------------------------------
|
||||
// DB Access State Machine Update (Seq)
|
||||
|
||||
when (dbStateReg === s_DB_READY){
|
||||
when (io.db.req.fire()){
|
||||
dbStateReg := s_DB_RESP
|
||||
dbRespReg := dbResult
|
||||
}
|
||||
} .elsewhen (dbStateReg === s_DB_RESP){
|
||||
when (io.db.req.fire()){
|
||||
dbStateReg := s_DB_RESP
|
||||
dbRespReg := dbResult
|
||||
}.elsewhen (io.db.resp.fire()){
|
||||
dbStateReg := s_DB_READY
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Debug ROM
|
||||
//--------------------------------------------------------------
|
||||
|
||||
val romRegFields = if (cfg.hasDebugRom) {
|
||||
cfg.debugRomContents.get.map( x => RegField.r(8, UInt(x.toInt & 0xFF)))
|
||||
} else {
|
||||
Seq(RegField(8))
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// System Bus Access
|
||||
//--------------------------------------------------------------
|
||||
|
||||
// Local reg mapper function : Notify when written, but give the value.
|
||||
def wValue (n: Int, value: UInt, set: Bool) : RegField = {
|
||||
RegField(n, value, RegWriteFn((valid, data) => {set := valid ; value := data; Bool(true)}))
|
||||
}
|
||||
|
||||
regmap(
|
||||
CLEARDEBINT -> Seq(wValue(sbIdWidth, CLEARDEBINTWrData, CLEARDEBINTWrEn)),
|
||||
SETHALTNOT -> Seq(wValue(sbIdWidth, SETHALTNOTWrData, SETHALTNOTWrEn)),
|
||||
RAMBASE -> ramMem.map(x => RegField(8, x)),
|
||||
ROMBASE -> romRegFields
|
||||
)
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Misc. Outputs
|
||||
//--------------------------------------------------------------
|
||||
|
||||
io.ndreset := ndresetCtrReg.orR
|
||||
io.fullreset := CONTROLReg.fullreset
|
||||
|
||||
}
|
||||
|
||||
/** Create a concrete TL2 Slave for the DebugModule RegMapper interface.
|
||||
*
|
||||
*/
|
||||
|
||||
class TLDebugModule(address: BigInt = 0)(implicit p: Parameters)
|
||||
extends TLRegisterRouter(address, "debug", Nil, beatBytes=p(XLen)/8, executable=true)(
|
||||
new TLRegBundle((), _ ) with DebugModuleBundle)(
|
||||
new TLRegModule((), _, _) with DebugModule)
|
||||
|
||||
|
||||
/** Synchronizers for DebugBus
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
object AsyncDebugBusCrossing {
|
||||
// takes from_source from the 'from' clock domain to the 'to' clock domain
|
||||
def apply(from_clock: Clock, from_reset: Bool, from_source: DebugBusIO, to_clock: Clock, to_reset: Bool, depth: Int = 1, sync: Int = 3) = {
|
||||
val to_sink = Wire(new DebugBusIO()(from_source.p))
|
||||
to_sink.req <> AsyncDecoupledCrossing(from_clock, from_reset, from_source.req, to_clock, to_reset, depth, sync)
|
||||
from_source.resp <> AsyncDecoupledCrossing(to_clock, to_reset, to_sink.resp, from_clock, from_reset, depth, sync)
|
||||
to_sink // is now to_source
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object AsyncDebugBusFrom { // OutsideClockDomain
|
||||
// takes from_source from the 'from' clock domain and puts it into your clock domain
|
||||
def apply(from_clock: Clock, from_reset: Bool, from_source: DebugBusIO, depth: Int = 1, sync: Int = 3): DebugBusIO = {
|
||||
val scope = AsyncScope()
|
||||
AsyncDebugBusCrossing(from_clock, from_reset, from_source, scope.clock, scope.reset, depth, sync)
|
||||
}
|
||||
}
|
||||
|
||||
object AsyncDebugBusTo { // OutsideClockDomain
|
||||
// takes source from your clock domain and puts it into the 'to' clock domain
|
||||
def apply(to_clock: Clock, to_reset: Bool, source: DebugBusIO, depth: Int = 1, sync: Int = 3): DebugBusIO = {
|
||||
val scope = AsyncScope()
|
||||
AsyncDebugBusCrossing(scope.clock, scope.reset, source, to_clock, to_reset, depth, sync)
|
||||
}
|
||||
}
|
1177
src/main/scala/uncore/devices/debug/Debug.scala
Normal file
1177
src/main/scala/uncore/devices/debug/Debug.scala
Normal file
File diff suppressed because it is too large
Load Diff
@ -48,8 +48,9 @@ module JTAGVPI
|
||||
output jtag_TMS,
|
||||
output jtag_TCK,
|
||||
output jtag_TDI,
|
||||
input jtag_TDO,
|
||||
input jtag_TRST, // unused
|
||||
input jtag_TDO_data,
|
||||
input jtag_TDO_driven,
|
||||
|
||||
input enable,
|
||||
input init_done);
|
||||
|
||||
@ -61,8 +62,7 @@ module JTAGVPI
|
||||
assign jtag_TMS = tms;
|
||||
assign jtag_TCK = tck;
|
||||
assign jtag_TDI = tdi;
|
||||
assign tdo = jtag_TDO;
|
||||
|
||||
assign tdo = jtag_TDO_driven ? jtag_TDO_data : 1'bz;
|
||||
|
||||
integer cmd;
|
||||
integer length;
|
||||
|
Loading…
Reference in New Issue
Block a user