找回密码
 注册
关于网站域名变更的通知
查看: 360|回复: 1
打印 上一主题 下一主题

#技术风云榜#jtag_tap模块分析

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2020-11-19 13:27 | 只看该作者 |只看大图 回帖奖励 |正序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x

, J4 \3 X* S1 Q) {3 U  A, \. \1 s+ w7 V引言" W4 e% y8 M& P0 m5 E: V
“知其然,还要知其所以然”,在搭建好ORPSoC的仿真环境和调试环境之后,我们有必要对仿真和调试系统中扮演重要角色的jtag_tap模块和adv_dbg_if模块进行进一步的分析,以了解其工作机制。" P- t. \% ?% b+ B1 K

) G& L: Z& I1 A9 l本小节就来分析advanced debug system中的tap_top模块。
3 \9 [5 W# J  h7 `. z, Y4 M* [5 `8 H2 M/ k

+ T6 i( H6 i: Z6 O0 y: R1,from SPI to JTAG' h2 ?! S7 J$ f# S
在分析JTAG的具体实现之前,我们先了解一下JTAGF的基本知识。* ?* [5 i& l5 C
$ j, p  t" }6 D, t: Q
A、JTAG协议的本质与SPI协议并没有什么不同,它等于一个复杂的SS状态机+变长的MOSI和MISO数据移位操作。不过所谓的变长,都是事先约定好的。 + Y, z2 \+ o, c- T& `' H
B、JTAG协议是一个同步通讯协议,它是全双工的。它的通讯原则是“以物易物”——即你如果想得到某些东西,你必须先给与相同长度的内容;你如果只是想发送一些数据,也会自动获取相同长度的内容,至于交换的内容是否有意义,这是另外一回事了。  ( y+ M6 j4 s/ N$ N" p6 R/ ^; N3 p
C、JTAG协议无论多么复杂,实际上只有4根线起作用(有时候还有两根鸡肋的nSRST和TRST),他们分别是TMS、TCK、TDI和TDO,他们分别对应SPI协议里面的SS、SCK、MOSI和MISO。在本质上,他们并没有什么不同。即便是ARM的JTAG那么多的引脚,实际上起作用JTAG的也就这4根线而已。
) f1 L- e/ ]/ K) bD、JTAG的数据操作都是基于移位寄存器的。 5 p: g  ?3 U# N1 M4 L, h5 I6 O
E、如果JTAG协议在某个下载仿真协议中只是用来发送控制信息和少量的数据,而大量的数据传输是通过额外的其它引脚进行的,即便这个协议被称为JTAG仿真其本质也早已超过JTAG了,严格来说,不应该称之为JTAG。因为JTAG协议中就只有4根线(有时候也算上nSRST和TRST)而已。典型的如NEXUS协议。
, b9 r5 `( r+ y" S" S% n7 |4 M+ o- `! D6 @" T* I7 B& w
这里面重点理解的是“以物易物”,这个概念,下面是SPI的工作机制以及one-to-one和one-to-many的组织。如下图所示:2 t$ \# Z9 g" j$ w; j
! t. L) m; D0 g' c2 z
( _5 v* ?/ u- J3 g) v- O

% W  l% v- Y+ E$ \4 K$ q) U; U' L9 T/ }% T* M
2,jtag_tap( p& n5 h6 P9 X# D/ ]  z
' T: H  [7 h4 Y, ]: ?. y$ M9 m
! S+ {$ I& a& S* J, {
1>architecture' o) T% V' G* ~: J7 N( x
  X+ {& S7 r7 t! F& g( }+ F: J( ?
TAP(test access port)的作用是提供adv_dbg_if模块和外部JTAG cable之间的桥梁作用,负责将jtag cable传来的数据传给tap支持的所有device,并将来自device的数据shift out到tdo上。
# a7 v( M- W, e) ~3 l" M( N下面是adv_debug_sys系统硬件部分的结构:  o, v3 C* _2 s2 z: R( |, ^, h0 o# ]

) _2 G6 {" Z. c( Q/ D1 B% {, I
0 E, a$ O1 J5 R  M 0 \* ~9 n: h$ [
jtag_tap一共支持4个chain(相当于SPI中SPI总线上挂有4个device),其中IDCODEchain在jtag_tap模块内部,其它3个在外部,如上图所示。
1 f* E% c6 J; \: C7 @, C
; E+ B0 N( `/ b. I5 a1 P; p# E# S" ^2>tap fsm
( i5 O8 m2 `0 F& _IEEE 1149.1中定义了FSM,所以几乎所有的JTAG模块都会实现相同的FSM。如下所示:
" m/ D) n8 w" w8 M' G1 _5 [
; \: l- o; T3 P- ]8 O 8 q1 R8 }9 E8 E0 D
- e; M3 }0 M% {
/ t( `' R/ @+ \+ Z
说明:: H1 P) Y7 G3 F) S' S) v
整个状态机分为三个部分:信道选择部分、数据信道和指令信道。所谓的信道选择,就是图中最顶上由四个状态组成的矩形,分别对应着四个状态:
6 F- @3 c" H( a: |0 ia,JTAG TAP状态机复位状态  
) U5 V, f$ i% X( ^; O, c顾名思义,就是进入该状态,将导致整个硬件TAP控制器复位,所有的寄存器都将被初始化。在TCK的上升沿,TMS为低电平时,进入下一个状态;否则保持不变。
/ L" t/ l2 C5 gb,JTAG TAP的Run-Test/Idle状态  
4 ]. a( w1 o4 T其实就是“开工”和“休息”的选择分支点。在TCK的上升沿,TMS的高电平将导致状态切换,进入数据信道的通讯状态;否则保持不变。
! W' I  G& |+ G& z; L4 Y: rc,JTAG TAP的Select-DR Scan状态  : F7 b- j9 d; s
Select DR Scan,就是当我们在该状态下,TCK的上升沿读取到了TMS的低电平将直接进入数据信道的操作子状态机;在TCK的上升沿读取到了TMS的高电平,将切换到指令信道的通讯状态。  8 c6 ]6 B: K, N, }$ \9 B- h( u, a
d,JTAG TAP的Select-IR Scan状态  
0 q* y% S5 |! c7 l' i1 P0 ]* ?, B4 FSelect-IR Scan,就是当我们在该状态下,TCK的上升沿读取到了TMS的低电平将直接进入指令信道的操作状态机;在TCK的上升沿读取到了TMS的高电平,将重新回到JTAG的复位状态。  7 @+ U. C! y; M: A' x" \) b4 b
数据信道和指令信道对应着两个子状态机,从本质上数据和指令并没有任何不同,只是习惯上,指令的长度固定为4个二进制位(AVR32的JTAG是5个),而数据则随着不同的指令选择了不同长度的指令寄存器,这个就需要具体查阅相关的协议说明了,比如JTAG IDCODE的长度固定为32位,而AVR32的复位指令却有5位。下面,只就常见的几个状态进行解释(以数据信道为例)。
, G; M: K0 m, w: b6 l  \
. o7 o; Z2 ~& l4 {  _3 R/ z7 C9 d4 H$ H, j2 B/ U
a,Capture DR状态  " P! C9 w8 W6 g* e% m
JTAG协议是基于移位寄存器的,其通讯具有“以物易物”的特性,在我们进入真正的数据传输之前,需要告知JTAG“准备通讯了哦?你有没有东西要给我哈?”,于是Capture DR就是一个给JTAG机会将需要传达给我们的数据放入指定的移位寄存器中的状态。  
" d/ O& E2 S% h" t6 R: lb,Shift DR状态  
6 ~) b6 h9 y  M/ j% f2 w- R& Z这个状态就是通过TDI和TDO进行数据传输的状态。需要说明的是,即便进入了该状态,TMS上的电平在TCK的上升沿也是会被读取的,从图中看到,一旦在TMS上读取到高电平,系统就会跳出Shift DR状态  
0 z# W1 @) @- P9 D5 G) k$ X2 M如果此时数据没有传输完成,造成的后果是不确定的。请大家注意,我所说的是不确定,而不是“很严重”:同样是因为移位寄存的传输特性,有时候并不要求一定要将所有的数据都完整的进行传输,比如在AVR32中,针对SAB的数据操作,往往只需要进行最关键的部分,详细地内容可以参照相关的数据手册;
* Y+ O. U7 x4 p但有的时候,数据的不完整传输则会导致很严重的后果,这取决于具体的JTAG通讯协议。所以,为了保险起见,一旦进入Shift DR状态,在发送最后一个数据之前,请保持TMS为低电平,当要发送最后一个数据时,应该将TMS设置为高电平,这样,当TCK跳变为上升沿时,系统既完成了最后一个数据的传输,也成功的退出了Shift DR状态。  
6 S# z( G. Z& ]& s4 ]9 Nc,Exit1 DR状态  
, R5 ^5 B0 c6 y) n4 @; U' y1 V; T该状态提供了我们一个在刚才输入的数据生效前,重新修改的机会。一般情况下,我们直接保持TMS的高电平,并在TCK的上升沿驱动TAP状态机,直接进入Update-DR状态。  * m& y! t' ~' J& A2 k" \6 y' Y
d,Update-DR状态  
% U4 {: R0 x; Z' I  Z顾名思义,就是使我们输入的数据生效——一般JTAG内部的动作就是触发一个锁存信号,将移位寄存器中的内容并行的读取到对应的寄存器中。Update-DR有两个出口,一个是,TMS的低电平对应Run-test/ Idle,还有一个是TMS的高电平对应的Select-DR Scan。这两个操作看似区别不大,但是意义非凡。前者往往会导致JTAG内部产生额外的时序(比如发生一个信号,表示完成了一个特定的周期操作,在AVR的JTAG下载中有此实例);后者则表示完成了一次数据操作,将进行下一个数据的操作,但是这些操作属于同一个操作周期。当然有些情况下,这两种方法是没有区别的。 / J0 H; M4 F$ e& n4 T5 D" U
9 ^8 m; j# i$ e7 d1 V" A0 ?
3,RTL分析
2 J+ t. H2 n7 Rjtag_tap是advanced debug system项目的一部分,整个advanced debug system我们之前已经介绍过,如有疑问请参考。- y" C0 |# B  u7 D
jtag_tap模块包含两个RTL文件:tap_defines.v和tap_top.v两个文件。
: L' {4 t7 U$ u9 m: C在了解了JTAG的一般知识之后,我们下面就分析jtag_tap的RTL。
* F- A4 |# Y; m! Z5 [6 Z7 e0 z; u* I+ t( d
1>tap_defines.v
! p0 c8 S" |  a0 ^! E( Q+ h首先,其内容如下:
/ g( t# x: q/ g" ^1 A7 W
. w) E2 k( V) H) e$ j! X7 z4 Y+ ^# s) A+ ?0 v
// Define IDCODE Value
( S8 h# K& E0 n0 o. h( N; N`define IDCODE_VALUE  32'h149511c36 E5 d5 k' S5 f9 z# g7 U' T
// 0001             version
2 j  F- I7 Y# ]+ J  ]8 g/ n. n// 0100100101010001 part number (IQ)3 H' ^9 U: X1 g$ S% H* S
// 00011100001      manufacturer id (flextronics)
& Z, S1 d, D. w8 |- J. I) d* g2 z// 1                required by standard: J6 J9 j( [/ x, S

4 k- T. F& a5 X- |9 N. W8 e' z  b- r) ?// Length of the Instruction register; Z! ?5 y# R5 s$ X, M! G4 O: ?# T
`define        IR_LENGTH        4
/ C  K- V& x" m3 \( G- P$ o 1 p  i9 f0 @! C! k: q6 i
// Supported Instructions; n7 Q! f% F+ Z; T* ?1 Z3 [5 }. ~4 V/ ]
`define EXTEST          4'b0000
) S0 y" T) Z7 a  z9 |9 e8 K`define SAMPLE_PRELOAD  4'b0001( n9 s5 N, f9 t( A  j4 A
`define IDCODE          4'b00101 g% J3 @9 e1 _3 k% Z7 s
`define DEBUG           4'b10003 Y2 H5 h7 {# j# v* v; R* L8 ]5 \) P
`define MBIST           4'b1001- R, v0 t, t$ q, R; q: T8 Q
`define BYPASS          4'b1111
# _3 M( k! L+ r, C$ M* i* p/ s- X4 D
文件包含三部分内容,IDCODE,IR_LENGTH,和instruction定义。
& S+ z5 W! u7 r& E% Oa,一般情况下,每个jtag device对应唯一的一个IDCODE,就像人的名字一样,这个名字用来jtag chain建立的时候‘点名’用的。jtag chain初始化时,读取所有的device的IDCODE,和BSDL文件中的IDCODE比较,获得device name,显示出来。
8 N4 [8 ]2 v% X+ T
* s  a* W3 O% m3 z  ib,IR_LENGTH是一个非常重要的变量。上面我们在介绍JTAG时,说过,JTAG是基于shift register的总线,所以这个shift register长度的重要性不言而喻。IR_LENGTH的值不是随便设置的,而是根据其对应的jtag具体实现来决定的。大多jtag tap的IR_LENGTH都是4(bit),当然也有不是4的。比如我用的ML501的板子上的4个jtag device的IR_LENGTH分别是10,8,8,12。
# N! a8 [; G: D& m' D7 n6 C( D
1 I# n: J% [6 qc,instruction就是操作jtag tap模块支持的指令,这个参数,不同的tap大不相同,具体支持什么指令,可从bsdl文件中获得。下面就是jtag_tap的bsdl文件:! O! |/ Y1 |% I3 U9 T
1 \5 d0 ^5 ~: i/ n* k  p6 N& t

