Code Anatomy On DebugTransportModule #3017
DecodeTheEncoded
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
The Debug Transport Module(JTAG DTM specifically) translates the access of JTAG data registers(
dmi
jtag data register particularly) from JTAG interface into access request to debug module'sdmi
address space viadmi
bus. The holistic logic of JTAG DTM is coordinated via JTAG state machine, there are 1 instruction register chain(irChain
) and 4 data registers chains in RC impl(bypassChain
,idcodeChain
,dmiAccessChain
,dtmInfoChain
), The debugger can write to instruction register chain the intended data chain index(shift-in a series of bits intoirChain
in state ofShift-IR
, and during theUpdate-IR
stateirChain
will have the correctly set data chain index, and corresponding data chain will be put into the scan chain), then the debugger can shift-in bits into the corresponding data register chain in stateShift-IR
; Once the data register chain has been filled with purpose data, debugger will direct JTAG go toUpdate-DR
; TheUpdate-DR
andCapture-DR
are 2 crucial states for DTM, as depicted in the debug spec 6.1.5:After a data register chain is selected by the instruction register, needed bits will be shifted into that data register chain by debugger in
Shift-DR
; After all needed bits are shifted, the debugger will transfer state toUpdate-DR
, the implementation will conduct specific logic depending on the selected data register chain and contents in that register. Particularly , for instance, after thedmi
data register chain is selected in instruction register, inUpdate-DR
state a dmi bus request will be sent to debug module(TLDebugModuleOuterAsync
, specifically). After some time interval, the debugger will transfer the states toCapture-DR
, during this state, some implementation dependent info will be loaded into the selected data register chain. In terms ofdmi
data register,the result of a previous access to thedmi
address space will be captured into the selected data register(if the time betweenUpdate-DR
andCapture-DR
is less than interval needed for a DMdmi
address space access,busyResp
will be captured instead. If the DM itself returns an error, extra logic is needed to handle this situation, will talk later). The holistic io interface of the DTM is depicted in figure below:There are mainly 4 scala class&objects in the implementation, see detailed depiction below:
CaptureUpdateChain
CaptureUpdateChain
represents a jtag register chain, theio
ofCaptureUpdateChain
module is as follows:The
io
of a register chain has two parts:ModIO
withChainIO
, theChainIO
part is connected with JTAG Controller, it's used to shift in&out the register chain under control of JTAG state.val chainIn = Input(new ShifterIO)
is used for shift into the register, andval chainOut = Output(new ShifterIO)
is used for shifting out. The fields inModIO
arecapture
andupdate
, theupdate.valid
notifies outside world that JTAG state is inUpdate-DR
(this is asserted byupdate
ofchainIn
), it's time to conduct specific operations using the bits in this register,update.bits
carries the bits in the selected data register chain.capture
has two sub-ios:bits
carries data needed to capture(store) into this register chain.capture.capture
is output indicating the JTAG state machine now is inCapture-IR
orCapture-DR
, this signal is asserted by thechainIn.capture
coming from JTAG controller. The implementation ofCaptureUpdateChain
is pretty straightforward:There are other versions of register chains:
CaptureChain
for read-only jtag registers likeidcode
, andJtagBypassChain
for defaultbypassChain
. It could be confusing sometimes in terms of capture-read juxtaposition. Callidcode
read-only is from perspective of debugger, that register chain can actually be updated using the given values inio.capture.capture
. The debugger can shift that value out, but it can not shift in some new value into that register. Therefore, it's a read-only register for debugger. Also note that the inner representationregs
will always shifts its LSB bit out intoio.chainOut.data := regs(0)
, and the shift-in bits go to the MSB.JtagTapController
JtagTapController
controls the state of JTAG TAP, there is aJtagStateMachine
insideJtagTapController
, it outputs the current jtag states according itsio.tms
signal.JtagTapController
has one instruction register chain(irChain
) inside(there is only one instruction register inside RC), and will shift in&out that instruction register from&to thejtag.TDI
andjtag.TDO
, assert theio.chainIn.capture
andio.chainIn.update
ofirChain
under specific jtag state(Shift-IR
,Capture-IR
andUpdate-IR
).JtagTapController
can also interact with selected data register chain basically the same way with embedded instruction register chain(irChain
), but via its io signaldataChainOut
anddataChainIn
, the selected data register chain is indicated with signalactiveInstruction
, it's normally updated at the state ofUpdate-IR
using the shift-in value ofirChain
. It's worth noting the name convention here,io.dataChainOut
ofJtagTapController
goes to thechainIn
of the selectedCaptureUpdateChain
, and the thechainOut
of the selectedCaptureUpdateChain
will flow back toio.dataChainIn
ofJtagTapController
; Moreover,jtag.TDO
is driven byTDOdata
(tdo
registered) which may come from selected data register chain(currState === JtagState.ShiftDR.U
) orirChian
(currState === JtagState.ShiftIR.U
) depending on the current jtag state. TheJtagTapController
code base is as follows with some comments adding on:This object is mainly for dynamically connecting
JtagTapController
'sdataChainOut
anddataChainIn
with thechainIn
andchainOut
of the selected data chain. It also defines intermediate wire(internalIo
) to propagate io signaljtag
andcontrol
(reset) of DTM intoJtagTapController
, andio.output
ofJtagTapController
to elsewhere.In terms of dynamic connection,
controllerInternal.io.dataChainIn
(input to theJtagTapController
) needs to be driven by selected data register'schainOut
, furthermorecontrollerInternal.io.dataChainOut
needs to drivechainIn
of of the selected data register,chainIn
of unselected data chains needs to be driven byunusedChainOut
. This is why we needfoldOutSelect
andmapInSelect
. ThefoldOutSelect
function is very sleek, it constructs a series ofelsewhen
clauses to represent choose only 1 from many signal. ThemapInSelect
just constructs awhen(){}.otherwise{}
clause for each data chains'io.chainIn
. Below is the code base ofJtagTapGenerator
with comment:DebugTransportModuleJTAG
With depictions above, we can now begin to clarify the main part of DTM logic; In shorter words, code in main
DebugTransportModuleJTAG
handlesupdate
orcapture
request for every data register chain. Furthermore, most of the code is for handling thedmi
data register chain.From
CaptureUpdateChain
we know that each register chain already carriesupdate
andcapture
signal that is combinationally asserted by thedataChainOut
(of TypeShifterIO
) fromJtagTapController
. Consequently, there is few logic coupled withJtagTapController
inDebugTransportModuleJTAG
.When
dmi
chain has itsupdate.valid
asserting, it indicates a new access request to dmi address space of dm should be initiated, but there are exceptions, see code sequence below:There are situations that we can't initiate a new request(during
update
):1, busy during previous
capture
;When an access to dmi address space of dm is still ongoing when the debugger decides to
capture
thedmi
chain, the signalbusy
is asserted(busy := (busyReg & !io.dmi.resp.valid) | stickyBusyReg;
):Note the sticky version of
busy
:stickyBusyReg
; Theop
field ofdmi
data chain can be used to indicate whether a previous access request succeeded; There are 3 states forop
: success(0), failed(2), busy(3). This field is sticky in terms of debugger read(capture
) so that any failed access request before will cause that field set stickily--that is, unless explicit reset by writingdmireset
indtmcs
chain, this field will hold that failed code so that no new access can be initiated. The rationale of making this field sticky is well explained in the spec:According to
busy := (busyReg & !io.dmi.resp.valid) | stickyBusyReg;
, Each time we considerbusy
duringupdate
state, we actually take thebusy
flag of history access into account(stickyBusyReg
),stickyBusyReg
is set duringCapture-DR
, and will keep asserting until explicitly reset. Ifbusy
is asserted duringUpdate-DR
, the access request will be somehow bypassed(Do Nothing);When
busy
is asserted duringcapture
, thebusyResp
will be loaded into thedmi
chain:2, Incorrect response during previous
capture
There are another unexpected situation in terms of
capture
, the returnedop
code by dm throughdmi
bus is non zero, therefore indicating an implementation specific error happened. This status is also sticky in RC impl(stickyNonzeroRespReg
),stickyNonzeroRespReg
will keep asserting once there was an error before, like the way howstickyBusyReg
is reset . But another signaldowngradeOpReg
is introduced,downgradeOpReg
is set duringcapture
:downgradeOpReg := (!busy & nonzeroResp)
; Duringupdate
ifdowngradeOpReg
is found asserted(instead of using the always-assertingstickyNonzeroRespReg
), it means a previous access is incorrect, therefore this access request atupdate
should be canceled:However, during
update
,downgradeOpReg
will be deasserted(take effect one cycle later):Therefore, if a
capture
captures incorrect response, the next access request initiated viaupdate
will be bypassed, however, if there are continuousupdate
s, the subsequentupdate
except for 1st one will succeed. I have no knowledge to understand this design consideration, why can't we just use the sticky version of non zero resp:stickyNonzeroRespReg
to decide whether to initiate a proper access request?? --A series dmi writes????;If none of the
stickyBusyReg
anddowngradeOpReg || (dmiAccessChain.io.update.bits.op === DMIConsts.dmi_OP_NONE)
is asserted. A proper access request can be initiated:Assertion of
dmiReqValidReg
will valid the dmi.req:io.dmi.req.valid := dmiReqValidReg
, the related handshake logic is as follows:Once a proper access request is sent via
dmi
bus, after some time interval, the dm may return the access result through dmi bus. The debugger can direct the state machine toCapture-DR
to capture the result intodmi
data chain.Note the logic of asserting
io.dmi.resp.ready
, for write operations confirm resp immediately because we don't care about data, I feel confused on this, this meansio.dmi.resp.ready := io.dmi.resp.valid
in terms ofDMIConsts.dmi_OP_WRITE
(anytimeio.dmi.resp.valid
, this handshake is considered firing ). If the dm returns ack info for this write access when the jtag state machine is not inCapture
, there are chances that this ack-for-write will not be properly loaded intodmi
data chain. Maybe in real world, they just assume the write to dm always succeed??Note that the
stickyNonzeroRespReg
andstickyBusyReg
can only be explicitly reset bydtmInfoChain.io.update.valid
:Other part of the main
DebugTransportModuleJTAG
is easy to understand, once you read comments above, below is the code base with some embedded comment:Beta Was this translation helpful? Give feedback.
All reactions