2016-11-28 01:16:37 +01:00
|
|
|
// See LICENSE.SiFive for license details.
|
|
|
|
|
2017-07-23 17:31:04 +02:00
|
|
|
package freechips.rocketchip.devices.debug
|
2016-08-19 18:46:43 +02:00
|
|
|
|
|
|
|
import Chisel._
|
2017-07-07 19:48:16 +02:00
|
|
|
|
|
|
|
import freechips.rocketchip.config._
|
|
|
|
import freechips.rocketchip.jtag._
|
|
|
|
import freechips.rocketchip.util._
|
2016-08-19 23:01:33 +02:00
|
|
|
|
2017-03-28 06:19:08 +02:00
|
|
|
case class JtagDTMConfig (
|
|
|
|
idcodeVersion : Int, // chosen by manuf.
|
|
|
|
idcodePartNum : Int, // Chosen by manuf.
|
|
|
|
idcodeManufId : Int, // Assigned by JEDEC
|
2017-06-03 03:53:14 +02:00
|
|
|
// Note: the actual manufId is passed in through a wire.
|
|
|
|
// Do not forget to wire up io.jtag_mfr_id through your top-level to set the
|
|
|
|
// mfr_id for this core.
|
|
|
|
// If you wish to use this field in the config, you can obtain it along
|
|
|
|
// the lines of p(JtagDTMKey).idcodeManufId.U(11.W).
|
2017-03-28 06:19:08 +02:00
|
|
|
debugIdleCycles : Int)
|
2016-08-19 18:46:43 +02:00
|
|
|
|
2017-09-09 03:33:44 +02:00
|
|
|
case object JtagDTMKey extends Field[JtagDTMConfig](new JtagDTMKeyDefault())
|
2016-08-19 18:46:43 +02:00
|
|
|
|
2017-03-28 06:19:08 +02:00
|
|
|
class JtagDTMKeyDefault extends JtagDTMConfig(
|
|
|
|
idcodeVersion = 0,
|
|
|
|
idcodePartNum = 0,
|
|
|
|
idcodeManufId = 0,
|
|
|
|
debugIdleCycles = 5) // Reasonable guess for synchronization.
|
2016-08-19 18:46:43 +02:00
|
|
|
|
2017-03-28 06:19:08 +02:00
|
|
|
object dtmJTAGAddrs {
|
|
|
|
def IDCODE = 0x1
|
|
|
|
def DTM_INFO = 0x10
|
|
|
|
def DMI_ACCESS = 0x11
|
|
|
|
}
|
2016-08-19 18:46:43 +02:00
|
|
|
|
2017-03-28 06:19:08 +02:00
|
|
|
class DMIAccessUpdate(addrBits: Int) extends Bundle {
|
|
|
|
val addr = UInt(width = addrBits)
|
|
|
|
val data = UInt(width = DMIConsts.dmiDataSize)
|
|
|
|
val op = UInt(width = DMIConsts.dmiOpSize)
|
2016-08-19 18:46:43 +02:00
|
|
|
|
2017-03-28 06:19:08 +02:00
|
|
|
override def cloneType = new DMIAccessUpdate(addrBits).asInstanceOf[this.type]
|
|
|
|
}
|
2016-08-19 18:46:43 +02:00
|
|
|
|
2017-03-28 06:19:08 +02:00
|
|
|
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]
|
|
|
|
|
|
|
|
}
|
2016-08-19 18:46:43 +02:00
|
|
|
|
2017-03-28 06:19:08 +02:00
|
|
|
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)
|
2016-08-19 18:46:43 +02:00
|
|
|
}
|
|
|
|
|
2017-07-23 17:31:04 +02:00
|
|
|
/** A wrapper around JTAG providing a reset signal and manufacturer id. */
|
|
|
|
class SystemJTAGIO extends Bundle {
|
|
|
|
val jtag = new JTAGIO(hasTRSTn = false).flip
|
|
|
|
val reset = Bool(INPUT)
|
|
|
|
val mfr_id = UInt(INPUT, 11)
|
|
|
|
}
|
|
|
|
|
2017-03-28 06:19:08 +02:00
|
|
|
class DebugTransportModuleJTAG(debugAddrBits: Int, c: JtagDTMConfig)
|
|
|
|
(implicit val p: Parameters) extends Module {
|
|
|
|
|
2016-08-19 18:46:43 +02:00
|
|
|
val io = new Bundle {
|
2017-03-28 06:19:08 +02:00
|
|
|
val dmi = new DMIIO()(p)
|
2017-07-23 17:31:04 +02:00
|
|
|
val jtag = Flipped(new JTAGIO(hasTRSTn = false)) // TODO: re-use SystemJTAGIO here?
|
2017-03-28 06:19:08 +02:00
|
|
|
val jtag_reset = Bool(INPUT)
|
2017-04-14 01:12:22 +02:00
|
|
|
val jtag_mfr_id = UInt(INPUT, 11)
|
2017-03-28 06:19:08 +02:00
|
|
|
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())
|
|
|
|
|
2017-04-19 22:17:22 +02:00
|
|
|
val busyResp = Wire(new DMIAccessCapture(debugAddrBits))
|
2017-03-28 06:19:08 +02:00
|
|
|
val nonbusyResp = Wire(new DMIAccessCapture(debugAddrBits))
|
2017-04-19 22:17:22 +02:00
|
|
|
val dmiResp = Wire(new DMIAccessCapture(debugAddrBits))
|
|
|
|
val nopResp = Wire(new DMIAccessCapture(debugAddrBits))
|
|
|
|
|
2017-03-28 06:19:08 +02:00
|
|
|
|
|
|
|
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)
|
2016-08-19 18:46:43 +02:00
|
|
|
}
|
2017-03-28 06:19:08 +02:00
|
|
|
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.
|
2017-04-19 22:17:22 +02:00
|
|
|
// But there is actually no case in the current design where you SHOULD get an error,
|
|
|
|
// as we haven't implemented Bus Masters or Serial Ports, which are the only cases errors
|
|
|
|
// can occur.
|
2017-03-28 06:19:08 +02:00
|
|
|
nonzeroResp := stickyNonzeroRespReg | (io.dmi.resp.valid & (io.dmi.resp.bits.resp != UInt(0)))
|
2017-04-19 22:17:22 +02:00
|
|
|
assert(!nonzeroResp, "There is no reason to get a non zero response in the current system.");
|
|
|
|
assert(!stickyNonzeroRespReg, "There is no reason to have a sticky non zero response in the current system.");
|
2017-03-28 06:19:08 +02:00
|
|
|
|
|
|
|
busyResp.addr := UInt(0)
|
2017-04-19 22:17:22 +02:00
|
|
|
busyResp.resp := Fill(DMIConsts.dmiRespSize, 1.U) // Generalizing busy to 'all-F'
|
2017-03-28 06:19:08 +02:00
|
|
|
busyResp.data := UInt(0)
|
|
|
|
|
2017-04-19 22:17:22 +02:00
|
|
|
dmiResp.addr := dmiReqReg.addr
|
|
|
|
dmiResp.resp := io.dmi.resp.bits.resp
|
|
|
|
dmiResp.data := io.dmi.resp.bits.data
|
|
|
|
|
|
|
|
nopResp.addr := UInt(0)
|
|
|
|
nopResp.resp := UInt(0)
|
|
|
|
nopResp.data := UInt(0)
|
2017-03-28 06:19:08 +02:00
|
|
|
|
|
|
|
//--------------------------------------------------------
|
|
|
|
// Debug Access Chain Implementation
|
|
|
|
|
2017-04-19 22:17:22 +02:00
|
|
|
dmiAccessChain.io.capture.bits := Mux(busy, busyResp, Mux(io.dmi.resp.valid, dmiResp, nopResp))
|
2017-03-28 06:19:08 +02:00
|
|
|
when (dmiAccessChain.io.update.valid) {
|
|
|
|
skipOpReg := Bool(false)
|
|
|
|
downgradeOpReg := Bool(false)
|
|
|
|
}
|
|
|
|
when (dmiAccessChain.io.capture.capture) {
|
|
|
|
skipOpReg := busy
|
|
|
|
downgradeOpReg := (!busy & nonzeroResp)
|
2017-04-19 22:17:22 +02:00
|
|
|
stickyBusyReg := busy
|
2017-03-28 06:19:08 +02:00
|
|
|
stickyNonzeroRespReg := nonzeroResp
|
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------
|
|
|
|
// Drive Ready Valid Interface
|
|
|
|
|
2017-04-19 22:56:47 +02:00
|
|
|
val dmiReqValidCheck = Wire(init = Bool(false))
|
|
|
|
assert(!(dmiReqValidCheck && io.dmi.req.fire()), "Conflicting updates for dmiReqValidReg, should not happen.");
|
2017-04-19 22:17:22 +02:00
|
|
|
|
2017-03-28 06:19:08 +02:00
|
|
|
when (dmiAccessChain.io.update.valid) {
|
|
|
|
when (skipOpReg) {
|
|
|
|
// Do Nothing
|
2017-04-19 23:07:21 +02:00
|
|
|
}.elsewhen (downgradeOpReg || (dmiAccessChain.io.update.bits.op === DMIConsts.dmi_OP_NONE)) {
|
2017-04-19 22:56:47 +02:00
|
|
|
//Do Nothing
|
2017-04-19 22:17:22 +02:00
|
|
|
dmiReqReg.addr := UInt(0)
|
|
|
|
dmiReqReg.data := UInt(0)
|
|
|
|
dmiReqReg.op := UInt(0)
|
2017-03-28 06:19:08 +02:00
|
|
|
}.otherwise {
|
2017-04-19 22:17:22 +02:00
|
|
|
dmiReqReg := dmiAccessChain.io.update.bits
|
2017-03-28 06:19:08 +02:00
|
|
|
dmiReqValidReg := Bool(true)
|
2017-04-19 22:56:47 +02:00
|
|
|
dmiReqValidCheck := Bool(true)
|
2017-03-28 06:19:08 +02:00
|
|
|
}
|
|
|
|
}
|
2017-04-19 22:56:47 +02:00
|
|
|
|
|
|
|
when (io.dmi.req.fire()) {
|
|
|
|
dmiReqValidReg := Bool(false)
|
|
|
|
}
|
2017-03-28 06:19:08 +02:00
|
|
|
|
|
|
|
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
|
2017-04-14 01:12:22 +02:00
|
|
|
val idcode = Wire(init = new JTAGIdcodeBundle().fromBits(0.U))
|
|
|
|
idcode.always1 := 1.U
|
|
|
|
idcode.version := c.idcodeVersion.U
|
|
|
|
idcode.partNumber := c.idcodePartNum.U
|
|
|
|
idcode.mfrId := io.jtag_mfr_id
|
2017-03-28 06:19:08 +02:00
|
|
|
|
|
|
|
val tapIO = JtagTapGenerator(irLength = 5,
|
2017-10-31 23:33:41 +01:00
|
|
|
instructions = Map(
|
|
|
|
dtmJTAGAddrs.DMI_ACCESS -> dmiAccessChain,
|
2017-03-28 06:19:08 +02:00
|
|
|
dtmJTAGAddrs.DTM_INFO -> dtmInfoChain),
|
2017-04-14 01:12:22 +02:00
|
|
|
icode = Some(dtmJTAGAddrs.IDCODE)
|
|
|
|
)
|
2017-03-28 06:19:08 +02:00
|
|
|
|
2017-04-14 01:12:22 +02:00
|
|
|
tapIO.idcode.get := idcode
|
2017-03-28 06:19:08 +02:00
|
|
|
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
|
|
|
|
|
2016-08-19 18:46:43 +02:00
|
|
|
}
|