7 }, i# y/ u$ L1 z9 p) u& C& h-- This is a minimal BSDL file describing the particulars
$ I" ]# n3 @) O6 T+ k* B( l-- of the OpenCores standard / native TAP.  It is designed, @* v- N  i& I" K: E6 v# d
-- only to be used by the adv_jtag_bridge program.  This: X* k: u: e( d' M: Z) u' J
-- file almost certainly lacks key entries and attributes
1 z' P# W6 f/ Y& t-- required by other JTAG / BSDL systems.
. T. e% t, j& [8 Q; f% i--
; y! J: r: U& c  h-- by Nathan Yawn (nathan.yawn@opencores.org)+ w# o% n. }' V9 C$ m7 x0 O
-- Copyright: This file is released into the public domain., R6 _4 I- B5 a( A
--
3 @2 W8 ?9 n# R  P/ ~/ a) e 4 L5 A4 B6 z9 R7 F+ z

3 l/ ?, x( b! X. zentity OC_TAP is
! B* z* C# S* d, ]
: H6 R) f8 O; G* f3 y
# i% n& U1 s- K3 A* \9 battribute INSTRUCTION_LENGTH of OC_TAP : entity is 4;
4 n* S3 u7 }" C5 I! D
7 h) z0 T( R# f( o9 V, Y
( x( m1 `. C% n' x  o8 {attribute INSTRUCTION_OPCODE of OC_TAP : entity is
  }$ ~3 r. T; C" p, U- E        "EXTEST            (0000)," &
2 f/ c: Z7 z8 F% h% k4 N* l        "SAMPLE_PRELOAD    (0001)," &, j& A) j- e# }( Q. `
        "IDCODE            (0010)," &. S' l+ _5 S5 |* H- D, W0 O6 |" o
        "MBIST             (1001)," &5 N0 k6 A; \" Q6 w; I1 K
        "DEBUG             (1000)," &) b1 p3 P; o# d8 Q) y0 F+ B; \
        "BYPASS            (1111),";
: M/ q4 p1 |( j , F' E; m6 F  \# K3 R& Z& N& h

# r1 w4 P$ O8 t# ]0 F7 w  [attribute IDCODE_REGISTER of OC_TAP : entity is7 e2 ?/ {+ d- f- W+ ?  F
        "0001" &        -- version: J  J7 j  e& O, m
        "0100100101010001" &        -- part number# W6 @. s2 P2 ~* U0 [: |& o
        "00011100001" &        -- manufacturer (flextronics)
( R9 H( ~) N8 U* \        "1";                -- required by 1149.1$ Y8 i7 R. {, s* ~5 |8 S' n
/ R6 M; R& s- |3 U# h/ s

3 P: O& b1 T2 s' [8 yend OC_TAP;
9 W1 J7 b% F( n( ~, |/ K
5 {& n6 n: _2 e( O9 g! @1 ]) I/ n0 p
+ a. ^) }( t; P  b: r1 V2>tap_top.v/ a% N; Z3 y2 o! c5 v8 A
这个文件是jtag_tap模块的具体逻辑实现文件。1 `! m, L5 u/ Z& b; J+ X
a,接口定义6 s/ z, f6 c& U( k9 H6 l4 P
4 R) W) s, r# ]  E& b! E6 h

) v9 j$ G3 |; f// Top module. g# z8 L4 |( l) u
module tap_top(
6 t0 O, r) r* \+ N% P- J                // JTAG pads
* A0 F" p% K) r  h- l4 ^6 I$ T" a* k                tms_pad_i,
# L1 w5 R  d2 T8 ]( e                tck_pad_i,
/ y* p; e' n  Y' \: n; i& \. f$ `                trstn_pad_i,
" u: n7 |) x+ `7 r% p7 r3 B, G                tdi_pad_i, " z: p6 c! H- g& ^2 e2 B
                tdo_pad_o, ) _' E4 m, ?2 L+ ~( z0 S2 u
                tdo_padoe_o,' ^" ^# J' P8 R, V) S: h

3 m4 `# y; ^2 R: a/ @                // TAP states
7 H+ W! D% u+ w8 t# w                                test_logic_reset_o,
3 H$ s) `; o* u+ x8 E  H+ a                                run_test_idle_o,3 I4 c# P5 j' p& X+ R
                shift_dr_o,
1 l0 g; B7 p9 X8 P& N7 e& R                pause_dr_o,
; k6 |/ f& J2 ]3 c- C2 e                update_dr_o,6 o8 ]2 h( M9 X/ {* J% X
                capture_dr_o,
5 B. _2 z1 a0 C( F' T+ h+ L) F               
% W+ F8 i5 }) j7 y7 p- {# V                // Select signals for boundary scan or mbist
7 T' l' I2 a* ]5 z                extest_select_o, + T8 v1 K* ^% }
                sample_preload_select_o,
4 k0 e- |( ^" D1 u                mbist_select_o,
) a: {9 G! o2 }0 f0 I0 i7 u                debug_select_o,) }- f- a! ~5 h
               
" l4 W4 [+ o, Y! p+ i8 r                // TDO signal that is connected to TDI of sub-modules.
2 ?9 j1 V7 y# f* u  f8 x                tdi_o,
+ J7 P$ F1 F$ i# i7 L                % L9 E# c+ |) G, ~: j; e
                // TDI signals from sub-modules
4 j; L0 C2 @: X& G+ y& @) c                debug_tdo_i,    // from debug module$ B5 ]( l8 P' A! g
                bs_chain_tdo_i, // from Boundary Scan Chain. T$ c* F/ @! V+ @/ U. |
                mbist_tdo_i     // from Mbist Chain
: J- o% J/ r" u. v' ?              );
& @+ m2 Y" p9 ^- C2 B$ D: a2 r- `( _1 c  f9 q: G
jtag_tap接口可分成5个部分:jtag信号,tap states信号,片选信号,tdi_o,以及从device来的数据信号。4 C) q- t  k' k
1》首先是jtag信号,除了我们常见的tms,tck,tdi,tdo之外还有两个鸡肋信号:trstn,tdo_oe,前者用来复位tap,后者用来使能tdo。其实这两个信号有没有都可以,tap的复位可以通过tms来实现,tdo使能也可不用。' w  [, C' X- B8 U. ^' l
说到这里,有一个小问题,如果不用trstn信号,上电之后tap的状态是随机的,那么有没有一个固定的tms序列来实现tap的复位呢?答案就在本小节中,如果有疑问的话就找找看吧。  x/ O% q% T; K' s2 s
2》其次是ap states信号,给device用的,指示tap的当前状态,device根据这个状态来完成某些操作。
, R9 X- k% }( P& ]8 x) Z& d) x. h( b3》片选信号,这个就不用多说了。对于jtag_tap来说,片选信号时根据IR reg中的不同位来决定片选的。2 W& ?! _" ~. ~* y( P& O( Z
代码如下:
; p4 @" i  w' r
7 n2 h. @; y2 n. E2 L& R/ \1 S+ A
/**********************************************************************************- |( ^. t: K6 t  G; l9 L) `4 J
*                                                                                 *
* k, A1 c2 H/ H6 ^*   Selecting active data register                                                *" h" I" i/ K0 Y  a
*                                                                                 *
% _; ?4 N& B6 w2 h7 r2 t% P  {**********************************************************************************/3 y) E; U9 c* c" Y* @, \0 W/ p
always @ (latched_jtag_ir)
  ]/ D* o. W* ]. nbegin
