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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x

' Q, }% e4 `. ~$ L' u引言
% Y8 G* N; f" I# E* o* E* v# q“知其然,还要知其所以然”,在搭建好ORPSoC的仿真环境和调试环境之后,我们有必要对仿真和调试系统中扮演重要角色的jtag_tap模块和adv_dbg_if模块进行进一步的分析,以了解其工作机制。
: M* r$ A0 z$ x8 F7 U. m' L2 G
# s7 G$ k% Q6 \+ x- m% r本小节就来分析advanced debug system中的tap_top模块。
& L' x" |) b+ M% f/ m+ k' k8 `# I$ d& ^8 T: J8 X. y
$ U* d# h+ G# j9 K. o
1,from SPI to JTAG
% x: T6 `, l  I- X在分析JTAG的具体实现之前,我们先了解一下JTAGF的基本知识。, s  |9 f% B, w4 Y4 {

/ F5 d; e" c+ h- X% b9 ^A、JTAG协议的本质与SPI协议并没有什么不同,它等于一个复杂的SS状态机+变长的MOSI和MISO数据移位操作。不过所谓的变长,都是事先约定好的。 2 a0 f* u& j8 Y5 G! `
B、JTAG协议是一个同步通讯协议,它是全双工的。它的通讯原则是“以物易物”——即你如果想得到某些东西,你必须先给与相同长度的内容;你如果只是想发送一些数据,也会自动获取相同长度的内容,至于交换的内容是否有意义,这是另外一回事了。  
- u7 Z8 J5 R1 t- G: S! Z% FC、JTAG协议无论多么复杂,实际上只有4根线起作用(有时候还有两根鸡肋的nSRST和TRST),他们分别是TMS、TCK、TDI和TDO,他们分别对应SPI协议里面的SS、SCK、MOSI和MISO。在本质上,他们并没有什么不同。即便是ARM的JTAG那么多的引脚,实际上起作用JTAG的也就这4根线而已。
4 q+ x0 I8 r. c% C7 F; j* @" t) W: iD、JTAG的数据操作都是基于移位寄存器的。
8 t: k0 M$ J" _5 EE、如果JTAG协议在某个下载仿真协议中只是用来发送控制信息和少量的数据,而大量的数据传输是通过额外的其它引脚进行的,即便这个协议被称为JTAG仿真其本质也早已超过JTAG了,严格来说,不应该称之为JTAG。因为JTAG协议中就只有4根线(有时候也算上nSRST和TRST)而已。典型的如NEXUS协议。 ! N# F6 b3 M1 J! t7 }3 ]% w7 P
/ V- l1 z5 M6 i4 V. h
这里面重点理解的是“以物易物”,这个概念,下面是SPI的工作机制以及one-to-one和one-to-many的组织。如下图所示:
2 O  ~  c2 W" C% Y7 `+ a. ]
3 _+ t5 i1 {  c; [9 r# N 8 P% k. o& g5 p4 D. P

$ f/ S1 g; ^) y7 F# D9 _
4 x- Z9 c9 |, @7 Y2 s2,jtag_tap
4 I2 o2 k) d* B3 t/ \) V% D, ]
* I# q5 t: Y! ^" Z
1>architecture! O7 v+ U( r9 `/ [" h, o% e8 x
8 o. K8 J. R6 L
TAP(test access port)的作用是提供adv_dbg_if模块和外部JTAG cable之间的桥梁作用,负责将jtag cable传来的数据传给tap支持的所有device,并将来自device的数据shift out到tdo上。
% ?. \. H. D, {* W" L下面是adv_debug_sys系统硬件部分的结构:1 M- C- M+ K' D2 n
) T' I; Y/ t2 a; u2 Q

  d5 j9 k  b; H( \
2 e, J: g2 e# {8 _  u* vjtag_tap一共支持4个chain(相当于SPI中SPI总线上挂有4个device),其中IDCODEchain在jtag_tap模块内部,其它3个在外部,如上图所示。
* f- e& ]4 _5 n+ ]5 z- ]& O$ {+ O; s% D: K) x
2>tap fsm
. S, N" n  K/ Y3 ^3 SIEEE 1149.1中定义了FSM,所以几乎所有的JTAG模块都会实现相同的FSM。如下所示:
" f1 p' _7 x3 v+ v/ [
( {! A" K1 D. x  [3 O, M) r! p9 F7 S) l9 U ; h  P  \/ Z% T8 ~- x4 o& X/ t

" d5 I3 H; o1 w/ I1 T
8 V# H* ^4 ?7 S( m! c  |9 ^说明:. G3 x7 ^# ^9 S) p9 w
整个状态机分为三个部分:信道选择部分、数据信道和指令信道。所谓的信道选择,就是图中最顶上由四个状态组成的矩形,分别对应着四个状态: # C7 z+ }3 S6 o$ f& p
a,JTAG TAP状态机复位状态  : W: a( L9 {0 s' k: r+ K, D
顾名思义,就是进入该状态,将导致整个硬件TAP控制器复位,所有的寄存器都将被初始化。在TCK的上升沿,TMS为低电平时,进入下一个状态;否则保持不变。 * D; q  Q0 X7 p) k( }" G
b,JTAG TAP的Run-Test/Idle状态  
4 b) A# h" V9 T' \! ?5 _- N2 r其实就是“开工”和“休息”的选择分支点。在TCK的上升沿,TMS的高电平将导致状态切换,进入数据信道的通讯状态;否则保持不变。
% {0 h7 q2 E8 F! C4 {& A; qc,JTAG TAP的Select-DR Scan状态  ' G3 I2 i  z2 B
Select DR Scan,就是当我们在该状态下,TCK的上升沿读取到了TMS的低电平将直接进入数据信道的操作子状态机;在TCK的上升沿读取到了TMS的高电平,将切换到指令信道的通讯状态。  0 W* i% H3 I5 d# @3 E1 S+ Y
d,JTAG TAP的Select-IR Scan状态  
2 M& Y" y- _' d: w1 f/ T4 R% ESelect-IR Scan,就是当我们在该状态下,TCK的上升沿读取到了TMS的低电平将直接进入指令信道的操作状态机;在TCK的上升沿读取到了TMS的高电平,将重新回到JTAG的复位状态。  
0 B: Y0 h) b% U" s& A! T数据信道和指令信道对应着两个子状态机,从本质上数据和指令并没有任何不同,只是习惯上,指令的长度固定为4个二进制位(AVR32的JTAG是5个),而数据则随着不同的指令选择了不同长度的指令寄存器,这个就需要具体查阅相关的协议说明了,比如JTAG IDCODE的长度固定为32位,而AVR32的复位指令却有5位。下面,只就常见的几个状态进行解释(以数据信道为例)。 9 k4 f9 ]  A/ W2 W7 W
+ ?2 L+ q+ G( k2 k* p

# x0 M6 l0 ~* E9 K8 n1 da,Capture DR状态  
* Y/ u7 X7 ~# t& W) ~$ Z0 b# WJTAG协议是基于移位寄存器的,其通讯具有“以物易物”的特性,在我们进入真正的数据传输之前,需要告知JTAG“准备通讯了哦?你有没有东西要给我哈?”,于是Capture DR就是一个给JTAG机会将需要传达给我们的数据放入指定的移位寄存器中的状态。  ' F9 J& w/ m8 u# \: i
b,Shift DR状态  
# ~2 N& U+ l0 H; b8 U6 p# |这个状态就是通过TDI和TDO进行数据传输的状态。需要说明的是,即便进入了该状态,TMS上的电平在TCK的上升沿也是会被读取的,从图中看到,一旦在TMS上读取到高电平,系统就会跳出Shift DR状态  
4 l6 f8 P% d0 X/ P/ X& {如果此时数据没有传输完成,造成的后果是不确定的。请大家注意,我所说的是不确定,而不是“很严重”:同样是因为移位寄存的传输特性,有时候并不要求一定要将所有的数据都完整的进行传输,比如在AVR32中,针对SAB的数据操作,往往只需要进行最关键的部分,详细地内容可以参照相关的数据手册; ; t/ X3 U0 L% O# Z8 V. D  `7 _
但有的时候,数据的不完整传输则会导致很严重的后果,这取决于具体的JTAG通讯协议。所以,为了保险起见,一旦进入Shift DR状态,在发送最后一个数据之前,请保持TMS为低电平,当要发送最后一个数据时,应该将TMS设置为高电平,这样,当TCK跳变为上升沿时,系统既完成了最后一个数据的传输,也成功的退出了Shift DR状态。  
2 Y0 i# m' g1 j9 K6 Y& qc,Exit1 DR状态  # ~5 P4 ~( x' |! }& [! w2 a
该状态提供了我们一个在刚才输入的数据生效前,重新修改的机会。一般情况下,我们直接保持TMS的高电平,并在TCK的上升沿驱动TAP状态机,直接进入Update-DR状态。  
5 ?. i) R( N6 {, r$ Z  Cd,Update-DR状态  
1 L7 b; J6 Z; Y  o顾名思义,就是使我们输入的数据生效——一般JTAG内部的动作就是触发一个锁存信号,将移位寄存器中的内容并行的读取到对应的寄存器中。Update-DR有两个出口,一个是,TMS的低电平对应Run-test/ Idle,还有一个是TMS的高电平对应的Select-DR Scan。这两个操作看似区别不大,但是意义非凡。前者往往会导致JTAG内部产生额外的时序(比如发生一个信号,表示完成了一个特定的周期操作,在AVR的JTAG下载中有此实例);后者则表示完成了一次数据操作,将进行下一个数据的操作,但是这些操作属于同一个操作周期。当然有些情况下,这两种方法是没有区别的。 0 c7 G7 Z- d1 j$ G0 V! d

: V" o! P  ^, C+ l3,RTL分析
# ?* i- k, a# w' pjtag_tap是advanced debug system项目的一部分,整个advanced debug system我们之前已经介绍过,如有疑问请参考。
% y9 Y$ a9 }) O9 w- M! ?jtag_tap模块包含两个RTL文件:tap_defines.v和tap_top.v两个文件。0 Z3 n" A' b. C$ W' E/ Q. U4 a9 G
在了解了JTAG的一般知识之后,我们下面就分析jtag_tap的RTL。7 O, L  x9 ]# x% g% d+ R3 e
( D  s9 H) @/ r) S, S0 @
1>tap_defines.v: u, o5 n$ e( {! z: |
首先,其内容如下:, b& {7 J$ s, r1 z2 C

0 U4 O+ |& a" z* g, P( i1 V2 a! p' U) @$ v& ?3 ^$ u  _
// Define IDCODE Value6 l( @  ~' [' H3 Y& u# O! k' S
`define IDCODE_VALUE  32'h149511c3( [. m/ P0 h. I; v( F, L
// 0001             version
) R0 I6 _+ j& ^% N3 D+ r- m0 ]$ _// 0100100101010001 part number (IQ)
" Q% g& c  X! l3 ~* R- \// 00011100001      manufacturer id (flextronics)
  N8 G" [0 i7 |' i) m( ^// 1                required by standard) `1 A* G! h; u5 Q: b4 h3 X

/ a$ y( Q  K; h9 G// Length of the Instruction register: Y; J- }, W& B
`define        IR_LENGTH        4& p# L$ p) a! T& y

  b9 q6 [2 H) V6 J2 P9 ^8 S// Supported Instructions
# x; f6 i, l3 R! s`define EXTEST          4'b0000
: D) U! m" V3 r; D  q" Y, ^`define SAMPLE_PRELOAD  4'b0001
" n# k- _/ u% z/ C`define IDCODE          4'b0010( U4 W/ a7 Y8 v9 B* S
`define DEBUG           4'b1000
  M$ n9 V) O' {/ V( V`define MBIST           4'b1001
. u2 b, G& L: P6 m# y`define BYPASS          4'b1111
9 z& S& Y( e: l; s
, _! t" R# k- x! E* ?# f# r/ t+ ^文件包含三部分内容,IDCODE,IR_LENGTH,和instruction定义。
* {& A8 G1 E" g" X8 t1 O! da,一般情况下,每个jtag device对应唯一的一个IDCODE,就像人的名字一样,这个名字用来jtag chain建立的时候‘点名’用的。jtag chain初始化时,读取所有的device的IDCODE,和BSDL文件中的IDCODE比较,获得device name,显示出来。
+ Z1 ^6 v8 c! P! P9 }: W9 L5 e) C# _8 Q3 L: I) Y/ E
b,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。
0 i. {& j1 `7 O( M# E- I0 F- K7 Z6 ]* R* ]/ F% U
c,instruction就是操作jtag tap模块支持的指令,这个参数,不同的tap大不相同,具体支持什么指令,可从bsdl文件中获得。下面就是jtag_tap的bsdl文件:( Y; X* s0 ?& p2 b$ M
- `: ~- j( i3 L% M/ Z
) v% e( X2 M8 V% N! F: W* W& O1 p
-- This is a minimal BSDL file describing the particulars1 _' h3 g+ u1 ^" y8 ^) M
-- of the OpenCores standard / native TAP.  It is designed
9 o1 X) l- t: J; ~( l, O-- only to be used by the adv_jtag_bridge program.  This
4 }% `5 z, [6 ~, ]-- file almost certainly lacks key entries and attributes
% H! n6 c( I) H" j-- required by other JTAG / BSDL systems.; q8 n2 l/ b  \7 n' \6 r
--
' M( L9 l; D6 C  V& [& Y-- by Nathan Yawn (nathan.yawn@opencores.org)
  x$ S/ t* O) z; r3 b8 {/ e) d-- Copyright: This file is released into the public domain.- B5 L& e# k2 m9 _! G" l6 e) g8 E
--
) V$ G) m* V1 I! M% a) h: a 5 N* P- M# j& C. _8 y3 E: `3 f% a
; g0 i) n' [' S. {
entity OC_TAP is3 m; _+ u/ f6 x3 m1 _4 Z1 c
! `# Z5 E: H( j6 O

' {8 [/ M  {% q2 k/ C. Xattribute INSTRUCTION_LENGTH of OC_TAP : entity is 4;
  m. K, r+ h7 H
9 f* N" ]6 {- z# E3 f' K* p2 h 8 K* ?4 d3 ]) \* ]& Q/ D9 W
attribute INSTRUCTION_OPCODE of OC_TAP : entity is  ?4 s* C' P2 n
        "EXTEST            (0000)," &
# |; }( J) o3 U; Q1 s/ p        "SAMPLE_PRELOAD    (0001)," && x6 c6 V# s2 ^# o$ j- B
        "IDCODE            (0010)," &5 K/ ]$ R/ ~+ w. N/ U: }; x/ {
        "MBIST             (1001)," &
) E& V& V" }6 k        "DEBUG             (1000)," &
) H1 U( Y. _2 O" K  f        "BYPASS            (1111),";
% I: a. D) C" g) w3 } 0 S2 _( R) @0 q! l! U

2 P( }2 |6 e, H7 N1 p4 ]( z& X7 tattribute IDCODE_REGISTER of OC_TAP : entity is
2 Y( M( x- N5 B4 y& b) _4 ~        "0001" &        -- version9 l! y: m0 G. `4 h0 T6 j7 k% |
        "0100100101010001" &        -- part number
& _. X: O' P; b        "00011100001" &        -- manufacturer (flextronics)* ]( G/ h4 D, J
        "1";                -- required by 1149.1* {; b) ]  {# [9 M; C2 S

/ \0 D( F1 _; H  C 2 Q* B% u5 U& F2 T
end OC_TAP;
/ g! [6 E" e0 q; c! {2 i$ H: `& T  o1 k5 ?: R1 x& {
, V, k/ L% [8 C3 s# ?
2>tap_top.v, Q% e. ^! U0 n
这个文件是jtag_tap模块的具体逻辑实现文件。7 C  V: f7 h/ J9 {2 ~! Y& x3 j
a,接口定义
, w8 Z; Z& h7 {: q+ `
# a+ @. c& X2 s/ w% B7 y$ ]3 f- s1 m$ d6 |( _9 e1 X6 g4 J( C
// Top module
% }  t) L. u- L& E( omodule tap_top(& C4 ^( n+ e& b. d0 `" z6 h
                // JTAG pads. _0 `+ X2 ?; m' @6 D4 N
                tms_pad_i,
% _6 E" j. ]/ T1 U9 n9 X                tck_pad_i,
4 a) ^  R" B! E, w2 q                trstn_pad_i, 3 X6 |" t4 u4 ~6 X8 a
                tdi_pad_i,
9 u! [0 h3 K9 b; w5 V" t2 i) X" S                tdo_pad_o, 9 ]* M& C( J& c+ A- _. p; N$ n9 p5 P
                tdo_padoe_o,1 F! w/ t" X" m4 S; P$ L
& g% W  e) Q: L+ H$ ?
                // TAP states& @3 F" D8 `( q( C/ r$ e
                                test_logic_reset_o,' H. _) c0 v& h" U2 w5 j, A+ ~8 l  L2 t
                                run_test_idle_o,( r" `# }0 d5 F& L9 i
                shift_dr_o,
8 o8 `2 S. o# Y) ?                pause_dr_o, " O  S1 N- G! {8 M2 g
                update_dr_o,
& l9 K: Z2 ]: H) l4 R                capture_dr_o,3 P' m( b. L: m
                ( `( p' ~( X9 Q& h5 U7 Q( D
                // Select signals for boundary scan or mbist( l/ |  a8 T  n2 K# Q7 c
                extest_select_o,
' g' d5 D) F) R% k# Q                sample_preload_select_o,' x: Q1 p  y# q" {
                mbist_select_o,0 D' L4 D5 ?$ N: F1 h
                debug_select_o,
+ U$ J- R+ W! X7 v) a' K  K5 y% j                . O! x& N/ I8 p7 a% O2 u
                // TDO signal that is connected to TDI of sub-modules.
: v9 j3 F7 n' H                tdi_o,
- f: C& |& k4 S( f               
7 t# g) H" R; `! v& J6 B' v                // TDI signals from sub-modules
. X; Y* W$ X% Y( P% C8 F3 I                debug_tdo_i,    // from debug module
& G6 O+ w) v+ f% s7 {                bs_chain_tdo_i, // from Boundary Scan Chain
# K; x7 `( w" ^) ]                mbist_tdo_i     // from Mbist Chain1 H! {4 {( J% F' q/ |; d
              );7 n! V6 A3 g9 C0 x5 V) _
' T! X1 H7 S7 |- H" @9 A
jtag_tap接口可分成5个部分:jtag信号,tap states信号,片选信号,tdi_o,以及从device来的数据信号。
1 E9 D* W9 a+ a. b$ N9 v1》首先是jtag信号,除了我们常见的tms,tck,tdi,tdo之外还有两个鸡肋信号:trstn,tdo_oe,前者用来复位tap,后者用来使能tdo。其实这两个信号有没有都可以,tap的复位可以通过tms来实现,tdo使能也可不用。! t$ T3 f3 M1 O
说到这里,有一个小问题,如果不用trstn信号,上电之后tap的状态是随机的,那么有没有一个固定的tms序列来实现tap的复位呢?答案就在本小节中,如果有疑问的话就找找看吧。
. x" w% x! R( p2》其次是ap states信号,给device用的,指示tap的当前状态,device根据这个状态来完成某些操作。- ?& j1 c' L2 G. V( B1 Q
3》片选信号,这个就不用多说了。对于jtag_tap来说,片选信号时根据IR reg中的不同位来决定片选的。! k% M4 B$ h! ]; x
代码如下:. M* P4 S' Y% f' M, y& d9 X) h
5 M; L2 M- y/ b+ F. E$ {, y5 n- d

) n9 C, `! M7 \! [% C7 h4 @: O1 e/**********************************************************************************; e3 v! V8 ~- Y
*                                                                                 *1 O5 s  p1 q/ d0 R1 e1 v" N
*   Selecting active data register                                                */ A0 y# ^) ~, S" v( J: E" L
*                                                                                 *# N! F* d3 e! l# n# O  O1 c, T
**********************************************************************************/; I+ P" u5 w9 l5 `1 J
always @ (latched_jtag_ir)4 D% a1 u- Z8 E9 R  ?' e* }8 S
begin4 m( c( ^# r' m; I. O8 ^; A
  extest_select           = 1'b0;$ m: F# p. X0 S% W( ^
  sample_preload_select   = 1'b0;
; N! b8 ?: ~8 P, P* P3 y  idcode_select           = 1'b0;
, f/ Y* d1 M% ?1 V. I2 M2 ]  mbist_select            = 1'b0;+ I3 R/ n4 R) k" G8 I0 T
  debug_select            = 1'b0;7 p/ l+ N6 f) `; w% b0 Y9 U
  bypass_select           = 1'b0;
' X4 P# i* ?8 h' I: p" g
! ^( ^2 `0 S1 @% B+ T  case(latched_jtag_ir)    /* synthesis parallel_case */ ; U9 o1 `2 o& t1 v4 h0 r! D
    `EXTEST:            extest_select           = 1'b1;    // External test
" @2 F0 ?) V; i3 M( p6 c    `SAMPLE_PRELOAD:    sample_preload_select   = 1'b1;    // Sample preload
6 [/ M" w) t% S& Y5 z8 T* j1 Z. i4 n    `IDCODE:            idcode_select           = 1'b1;    // ID Code
& p! S8 r5 n* i    `MBIST:             mbist_select            = 1'b1;    // Mbist test9 D5 ^) U/ T; s8 G  T; ^0 q
    `DEBUG:             debug_select            = 1'b1;    // Debug4 D' y6 q' d- ]  N1 X1 H$ [) S0 `
    `BYPASS:            bypass_select           = 1'b1;    // BYPASS& E. N1 x( y. h! y; M
    default:            bypass_select           = 1'b1;    // BYPASS) k6 U4 \3 h( ]/ b
  endcase
) f' v0 b5 J) V- s7 D! G: Dend
1 u8 o1 h# J/ v/ q6 P" Q& S$ R+ ^+ v0 N; T

) e3 M# N- A# `: U/ g3 d
% ?8 ~3 z2 k6 b3 k, R" N+ db,fsm
7 l5 e7 y, k0 [! b# vjtag_tap的核心就是tap controller的FSM了,常见的三段式风格:3 E4 s; N! r2 Z- Z. F

. }7 B: I* f/ t7 j- S  C+ B2 `' E/ v) N# v  a: K+ R
/**********************************************************************************
- e3 R" W: R! ~  W3 Z*                                                                                 *: S' Q2 Z8 ?% s! ~8 m* r
*   TAP State Machine: Fully JTAG compliant                                       *7 S( e% T6 d1 {
*                                                                                 *
: T* A9 b" W' m( x0 X& `4 V**********************************************************************************/
! u9 n& x: b- b+ y// Definition of machine state values.  We could one-hot encode this, and use 16
. u, G# e! j- t! _& [7 y- `0 w// registers, but this uses binary encoding for the minimum of 4 DFF's instead.9 C$ s6 K; h$ B2 u* y, @
`define STATE_test_logic_reset 4'hF
' _* V; r. |8 R% t! R3 z/ O& P`define STATE_run_test_idle    4'hC
5 v* F$ o( V( o1 H- f`define STATE_select_dr_scan   4'h7
. w5 @. B6 W1 v- |2 |* a`define STATE_capture_dr       4'h6
  ~% b  m' }$ L! d  e`define STATE_shift_dr         4'h22 p# q! L1 T8 S" c$ M: ^% ?% v
`define STATE_exit1_dr         4'h1
/ L; @' e: v8 {: A1 q`define STATE_pause_dr         4'h39 f& V; ^( c# n1 @& u1 ~0 i
`define STATE_exit2_dr         4'h0  u, j6 o: @" @/ W/ s& m/ q0 o* r
`define STATE_update_dr        4'h5% Q7 C5 C) \5 I9 k
`define STATE_select_ir_scan   4'h43 E2 V- I+ x0 v! j& d- T$ S, v
`define STATE_capture_ir       4'hE# e5 [9 a3 u+ Y* r: y# ]
`define STATE_shift_ir         4'hA! H' i  H$ S; T" I& g1 G' Z
`define STATE_exit1_ir         4'h9
' y% X) K6 n  B2 R+ c$ z`define STATE_pause_ir         4'hB7 A( A* e% u' @* m: ?4 p0 G/ @
`define STATE_exit2_ir         4'h83 R5 U- Q/ T) z" z
`define STATE_update_ir        4'hD6 h( r/ a. c" z9 T' A% R
1 F4 I! L% a6 W2 R  C
reg [3:0] TAP_state = `STATE_test_logic_reset;  // current state of the TAP controller. G: i: R2 r+ P
reg [3:0] next_TAP_state;  // state TAP will take at next rising TCK, combinational signal
2 ]& B) \9 B0 ^
! p$ j/ l6 {" R  O// sequential part of the FSM# J: ^7 ^7 ?' @6 K, ~' W  R6 b
always @ (posedge tck_pad_i or negedge trstn_pad_i)8 O, G9 Q" ]1 O4 s, h
begin0 j+ u) N: _) u. h, z- |
        if(trstn_pad_i == 0)
- }$ r( v4 W8 S* }8 D1 S; u                TAP_state = `STATE_test_logic_reset;
$ u; p5 H1 |* f4 R3 j/ V) x        else
2 I: f: @/ n2 a# O: [                TAP_state = next_TAP_state;+ \( t* U3 C1 D+ Y' o/ F
end
$ v5 N; q2 U4 s* \
" f0 Y0 T9 e+ B- k8 G4 {+ T ) q: G6 l4 t, x5 H7 E
// Determination of next state; purely combinatorial
, D- \: F8 Y- _) }6 o) halways @ (TAP_state or tms_pad_i)# @1 x( g1 q0 v' B0 Q6 I! q
begin7 O' u+ h; e5 y6 G* h5 }5 s  d$ X
        case(TAP_state)7 _4 V2 I. z4 u5 @" b
                `STATE_test_logic_reset:
0 `9 u, M" q2 x, V% ^( B- d                        begin* F# H0 F6 E* o6 X0 Q% ?2 e
                        if(tms_pad_i) next_TAP_state = `STATE_test_logic_reset;
& P3 z4 Q0 c, u. k4 \. t                        else next_TAP_state = `STATE_run_test_idle;$ ^& s: D- Z+ y8 z* X3 r8 P& \
                        end' M; F% F) C3 e3 Y/ }- ^
                `STATE_run_test_idle:! t6 k3 ?: d2 O* _; E
                        begin9 ^9 Y% c5 k; W* N
                        if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan;
! v& L" L- ?9 x. p; H* W% w                        else next_TAP_state = `STATE_run_test_idle;$ [. ~, r+ o: |, J: Z
                        end
( M5 }+ P( ]) c                `STATE_select_dr_scan:  K/ ]  [. t( Z% e$ i0 i$ q- O
                        begin6 H/ R  k: C+ I- N: z9 l) @" J
                        if(tms_pad_i) next_TAP_state = `STATE_select_ir_scan;
' g  d- p0 J  o3 e# a% o                        else next_TAP_state = `STATE_capture_dr;
% l$ F) |, Q3 I9 J: e! Q7 E                        end" h6 e% r1 H8 i
                `STATE_capture_dr:
% g4 r0 b3 K; u7 c& S                        begin
% a4 V+ E& _$ n: t$ P- p                        if(tms_pad_i) next_TAP_state = `STATE_exit1_dr; ; l' t- h2 {. d
                        else next_TAP_state = `STATE_shift_dr;
" t- ]7 k4 `0 D" e  h                        end
7 P9 E, s  w: Z$ X( @0 h                `STATE_shift_dr:
  b7 I0 m- @0 ^. e+ |. r                        begin) K+ Z% O6 J0 W
                        if(tms_pad_i) next_TAP_state = `STATE_exit1_dr;
! E+ h* S% ^# {/ E4 F# k& R7 i0 w$ N                        else next_TAP_state = `STATE_shift_dr;
8 L/ F% N" O! e, P* S                        end
, n* H  x0 z; T- R4 T+ K3 E                `STATE_exit1_dr:
  n6 ?& v+ L0 h) R8 C                        begin! k0 q" ~% Z0 L$ s* t0 F$ A4 t- v
                        if(tms_pad_i) next_TAP_state = `STATE_update_dr;
% ~+ b0 `8 b3 _# |# u* f0 k+ `$ x: _                        else next_TAP_state = `STATE_pause_dr;
" w# p/ x8 G7 @3 Q; h, f9 `6 M                        end
0 V5 s. {5 C. [4 D, x/ X' Q# a                `STATE_pause_dr:! E( F7 P  F# _, @
                        begin/ g0 v9 t# l( p& M
                        if(tms_pad_i) next_TAP_state = `STATE_exit2_dr;
; Z: Q) u0 x' }; f* s% Y. f2 J6 f                        else next_TAP_state = `STATE_pause_dr;
( v9 G, k) O5 F; [5 x                        end$ p) D2 q) T1 D
                `STATE_exit2_dr:
# D; \& ?+ x' B8 O- s' l                        begin
3 [7 _# B8 G" f# @7 l                        if(tms_pad_i) next_TAP_state = `STATE_update_dr;
" s6 h: c/ G: m$ h                        else next_TAP_state = `STATE_shift_dr;0 ]) q# w1 h# {- c
                        end- e% k2 e- \: d3 [8 ~
                `STATE_update_dr:0 x+ O% d, j! M, n. T2 n
                        begin
4 p9 H) R1 \% k3 J4 K5 Z( z3 U                        if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan; 7 C# v5 ]/ Y+ E8 H! W. u# v- j. i
                        else next_TAP_state = `STATE_run_test_idle;$ t) o! G( ^6 K- ~. {% X1 ~- S# u
                        end! p, ?7 O. A/ Z+ A# U
                `STATE_select_ir_scan:/ @% }' i1 R1 s0 s, _- Q
                        begin
- @  b5 j3 U! r0 }5 R# H                        if(tms_pad_i) next_TAP_state = `STATE_test_logic_reset;2 K" l0 x! z. f
                        else next_TAP_state = `STATE_capture_ir;8 b$ o* _/ Z& v8 s1 @
                        end
0 b; M# Z% V1 n6 [  a                `STATE_capture_ir:) H2 }8 g% r( p6 x- j
                        begin
( [7 B, Z7 j4 m4 ]6 e                        if(tms_pad_i) next_TAP_state = `STATE_exit1_ir; ! j- P0 m- S! |/ S
                        else next_TAP_state = `STATE_shift_ir;
7 V: {4 G4 d8 {; V" t9 n                        end
. E! L% D* S9 m4 V                `STATE_shift_ir:  o! s7 Q, B4 x5 e3 M6 }
                        begin& ]$ }$ D2 P, i' Y* ~! v  A
                        if(tms_pad_i) next_TAP_state = `STATE_exit1_ir; & j7 e6 K, l0 i7 v& x# @
                        else next_TAP_state = `STATE_shift_ir;/ K* r2 `) `" m7 A, z
                        end
( _) I# P4 [$ u# ^8 K                `STATE_exit1_ir:& `: m% i% Q, z& D5 g# i' i
                        begin
+ X+ ?- k% m( `                        if(tms_pad_i) next_TAP_state = `STATE_update_ir;  p6 o# H! }4 D' D4 G& O% _
                        else next_TAP_state = `STATE_pause_ir;
5 D" c) e+ t3 y# {: K1 Q                        end1 N: c5 z4 w# x, z. }& z
                `STATE_pause_ir:
* T0 L" h) h' u                        begin
$ j+ c8 a4 m' N7 r6 {" `: N                        if(tms_pad_i) next_TAP_state = `STATE_exit2_ir;8 S. Y: N3 w% x' X5 m
                        else next_TAP_state = `STATE_pause_ir;
, y4 z( s' \) u, n  A3 S                        end
# J9 K+ a, }; q3 G6 r" [* [; R( i, ~                `STATE_exit2_ir:
  |8 W/ g# k% X/ h# ]                        begin- ?/ X6 z8 Y  T, r, H$ l
                        if(tms_pad_i) next_TAP_state = `STATE_update_ir;/ r/ V! n$ p8 h$ X7 ?& D5 b& w* e" O; S
                        else next_TAP_state = `STATE_shift_ir;
0 B8 @; a. m" ~: ]# Q0 [                        end+ o3 D) a6 B% Z: h$ N* A
                `STATE_update_ir:6 ]) y( H9 e9 u! P4 [
                        begin
! e, o" P$ d# J, z8 G  ^' [                        if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan;: q! i  [8 Q2 i: b3 r
                        else next_TAP_state = `STATE_run_test_idle;( ^! J3 {: B8 N4 r( y! ^' s4 A1 F- \
                        end
& T/ P7 K/ L) \2 Q  O& K' Z0 H+ ^( q9 W                default: next_TAP_state = `STATE_test_logic_reset;  // can't actually happen
5 R2 X  Z0 J% C, t& w        endcase+ e+ G: o, c+ V+ K
end
$ r7 @0 z  n( o0 D, f8 O
& |3 u& Y0 u& [6 u; f5 z" ~  s
( `& [2 P4 P) _- F6 b: l$ [// Outputs of state machine, pure combinatorial" A* h6 Q) H% n! x; A7 X
always @ (TAP_state)
4 _% ^. k0 `' m) j- B0 L4 nbegin
& S. U4 I6 ~! ^        // Default everything to 0, keeps the case statement simple" I5 S6 E4 n! ]7 @2 z) C& z
        test_logic_reset = 1'b0;0 B$ s3 @+ A, @& I
        run_test_idle = 1'b0;4 R  h; I) {% h% l' A
        select_dr_scan = 1'b0;! ]) ]1 C/ X5 I$ D  d4 ^* q4 A3 S
        capture_dr = 1'b0;4 |/ o) G- O( ~; O/ t, W5 e: ~' T
        shift_dr = 1'b0;, j/ x0 C! H8 j+ Z- v( @
        exit1_dr = 1'b0;0 o' u( x6 j( j- h' F/ d9 N# h& d4 f
        pause_dr = 1'b0;
7 J3 s  S3 m5 s0 p0 O! h        exit2_dr = 1'b0;
: e8 w* V" F) o/ I/ {        update_dr = 1'b0;- J( `' G( k, T. J
        select_ir_scan = 1'b0;0 o3 D1 y" ]) ^/ H6 }% T/ e4 h
        capture_ir = 1'b0;5 W" t4 t% @% S) n  R
        shift_ir = 1'b0;' n8 j' a7 k" |
        exit1_ir = 1'b0;
1 B4 k* M, O, w' r0 g/ k        pause_ir = 1'b0;
! q: S' V: K6 {5 p        exit2_ir = 1'b0;' i3 q1 G' \! [4 }5 a8 c9 L
        update_ir = 1'b0;) }; I9 O' t4 F5 n9 G

+ Y# @: {: q* X0 E0 }; F" w6 Y        case(TAP_state)
  z1 r3 H1 z/ l* v                `STATE_test_logic_reset: test_logic_reset = 1'b1;
: f) h9 X/ d7 T2 V                `STATE_run_test_idle:    run_test_idle = 1'b1;4 R( b' l0 w  t+ K% v  [
                `STATE_select_dr_scan:   select_dr_scan = 1'b1;& y3 O% i! F, R3 C3 ?& Y8 Q
                `STATE_capture_dr:       capture_dr = 1'b1;' _6 j7 G4 e$ K" [  h4 w
                `STATE_shift_dr:         shift_dr = 1'b1;
( I2 \$ v( z1 V/ W; L                `STATE_exit1_dr:         exit1_dr = 1'b1;
1 T8 ~0 W2 O- V- k                `STATE_pause_dr:         pause_dr = 1'b1;
( n0 f6 V! h* ^6 l$ @9 M                `STATE_exit2_dr:         exit2_dr = 1'b1;9 ~4 y( F: M9 S! t$ W! x  L
                `STATE_update_dr:        update_dr = 1'b1;
2 p$ o5 ?0 H  A                `STATE_select_ir_scan:   select_ir_scan = 1'b1;/ @8 R. A  x( a' y- n/ E: U
                `STATE_capture_ir:       capture_ir = 1'b1;* X4 y2 p! G3 {' ?+ C7 ~2 k0 t
                `STATE_shift_ir:         shift_ir = 1'b1;
8 x/ d- u$ z5 z; z                `STATE_exit1_ir:         exit1_ir = 1'b1;* L8 B, s; N# G0 H- |8 }% w
                `STATE_pause_ir:         pause_ir = 1'b1;
4 b) F4 E0 a, @) s                `STATE_exit2_ir:         exit2_ir = 1'b1;$ D: |& m( k$ G0 Z  o. x
                `STATE_update_ir:        update_ir = 1'b1;
6 r8 K) b- S, f8 W' L* W/ ?                default: ;
4 K' ^3 y0 h5 z0 z) f0 T( N        endcase
; P9 M1 E, x+ N% H! w/ j% Y: uend) K( o- n7 ?5 {) O

, S* P2 k- G7 y2 C" w- [' i( i) v; t/**********************************************************************************
0 t- z, Q( Y9 M( B0 }$ D*                                                                                 *# C9 l7 h8 m; ?6 C$ m. D) E
*   End: TAP State Machine                                                        *
' R. d: M7 |* D7 X, K0 p0 B3 n- y*                                                                                 *4 S8 c" g7 D) w, W. ?( ]
**********************************************************************************/
7 W7 i3 |. ^, I; ~; v# q* W  k) E  c7 ^# M( v
  I) f8 G, a" o+ |
c,shift reg% O. f, d# _! ^+ s
上面说过,JTAG的本质和SPI相同,都是基于shift register的,也就是“以物易物”的思想。那么,如何操作tap呢?通过向tap中写入相应的指令。那么如何将指令写入tap呢?向tap移入任何IR_LENGTH的支持的指令,tap就会移出等长的数据,这个数据没用,直接舍弃即可。$ K/ p( }' c% h5 |7 W
整个过程非常简单,代码如下:# |: [" L$ B9 {
0 Z! h) e7 _9 L0 K
/**********************************************************************************0 U* l( U* B2 B  y* @! g; X; `( ~
*                                                                                 *- N5 ?; Q+ x) P
*   jtag_ir:  JTAG Instruction Register                                           *3 g* P6 U6 a) m9 H0 U( m
*                                                                                 *5 F) h2 r8 |, z
**********************************************************************************/2 l$ P8 _) x0 G1 ^3 o# W
reg [`IR_LENGTH-1:0]  jtag_ir;          // Instruction register
: E& |  D) L; o( v; u) Wreg [`IR_LENGTH-1:0]  latched_jtag_ir; //, latched_jtag_ir_neg;  p' I8 o# `; i  @8 _, _1 Y. Y
wire                  instruction_tdo;  A; p& x( _4 [1 K
0 b2 O0 U6 @3 w" J8 x+ I& I1 F
always @ (posedge tck_pad_i or negedge trstn_pad_i)7 N& z) r  T: ^; A
begin6 `- W# A% S) [# I7 Y8 x6 h& d
  if(trstn_pad_i == 0)
$ P& K$ v/ r) Q1 c# p+ p    jtag_ir[`IR_LENGTH-1:0] <= `IR_LENGTH'b0;" k7 U; k. W& [
  else if (test_logic_reset == 1)+ e7 i+ ^# s5 Q' l; |/ L# D4 Y
        jtag_ir[`IR_LENGTH-1:0] <= `IR_LENGTH'b0;
6 R; k. `6 E0 @  else if(capture_ir)5 N/ [- A2 [2 B6 F- V4 p
    jtag_ir <= 4'b0101;          // This value is fixed for easier fault detection  A8 V+ V& i+ U& A# a' b( r3 B7 S
  else if(shift_ir)
7 Z/ w7 J8 N& s    jtag_ir[`IR_LENGTH-1:0] <= {tdi_pad_i, jtag_ir[`IR_LENGTH-1:1]};+ r! a6 s8 J1 j, y1 t9 b
end# Q5 ?, n8 Z. E, w
assign instruction_tdo = jtag_ir[0];  // This is latched on a negative TCK edge after the output MUX
6 D8 |6 X+ r- s9 v// Updating jtag_ir (Instruction Register)' n& ?% A/ @4 [- f4 C
// jtag_ir should be latched on FALLING EDGE of TCK when capture_ir == 1, h) e' |$ b; I
always @ (negedge tck_pad_i or negedge trstn_pad_i)- n8 K7 j% {/ D/ Y& e* u0 Y# ~
begin
8 S3 q/ U. w8 K# L! |+ L  if(trstn_pad_i == 0)
- C( m+ O& M; _, w9 z    latched_jtag_ir <= `IDCODE;   // IDCODE selected after reset1 d: z7 @3 H+ G* m2 `, Y* L
  else if (test_logic_reset)1 N% R9 \+ m4 a, T: v- x/ m) ^
    latched_jtag_ir <= `IDCODE;   // IDCODE selected after reset  m9 Y; ^2 \- G5 ~7 [. \+ }4 B
  else if(update_ir)1 `, P2 M' ^- n; @) J+ ]& T5 W
    latched_jtag_ir <= jtag_ir;3 S, ?% }: f( |8 d  t6 z  B
end( |' a$ i5 ~* n4 f. C* `: d6 ]. r
/**********************************************************************************
5 ^& ]& t. z2 m& E1 Q4 J*                                                                                 *
2 r8 m& P( X& R# o) y& m% c*   End: jtag_ir                                                                  *+ {" G0 j1 c+ J; {$ P$ X
*                                                                                 *
6 A1 ]$ J& u( @: w/ J**********************************************************************************/& X2 i* v4 g$ X' c9 i& ?1 X
2 v# K/ X/ v) U' ?0 }* p
上面的代码可分成三部分来看,指令移入,指令移出,指令生效。需要注意的地方有以下几点:" k$ x/ j! [  n' q
首先,在移出之前,如果想读指令的话(进入 capture_ir状态),移出的将是0101。
6 ~) E+ |4 [; C* R) ], E; ?其次,从jtag cable移进来的数据放在jtag_ir寄存器里面,实际生效以后存放在latched_jtag_ir中。" i$ J+ t/ f0 z5 ?- w& x
最后,移出的数据来自jtag_ir,而不是latched_jtag_ir。所以说latched_jtag_ir是送给device的,而从device来的数据是放在jtag_ir中的。但是,需要移出的数据暂时存放在instruction_tdo中,最终移到tap外面的数据(tdo)并不一定是instruction_tdo,还有其他很多来源。这个后面会看清楚。
3 N1 \* e( y4 S
& g- M: p( d4 m; j, e
5 M) @5 M' {% Zd,read IDCODE9 r, P' A. v  w2 `
上面,我们解释过IDCODE的作用,那么怎么才能读到IDCODE呢,还是“以物易物”的思想,代码如下:
- \, U6 K. r) i% e5 M5 b# Q
) M* T9 B3 v8 x/ x
6 v6 h  t! ~7 Y: F4 y& [/**********************************************************************************
4 n" C# J/ X: ^3 c, q1 s*                                                                                 *1 B$ w0 ^: A/ s3 V0 b2 B
*   idcode logic                                                                  *% C0 R' j" k8 t# |. X, l/ g
*                                                                                 *
+ U+ h( P- P! [* s**********************************************************************************/  S" T  P2 w* \. L4 h
reg [31:0] idcode_reg;) Q1 C5 @$ |' h, _4 M4 A
wire        idcode_tdo;, {& Q- F7 Q$ @0 F% q1 \9 Y

- w3 w# \( {/ e% G7 yalways @ (posedge tck_pad_i or negedge trstn_pad_i)
1 l8 I/ F& P! U5 H; ebegin9 I  y2 u, |' |! C
  if(trstn_pad_i == 0)" ~1 P, A. z& Y/ j9 {: |, f2 I
    idcode_reg <= `IDCODE_VALUE;   // IDCODE selected after reset
$ l$ A$ b' U( w& C7 I8 x: R  else if (test_logic_reset)
) e) C1 M, u3 N& s  O' X% Q    idcode_reg <= `IDCODE_VALUE;   // IDCODE selected after reset  |3 |; E  A2 C& o! k. @
  else if(idcode_select & capture_dr)
6 R- A8 i' K: k8 h; o    idcode_reg <=  `IDCODE_VALUE;
8 c$ e# _2 B/ l, G# e; y5 i, R# @  else if(idcode_select & shift_dr)8 a2 ~4 v5 t3 m
    idcode_reg <=  {tdi_pad_i, idcode_reg[31:1]};, u6 D+ b3 r5 g; o% I
end2 |' @1 h. Z. f8 w6 U
assign idcode_tdo = idcode_reg[0];   // This is latched on a negative TCK edge after the output MUX
8 `' o- X  t5 j0 ]6 R/**********************************************************************************% E# w  b3 ]$ z" P" V  M8 w( @( @
*                                                                                 *( g0 ~: A, L" f2 _0 t% U, I
*   End: idcode logic                                                             *0 [9 l/ s! g( p+ i
*                                                                                 *
/ @) d% A2 b$ `  P8 t**********************************************************************************/8 |1 E' l6 \' C. d
, j  W: e" x" s  P
读IDCODE的过程和写指令的过程相同,不同在于向tap写指令是不用关心tap移出的内容(0101),但读IDCODE,不用关心向tap移入的内容,关心的是tap移出的内容(IDCODE)。' ]' e8 A: L' O) R8 ^3 q

  f) q$ J* J  o8 I8 C) @2 k: N  p
" y2 n- A: q+ h& A/ _* H
e,bypass
9 d) [8 @. R' L) L, Vadv_dbg_if在使用时,和他在一条jtag chain上的设备必须全部bypass,否则,数据就到不了adv_dbg_if,也就无法工作。这个很好理解,jtag chain,顾名思义,就是一条链,就好像打电话时的总机和分机。如果你想给某个分机打电话的话,那么总机肯定不能接,也就是总机bypass。8 f8 K  a0 Z) n  Z9 O& I
jtag_tap 工作在bypass模式是时,一个耳朵进,一个耳朵出,唯一的影响是会造成1个cycle的延迟。; }8 }9 R+ {+ K5 |- n
代码如下:9 |/ \5 E0 j- V/ ?8 i6 m
3 i# Y# a  U% @! Q* X! H

4 R2 l7 d) C* O: u$ S1 O/**********************************************************************************) r. D3 j# Z- _& H8 W
*                                                                                 *
! |1 @' n1 Q$ `$ i% s! h- P1 w# y( A*   Bypass logic                                                                  *; o* ]2 v  b2 k
*                                                                                 *
9 v) ~/ w. L8 y% z. V" t**********************************************************************************/. u- A4 N" \3 M8 c8 D) }4 V
wire  bypassed_tdo;
  v6 I6 l, m' u5 d# areg   bypass_reg;  // This is a 1-bit register( p$ J: c! F/ V! F
% f5 B$ _8 g* X1 I1 M
always @ (posedge tck_pad_i or negedge trstn_pad_i)
8 }7 y  i) E: q  x$ M/ y, O9 Q( qbegin) c9 F) ]  z0 {' m" _; v+ h9 Z: Y
  if (trstn_pad_i == 0)
( ~. G! b; k$ C& X     bypass_reg <=  1'b0;( D9 H& E4 b  B8 Y
  else if (test_logic_reset == 1)
# {7 C& M' \5 L- Z0 Y0 N     bypass_reg <=  1'b0;
. j5 h4 t- F3 p& }  else if (bypass_select & capture_dr)9 }! U& G7 U! z, H: t) V: q
    bypass_reg<= 1'b0;4 t0 F) M' P1 A" Z
  else if(bypass_select & shift_dr)' q, |" S3 k+ w- y
    bypass_reg<= tdi_pad_i;- ^+ }2 ?0 q9 `! ^8 N" t! d
end
* ?5 J; _; F1 G; i& xassign bypassed_tdo = bypass_reg;   // This is latched on a negative TCK edge after the output MUX$ h$ X) @, L! e$ h/ M5 u% D
/**********************************************************************************
% `9 m9 ~) R  ^0 {*                                                                                 *
* l* P( g2 T0 M  {8 F8 T5 L*   End: Bypass logic                                                             *8 U6 k2 U7 F, Y2 \6 W2 L/ Z
*                                                                                 *
% S( q. X8 o1 [0 D2 Y6 M, a5 n**********************************************************************************/+ Y' K, u3 Y* g0 t& ?. @5 @# l3 [1 a$ M
7 Y. Z! V% A. f
f,mux output
+ k: ~, v( T! O$ U' z0 ktap扮演着多个device(分机)的总机的角色。当公司内部的分机有很多,但总机只有一个。所以总机需要有多路选择器的功能。
4 c3 W) E- P+ d  @- O代码如下:
3 Q  Z& `8 q3 ~
( E* C% ~3 s5 H1 R# w' u$ V
4 ?* U( W7 ~7 u2 N( L' ]/**********************************************************************************
: `' `. Y* i6 h5 n5 }*                                                                                 *
" h9 j( y9 a' Q*   Multiplexing TDO data                                                         *
! `' E8 c$ Q! v0 W2 h  O, o*                                                                                 *, q: ^+ l5 V! z: Q6 h) _/ f# e
**********************************************************************************/& F  u) b# Y& E' Q
reg tdo_mux_out;  // really just a wire+ [6 Z& T; ~5 J! |4 }  J2 T! X- P

6 C: m  {5 o! W3 K( s7 lalways @ (shift_ir or instruction_tdo or latched_jtag_ir or idcode_tdo or: N! _$ Q5 P. M6 ~+ E
          debug_tdo_i or bs_chain_tdo_i or mbist_tdo_i or bypassed_tdo or
/ p9 W! D3 ]! y4 q- y                        bs_chain_tdo_i)( Z. z! r  y0 e
begin
) x0 I( |  Q; s. r  if(shift_ir)
% K: ?2 w5 e& {; U+ x$ \" p    tdo_mux_out = instruction_tdo;% t! e4 e1 f, v8 B. A
  else% G; g- u! F& f4 V  V) F8 C
    begin
" r9 [. ]' u( S: B+ P2 y      case(latched_jtag_ir)    // synthesis parallel_case
% H4 n$ w" b) i2 d/ _& t& t3 M        `IDCODE:            tdo_mux_out = idcode_tdo;       // Reading ID code: {, F1 p4 U) u8 R2 z: ~
        `DEBUG:             tdo_mux_out = debug_tdo_i;      // Debug$ s4 C8 b. z& ~5 p
        `SAMPLE_PRELOAD:    tdo_mux_out = bs_chain_tdo_i;   // Sampling/Preloading3 N( N$ }+ X" f  a
        `EXTEST:            tdo_mux_out = bs_chain_tdo_i;   // External test# H8 p% N  v- q& w9 [; P. f
        `MBIST:             tdo_mux_out = mbist_tdo_i;      // Mbist test
" K! U: C) D4 ?& ^. I        default:            tdo_mux_out = bypassed_tdo;     // BYPASS instruction
: @2 x* G! A2 v1 M  R* }      endcase
% G# z9 c: R' w3 d    end0 W% {, U/ T6 A% n7 ]
end
1 }2 ]7 n1 q. V5 E, B" C6 T
5 n/ x4 H' @# w- O
7 G) B9 q1 H4 I9 y8 U: s// TDO changes state at negative edge of TCK' {- w! ?( W$ c
always @ (negedge tck_pad_i)& W/ j' J# f( |/ ]6 s9 R+ e% B
begin
7 D/ S" b: D+ ^        tdo_pad_o = tdo_mux_out;
$ }  O. X8 ~5 Gend
$ F% D( L1 F( V% K, [  L 8 `) X! \# A( [2 ^

  p8 I/ }1 b$ q6 \+ f) p5 S, `// Tristate control for tdo_pad_o pin- g: \3 F$ w! D; ]6 Q* A
always @ (posedge tck_pad_i)
! j! n2 ?5 i1 y3 Z& Gbegin
2 r  c( r, F0 U. E  tdo_padoe_o <= shift_ir | shift_dr;$ c8 ^1 P# m* G8 W2 q
end+ [' Z4 f6 ?: ~1 |
/**********************************************************************************6 l& Z9 X4 B3 J
*                                                                                 *
6 B: I# S# |; f*   End: Multiplexing TDO data                                                    *
% S+ v0 r9 ^$ D8 ~7 P; B*                                                                                 *0 e1 o+ q1 J$ k+ |* k% M) q
**********************************************************************************/
; ^+ C3 s2 p) I  t
' r* a# _( r$ ~7 Y7 o4 @$ j8 h- r4,jtag_tap的使用2 e: q& F0 v; z  a$ q; P/ w
要想使用jtag_tap,需要相应的驱动程序,才行。驱动的作用就是根据FSM的定义,以及命令格式,以及adv_dbg_if的实现,来操作jtag的4根线,达到某种目的。在adv_debug_sys的adv_jtag_bridge中的chain_commamds.c中有相关函数,代码如下:1 c3 S2 L3 h; _8 {9 {
2 ~% g$ Q! ~# L! V% Q7 O6 R
4 t) l, K- O& V* e' |. o
//
0 q1 x( e* R- k% ]7 E4 n// Functions which operate on the JTAG TAP
7 }$ E! k0 \" e( x2 P8 k
* n- y1 {" h2 w* f* p) ] 1 X5 L5 B& x6 O$ `' T8 {0 \
/* Resets JTAG - Writes TRST=1, and TRST=0.  Sends 8 TMS to put the TAP- I6 |, R- B4 W, j7 m( @" L' p
* in test_logic_reset mode, for good measure.8 {7 F3 J/ |+ `3 _# ?# w/ @* G
*/
+ ^5 q* \$ r# o- I# `int tap_reset(void) {$ h2 _% ]/ m/ O: @, n
  int i;
3 V, B3 e( x) V5 M7 `  int err = APP_ERR_NONE;" l1 E; U1 X: B0 t, R/ ?

' }5 v+ W, }' w- j2 D$ ^+ ~) C  debug("\nreset(");2 ]$ D3 n* g: Q) d
  err |= jtag_write_bit(0);7 x" Q4 V' u7 |/ y% x
  JTAG_RETRY_WAIT();
9 A, I6 k: }# P: L3 g) K  /* In case we don't have TRST reset it manually */
8 T+ a; p; m% S% a3 o6 w& q, d8 \  for(i = 0; i < 8; i++) err |= jtag_write_bit(TMS);
/ ~2 T+ z0 H7 T* u/ x4 m  err |= jtag_write_bit(TRST);  // if TRST not supported, this puts us in test logic/reset
; j9 B9 z1 @# ]# J+ b  n* |/ s  JTAG_RETRY_WAIT();
7 ~" s0 ]6 B! f: }5 H0 P/ _  err |= jtag_write_bit(0);  // run test / idle; z( h# T# \7 ?$ K, X; S5 _' O
  debug(")\n");
2 {( @  J6 p4 j0 E* ]: ]  // Reset data on current module/register selections0 X& i7 Q+ g: {/ G: f
  current_chain = -1;+ r/ }% `  ?, |! W/ c, W, p3 j
  // (this is only for the adv. debug i/f...bit of a kludge)$ {: ~, n& o$ g2 I3 \
  for(i = 0; i < DBG_MAX_MODULES; i++)
4 V# A: I( w! k' W+ Q    current_reg_idx = -1;4 Z7 \. K2 C1 `* c' V7 }: ]
  return err;
' Q9 O; X# f# X" L* e0 j}' J5 c# K4 c! P" q; N/ x: ^
  // Set the IR with the DEBUG command, one way or the other
; j6 x' d3 c# F7 _) G6 Mint tap_enable_debug_module(void)( l- N$ P, W' U2 m
{5 Q  @- V7 y- E! _, d3 ?# ~
  uint32_t data;
; V+ o/ E* Y, T2 j int err = APP_ERR_NONE;
- ~7 e0 w! x% P6 r' l* j# }  if(global_altera_virtual_jtag) {0 a+ i. e" m7 Y4 x7 G! m8 [
    /* Set for virtual IR shift */0 |. B( X/ Z, N3 S: ~
    err |= tap_set_ir(vjtag_cmd_vir);  // This is the altera virtual IR scan command3 b1 A/ y! Q5 V
    err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
  M. x; u' B7 K* g2 Z6 {7 d    err |= jtag_write_bit(0); /* CAPTURE_DR */
+ f1 D; X+ c& M& B    err |= jtag_write_bit(0); /* SHIFT_DR */# Y0 [3 B- K- e) b
    " F+ I5 D' T- _8 C7 c
    /* Select debug scan chain in  virtual IR */
3 O& H8 L5 A2 s! \9 J7 F3 O    data = (0x1<<ALT_VJTAG_IR_SIZE)|ALT_VJTAG_CMD_DEBUG;; u+ f5 T* \( r; u
    err |= jtag_write_stream(&data, (ALT_VJTAG_IR_SIZE+1), 1);  // EXIT1_DR
  o1 R. _! i1 ^# s    err |= jtag_write_bit(TMS); /* UPDATE_DR */5 i+ K; [. K" M' }
    err |= jtag_write_bit(0); /* IDLE */ ! A) f  l/ o6 Y; L# H
    // This is a command to set an altera device to the "virtual DR shift" command
6 s1 o( P# |4 u; x6 n' [6 R    err |= tap_set_ir(vjtag_cmd_vdr);) w5 X9 l6 [, H( S- t7 Q
  }
' r6 C$ c2 B/ n9 M  else {
" J* z, e4 G! J$ ?; l. j- u    /* select debug scan chain and stay in it forever */
+ m! F, K$ W  S* c! |& W    err |= tap_set_ir(global_jtag_cmd_debug);, K0 c: r! J. T0 r7 F4 D
  }6 ?$ n5 T( o4 T
  return err;
8 B7 i1 S, H. M2 J}
5 S: Y5 ]7 |% z: o/* Moves a value into the TAP instruction register (IR)
9 j. ]0 z# ^0 e  n3 x4 `* O# K1 L * Includes adjustment for scan chain IR length.
. i, f: ~- p0 L  G */; j, A" ]( n) W6 W
uint32_t *ir_chain = NULL;
2 R- H  U/ p2 o' hint tap_set_ir(int ir) {: M9 r& t' ~2 [! G: R  m
  int chain_size;
' ]! \/ a4 u% F, }3 Y2 k  int chain_size_words;; I, s  _" g( N6 y6 x' I
  int i;% [( c7 u7 ]$ [" ]9 f4 U/ c+ Q; ^
  int startoffset, startshift;
* R3 d, O0 [: [& f' Q  int err = APP_ERR_NONE;/ @' P& l( E) _( G
  : K7 Z5 P6 B! q+ W6 {6 z  N6 ]
  // Adjust desired IR with prefix, postfix bits to set other devices in the chain to BYPASS! h- N; F1 C8 R: M/ y% u
  chain_size = global_IR_size + global_IR_prefix_bits + global_IR_postfix_bits;- I- T9 V0 I4 _# i4 K4 X
  chain_size_words = (chain_size/32)+1;3 I6 J, i, x. I' O0 V
  if(ir_chain == NULL)  { // We have no way to know in advance how many bits there are in the combined IR register
3 }2 Y$ l7 f' t+ V8 E% `    ir_chain = (uint32_t *) malloc(chain_size_words * sizeof(uint32_t));
" n) a% ]) ?! M: y9 P' W+ f; t    if(ir_chain == NULL)- c# |% ?/ P; o  C8 {, n3 t
      return APP_ERR_MALLOC;9 G! L. }3 v( O/ B5 i' f
  }
% z2 W7 B+ Y3 `  M$ \  for(i = 0; i < chain_size_words; i++)
  G  C" U5 I: l" F0 p3 u    ir_chain = 0xFFFFFFFF;  // Set all other devices to BYPASS' [0 L. i7 Y) A( t0 A" H% r- s/ g
  // Copy the IR value into the output stream
) [$ N# W) b% D& f/ R0 Z  \  startoffset = global_IR_postfix_bits/32;; m/ ?: K& y  g# P2 O
  startshift = (global_IR_postfix_bits - (startoffset*32));
9 x6 A; b' O' c/ }  ir_chain[startoffset] &= (ir << startshift);) M; W7 B* e3 Q. d, G5 ~  T' X
  ir_chain[startoffset] |= ~(0xFFFFFFFF << startshift);  // Put the 1's back in the LSB positions
. A8 |  j$ B( M0 h9 F, s0 J  ir_chain[startoffset] |= (0xFFFFFFFF << (startshift + global_IR_size));  // Put 1's back in MSB positions, if any * \" C, G6 I) P
  if((startshift + global_IR_size) > 32) { // Deal with spill into the next word3 J+ M9 [, Q1 f* D  J9 }
    ir_chain[startoffset+1] &= ir >> (32-startshift);
* ^2 C1 N/ {% J) V8 V: X    ir_chain[startoffset+1] |= (0xFFFFFFFF << (global_IR_size - (32-startshift)));  // Put the 1's back in the MSB positions
  v1 U  D% g' b8 T4 X* F/ e" @6 P6 h; x  }
" [9 v2 t2 g( o7 i5 D& b) \2 \  // Do the actual JTAG transaction
) V/ C' F1 I4 w2 ^- F1 Y' C  debug("Set IR 0x%X\n", ir);* d7 k: D7 R3 R7 R# P/ |& m- {
  err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */- I4 w6 I) ?% w% o! F
  err |= jtag_write_bit(TMS); /* SELECT_IR SCAN */
% |1 c8 H' ^* u: }7 H- @7 S  err |= jtag_write_bit(0); /* CAPTURE_IR */- i9 y* b' D1 u7 ~" P) A# d
  err |= jtag_write_bit(0); /* SHIFT_IR */   
- t% g! V7 L  o3 a& ~6 A  /* write data, EXIT1_IR */5 K3 N- Y& i% f3 F
  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);
2 z! e7 O! [' O& g, S& e  err |= cable_write_stream(ir_chain, chain_size, 1);  // Use cable_ call directly (not jtag_), so we don't add DR prefix bits
& W( a% _! O4 v/ P/ t8 l  debug("Done setting IR\n");( W' f2 K: F# x0 A1 |, M
  err |= jtag_write_bit(TMS); /* UPDATE_IR */1 g) l* F3 p1 Y7 `) e8 I
  err |= jtag_write_bit(0); /* IDLE */  
* y1 G% P% t( ?( Y  current_chain = -1;8 V- p& A: X  V  K" U. U7 A
  return err;# l3 p- e8 v$ D, k  r7 n
}+ I6 z3 k6 j2 l( [
// This assumes we are in the IDLE state, and we want to be in the SHIFT_DR state.
6 e7 P( s1 ?6 F% c( tint tap_set_shift_dr(void)
/ V, T9 O! v+ x1 U{0 Z4 q$ R0 T* K; ~9 @/ M9 R( v% A. c  |
  int err = APP_ERR_NONE;+ L! |8 F# w! P7 z
  err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
% X) t% ?% D0 h+ [  a) c  err |= jtag_write_bit(0); /* CAPTURE_DR */
: b+ L( z& a! A  err |= jtag_write_bit(0); /* SHIFT_DR */' V7 ^4 e9 l( C& n
  return err;
; }2 [2 u2 s6 y* B8 [9 G}
- ?8 w* i# Y/ O: @% a" P% u// This transitions from EXIT1 to IDLE.  It should be the last thing called! Y2 w6 x8 U+ `" g0 {  T
// in any debug unit transaction.& F& }( Z9 G( d6 r+ I2 X! {1 @# b
int tap_exit_to_idle(void)/ G3 d" m1 B9 D0 p5 `$ ?
{
) C; X' X6 Q( k7 b& \- E, X  int err = APP_ERR_NONE;$ S$ v. X2 C8 c0 J
  err |= jtag_write_bit(TMS); /* UPDATE_DR */
. H# d; c0 d4 R  err |= jtag_write_bit(0); /* IDLE */
6 a3 {2 s9 c( x0 Q, K5 d  return err;
& u9 `3 ~5 I8 I! r0 x8 q; P3 ~}
: f9 W& H9 h& E) O! E// Operations to read / write data over JTAG3 {% {5 c) F9 m$ T
/* Writes TCLK=0, TRST=1, TMS=bit1, TDI=bit0
. f7 U: U1 J7 t   and    TCLK=1, TRST=1, TMS=bit1, TDI=bit0
6 x9 D4 Q! ~* O) f1 _*/
! O8 @& c1 V/ w# a! `; u% wint jtag_write_bit(uint8_t packet) {2 |% }' O3 o1 A0 }
  debug("Wbit(%i)\n", packet);
& N' H: i' O6 ]( D; w# C  return cable_write_bit(packet);
( C4 h3 j, S. K8 C( T5 C( l}
/ Z0 b0 V- n& o7 R$ M6 n& j9 ]int jtag_read_write_bit(uint8_t packet, uint8_t *in_bit) {
! M+ O3 ?- @) ~8 }9 [  int retval = cable_read_write_bit(packet, in_bit);
/ {" m0 d4 Q7 p) M& N+ q; _  debug("RWbit(%i,%i)", packet, *in_bit);
! P! d2 p8 m. ~4 j, d  return retval;& b4 i$ _9 j# ^) ^
}6 I/ O- V1 n4 ^  S4 r1 |
// This automatically adjusts for the DR length (other devices on scan chain)
! B3 b6 c7 M2 O( J' _// when the set_TMS flag is true.) X/ e! x  g# S; Y# W
int jtag_write_stream(uint32_t *out_data, int length_bits, unsigned char set_TMS)
0 j0 `. H9 K2 s( Y9 v{  V9 p' H' o0 M: I! E
  int i;6 q" B5 D% D) y5 ~( u9 H% K0 S
  int err = APP_ERR_NONE;; w( a; E9 e8 u* j. ^; i
  if(!set_TMS)
) t2 B* x% E" X- f+ J4 B    err |= cable_write_stream(out_data, length_bits, 0);
! K( i- z. I  e  n: M. }; L  else if(global_DR_prefix_bits == 0)' s1 u) d: f! J5 o
    err |= cable_write_stream(out_data, length_bits, 1);
) S% R6 @) V& m0 O! b  else {$ L- a0 z2 K- [# I% c/ c( o& q6 d
    err |= cable_write_stream(out_data, length_bits, 0);( N$ h' x! e* K1 ^# p0 |
    // It could be faster to do a cable_write_stream for all the prefix bits (if >= 8 bits),) ?" X  c9 ?9 w  c4 W% X6 a! m6 l
    // but we'd need a data array of unknown (and theoretically unlimited): o0 V9 c& W7 h1 I5 m
    // size to hold the 0 bits to write.  TODO:  alloc/realloc one.
& f6 I7 R2 o. l* }) h8 A1 ]- f    for(i = 0; i < (global_DR_prefix_bits-1); i++)4 H" J4 K! n! G1 q9 G0 O9 s
      err |= jtag_write_bit(0);+ K) E4 J  F! z5 D; Z- {' x
    err |= jtag_write_bit(TMS);- p( @' p7 X, L0 f1 U9 m" D6 \! l7 B! @
  }
% B$ w- Q- \# P1 j  return err;* p0 f) r" t% V4 V! L' [
}
: H0 Y8 t3 z# ?2 d6 P// When set_TMS is true, this function insures the written data is in the desired position (past prefix bits)
- w9 d: h% F, Q# {// before sending TMS.  When 'adjust' is true, this function insures that the data read in accounts for postfix  h# L* k. e% _+ G) a/ @! u4 R
// bits (they are shifted through before the read starts).- K* Y/ h6 c! h" E
int jtag_read_write_stream(uint32_t *out_data, uint32_t *in_data, int length_bits, unsigned char adjust, unsigned char set_TMS)4 [3 `6 J& V- i0 G
{
+ H8 F. O( y/ P4 e3 }  int i;
' e* M% m& C/ ?3 b$ U  int err = APP_ERR_NONE;( V9 y/ {4 V  i, p
  if(adjust && (global_DR_postfix_bits > 0)) {" l+ K9 S( M8 q8 X# A* x' F( t. @
    // It would be faster to do a cable_write_stream for all the postfix bits,
% I- d3 C: W# j, W! N! V    // but we'd need a data array of unknown (and theoretically unlimited)
6 C9 F- r: i. O8 o6 a, _    // size to hold the '0' bits to write.
1 t  z4 C7 W/ y$ |5 \$ Y, {. F    for(i = 0; i < global_DR_postfix_bits; i++)
6 I+ M( _( l" r5 \      err |= cable_write_bit(0);6 s* }& I6 _! P' s3 L  Z) ?
  }
2 A% o  G- X9 u  // If there are both prefix and postfix bits, we may shift more bits than strictly necessary.7 o2 Z6 \1 g4 m: v
  // If we shifted out the data while burning through the postfix bits, these shifts could be subtracted8 ?. y4 ]- b2 Q
  // from the number of prefix shifts.  However, that way leads to madness.
4 y* I  b. |  v& l& U( }  if(!set_TMS)
5 n& @' W8 J9 Q+ A* t2 x    err |= cable_read_write_stream(out_data, in_data, length_bits, 0);  
: ?; ~" \, |8 X( P4 X/ M5 x  else if(global_DR_prefix_bits == 0)
5 n1 P# Z4 |. z0 J    err |= cable_read_write_stream(out_data, in_data, length_bits, 1);  5 z7 `! W8 q. {3 ^8 l
  else {
/ Z; h" C- j/ v9 ?    err |= cable_read_write_stream(out_data, in_data, length_bits, 0);
% u/ e9 @7 }" {" S# N    // It would be faster to do a cable_write_stream for all the prefix bits,! n$ }' q& }6 M3 N7 }
    // but we'd need a data array of unknown (and theoretically unlimited)
/ k3 F) C# q+ j5 L) V" g! z7 V, f    // size to hold the '0' bits to write.
4 k& v8 I7 N, {: v    for(i = 0; i < (global_DR_prefix_bits-1); i++)
! w1 O9 x& b0 C7 q3 L0 O& l: }      err |= jtag_write_bit(0);
  E1 Y! w8 O$ z; M    err |= jtag_write_bit(TMS);
0 f& [9 B( M' r% \3 X0 D: {  f$ U  }1 R( r, `# o) ~( d
  return err;( }" i" [' k' g. A3 I+ x$ X/ s
}; n3 F7 p9 }" W# U' v
// This function attempts to determine the structure of the JTAG chain7 a% c  d7 V3 v1 Y
// It can determine how many devices are present.
% I2 \. a+ ^$ b: J" w1 Y// If the devices support the IDCODE command, it will be read and stored.
' F7 ?3 n  G6 [$ w// There is no way to automatically determine the length of the IR registers - . V3 P( f7 T* x: B# S* D
// this must be read from a BSDL file, if IDCODE is supported.
7 X# i( z7 B! b. q& o& t// When IDCODE is not supported, IR length of the target device must be entered on the command line.: n; q! H4 E$ B9 s& g8 `* ^; W6 P; |
#define ALLOC_SIZE 64& B& ^& N5 C; U2 w
#define MAX_DEVICES 1024- P5 L; Q( D6 ]
int jtag_enumerate_chain(uint32_t **id_array, int *num_devices)# \' l$ K; n6 t7 ^
{) q' E+ z( |4 J9 t" a
  uint32_t invalid_code = 0x7f;  // Shift this out, we know we're done when we get it back+ u( K: n7 e9 u1 t
  const unsigned int done_code = 0x3f;  // invalid_code is altered, we keep this for comparison (minus the start bit)( {- S+ y- d) }9 L0 x; h8 Y6 [( T- T
  int devindex = 0;  // which device we are currently trying to detect& A0 ^" O/ |0 }; v5 n- k$ f9 g: U
  uint32_t tempID;. p: k/ s' T" v- j! s! N7 |: N; F$ K
  uint32_t temp_manuf_code;$ @8 y- c  e6 q" z5 Y6 X
  uint32_t temp_rest_code;0 n+ w. P7 U0 B$ q
  uint8_t start_bit = 0;7 I; k; j8 C7 \+ c& z
  uint32_t *idcodes;& J5 p4 i0 A  v
  int reallocs = 0;+ a* M+ O& r; U
  int err = APP_ERR_NONE;
9 X2 ^4 i( w. }3 o0 r$ X1 b6 ^  // Malloc a reasonable number of entries, we'll expand if we must.  Linked lists are overrated.2 D% J  Q9 ~& H* S, ?4 O- q+ G2 g
  idcodes = (uint32_t *) malloc(ALLOC_SIZE*sizeof(uint32_t));
: D. W3 L, x1 f1 W  if(idcodes == NULL) {
' d& {- w' Z, O/ l4 i    printf("Failed to allocate memory for device ID codes!\n");
/ [( q% W0 X( w% d4 N    return APP_ERR_MALLOC;; y1 M! m. F8 V+ v: H
  }
+ ^2 U' g$ R% _- O  l8 w  // Put in SHIFT-DR mode
' T4 W. F( Y( x6 S$ T5 h; `6 c# i  err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
: _$ {7 |' e1 f( `" E  err |= jtag_write_bit(0); /* CAPTURE_DR *// W$ p! w5 |9 [
  err |= jtag_write_bit(0); /* SHIFT_DR */
& G, J1 {0 `2 K+ `4 a  printf("Enumerating JTAG chain...\n");  ?3 d+ P$ q, f. E( w* ]
  // Putting a limit on the # of devices supported has the useful side effect
' o. _5 P' k9 w& ^( H% M) x  // of insuring we still exit in error cases (we never get the 0x7f manuf. id)" d. P; E" ]4 j! K0 x
  while(devindex < MAX_DEVICES) {
  e9 `! b. e+ Q7 A! ?    // get 1 bit. 0 = BYPASS, 1 = start of IDCODE0 q1 }( f0 h/ M, }* \3 r4 Y
    err |= jtag_read_write_bit(invalid_code&0x01, &start_bit);. q; F. ~: [! B+ N( d- m- }
    invalid_code >>= 1;; m9 m( l9 D3 u% h( v8 ^4 y" P( ?

& R4 W; b# Q% l  F    if(start_bit == 0) {* P, T) e. t5 s" }
      if(devindex >= (ALLOC_SIZE << reallocs)) {  // Enlarge the memory array if necessary, double the size each time$ T7 `, C9 S+ S. n
        idcodes = (uint32_t *) realloc(idcodes, (ALLOC_SIZE << ++reallocs)*sizeof(uint32_t));
- r& g* m' L0 G( Q! @: u: c3 f        if(idcodes == NULL) { ; T* f# f: @$ Y8 q! A3 l) O
          printf("Failed to allocate memory for device ID codes during enumeration!\n"); % f* [; b8 g) }/ L
          return APP_ERR_MALLOC;9 |- e& x# y9 {$ V2 e
        }
& t" p$ g/ J2 k& K& Y/ }      }( Q) B$ o! r8 }: g
      idcodes[devindex] = -1;  R% d; @0 E. @8 ]
      devindex++;: @1 }9 x8 i7 x) F7 g3 x
    }
* m5 x/ v- f: A4 T    else {
6 }5 r1 H  O1 a      // get 11 bit manufacturer code8 g3 G: o; W0 b1 I: X/ t
      err |= jtag_read_write_stream(&invalid_code, &temp_manuf_code, 11, 0, 0);
3 l& ]) a( O! ^: n: |  _      invalid_code >>= 11;
( F5 Z( K1 w+ C0 |) D3 P/ E; t      
4 F# ~  Z8 E( i5 e) @- s2 W      if(temp_manuf_code != done_code) {
5 I  h# a9 w, G/ {1 b0 Z# l8 ~# h# ]        // get 20 more bits, rest of ID: e7 W: m) I- w9 \1 f
        err |= jtag_read_write_stream(&invalid_code, &temp_rest_code, 20, 0, 0);; A# I: C% A, V  @  s: @& A
        invalid_code >>= 20;
& C8 G, Y, P  r, Z  m) K        tempID = (temp_rest_code << 12) | (temp_manuf_code << 1) | 0x01;
! [1 d+ J# ]* o        if(devindex >= (ALLOC_SIZE << reallocs)) {  // Enlarge the memory array if necessary, double the size each time
8 I9 S5 b, J5 s! [          idcodes = (uint32_t *) realloc(idcodes, (ALLOC_SIZE << ++reallocs)*sizeof(unsigned long));$ Q) l9 \! e  K- O5 L: \$ B
          if(idcodes == NULL) {
0 X6 D, F0 T! n. z" D( t' V            printf("Failed to allocate memory for device ID codes during enumeration!\n");
, W  }8 M1 p4 j1 Z) ?            return APP_ERR_MALLOC;% x, P5 {1 k, S* T* m; y: L4 G! l# }
          }
& U. B/ \0 o  q: |* Y, w7 R        }( z. q, D: r* N( l# @2 x& E6 m
        idcodes[devindex] = tempID;6 S) H) \' @0 U  K$ f1 _
        devindex++;
! O: {/ ^1 D8 z      } else {" ?* {% F+ _( ?4 N8 n; d: K: i% f
        break;7 m* M' F3 I; f& l  p* H  R
      }
+ Y- ^  h- m" `/ k    }
& y' I( ?/ K& w: R8 E* X, i1 \- D5 |4 @6 L    if(err)  // Don't try to keep probing if we get a comm. error* c/ C% [7 i0 Y# r- j
      return err;+ I% A, Z0 S" D. f+ l
  }# z  K( K1 G7 t6 P2 U
  if(devindex >= MAX_DEVICES)
) g5 ]4 Z- n. N- i    printf("WARNING: maximum supported devices on JTAG chain (%i) exceeded.\n", MAX_DEVICES);8 L5 s8 y; v) Q. \0 d9 r( G

- g/ M" K. F4 V0 x# X  // Put in IDLE mode
2 `9 c( }& t- Z/ y2 d  err |= jtag_write_bit(TMS); /* EXIT1_DR */  t# I! O* P7 Q0 W8 Z- M5 F- y4 f2 Q
  err |= jtag_write_bit(TMS); /* UPDATE_DR */' L* L9 i: c+ V$ b* W6 G3 V
  err |= jtag_write_bit(0); /* IDLE */ ; [8 {& E! w$ ]/ _& w. n4 Z

# E" Z% S% o0 p* H! H( x( S7 r+ N  *id_array = idcodes;' f# e% c  W0 s1 T
  *num_devices = devindex;$ r* \2 p# R# r4 ]3 B+ m$ T

9 f& Y2 `$ P* w9 w! z5 C; Y  return err;, m6 E) C( F* j) y( V7 j- w$ K
}
! [1 R6 f1 D& w# V$ ~5 j- {! V8 ? ' R6 w9 D4 s3 U
3 [7 V2 k6 I, Y$ ^. j/ M
$ Z6 A/ P% ], o+ a2 G. W
int jtag_get_idcode(uint32_t cmd, uint32_t *idcode)6 J0 k7 H# E; \6 {/ P1 J5 g7 K4 d- Z% \
{; r) Z" ]  Z- E5 l+ B0 r8 O
  uint32_t data_out = 0;
& s( D; M- L. Z3 I# n2 V2 f6 Y2 `  int err = APP_ERR_NONE;
" G" k0 _+ O+ _: W  unsigned char saveconfig = global_altera_virtual_jtag;8 j* m3 z& N& S: z
  global_altera_virtual_jtag = 0; // We want the actual IDCODE, not the virtual device IDCODE
0 m# E" {3 B' ]; x5 _
. Y1 A8 R$ \# i  k  err |= tap_set_ir(cmd);
3 y7 ]% B% K' n+ O( n$ U  err |= tap_set_shift_dr();
$ W: O7 m0 p7 N# u1 W& ^( z; y  err |= jtag_read_write_stream(&data_out, idcode, 32, 1, 1);       /* EXIT1_DR */
' i  h6 L3 c* v/ t* B$ I/ u# n
; k# c9 D% P" G/ w  if(err)
" k9 X' F3 A/ [    printf("Error getting ID code!\n");! @& Y! B# p. X5 n4 M

2 ^* {  R1 @4 c7 c2 z! ^) v& k: m  // Put in IDLE mode
" N" e! m5 A2 y1 |+ D0 ~  err |= jtag_write_bit(TMS); /* UPDATE_DR */9 D! t. I! M) r1 q4 b5 y
  err |= jtag_write_bit(0); /* IDLE */
% e, b9 R5 h4 x0 e+ v* G4 P
) J9 I0 q" f$ N! ?, r# [4 R  global_altera_virtual_jtag = saveconfig;! i4 e5 s) @1 ^5 q
  return err;
0 _' \0 @2 B, s}/ Y, _& L/ F5 b+ x4 l* p
7 y8 E. D* z; b; q* q5 s
: r: f1 q' U, x4 V
/+ K$ a+ C) I. |7 \
// Helper functions
$ u) L- U7 w# W" H2 ]4 ^6 M
7 o' Q0 _( J6 F9 P4 i0 T/* counts retries and returns zero if we should abort */7 m% d1 x1 d+ \
/* TODO: dynamically adjust timings */+ f/ [/ G& K& i7 _7 x6 r: p
int retry_do() {+ `  P" Z9 f+ h9 ?( ]  [2 u
  int err = APP_ERR_NONE;
: D( C+ ~" e* x ! j5 i2 Z& t. e, I- E8 Z' @& T
  if (soft_retry_no >= NUM_SOFT_RETRIES) {
, C2 z7 c1 k& {      return 0;
0 u$ e* D, q0 U" W- r6 A3 a  ^4 J# F$ @
5 X8 G. w/ S0 u      // *** TODO:  Add a 'hard retry', which re-initializes the cable, re-enumerates the bus, etc.' t4 b4 F/ D. q  p4 D

  k; n. N/ I  l! |' ]$ O& Y# {; z  } else { /* quick reset */
$ e+ s3 j7 B7 J( O    if(err |= tap_reset()) {2 y- c7 L( ~/ G3 z
      printf("Error %s while resetting for retry.\n", get_err_string(err));
" j2 m( T$ J) l      return 0;
$ s$ j8 N0 o6 Y" q  K7 @    }
. \" _# ?* G2 S* H3 r
* o& Q- x6 B# v: H! w6 e: i, V    // Put us back into DEBUG mode8 ^, f4 S: j* G, E8 P9 n  u
    if(err |= tap_enable_debug_module()) {& g, ~7 v7 h3 ^6 U; ^2 I& f% T
      printf("Error %s enabling debug module during retry.\n", get_err_string(err));
) m* J2 S4 ?+ `% e/ c& R      return 0;& u: ^/ I8 a. @1 S; C' B
    }: o& x- Q2 ]" N. }2 I+ c: K
+ M6 H+ p1 |8 r6 R# ^) j+ L% C
    soft_retry_no++;
; W6 s9 f( ]5 z& `8 ], n# w    printf("Retry...\n");
' x# e/ `+ |" l5 ~; T2 E  }
1 t% w9 w2 S7 s' ^: N1 g" n( s 5 f& m/ D% }# E" V
  return 1;
  g* B. `& w- K1 h}
: H# H( B- j3 |1 G4 O4 L# E- U! Y6 b: P$ ^: O3 R
; t1 B- N4 _1 x  t/ v
) n$ T5 `# S$ Q9 N

+ h! Q0 A6 [) n7 o) H; p0 B5 h) h4 ~" q: f4 ?/ t: C
5,小结- @' x/ `; Q( x* }/ c6 P. c5 z5 `
本小节我们分析了advanced debug system中的jtag_tap模块的具体实现。用一句话来概括的话就是,JTAG就是类似SPI总线的一种总线,jtag_tap就相当于SPI总线的arbiter。
* v+ s* y, ]( f+ L7 B

该用户从未签到

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

本版积分规则

关闭

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

EDA365公众号

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

GMT+8, 2025-11-24 20:08 , Processed in 0.234375 second(s), 26 queries , Gzip On.

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

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

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