Add JTAG DTM and test support in simulation
Initial cut checkpoint which compiles and runs but there is some off-by-1 in the protocol Debugging the clock crossing logic checkpoint which works Clean up the AsyncMailbox black box
This commit is contained in:
124
src/main/scala/DebugTransport.scala
Normal file
124
src/main/scala/DebugTransport.scala
Normal file
@ -0,0 +1,124 @@
|
||||
package rocketchip
|
||||
|
||||
import Chisel._
|
||||
import uncore.devices.{DebugBusIO, AsyncDebugBusTo, AsyncDebugBusFrom, DebugBusReq, DebugBusResp, DMKey}
|
||||
import junctions._
|
||||
import cde.{Parameters}
|
||||
|
||||
/* 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 number of black-boxed
|
||||
* modules. The black-boxing is due to the fact that
|
||||
* 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 AsyncDebugBus parameter here is overloaded.
|
||||
* The DebugTransportModule JTAG definately needs a synchronizer,
|
||||
* the parameter just currently selects whether the Chisel-generated
|
||||
* crossing is used or the black-boxed crossing is used.
|
||||
* Although Top is also capable of generating the
|
||||
* Chisel sychnronizers, it is done here for consistency
|
||||
* of keeping the synchronizers in one place when
|
||||
* instantiating the DTM.
|
||||
*
|
||||
*/
|
||||
|
||||
class JtagDTMWithSync(implicit val p: Parameters) extends Module {
|
||||
|
||||
// IO <-> [Chisel Sync?] <-> [DebugBusIO<->UInt] <-> [Black Box Sync?] <-> DTM Black Box
|
||||
|
||||
val io = new Bundle {
|
||||
|
||||
val jtag = new JtagIO(true).flip()
|
||||
val debug = new DebugBusIO()(p)
|
||||
|
||||
}
|
||||
|
||||
val req_width = io.debug.req.bits.getWidth
|
||||
val resp_width = io.debug.resp.bits.getWidth
|
||||
|
||||
val jtag_dtm = Module (new DebugTransportModuleJtag(req_width, resp_width))
|
||||
|
||||
jtag_dtm.io.jtag <> io.jtag
|
||||
|
||||
val dtm_req = Wire(new DecoupledIO(UInt(width = req_width)))
|
||||
val dtm_resp = Wire(new DecoupledIO(UInt(width = resp_width)))
|
||||
|
||||
val io_debug_bus = Wire (new DebugBusIO)
|
||||
|
||||
// Optionally instantiate the Chisel synchronizers.
|
||||
// These go on this side of the DebugBusIO->UInt translation
|
||||
// because the Chisel synchronizers understand these data structures.
|
||||
if (p(AsyncDebugBus)){
|
||||
io.debug <> AsyncDebugBusFrom(io.jtag.TCK, io.jtag.TRST, io_debug_bus)
|
||||
} else {
|
||||
io.debug <> io_debug_bus
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// Optionally instantiate the black-box synchronizers
|
||||
// instead of the chisel ones.
|
||||
// These go on this side of the DebugBusIO->UInt translation
|
||||
// because they do not understand the DebugBusIO data structures.
|
||||
|
||||
if (p(AsyncDebugBus)) {
|
||||
dtm_req <> jtag_dtm.io.dtm_req
|
||||
jtag_dtm.io.dtm_resp <> dtm_resp
|
||||
} else {
|
||||
val req_sync = Module (new AsyncMailbox())
|
||||
val resp_sync = Module (new AsyncMailbox())
|
||||
req_sync.io.enq := jtag_dtm.io.dtm_req
|
||||
req_sync.io.enq_clock.get := io.jtag.TCK
|
||||
req_sync.io.enq_reset.get := io.jtag.TRST
|
||||
req_sync.io.deq_clock.get := clock
|
||||
req_sync.io.deq_reset.get := reset
|
||||
dtm_req := req_sync.io.deq
|
||||
|
||||
jtag_dtm.io.dtm_resp := resp_sync.io.deq
|
||||
resp_sync.io.deq_clock.get := io.jtag.TCK
|
||||
resp_sync.io.deq_reset.get := io.jtag.TRST
|
||||
resp_sync.io.enq_clock.get := clock
|
||||
resp_sync.io.enq_reset.get := reset
|
||||
resp_sync.io.enq := dtm_resp
|
||||
}
|
||||
}
|
||||
|
||||
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 AsyncMailbox extends BlackBox {
|
||||
|
||||
// This Verilog module is parameterized, but until this is supported by Chisel,
|
||||
// this mailbox just has a fixed width of 64 bits, which is enough
|
||||
// for our specific purpose here.
|
||||
|
||||
val io = new Crossing(UInt(width=64), true, true)
|
||||
|
||||
}
|
@ -122,6 +122,7 @@ class BasePlatformConfig extends Config (
|
||||
case BuildCoreplex => (p: Parameters) => Module(new DefaultCoreplex(p))
|
||||
case NExtInterrupts => 2
|
||||
case AsyncDebugBus => false
|
||||
case IncludeJtagDTM => false
|
||||
case AsyncMMIOChannels => false
|
||||
case ExtraDevices => Nil
|
||||
case ExtraTopPorts => (p: Parameters) => new Bundle
|
||||
@ -277,4 +278,18 @@ class WithTestRAM extends Config(
|
||||
}
|
||||
Seq(new TestRAMDevice)
|
||||
}
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
class WithAsyncDebug extends Config (
|
||||
(pname, site, here) => pname match {
|
||||
case AsyncDebugBus => true
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class WithJtagDTM extends Config (
|
||||
(pname, site, here) => pname match {
|
||||
case IncludeJtagDTM => true
|
||||
}
|
||||
)
|
||||
|
@ -87,9 +87,10 @@ class TopIO(implicit p: Parameters) extends BasicTopIO()(p) {
|
||||
val mmio_axi = Vec(p(NExtMMIOAXIChannels), new NastiIO)
|
||||
val mmio_ahb = Vec(p(NExtMMIOAHBChannels), new HastiMasterIO)
|
||||
val mmio_tl = Vec(p(NExtMMIOTLChannels), new ClientUncachedTileLinkIO()(outermostMMIOParams))
|
||||
val debug_clk = if (p(AsyncDebugBus)) Some(Clock(INPUT)) else None
|
||||
val debug_rst = if (p(AsyncDebugBus)) Some(Bool(INPUT)) else None
|
||||
val debug = new DebugBusIO()(p).flip
|
||||
val debug_clk = if (p(AsyncDebugBus) & !p(IncludeJtagDTM)) Some(Clock(INPUT)) else None
|
||||
val debug_rst = if (p(AsyncDebugBus) & !p(IncludeJtagDTM)) Some(Bool(INPUT)) else None
|
||||
val debug = if (!p(IncludeJtagDTM)) Some(new DebugBusIO()(p).flip) else None
|
||||
val jtag = if ( p(IncludeJtagDTM)) Some(new JtagIO(true).flip) else None
|
||||
val extra = p(ExtraTopPorts)(p)
|
||||
}
|
||||
|
||||
@ -137,10 +138,18 @@ class Top(topParams: Parameters) extends Module with HasTopLevelParameters {
|
||||
periphery.io.mem_in <> coreplex.io.mem
|
||||
coreplex.io.ext_clients <> periphery.io.clients_out
|
||||
|
||||
coreplex.io.debug <>
|
||||
if (p(IncludeJtagDTM)) {
|
||||
// JtagDTMWithSync is a wrapper which
|
||||
// handles the synchronization as well.
|
||||
val jtag_dtm = Module (new JtagDTMWithSync()(p))
|
||||
jtag_dtm.io.jtag <> io.jtag.get
|
||||
coreplex.io.debug <> jtag_dtm.io.debug
|
||||
} else {
|
||||
coreplex.io.debug <>
|
||||
(if (p(AsyncDebugBus))
|
||||
AsyncDebugBusFrom(io.debug_clk.get, io.debug_rst.get, io.debug)
|
||||
else io.debug)
|
||||
AsyncDebugBusFrom(io.debug_clk.get, io.debug_rst.get, io.debug.get)
|
||||
else io.debug.get)
|
||||
}
|
||||
|
||||
def asyncAxiTo(clocks: Seq[Clock], resets: Seq[Bool], inner_axis: Seq[NastiIO]): Seq[NastiIO] =
|
||||
(clocks, resets, inner_axis).zipped.map {
|
||||
|
@ -6,6 +6,7 @@ import Chisel._
|
||||
import cde.{Parameters, Field}
|
||||
import rocket.Util._
|
||||
import junctions._
|
||||
import uncore.devices.{IncludeJtagDTM}
|
||||
|
||||
class TestHarness(implicit p: Parameters) extends Module {
|
||||
val io = new Bundle {
|
||||
@ -26,9 +27,6 @@ class TestHarness(implicit p: Parameters) extends Module {
|
||||
require(dut.io.mmio_axi.isEmpty)
|
||||
require(dut.io.mmio_ahb.isEmpty)
|
||||
require(dut.io.mmio_tl.isEmpty)
|
||||
require(dut.io.debug_clk.isEmpty)
|
||||
require(dut.io.debug_rst.isEmpty)
|
||||
require(dut.io.debug_rst.isEmpty)
|
||||
require(dut.io.extra.elements.isEmpty)
|
||||
|
||||
for (int <- dut.io.interrupts)
|
||||
@ -41,14 +39,42 @@ class TestHarness(implicit p: Parameters) extends Module {
|
||||
Module(new SimAXIMem(memSize / dut.io.mem_axi.size)).io.axi <> axi
|
||||
}
|
||||
|
||||
val dtm = Module(new SimDTM)
|
||||
dut.io.debug <> dtm.io.debug
|
||||
dtm.io.clk := clock
|
||||
dtm.io.reset := reset
|
||||
io.success := dut.io.success.getOrElse(dtm.io.exit === 1)
|
||||
when (dtm.io.exit >= 2) {
|
||||
printf("*** FAILED *** (exit code = %d)\n", dtm.io.exit >> 1)
|
||||
stop(1)
|
||||
if (p(IncludeJtagDTM)) {
|
||||
val jtag_vpi = Module (new JtagVpi)
|
||||
dut.io.jtag.get <> jtag_vpi.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.
|
||||
|
||||
dut.io.jtag.get.TRST := reset
|
||||
jtag_vpi.io.enable := ~reset
|
||||
jtag_vpi.io.init_done := ~reset
|
||||
io.success := Bool(false)
|
||||
}
|
||||
else {
|
||||
val dtm = Module(new SimDTM)
|
||||
dut.io.debug.get <> dtm.io.debug
|
||||
|
||||
// Todo: enable the usage of different clocks.
|
||||
|
||||
val dtm_clock = clock
|
||||
val dtm_reset = reset
|
||||
dtm.io.clk := dtm_clock
|
||||
dtm.io.reset := dtm_reset
|
||||
if (dut.io.debug_clk.isDefined)
|
||||
dut.io.debug_clk.get := dtm_clock
|
||||
if (dut.io.debug_rst.isDefined)
|
||||
dut.io.debug_rst.get := dtm_reset
|
||||
|
||||
io.success := dut.io.success.getOrElse(dtm.io.exit === 1)
|
||||
when (dtm.io.exit >= 2) {
|
||||
printf("*** FAILED *** (exit code = %d)\n", dtm.io.exit >> 1)
|
||||
stop(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,3 +140,13 @@ class SimDTM(implicit p: Parameters) extends BlackBox {
|
||||
val exit = UInt(OUTPUT, 32)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class JtagVpi(implicit val p: Parameters) extends BlackBox {
|
||||
|
||||
val io = new Bundle {
|
||||
val jtag = new JtagIO(false)
|
||||
val enable = Bool(INPUT)
|
||||
val init_done = Bool(INPUT)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import uncore.util._
|
||||
import junctions._
|
||||
import cde.{Parameters, Config, Field}
|
||||
|
||||
// *****************************************
|
||||
// *****************************************r
|
||||
// Constants which are interesting even
|
||||
// outside of this module
|
||||
// *****************************************
|
||||
@ -264,7 +264,7 @@ class DefaultDebugModuleConfig (val ncomponents : Int, val xlen:Int)
|
||||
nNDResetCycles = 1)
|
||||
|
||||
case object DMKey extends Field[DebugModuleConfig]
|
||||
|
||||
case object IncludeJtagDTM extends Field[Boolean]
|
||||
|
||||
// *****************************************
|
||||
// Module Interfaces
|
||||
@ -277,8 +277,8 @@ case object DMKey extends Field[DebugModuleConfig]
|
||||
|
||||
class DebugBusReq(addrBits : Int) extends Bundle {
|
||||
val addr = UInt(width = addrBits)
|
||||
val op = UInt(width = DbBusConsts.dbOpSize)
|
||||
val data = UInt(width = DbBusConsts.dbDataSize)
|
||||
val op = UInt(width = DbBusConsts.dbOpSize)
|
||||
|
||||
override def cloneType = new DebugBusReq(addrBits).asInstanceOf[this.type]
|
||||
}
|
||||
@ -287,8 +287,9 @@ class DebugBusReq(addrBits : Int) extends Bundle {
|
||||
/** Structure to define the contents of a Debug Bus Response
|
||||
*/
|
||||
class DebugBusResp( ) extends Bundle {
|
||||
val resp = UInt(width = DbBusConsts.dbRespSize)
|
||||
val data = UInt(width = DbBusConsts.dbDataSize)
|
||||
val resp = UInt(width = DbBusConsts.dbRespSize)
|
||||
|
||||
}
|
||||
|
||||
/** Structure to define the top-level DebugBus interface
|
||||
@ -999,3 +1000,5 @@ object AsyncDebugBusTo { // OutsideClockDomain
|
||||
sink
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user