* K$ {9 X0 y0 P, W/ c! V1 F# e7 q  extest_select           = 1'b0;
: x% i% i5 `. g- E* {  sample_preload_select   = 1'b0;+ p* X5 F8 n! ?! g7 B
  idcode_select           = 1'b0;
4 C. G2 S, t8 r5 i* z$ J  mbist_select            = 1'b0;) ?- \  y6 F, o. f" z3 z+ ?8 @! `0 r6 G
  debug_select            = 1'b0;
. g, A7 F+ t, o8 J2 v( \" w3 T  bypass_select           = 1'b0;
' \/ g) q3 K2 p- Y& I% ` 7 [7 z! S8 V) y4 {' b
  case(latched_jtag_ir)    /* synthesis parallel_case */ ' p3 f, \, w4 B( n# }
    `EXTEST:            extest_select           = 1'b1;    // External test( w5 ?- Q" r6 x. s( x3 p- r5 }
    `SAMPLE_PRELOAD:    sample_preload_select   = 1'b1;    // Sample preload6 w9 c" W% n" r* `
    `IDCODE:            idcode_select           = 1'b1;    // ID Code
7 J1 s& x0 m4 n: K9 M3 @$ Z6 m    `MBIST:             mbist_select            = 1'b1;    // Mbist test
" p5 T  l' U# q* I    `DEBUG:             debug_select            = 1'b1;    // Debug
1 m4 S1 }7 k: A0 q    `BYPASS:            bypass_select           = 1'b1;    // BYPASS
( J* |/ [6 i! Z7 Z, e    default:            bypass_select           = 1'b1;    // BYPASS
7 b  t, e  m% |+ Y  v' b  endcase
( @! _3 ~3 z" ?1 Y5 v9 i0 z) |' _% Qend4 Q  o# y1 D* `8 A

! e7 y1 k' g! t9 ^; F
( n1 I$ u3 O+ H
# @, m  N9 k8 j6 pb,fsm
9 x7 v( I) s" Gjtag_tap的核心就是tap controller的FSM了,常见的三段式风格:1 @: F0 k; W7 C) ^4 r7 l

3 z+ ?( S$ G/ P, o
) G3 r1 _- L3 s/**********************************************************************************
4 ]# o/ P8 p' w2 h# Q/ ]! w# u*                                                                                 *
' O# Q9 Q% ]% V. ~*   TAP State Machine: Fully JTAG compliant                                       *
" A! h, @  D7 b& \/ `8 k) E& u& C*                                                                                 *
& n: ?4 L; r: C: X3 n4 O( N8 m**********************************************************************************/
. b: c$ p- O2 m7 e// Definition of machine state values.  We could one-hot encode this, and use 164 x( [2 T9 X& @3 E6 o, }
// registers, but this uses binary encoding for the minimum of 4 DFF's instead.
( W$ g4 ~4 O& J: s/ i8 J: y% `3 i`define STATE_test_logic_reset 4'hF
7 _. \# j3 Z' `- G* m`define STATE_run_test_idle    4'hC
$ u! G5 }5 B$ R3 e6 o8 d`define STATE_select_dr_scan   4'h7
% o2 K- G  q3 R+ E' ^' Q* D`define STATE_capture_dr       4'h6- c. I- M4 Y7 Q; t- q0 L& l
`define STATE_shift_dr         4'h2
) O+ b" Q& ]5 R# i1 n4 {`define STATE_exit1_dr         4'h1
! M- k0 D: [" w* g2 \`define STATE_pause_dr         4'h3
; r" K* t4 K$ X8 }( m`define STATE_exit2_dr         4'h0' q5 ]; O: J  w  b* Z' x# q/ Q
`define STATE_update_dr        4'h5
$ w3 w- a- B8 o/ i  ^' ?1 H`define STATE_select_ir_scan   4'h4: d, i2 P9 ?2 g5 ~; K  T% W1 z# I
`define STATE_capture_ir       4'hE
3 M9 I. Y2 X. U( M+ W`define STATE_shift_ir         4'hA
  W5 Q* }0 i7 |/ ^`define STATE_exit1_ir         4'h9$ f9 D1 W8 Z1 U
`define STATE_pause_ir         4'hB
" N$ v; `3 U2 ?7 J9 B6 ``define STATE_exit2_ir         4'h8
$ D, l% l' z0 u- J`define STATE_update_ir        4'hD! N, b) g0 y% D5 a6 g" r% ^  r

2 l9 ^- l3 _3 Zreg [3:0] TAP_state = `STATE_test_logic_reset;  // current state of the TAP controller1 i, C5 `4 O% a0 w) F; u/ z# V
reg [3:0] next_TAP_state;  // state TAP will take at next rising TCK, combinational signal7 \0 [- ^8 u' d1 T( @) B
! E. o* Y0 n4 i& T1 V
// sequential part of the FSM
! ~$ l( x1 b: J- X6 ralways @ (posedge tck_pad_i or negedge trstn_pad_i)
- N) K& d: a+ ]% G( ]4 Qbegin
3 Q+ Y" Q/ Y4 X4 y        if(trstn_pad_i == 0)7 i3 V) C4 Q( f% e
                TAP_state = `STATE_test_logic_reset;
6 _( ~8 `- `! |! F- H6 r        else
! l- z9 z& i3 l- C! P  \                TAP_state = next_TAP_state;
4 T7 i0 j8 Q# ]6 Y, y& [end
8 h4 V: {. E- r
9 N, Y: B  T& ]2 }3 ^# T/ W
8 y4 `: E/ e: ~7 J5 Q# W5 s, y// Determination of next state; purely combinatorial
, R) F, X( R. T' e4 n/ @9 Ialways @ (TAP_state or tms_pad_i)* Q- x4 I: r6 [  P
begin
1 K8 ?& w; i: k& M! F7 P% R- G) p& E        case(TAP_state)
# K/ Y  _" n( L. y( c4 ^                `STATE_test_logic_reset:) o% B' j/ ]% S3 Z- ~; c5 r$ |
                        begin
' G, {* {: j8 w; h                        if(tms_pad_i) next_TAP_state = `STATE_test_logic_reset;
, i% X; d* L9 g! q9 j1 [: L0 A                        else next_TAP_state = `STATE_run_test_idle;0 W& ?' h8 G$ Y1 l. B
                        end
: u1 F" N+ w: a$ k9 d; S/ Z+ D. G                `STATE_run_test_idle:
4 s/ E% R9 B+ ?1 h                        begin
7 O( F" D$ M6 {. H( ^                        if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan;
- w( o/ G! j9 {. }) J1 R7 J                        else next_TAP_state = `STATE_run_test_idle;6 L4 }# d: u4 ?8 K& T# v
                        end. i$ u+ x: Q! r) t% {  t6 n) J$ n  F
                `STATE_select_dr_scan:# F$ J8 D& l' [  k7 D
                        begin
( W( ]8 m  B; `! s9 ?                        if(tms_pad_i) next_TAP_state = `STATE_select_ir_scan; # I# N5 [( A" N# U  U
                        else next_TAP_state = `STATE_capture_dr;' P) q& }* w% J) P9 Y
                        end
: _! n- y2 [9 G8 M# e1 F, Y                `STATE_capture_dr:
& q7 O) u  h5 }$ e                        begin
% P9 K" Q9 ^; C& w0 r. p                        if(tms_pad_i) next_TAP_state = `STATE_exit1_dr; / U) y6 ~  ~& x" i# O! d; ~  N
                        else next_TAP_state = `STATE_shift_dr;
0 z& ~5 k6 w; L0 ^                        end
7 a" u/ v6 m. p5 m1 I" F/ l                `STATE_shift_dr:' I$ @" j9 S- j! A. H2 k/ l9 z8 g
                        begin
! p5 \5 w/ ~1 I# Q3 V% }, b                        if(tms_pad_i) next_TAP_state = `STATE_exit1_dr;
# z9 [. A$ ?/ `5 S; W0 ?" n                        else next_TAP_state = `STATE_shift_dr;0 r+ X0 q8 D$ s6 ^9 m
                        end
/ ^; r& g) G( F0 b                `STATE_exit1_dr:' ?5 J0 b" K5 X# ]+ B) S  o( G9 b# W1 J
                        begin" c( }% q) u. _. s9 i1 x6 h. m
                        if(tms_pad_i) next_TAP_state = `STATE_update_dr;
: o% ~5 i( m2 O9 g                        else next_TAP_state = `STATE_pause_dr;
* J# m3 K" t9 d4 H7 h! Z                        end
/ M- s2 T+ C6 J/ x! ?8 P% s                `STATE_pause_dr:
) X8 V- u+ M, D                        begin
( N5 O& Z+ I  Z( ^" b' K0 J                        if(tms_pad_i) next_TAP_state = `STATE_exit2_dr; 8 o/ L, w2 h- m' u/ `: ?
                        else next_TAP_state = `STATE_pause_dr;
- w/ g' ?$ ?* r2 u9 A                        end" n4 i) T: Z* j7 w  W3 x' c- J5 c
                `STATE_exit2_dr:( E, m" J8 q5 w3 Y$ A" P# C
                        begin
4 ?# r* _7 `+ B- ]                        if(tms_pad_i) next_TAP_state = `STATE_update_dr; ( ?8 @9 G! u* x' u+ u
                        else next_TAP_state = `STATE_shift_dr;+ f- O9 P  U: y' ^
                        end) z. E& ~" N1 I
                `STATE_update_dr:
" B' H& {) n" s' F5 I( j9 z' G                        begin
5 r4 m% E  _0 @2 i( K* k                        if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan; 2 u- w7 c0 l- x4 k' B+ a/ F
                        else next_TAP_state = `STATE_run_test_idle;
; x7 r' M  e. L9 r! T                        end
/ }: P# x. l! h" i& d0 C                `STATE_select_ir_scan:% A: Q. N! q! D8 b4 L
                        begin
  N: Q5 G+ Q& @* W                        if(tms_pad_i) next_TAP_state = `STATE_test_logic_reset;
- A- e- j. b3 f3 m8 m$ m* X5 L8 c                        else next_TAP_state = `STATE_capture_ir;
9 N0 g6 I6 P. K                        end) w! j, q' l5 z+ Z' `- h
                `STATE_capture_ir:
6 z( c6 m7 ?) V                        begin
  N$ m$ n( s! e6 G. q1 q- F: ]7 e                        if(tms_pad_i) next_TAP_state = `STATE_exit1_ir; 0 G7 ]0 H4 m- Z' {& u) K
                        else next_TAP_state = `STATE_shift_ir;. H2 q: V% n- Q6 N; z
                        end
  o) y; V+ c9 ~$ M# X. b3 c% U* e! s                `STATE_shift_ir:) w4 f1 h$ V3 @2 M) O# D
                        begin
+ s, k/ h# L8 o$ P                        if(tms_pad_i) next_TAP_state = `STATE_exit1_ir; 5 ~8 n. O& r! P
                        else next_TAP_state = `STATE_shift_ir;
7 N7 G) @3 T3 m: [" G                        end2 o; \8 N+ `& ^
                `STATE_exit1_ir:" e4 ]: b' Z$ K5 q
                        begin. B" N! B5 U. l* I8 T. l
                        if(tms_pad_i) next_TAP_state = `STATE_update_ir;
$ G4 {) g! D1 \! _9 ?6 w                        else next_TAP_state = `STATE_pause_ir;
7 r* _% F6 e5 ^* L                        end# p. u, M4 X8 J, W9 n2 b/ c! \
                `STATE_pause_ir:
0 Q* `8 h0 D1 ^& O9 P                        begin
. x, x, N& W4 U+ N5 D% K. j                        if(tms_pad_i) next_TAP_state = `STATE_exit2_ir;
! l8 }- W* A  z- N: m8 d                        else next_TAP_state = `STATE_pause_ir;4 n7 ^* f) P6 x& X: w: ]
                        end& C* }' u* c3 p! H% Y
                `STATE_exit2_ir:
6 i2 F' r7 _4 P6 F7 Q2 l- u" F/ A                        begin; k- m/ ?7 f. y6 E/ ~' z
                        if(tms_pad_i) next_TAP_state = `STATE_update_ir;8 a% L4 J0 b+ B( g5 k
                        else next_TAP_state = `STATE_shift_ir;+ i4 X# ~1 m% k$ S6 V- R
                        end. X9 V% L8 E' H$ ?/ [: M
                `STATE_update_ir:
) |6 ?7 {) \' {: U2 F                        begin& [- [! q$ G& L3 }: M; }/ H; k$ d
                        if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan;5 E  O: U# t& h* G
                        else next_TAP_state = `STATE_run_test_idle;) ?- ?9 f3 V% @1 A
                        end4 Y7 R0 s8 f) E# G/ F! H4 p7 J
                default: next_TAP_state = `STATE_test_logic_reset;  // can't actually happen( P% v4 ]7 w% ?% w
        endcase
3 \) E2 E: X! j& O% f" Rend
4 I8 ^* D8 Q4 T5 K# P6 [1 _. ~$ C, F
6 G: h- }) W0 @  J* i% ?- c 6 {2 Y9 S0 W5 d
// Outputs of state machine, pure combinatorial
, m3 ?; d  E6 N' |4 a3 {always @ (TAP_state)
- @4 r1 }5 b1 }6 m! Fbegin
) J7 y  n" F' W( ?) o7 M        // Default everything to 0, keeps the case statement simple' G9 R* |& F$ N* F/ G+ G
        test_logic_reset = 1'b0;
8 F" }  {2 D4 l3 u        run_test_idle = 1'b0;
' B/ ]( o* _; J+ ?# q) e        select_dr_scan = 1'b0;  j( }: b2 c: j! ?; ^  n8 n& b
        capture_dr = 1'b0;  T. ^; `. g" l! G' m- r2 u
        shift_dr = 1'b0;( s1 L- R6 O$ v! n7 B# ~) w7 @
        exit1_dr = 1'b0;. f- g' u- }- L8 p
        pause_dr = 1'b0;" k6 j7 w2 q2 c3 L
        exit2_dr = 1'b0;
8 ]1 ~7 s4 z) F5 j. }- H        update_dr = 1'b0;
# |0 r+ W6 o, S/ k; m* X3 Y        select_ir_scan = 1'b0;; _' @% m4 q& c, M/ @3 W5 r
        capture_ir = 1'b0;$ k+ ^1 E) q- ^1 N% c; ~8 q( m
        shift_ir = 1'b0;
! a) \) Q5 G3 x' Q+ a        exit1_ir = 1'b0;; X# Z) P7 P$ M. C% B
        pause_ir = 1'b0;
$ Q; P% ^# Q: ]        exit2_ir = 1'b0;
! q8 Z, a' ?: O9 I4 H! V5 i/ R& ^        update_ir = 1'b0;
2 C0 T7 `( h6 f7 v; H
2 N4 p0 p* ?& ?: I        case(TAP_state)
5 V2 a0 }" {7 }4 ~% R9 }                `STATE_test_logic_reset: test_logic_reset = 1'b1;
; R' c) ^/ x. \: d/ X5 W" E5 l) L2 B9 D                `STATE_run_test_idle:    run_test_idle = 1'b1;
/ @  j. p; {# `9 Y5 y                `STATE_select_dr_scan:   select_dr_scan = 1'b1;
0 H5 Z9 E0 r) a& a* E                `STATE_capture_dr:       capture_dr = 1'b1;- W4 r6 w& w+ n; b
                `STATE_shift_dr:         shift_dr = 1'b1;
0 ?* m# M$ k( D8 n4 U                `STATE_exit1_dr:         exit1_dr = 1'b1;
/ l  m$ I% H5 @5 c/ P) C4 X                `STATE_pause_dr:         pause_dr = 1'b1;
4 n5 K% B/ _  }5 l8 N% q                `STATE_exit2_dr:         exit2_dr = 1'b1;
; _/ [8 p6 B  m4 ]) z                `STATE_update_dr:        update_dr = 1'b1;/ `' r& |6 Y1 x$ x2 k  S
                `STATE_select_ir_scan:   select_ir_scan = 1'b1;8 i/ C2 M0 N2 e( U+ s
                `STATE_capture_ir:       capture_ir = 1'b1;
7 U1 x$ F( _( i8 M( l# j& B                `STATE_shift_ir:         shift_ir = 1'b1;8 W& e6 o% }9 w; r8 g
                `STATE_exit1_ir:         exit1_ir = 1'b1;: |5 M) t! a0 B
                `STATE_pause_ir:         pause_ir = 1'b1;
% I4 t" H( m: m                `STATE_exit2_ir:         exit2_ir = 1'b1;
) M5 P  F; C% I/ A+ s                `STATE_update_ir:        update_ir = 1'b1;
, Z6 T3 {  L7 u- ]" ]! A                default: ;
1 n4 X7 R2 v5 {) _9 a        endcase
" @5 D" s; u; @7 h. Wend
* b$ Q$ J0 ?7 ?& w' C5 C8 _$ a
7 s& m, i" g6 X$ H2 s3 V5 L9 ~/**********************************************************************************
9 l5 r) A4 g% A*                                                                                 *' J) C/ o/ e- s& I7 {! v+ B
*   End: TAP State Machine                                                        *
! y* v8 [6 {- A, [: y8 k+ c3 [3 U*                                                                                 *
) A' T# `& h  D, h: e# p" ?**********************************************************************************/
. U0 X" _" c) ?, `* z  x) B5 z4 `9 e: F- L$ p: r5 W

1 z! H( _  q4 s2 W5 O4 g0 K) j9 M6 c' uc,shift reg
1 T$ ]0 y/ V, v: J! K! s% Z上面说过,JTAG的本质和SPI相同,都是基于shift register的,也就是“以物易物”的思想。那么,如何操作tap呢?通过向tap中写入相应的指令。那么如何将指令写入tap呢?向tap移入任何IR_LENGTH的支持的指令,tap就会移出等长的数据,这个数据没用,直接舍弃即可。
" @4 o/ ^7 e; k; D. C( T% F3 Z整个过程非常简单,代码如下:
. S% ]. X. X: \2 @9 S# P% a8 t9 ~) z9 J- m. L, r
/**********************************************************************************7 ]' a% e# {, W3 }
*                                                                                 *
0 {$ `6 B1 W/ m*   jtag_ir:  JTAG Instruction Register                                           *! {6 Z" y) C* S
*                                                                                 *
: j; y: m( Y( i0 G5 i( ]**********************************************************************************/" c+ x, A  y9 C$ c5 D
reg [`IR_LENGTH-1:0]  jtag_ir;          // Instruction register
5 k6 u& j7 z. V3 k* y  Zreg [`IR_LENGTH-1:0]  latched_jtag_ir; //, latched_jtag_ir_neg;
* `- c% I+ e* `% ~, w! ^wire                  instruction_tdo;6 g& A  g2 k% e$ i: j, N, T+ G" ~

+ M' q  ^$ G/ u/ ]6 V+ H, @$ r& ialways @ (posedge tck_pad_i or negedge trstn_pad_i)9 G5 \" I. \$ L4 Z* r
begin
+ w+ _/ x3 T) f, G6 M+ n" h7 x  if(trstn_pad_i == 0)# B0 M0 @) M2 F" A! B7 K2 f0 o* c
    jtag_ir[`IR_LENGTH-1:0] <= `IR_LENGTH'b0;6 i: p5 a" K. \6 Q1 Y) ^4 p! Q% `
  else if (test_logic_reset == 1)( R! S1 J! j$ q/ ?! S
        jtag_ir[`IR_LENGTH-1:0] <= `IR_LENGTH'b0;
; q# K3 k$ o3 r  else if(capture_ir)
/ L; I9 g+ Y: q9 S    jtag_ir <= 4'b0101;          // This value is fixed for easier fault detection  A* M# f* O" n; d; Q( ^. S
  else if(shift_ir)
( C. T3 F1 ]! y: {    jtag_ir[`IR_LENGTH-1:0] <= {tdi_pad_i, jtag_ir[`IR_LENGTH-1:1]};) J' M3 W, g8 O4 H# X7 x
end2 |6 f0 L6 B, ?1 q' g2 |
assign instruction_tdo = jtag_ir[0];  // This is latched on a negative TCK edge after the output MUX9 _0 }* ]7 r: ?! J6 L- F
// Updating jtag_ir (Instruction Register)+ `0 d* z- J8 ~7 D9 D) N
// jtag_ir should be latched on FALLING EDGE of TCK when capture_ir == 1
3 L8 N  ~# r# _4 ^) qalways @ (negedge tck_pad_i or negedge trstn_pad_i)
! t2 E6 Q: A8 v' Lbegin$ T% O! q5 ~( }
  if(trstn_pad_i == 0). Z& I0 E( U, n1 U  s2 [$ O
    latched_jtag_ir <= `IDCODE;   // IDCODE selected after reset
5 G9 S8 [* a! [0 p  else if (test_logic_reset)
( k$ w* J0 K2 o" n% O! v    latched_jtag_ir <= `IDCODE;   // IDCODE selected after reset# A* [* j. G% v' k- V
  else if(update_ir)
' b  E% A, j$ h. e% Y' K    latched_jtag_ir <= jtag_ir;
( }7 w8 z, K- V3 y  mend
# u4 M2 f4 }5 f" K* c6 K6 V, D/**********************************************************************************
8 x5 p4 |  F0 O1 L* W# T. f) t*                                                                                 *  W( j. V( w; Q0 }
*   End: jtag_ir                                                                  *
) B. J) P0 a6 G% p8 s*                                                                                 *
: Y9 J5 Y$ `9 Q5 `, F**********************************************************************************/9 A4 ]1 V/ T6 t. S, F: m
& Y9 @( W2 J; ^. L! Y/ Z
上面的代码可分成三部分来看,指令移入,指令移出,指令生效。需要注意的地方有以下几点:: G$ u2 N( `$ a1 _
首先,在移出之前,如果想读指令的话(进入 capture_ir状态),移出的将是0101。
# _0 q, {8 g1 }0 D6 A( F4 ]$ ^其次,从jtag cable移进来的数据放在jtag_ir寄存器里面,实际生效以后存放在latched_jtag_ir中。
: ~) x( z9 v7 r! y0 @. J最后,移出的数据来自jtag_ir,而不是latched_jtag_ir。所以说latched_jtag_ir是送给device的,而从device来的数据是放在jtag_ir中的。但是,需要移出的数据暂时存放在instruction_tdo中,最终移到tap外面的数据(tdo)并不一定是instruction_tdo,还有其他很多来源。这个后面会看清楚。
. q7 y! p# p' ?) J: o6 |, X+ f5 _# T3 H4 q6 H$ L
2 G* Z. E+ ]: _  b' r9 A' c# {# a
d,read IDCODE9 r+ o/ x  I0 [+ B! `
上面,我们解释过IDCODE的作用,那么怎么才能读到IDCODE呢,还是“以物易物”的思想,代码如下:
# n( \9 Q" k% p2 w0 @% ~; z
" f- v; J) U0 z, @2 Z. M
5 d3 c4 r7 V. m& ]" q0 X' v! }0 `/**********************************************************************************
( e" i: _& A0 h7 e: V*                                                                                 *
/ t6 \; [: }/ a$ h" ?*   idcode logic                                                                  *
7 O; W3 T( @8 F, m" Z4 p7 A5 i. O7 d*                                                                                 */ w7 v: T9 o9 b% e4 g! [
**********************************************************************************/
; }3 n7 k7 J. T! ]reg [31:0] idcode_reg;1 Q7 w6 X& u% I0 q1 q/ N) I4 ?
wire        idcode_tdo;* z# N' N2 j3 E4 A( e2 u

4 c0 i9 ^% O, ?' falways @ (posedge tck_pad_i or negedge trstn_pad_i)
; M4 ^0 k6 A9 ^: }2 e% ]' u; z  V0 Mbegin- L0 l) o7 |  g2 S0 ^
  if(trstn_pad_i == 0)
6 B) Y9 b% F. w3 K    idcode_reg <= `IDCODE_VALUE;   // IDCODE selected after reset% x+ k9 _! w% g2 a  C
  else if (test_logic_reset)
  s& u' i& h, w1 a    idcode_reg <= `IDCODE_VALUE;   // IDCODE selected after reset4 n& S+ S) _8 b2 }: Z4 S' |& O( T+ w& q9 z
  else if(idcode_select & capture_dr)
; H" f& j: e7 O7 r6 C( i+ \4 Q    idcode_reg <=  `IDCODE_VALUE;
: I" d6 x# n, ~+ \) a  else if(idcode_select & shift_dr)* g5 x8 G0 M9 l
    idcode_reg <=  {tdi_pad_i, idcode_reg[31:1]};
! Y% T4 n6 p2 a/ yend
* X2 J5 I1 G/ Jassign idcode_tdo = idcode_reg[0];   // This is latched on a negative TCK edge after the output MUX9 m: W4 a/ L' q5 a+ T6 n7 Z$ p( C
/**********************************************************************************
6 ]7 f( R% B/ m3 q* e*                                                                                 *
+ P9 h! C) I& z3 y*   End: idcode logic                                                             *
1 O. z& m4 i5 L4 t3 N*                                                                                 *
! y7 b! j8 V+ X5 z+ n( ^**********************************************************************************/
0 _' c' U4 u6 k1 p) }6 T0 V; b6 |
0 n4 A- X/ N3 s读IDCODE的过程和写指令的过程相同,不同在于向tap写指令是不用关心tap移出的内容(0101),但读IDCODE,不用关心向tap移入的内容,关心的是tap移出的内容(IDCODE)。3 {! f2 [% C  r( G5 Q

6 M+ @/ T3 }: P$ M' l6 _7 A0 l+ J$ t8 }
- G+ F3 |/ M7 Z# O( p
e,bypass9 t6 H0 J3 L  [& D; g! a0 v% Q2 ~
adv_dbg_if在使用时,和他在一条jtag chain上的设备必须全部bypass,否则,数据就到不了adv_dbg_if,也就无法工作。这个很好理解,jtag chain,顾名思义,就是一条链,就好像打电话时的总机和分机。如果你想给某个分机打电话的话,那么总机肯定不能接,也就是总机bypass。
8 H% b, ^& Y- j  a  a. C1 rjtag_tap 工作在bypass模式是时,一个耳朵进,一个耳朵出,唯一的影响是会造成1个cycle的延迟。2 M7 n6 n  ]) F3 S: W4 ~
代码如下:
: @+ C! H( r' l( ?5 R* f5 l" b+ z9 ^& U; f+ ~

0 V' p% s8 Z7 D6 x/**********************************************************************************
0 L8 I  D+ I: g0 A, e*                                                                                 *
6 Z: h  K& U) p  c1 Z  G*   Bypass logic                                                                  *( F" _0 m% X# `: o, U/ Z: @9 f
*                                                                                 *' ?1 Q5 v) ?2 M5 ]- [, ~  T
**********************************************************************************/
8 C: A; R( t9 r6 K# xwire  bypassed_tdo;
$ m! E- m5 Q* v* Rreg   bypass_reg;  // This is a 1-bit register
, }0 q1 J! {" C - S% h) {3 b, [) t3 t+ `0 M
always @ (posedge tck_pad_i or negedge trstn_pad_i)
; h' i( ~4 K% @: J% ?begin' r* _6 K( V0 t! t+ k. ?
  if (trstn_pad_i == 0)
" [" G+ b+ Y# E& R- k! r     bypass_reg <=  1'b0;+ Q% b! i1 r! o6 c
  else if (test_logic_reset == 1)$ ~; y% J# E( o8 n/ G2 Q
     bypass_reg <=  1'b0;: d6 N8 Q1 |3 d( u+ T  H
  else if (bypass_select & capture_dr)+ u- n/ \6 Q7 k) ]5 O  R" A
    bypass_reg<= 1'b0;! @* E8 ]8 D, n6 G/ Q3 x
  else if(bypass_select & shift_dr)
2 O" d, q5 j4 g, B" R    bypass_reg<= tdi_pad_i;
" ~* R  ~! U: ?3 c$ wend' a! Z& P4 o( H5 J
assign bypassed_tdo = bypass_reg;   // This is latched on a negative TCK edge after the output MUX
( I7 S8 J( @6 Y5 u0 \/**********************************************************************************
3 u' C# V4 O6 W6 D*                                                                                 *
4 H! l! G0 D  R. g- _*   End: Bypass logic                                                             *
. |3 r# b) J/ U; c*                                                                                 *
) |; y; f3 }' A/ ~' Z1 C6 |**********************************************************************************/) [8 E( h, x4 f

. w6 l% a* N2 k/ hf,mux output( l  F( [2 u% g
tap扮演着多个device(分机)的总机的角色。当公司内部的分机有很多,但总机只有一个。所以总机需要有多路选择器的功能。- i* S! q- b( Q3 w8 m
代码如下:
  [( I5 E, s- W' j
  n- p1 c1 U5 r9 I, ]* i1 z) k. W  ?* ]" J6 |7 y8 d
/**********************************************************************************! D$ `2 {( a6 H( w
*                                                                                 *5 x) P6 _1 U% ^
*   Multiplexing TDO data                                                         *
9 C0 w/ |* P" D1 A- A4 E! }*                                                                                 *
2 J/ s: e, l1 _# }# Q& U2 G**********************************************************************************/
9 d+ g, T- G) ^; _reg tdo_mux_out;  // really just a wire3 I' D1 ]% F- E6 \$ A
8 R7 _$ E* i7 `6 d6 v8 {
always @ (shift_ir or instruction_tdo or latched_jtag_ir or idcode_tdo or9 j7 U  x, R) o2 v
          debug_tdo_i or bs_chain_tdo_i or mbist_tdo_i or bypassed_tdo or' o; T9 m. X3 _- Q) u6 a0 C
                        bs_chain_tdo_i)
! e4 _! x" U: o; E8 o+ w3 ?begin, h: Y; U( k5 X+ b* |
  if(shift_ir)
' o1 M* A8 N- k) X( A    tdo_mux_out = instruction_tdo;  j( z  s' N7 |: G: h+ M/ K  V1 U
  else
5 ^0 B4 q0 T% s8 Y& u    begin
' Q7 T1 I2 q1 e/ x      case(latched_jtag_ir)    // synthesis parallel_case
+ ]8 Y4 S: Y0 }, D+ v9 S- v        `IDCODE:            tdo_mux_out = idcode_tdo;       // Reading ID code
; ]' X. Q) l' Z2 B# B6 f        `DEBUG:             tdo_mux_out = debug_tdo_i;      // Debug
, s$ g1 h4 Q" K( H; m        `SAMPLE_PRELOAD:    tdo_mux_out = bs_chain_tdo_i;   // Sampling/Preloading
8 m2 c; [8 |! Y        `EXTEST:            tdo_mux_out = bs_chain_tdo_i;   // External test: s% H6 O' \) c% ?: N4 v
        `MBIST:             tdo_mux_out = mbist_tdo_i;      // Mbist test* X: C. Y* d5 v) v2 U" x
        default:            tdo_mux_out = bypassed_tdo;     // BYPASS instruction
$ m# l7 x4 K, W3 j2 q      endcase& ?' D% _1 ~! ?4 e
    end
  D/ {* u7 i! ?9 bend
) K2 [4 q4 A8 T; O8 S
  k) Z  @: f6 _% ~8 L* I% |0 U& Q 6 _9 J5 \. N2 J2 ^. s; n) h8 ~
// TDO changes state at negative edge of TCK2 F* ^: x) t& v; B
always @ (negedge tck_pad_i)  [  e6 M- p* E/ V' U
begin3 r& j' f3 t* h4 Q
        tdo_pad_o = tdo_mux_out;
! F" E! C6 K- N* a' {2 @* Vend( e' F+ d6 s8 N

/ a0 l8 ~) e' ?( I ; d9 u- Y' P+ @$ X; I
// Tristate control for tdo_pad_o pin* i) h' ~  ]' [9 `
always @ (posedge tck_pad_i)" V* N! `: [  X7 q6 Y7 Q
begin( @9 j  h3 D! C
  tdo_padoe_o <= shift_ir | shift_dr;
3 V$ t% Z- s6 _  D  ~8 X8 n0 x* ?end0 P4 P# c  a5 a" S8 }
/**********************************************************************************. i8 d, S6 J3 J% u, W. h7 e
*                                                                                 *
/ E; X/ Z$ {. g8 X$ x& M5 L8 q4 A*   End: Multiplexing TDO data                                                    *4 v! d; g  Q# x  ^2 ^. j" ^8 T
*                                                                                 *
* x! ^- c5 h/ r. [7 t**********************************************************************************/
2 g, \" s6 p3 N3 d! e1 G% {  H7 E, M
4,jtag_tap的使用  w* R* E2 m( C) E  r
要想使用jtag_tap,需要相应的驱动程序,才行。驱动的作用就是根据FSM的定义,以及命令格式,以及adv_dbg_if的实现,来操作jtag的4根线,达到某种目的。在adv_debug_sys的adv_jtag_bridge中的chain_commamds.c中有相关函数,代码如下:0 i) ^7 G! S  v
/ }% g. {6 W% I  q; ?

5 r( g4 d/ @8 ~2 j; }* c//
( p0 V" J5 ]' J% s( s// Functions which operate on the JTAG TAP
. }0 X1 u/ q9 U& r4 z0 N5 j4 J# m. a" C
/ |/ y5 ^2 L. \# U9 \4 O ) O# }" {( I* B( x8 J# h+ }) \! Q
/* Resets JTAG - Writes TRST=1, and TRST=0.  Sends 8 TMS to put the TAP
) u) {5 z9 Q/ G6 a+ Y4 k * in test_logic_reset mode, for good measure.; w" ]" D: c! p3 \$ ]
*/
0 J; g+ E8 l( T$ y( q8 Bint tap_reset(void) {
6 ^5 O1 g4 g% ^$ U9 f+ ]5 ~$ }% M  int i;
; t7 ]0 j, i( R  int err = APP_ERR_NONE;
2 ^' `+ Z9 V! S1 o0 h + V$ ^8 @$ [0 G3 V7 u
  debug("\nreset(");
8 s) T( y9 v' P" E  err |= jtag_write_bit(0);  B: z5 a2 H$ P! f; m4 V
  JTAG_RETRY_WAIT();# O4 z3 ?# Y( @* d5 y5 o# A  P
  /* In case we don't have TRST reset it manually */
, a2 o% w& Q  E. y  for(i = 0; i < 8; i++) err |= jtag_write_bit(TMS);* l5 S5 [5 E4 }9 j* i
  err |= jtag_write_bit(TRST);  // if TRST not supported, this puts us in test logic/reset
) s/ M: U' J9 g; ]6 q, T; M; q  JTAG_RETRY_WAIT();
! h' w1 q6 {! r2 ]- I  err |= jtag_write_bit(0);  // run test / idle1 m3 ?% s- u6 `5 f! R0 g
  debug(")\n");0 G0 u! R% P- h; k4 `
  // Reset data on current module/register selections  Y7 Z  U+ G7 d* q+ Q
  current_chain = -1;; f1 u" _6 I' T$ B/ r0 _
  // (this is only for the adv. debug i/f...bit of a kludge)% Y/ w5 e+ s5 w5 m1 C
  for(i = 0; i < DBG_MAX_MODULES; i++)
1 T* A: L# a( Q7 w. q    current_reg_idx = -1;* _( L8 M+ A: `# h
  return err;8 X1 r+ Z8 o2 C" E9 [
}
" I( \, Z; t% q- e* |  // Set the IR with the DEBUG command, one way or the other  y5 I5 o" N$ [8 Q" o' Z6 _
int tap_enable_debug_module(void)
3 Z, _/ L- \$ x! z{. O" k3 m3 {) V) U" A5 |1 n
  uint32_t data;, M6 h3 v, b  S2 \% R# e- ?
int err = APP_ERR_NONE;
% L; S4 e2 p4 b8 ^& Z+ C+ R/ I  if(global_altera_virtual_jtag) {; t# b% Y) ~- S
    /* Set for virtual IR shift */# R$ G  u% N+ a9 X7 e
    err |= tap_set_ir(vjtag_cmd_vir);  // This is the altera virtual IR scan command! [: i8 {+ W( r3 L' \
    err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
/ u- z- A0 A  p; |9 h8 K2 a    err |= jtag_write_bit(0); /* CAPTURE_DR */
0 @6 I& G; v; K# J! {    err |= jtag_write_bit(0); /* SHIFT_DR */  H5 T% u! Y8 v3 p9 g4 p) X: j; C+ L
    ' A! P: c' {9 p9 S; h
    /* Select debug scan chain in  virtual IR */
& O( k2 U$ c% G2 I1 \    data = (0x1<<ALT_VJTAG_IR_SIZE)|ALT_VJTAG_CMD_DEBUG;& D2 P# l0 d" H7 a
    err |= jtag_write_stream(&data, (ALT_VJTAG_IR_SIZE+1), 1);  // EXIT1_DR8 c! y1 a, u* a2 p, C8 p
    err |= jtag_write_bit(TMS); /* UPDATE_DR */
& Z& [$ L! C7 e, d. o9 ~; t+ D    err |= jtag_write_bit(0); /* IDLE */ 1 c3 x2 |/ r4 D6 `+ h) z
    // This is a command to set an altera device to the "virtual DR shift" command
- }  F, h( l% |; `9 b    err |= tap_set_ir(vjtag_cmd_vdr);! V! t0 I6 E3 D' N- s4 P1 @; [
  }+ ?7 ~% G& ]7 J3 v+ ]' E$ n
  else {( E" q% _+ s8 o' [
    /* select debug scan chain and stay in it forever */
5 {' F4 [7 k$ j7 D; h    err |= tap_set_ir(global_jtag_cmd_debug);
5 u. e) T! a& X  }
; I4 W. E8 M; ~  return err;+ Q+ k; G, a- R3 A5 T+ |
}3 |# n: \# Q) V4 J7 V2 N
/* Moves a value into the TAP instruction register (IR); Z2 e! L5 F! i, R. W7 h" y  r& l
* Includes adjustment for scan chain IR length.( c4 k! k& K1 q. U
*/' D) B- [* r/ r+ P) c
uint32_t *ir_chain = NULL;
- c/ _; b  k4 e1 {int tap_set_ir(int ir) {  K" G; u4 Q/ L  I  \# Y4 f
  int chain_size;
5 M/ j0 X0 b" @3 j  int chain_size_words;7 _$ g7 I2 q5 C( m0 Q9 X1 h% D2 H
  int i;
, U8 s( ?2 ?) Q1 ?9 p2 ^& @/ [9 E  int startoffset, startshift;" g  A, F/ c" |8 I
  int err = APP_ERR_NONE;
3 k6 Y/ f  s3 V" k" b  
8 r) O4 T& o/ f0 `4 n2 @  o* `1 b  // Adjust desired IR with prefix, postfix bits to set other devices in the chain to BYPASS
7 O/ N8 c! S! H2 G! u  chain_size = global_IR_size + global_IR_prefix_bits + global_IR_postfix_bits;
; h/ \- L( f& N' T4 @  [/ v  chain_size_words = (chain_size/32)+1;
) S9 P0 |' ?2 [* B  if(ir_chain == NULL)  { // We have no way to know in advance how many bits there are in the combined IR register' T7 a# U2 `$ n' I  G: m. X: R
    ir_chain = (uint32_t *) malloc(chain_size_words * sizeof(uint32_t));
" x1 K+ j- {3 C& P: G# W    if(ir_chain == NULL)
" z: [3 z" x& s3 i      return APP_ERR_MALLOC;( ?% P1 L$ F1 E) b, T1 ]
  }
$ L4 o+ w. G) v, ?5 M8 z& M3 `5 u1 I  for(i = 0; i < chain_size_words; i++)
# M5 q  @3 [" M0 o, s! j4 g  |    ir_chain = 0xFFFFFFFF;  // Set all other devices to BYPASS
' G: i4 m% H. A8 e  // Copy the IR value into the output stream. c9 W3 P7 k" J- _4 ]% m0 g: f
  startoffset = global_IR_postfix_bits/32;
8 x: l) r; C3 ~4 K( N4 s  startshift = (global_IR_postfix_bits - (startoffset*32));: G$ w7 N% E$ y2 P1 ^: a
  ir_chain[startoffset] &= (ir << startshift);
" w# a5 @: q# k* e2 i  ir_chain[startoffset] |= ~(0xFFFFFFFF << startshift);  // Put the 1's back in the LSB positions
) q8 S, f3 Y. j  q/ ]3 p; ?9 k! n: g  ir_chain[startoffset] |= (0xFFFFFFFF << (startshift + global_IR_size));  // Put 1's back in MSB positions, if any 0 p" J: T0 _1 x7 j! B) q
  if((startshift + global_IR_size) > 32) { // Deal with spill into the next word6 C( N& T7 R& T$ ?; q# D
    ir_chain[startoffset+1] &= ir >> (32-startshift);. t  y- j" d3 M6 S9 _
    ir_chain[startoffset+1] |= (0xFFFFFFFF << (global_IR_size - (32-startshift)));  // Put the 1's back in the MSB positions
( S/ M0 ~5 B( S" n  }: `( G8 Z8 N# C+ U4 T! B- o: W
  // Do the actual JTAG transaction
* z7 l4 j4 ?+ t( B7 o1 i  debug("Set IR 0x%X\n", ir);
0 r& \3 b# D' E' \, _  err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */* V/ T  y- S4 N& X5 Z
  err |= jtag_write_bit(TMS); /* SELECT_IR SCAN */
- T9 _; [; z: U% l+ {  err |= jtag_write_bit(0); /* CAPTURE_IR */
0 C# n* D2 U& F9 X, T8 f  err |= jtag_write_bit(0); /* SHIFT_IR */   
" X0 |* O% m; G  /* write data, EXIT1_IR */
# {% J/ ], ~" w9 U! j$ W" d  debug("Setting IR, size %i, IR_size = %i, pre_size = %i, post_size = %i, data 0x%X\n", chain_size, global_IR_size, global_IR_prefix_bits, global_IR_postfix_bits, ir);
3 k3 t- |4 n) u( F+ g1 J4 j  err |= cable_write_stream(ir_chain, chain_size, 1);  // Use cable_ call directly (not jtag_), so we don't add DR prefix bits
$ w+ k. n+ ?9 Z: e% N  debug("Done setting IR\n");) l2 ~6 J* o5 d( H' Q
  err |= jtag_write_bit(TMS); /* UPDATE_IR */) y  c# p" Y. c2 \
  err |= jtag_write_bit(0); /* IDLE */  & O* C; ]8 i' G& Y! h
  current_chain = -1;2 G6 A) [. D+ u# [
  return err;
5 Z5 U" @  e3 @+ m$ y}
) r( t% W9 d+ s9 m// This assumes we are in the IDLE state, and we want to be in the SHIFT_DR state.* X; ]$ a+ u* ]2 h2 @- _
int tap_set_shift_dr(void)6 E+ H- S7 i+ X1 J$ T* z: j
{; |) e! G! W; ~/ c+ o
  int err = APP_ERR_NONE;4 g, j; p! ~- i$ k
  err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
7 @- {# e3 q. e4 o$ T! X  err |= jtag_write_bit(0); /* CAPTURE_DR */
. n! Y5 ^, w1 F; J+ y  err |= jtag_write_bit(0); /* SHIFT_DR */
  M! M' C0 r# v9 u! M/ c! M( {  return err;+ z! S% k% u6 M+ ~4 r7 |& o' _. i6 |
}4 U) W: d4 y3 T+ M* }
// This transitions from EXIT1 to IDLE.  It should be the last thing called
0 g4 H' u; b: d" }- C# Z// in any debug unit transaction.
' q6 ]" i2 f8 s2 ~* Y- h' _1 dint tap_exit_to_idle(void), D! Z/ \- ^$ m- C) I
{* D3 S+ X6 Y; c0 H) ]
  int err = APP_ERR_NONE;
; A2 \) s) R' v$ l; H  err |= jtag_write_bit(TMS); /* UPDATE_DR */
3 n+ D) f2 u7 w2 Q% ~7 E$ B  err |= jtag_write_bit(0); /* IDLE */& E& y/ s. J) C. T' [5 q
  return err;
" {4 u# C; |# E6 x" N}
: H+ v# v+ u  a2 F4 c// Operations to read / write data over JTAG
; p' u0 @/ S9 X/ ]  a/* Writes TCLK=0, TRST=1, TMS=bit1, TDI=bit0: q, F! F+ ^7 r) v: o  K, O/ H: i
   and    TCLK=1, TRST=1, TMS=bit1, TDI=bit0/ D8 p7 |/ z# U' U
*/
7 Y0 H: E( M: B: V( u, f1 J4 j. Oint jtag_write_bit(uint8_t packet) {" T9 l' M  V( u8 U" d- Q0 M1 |
  debug("Wbit(%i)\n", packet);
3 _# Z( y2 a4 |- j7 T5 J3 p  return cable_write_bit(packet);
2 C! v! ^! a' P5 I5 ~}
, d+ M5 b& W/ b. ]$ {$ Kint jtag_read_write_bit(uint8_t packet, uint8_t *in_bit) {$ w0 c: ?, o; a; s5 Y1 @6 G/ z& o* N
  int retval = cable_read_write_bit(packet, in_bit);$ I. t& R3 W$ w* }( y* O
  debug("RWbit(%i,%i)", packet, *in_bit);4 p! n7 U0 D+ X5 a
  return retval;  B' _& y+ _! y4 Q# f
}
0 b3 ]" V" ^* ^" n2 J5 W// This automatically adjusts for the DR length (other devices on scan chain)
$ j& Q! Z% n) ~: K1 T; i// when the set_TMS flag is true.
4 h* s7 H* Y7 I" X  vint jtag_write_stream(uint32_t *out_data, int length_bits, unsigned char set_TMS)2 O) D& K( e4 ?" a7 U
{
7 B3 L$ j4 X; A) [$ F/ B  int i;# t- [% y) _# n/ f/ h$ u& S9 U
  int err = APP_ERR_NONE;
8 O0 j, S$ Y6 `; H  if(!set_TMS)
/ P) \1 v& H7 j1 [( m. _4 j    err |= cable_write_stream(out_data, length_bits, 0);( U$ }1 h* i% \# i
  else if(global_DR_prefix_bits == 0)# [; v% X+ a0 W' M' Z& E/ H7 S
    err |= cable_write_stream(out_data, length_bits, 1);
9 e( ?5 c8 p" a5 n/ p6 u  else {8 x* D3 G% d; W% V
    err |= cable_write_stream(out_data, length_bits, 0);: [7 {7 X( q9 q; E9 j- s+ S; J
    // It could be faster to do a cable_write_stream for all the prefix bits (if >= 8 bits),
. b. k& k1 x& Q9 V, f    // but we'd need a data array of unknown (and theoretically unlimited)( ^1 F/ I4 H/ y; }5 r/ E
    // size to hold the 0 bits to write.  TODO:  alloc/realloc one.
* d1 A6 J, U: n; H$ D$ H: _+ e, Q    for(i = 0; i < (global_DR_prefix_bits-1); i++), y( U* z) R6 B! [
      err |= jtag_write_bit(0);
2 G+ f/ k, H. S# i% R    err |= jtag_write_bit(TMS);
" ]. E! c% R7 {) C  }
: z% M5 f5 }2 q6 X  return err;+ c8 S$ X) o; d' a& C. L) t
}" z( P! G& ~. A# Q8 w
// When set_TMS is true, this function insures the written data is in the desired position (past prefix bits)
  o3 x" ~# Z5 P! n' \" t. n// before sending TMS.  When 'adjust' is true, this function insures that the data read in accounts for postfix9 X( ^$ m/ _* u( c- q# V9 r; ~& n8 M9 U. N
// bits (they are shifted through before the read starts).
5 y  v/ ^2 i" E3 ]$ |. Uint jtag_read_write_stream(uint32_t *out_data, uint32_t *in_data, int length_bits, unsigned char adjust, unsigned char set_TMS)
% I& t, ]8 N- z8 _( V8 `" s{( E" D6 r9 n' k+ O8 e4 a& u  i
  int i;
# k/ U4 Y- G5 k3 i  int err = APP_ERR_NONE;
4 E2 d* |* C! ]  if(adjust && (global_DR_postfix_bits > 0)) {
: R5 O2 W% c9 t0 }! l1 \# m# @    // It would be faster to do a cable_write_stream for all the postfix bits,
. G2 j" x1 Z$ ?1 \2 s& u# a    // but we'd need a data array of unknown (and theoretically unlimited)4 U' c# B8 d8 M5 P; U0 ]
    // size to hold the '0' bits to write.9 i" d. {5 Z" ]! s: Z, a1 v& B3 J
    for(i = 0; i < global_DR_postfix_bits; i++)9 u9 ^$ o) Z  i; H' d
      err |= cable_write_bit(0);# N# k; @& l3 z4 k
  }
# E8 g$ ?/ K/ H1 \- C  // If there are both prefix and postfix bits, we may shift more bits than strictly necessary.
8 y2 H( m" k% O9 `. ?1 r  // If we shifted out the data while burning through the postfix bits, these shifts could be subtracted/ b" a/ i$ w6 f% t5 j0 C
  // from the number of prefix shifts.  However, that way leads to madness.
. W- j6 a' I0 ?  if(!set_TMS)0 `. n. Z# S+ s* G+ ^% y
    err |= cable_read_write_stream(out_data, in_data, length_bits, 0);  % F% H& c8 f/ @# r
  else if(global_DR_prefix_bits == 0): U' z$ g' r' V! a+ a$ K
    err |= cable_read_write_stream(out_data, in_data, length_bits, 1);  
4 H0 r- w% y% j0 o" `& i  else {
4 [4 j' g8 C9 g- d* F) M5 P    err |= cable_read_write_stream(out_data, in_data, length_bits, 0);
2 s  W! R) f) K: k" q    // It would be faster to do a cable_write_stream for all the prefix bits,
! ~. [# B( @3 K& o' e# y# g2 V1 B9 F    // but we'd need a data array of unknown (and theoretically unlimited)
, g8 O; t" g; [+ b    // size to hold the '0' bits to write.3 e0 c* f* l1 ]$ H
    for(i = 0; i < (global_DR_prefix_bits-1); i++)
0 g2 P+ }' Q: o! i/ t' v; V6 r      err |= jtag_write_bit(0);' w2 X5 I% t- B0 x3 u$ M+ Y
    err |= jtag_write_bit(TMS);: j* _% q7 k* T% v1 c) S
  }
; V% J1 f2 C! `! c, U  return err;
* e( D0 g( j) T# k+ q& u}
7 V& @" b4 j* \# w; s1 p// This function attempts to determine the structure of the JTAG chain& ~% w7 E/ F3 ]4 n
// It can determine how many devices are present.* A9 ]5 i! K, J. f6 j/ c7 r
// If the devices support the IDCODE command, it will be read and stored.6 J% I8 _+ m! D! @; M
// There is no way to automatically determine the length of the IR registers -
' U* p; w2 L& K+ l9 B1 n// this must be read from a BSDL file, if IDCODE is supported.
) l' @* j* {9 m// When IDCODE is not supported, IR length of the target device must be entered on the command line.
2 M: u- E3 [1 I2 W. k#define ALLOC_SIZE 643 C. _0 @2 H) u3 |
#define MAX_DEVICES 1024
+ h1 ]& x* U$ r% y' rint jtag_enumerate_chain(uint32_t **id_array, int *num_devices)% i0 G+ f% Q6 }& L9 _- B
{
! {$ Z0 I: ^; V3 A. M  uint32_t invalid_code = 0x7f;  // Shift this out, we know we're done when we get it back
9 B' O3 j4 P# `- x; o  const unsigned int done_code = 0x3f;  // invalid_code is altered, we keep this for comparison (minus the start bit)
3 k0 \# i3 u0 h  int devindex = 0;  // which device we are currently trying to detect$ R& G% n( `& X" |( n
  uint32_t tempID;
2 a. w0 s/ a" ]- _$ h9 J  d  uint32_t temp_manuf_code;% `4 H# r+ u: @* ^% J  Z  g7 j
  uint32_t temp_rest_code;
3 S+ M6 _) p/ ]7 c! l  uint8_t start_bit = 0;
7 E, [1 {/ U5 F; u( d& j  uint32_t *idcodes;* V8 _  l. O( K
  int reallocs = 0;" i$ c! c8 J1 }4 @$ }  G
  int err = APP_ERR_NONE;$ {" F/ ?% m4 d( O9 x
  // Malloc a reasonable number of entries, we'll expand if we must.  Linked lists are overrated.
% T% \; _& y; N: k, J9 B# }  idcodes = (uint32_t *) malloc(ALLOC_SIZE*sizeof(uint32_t));
* a1 w& Z& h2 T" c9 X$ V/ N( F  if(idcodes == NULL) {
5 _3 Q) j, d# L3 l/ Q# p  z4 k    printf("Failed to allocate memory for device ID codes!\n");
/ `( n# Y6 q" I6 g4 r    return APP_ERR_MALLOC;
7 U* G- H- e% B' i  }* F: k6 ?7 O3 z0 m8 H
  // Put in SHIFT-DR mode
# l4 U& F" i& i; O  err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
4 P6 ^( Y0 p7 X/ y  err |= jtag_write_bit(0); /* CAPTURE_DR */5 U/ M- Z6 V4 `
  err |= jtag_write_bit(0); /* SHIFT_DR */
& a9 z5 x  I# o/ g  printf("Enumerating JTAG chain...\n");
. A0 X( x0 \# ?, `$ U* s! O  // Putting a limit on the # of devices supported has the useful side effect" i' n& @6 [7 w% H1 J- x
  // of insuring we still exit in error cases (we never get the 0x7f manuf. id)
" L  k' `" F, l8 q  while(devindex < MAX_DEVICES) {
+ M. b" R* C  X5 N7 L+ l4 \    // get 1 bit. 0 = BYPASS, 1 = start of IDCODE" M9 P! g; o7 T
    err |= jtag_read_write_bit(invalid_code&0x01, &start_bit);# P, \/ M+ A- `9 c
    invalid_code >>= 1;
, i+ e( C1 Z; H. o % N" r, Z% W4 {) A) L7 e* e
    if(start_bit == 0) {4 J$ b) j- |& Q. t: b; u% f4 p8 p
      if(devindex >= (ALLOC_SIZE << reallocs)) {  // Enlarge the memory array if necessary, double the size each time  J, H+ F; n% ]/ }+ o; Z, N1 J
        idcodes = (uint32_t *) realloc(idcodes, (ALLOC_SIZE << ++reallocs)*sizeof(uint32_t));) m3 Z0 M) w$ B
        if(idcodes == NULL) { ; o3 c  p0 v3 m# z9 Z! I" f6 ]
          printf("Failed to allocate memory for device ID codes during enumeration!\n"); ( I/ x4 z* Q* v
          return APP_ERR_MALLOC;
/ }, V, C4 }2 Z5 C0 S        }- K& n" p/ D* Z  n
      }" R: }$ O! m% }2 `! w$ A+ l- \
      idcodes[devindex] = -1;
# ?# X% ^! a+ m: F$ u7 l+ A      devindex++;
& Q' ?* I; T. i$ V    }
0 n, Z% p5 I$ ~  }. c" B4 u/ `    else {( v7 m9 A. D- E$ _$ W( W; U& F, A
      // get 11 bit manufacturer code2 ~! `3 Q3 ?. v# r0 r8 d/ n6 n2 W5 B1 D
      err |= jtag_read_write_stream(&invalid_code, &temp_manuf_code, 11, 0, 0);
& X; Q7 x) i: T- {      invalid_code >>= 11;
( D' c2 ]1 _5 p! B! [, P) Y3 }      ) T+ h0 _! B) j  w) f. e
      if(temp_manuf_code != done_code) {3 G  y& d: B) u, l
        // get 20 more bits, rest of ID* i  K& x% m# ^, Q4 u% E  @
        err |= jtag_read_write_stream(&invalid_code, &temp_rest_code, 20, 0, 0);9 i, c$ ?# z# K! E) i# B* u- O) l  r
        invalid_code >>= 20;0 i& r% C/ Z5 b' V9 A  G# B
        tempID = (temp_rest_code << 12) | (temp_manuf_code << 1) | 0x01;
+ _& g9 M7 u9 w2 R9 f" w- k        if(devindex >= (ALLOC_SIZE << reallocs)) {  // Enlarge the memory array if necessary, double the size each time
4 P5 ?$ d- E$ l. M/ T) @          idcodes = (uint32_t *) realloc(idcodes, (ALLOC_SIZE << ++reallocs)*sizeof(unsigned long));
" C- G; P7 U* h9 M          if(idcodes == NULL) { : R9 B8 R8 _$ d7 \0 y& u+ Z% p
            printf("Failed to allocate memory for device ID codes during enumeration!\n"); 7 s' `7 u3 @. d+ E4 g
            return APP_ERR_MALLOC;
. x6 s: S5 g9 Z: I. e3 v$ E          }
# a- E- z- n8 r5 \/ @- ~        }
% f& O1 s& H4 L+ d9 \0 A        idcodes[devindex] = tempID;8 S) K0 ?2 R# z( b! p% e/ _  W
        devindex++;2 K$ B9 a  L1 ]: c$ E( Y
      } else {
3 v; h8 F+ [4 |        break;
+ n. H0 N2 Q6 @; t( y      }
5 @* |: ^$ Y  X; s3 V6 }    }( f8 S7 X/ a  @5 T. f6 ?/ J
    if(err)  // Don't try to keep probing if we get a comm. error4 m: u- m. `8 a8 b' n* M3 s
      return err;
2 [) R! z- ?# Y7 s- G  }) Y* ]7 o4 P6 h4 o: I: d) S
  if(devindex >= MAX_DEVICES)
' P6 Z% l/ @* E: ]- S% ]/ K- V3 G    printf("WARNING: maximum supported devices on JTAG chain (%i) exceeded.\n", MAX_DEVICES);
! k% g9 \/ M- `
" v7 C0 t( c( h2 a0 r- L  // Put in IDLE mode8 ~  C. d, k: I( t2 K+ G
  err |= jtag_write_bit(TMS); /* EXIT1_DR */) M% T0 l6 D7 [' U& R8 N
  err |= jtag_write_bit(TMS); /* UPDATE_DR */3 X6 a/ L( b1 E0 P' A; X
  err |= jtag_write_bit(0); /* IDLE */ & N6 l: g+ S% o" j! N0 e- z* e7 Q
# Z& m% ~+ X! Y: o3 `7 t3 K
  *id_array = idcodes;' w0 b0 e# J  _; Z* p0 @+ m1 P
  *num_devices = devindex;
2 h& u. w0 ]( r0 U1 F
* a2 c+ q/ w( u* w; E* j$ i8 B1 y% r  return err;# T& [4 L8 P% y3 I5 B
}3 `/ @* B6 M- E( _

6 E  i5 q+ Q" a" Z + z- ~9 a  K/ }0 _3 X+ ~  O! P

& t3 C/ N, U* n* m9 Vint jtag_get_idcode(uint32_t cmd, uint32_t *idcode)9 Y8 H$ _: F' y5 F" _
{* ~# |& A) d8 h" g4 t
  uint32_t data_out = 0;
  s% v6 ~8 E# d0 k8 \1 H* |  int err = APP_ERR_NONE;
( N- ?' }1 Q; r! O7 ~( f4 v* C# W  unsigned char saveconfig = global_altera_virtual_jtag;
4 x' c% i' w0 }  global_altera_virtual_jtag = 0; // We want the actual IDCODE, not the virtual device IDCODE$ {! n5 X. Q; I5 z1 U" f4 ~8 i
' o9 v3 A! Y8 q
  err |= tap_set_ir(cmd);
. y: U: y* C  ~2 a  err |= tap_set_shift_dr();
  n2 U3 r1 `8 x+ F7 g% X: C5 M  err |= jtag_read_write_stream(&data_out, idcode, 32, 1, 1);       /* EXIT1_DR */  |. F1 |: {" c1 c3 D: l4 B
% X8 I) X  {0 f7 n! }8 j, O
  if(err)
7 \$ f  [; `3 H' h( v    printf("Error getting ID code!\n");# {6 G, `( L* X0 f
, M3 Y, m9 F+ B, q1 _
  // Put in IDLE mode; _! D: \: \! ?2 m/ g+ I
  err |= jtag_write_bit(TMS); /* UPDATE_DR */
4 p6 \" X, ?9 ^5 C. ^+ X  err |= jtag_write_bit(0); /* IDLE */ 5 I+ v, f. Y  M6 ^
0 x5 l6 c6 @* n3 E, _
  global_altera_virtual_jtag = saveconfig;% O4 C( \9 {6 q: t: Q
  return err;) P4 X- p# M: \- ?  O. T
}) ]( P5 u% \% G3 J
/ j/ `( c! R5 s
( h" C7 C" \5 P1 u) r7 ^! `% P4 [
/
$ c1 h7 g) M7 H6 S5 t2 p( p// Helper functions
; G4 U1 D6 z5 _ ' P% i( C# H0 E
/* counts retries and returns zero if we should abort */  w' S* }% O! Y
/* TODO: dynamically adjust timings */8 h  s. J% p$ @
int retry_do() {1 l- b' j; M1 w7 N% q1 G- u
  int err = APP_ERR_NONE;2 {" e9 W: }+ B1 P0 d: ~4 B

& K! F( ]" }5 N( `# ?- e  if (soft_retry_no >= NUM_SOFT_RETRIES) {
6 ^9 ?, G% ?  i- c2 Y      return 0;
& p% H! I3 n- w8 X7 M
1 u# u& L4 U5 {2 M7 o- O+ ~      // *** TODO:  Add a 'hard retry', which re-initializes the cable, re-enumerates the bus, etc.
3 G7 _$ M  ^4 L2 h $ f3 H) j; D, [; b, R' g, L6 |
  } else { /* quick reset */
4 S" m8 `1 a4 U; H. J    if(err |= tap_reset()) {
6 f6 K: x) ~- E      printf("Error %s while resetting for retry.\n", get_err_string(err)); 2 i, r9 n: D+ f
      return 0;$ k6 K' J# E8 ~7 u& v3 k, F) R( @/ O5 V
    }& Z* O6 Z' W4 M2 y7 X4 h( g1 N, U+ |
! \) t* b3 M) }: @# q, K7 W
    // Put us back into DEBUG mode* Z- i% ?0 T: a- ~. y# Z. W
    if(err |= tap_enable_debug_module()) {
, Q- v" N( u0 b% B. ^8 X* e4 R      printf("Error %s enabling debug module during retry.\n", get_err_string(err)); 5 f8 ?7 |  K* T5 e8 W/ Z
      return 0;
1 B* K. y2 y  f4 n9 ?9 T& V& U    }
  G" I  C: P; N  Z2 J/ X
4 e/ j% o+ q! n2 w6 s6 i    soft_retry_no++;
' Y; O8 i8 ^4 L    printf("Retry...\n");! S$ i& k6 f3 o2 v/ g+ o/ U1 x) U, q) D
  }
" n: Z4 u9 y0 R$ m) k5 m% t 7 j6 R3 E1 ^) K. T, p
  return 1;
+ Z! i& b/ Z* R' s* m}
* D: Z; n& [: X: j1 Y; R' Q$ |- m" {

2 l. }) Y* m0 a: g9 n3 u
) W% K" {8 |1 T. y! l9 v6 l9 r8 y. A7 U

: i0 P3 W  }7 j, {, N, _7 z/ u, p5,小结
: c0 l; w2 j0 J7 K) C2 G! [4 m本小节我们分析了advanced debug system中的jtag_tap模块的具体实现。用一句话来概括的话就是,JTAG就是类似SPI总线的一种总线,jtag_tap就相当于SPI总线的arbiter。1 D; e2 V8 n: Z( D

该用户从未签到

2#
发表于 2020-11-19 14:11 | 只看该作者
jtag_tap模块分析
您需要登录后才可以回帖 登录 | 注册

本版积分规则

关闭

推荐内容上一条 /1 下一条

EDA365公众号

关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

GMT+8, 2025-11-24 23:59 , Processed in 0.234375 second(s), 28 queries , Gzip On.

深圳市墨知创新科技有限公司

地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

快速回复 返回顶部 返回列表