EDA365电子论坛网

标题: #技术风云榜#jtag_tap模块分析 [打印本页]

作者: qsoiuwisjiuw    时间: 2020-11-19 13:27
标题: #技术风云榜#jtag_tap模块分析
+ D& G4 G! U3 E; k
引言  {, I: G" k" T" T3 O
“知其然,还要知其所以然”,在搭建好ORPSoC的仿真环境和调试环境之后,我们有必要对仿真和调试系统中扮演重要角色的jtag_tap模块和adv_dbg_if模块进行进一步的分析,以了解其工作机制。
$ ^# L: x3 @" q
0 U" r2 i3 i. s8 V. T! y3 U本小节就来分析advanced debug system中的tap_top模块。
5 z) x+ x7 s' E; S4 z% V/ Q! D
) f+ e- z4 t- [+ Z: Q! j! g: t( L: Y( f" {4 y6 Q
1,from SPI to JTAG* M7 `& Q$ l4 `/ h: ~
在分析JTAG的具体实现之前,我们先了解一下JTAGF的基本知识。6 u8 Q# b& V" Y. `$ `* ^+ O, V
: h$ n1 f4 u5 i  C0 k
A、JTAG协议的本质与SPI协议并没有什么不同,它等于一个复杂的SS状态机+变长的MOSI和MISO数据移位操作。不过所谓的变长,都是事先约定好的。 # W/ F& n- X; v8 O9 o7 a/ G& r( o
B、JTAG协议是一个同步通讯协议,它是全双工的。它的通讯原则是“以物易物”——即你如果想得到某些东西,你必须先给与相同长度的内容;你如果只是想发送一些数据,也会自动获取相同长度的内容,至于交换的内容是否有意义,这是另外一回事了。  5 R% ?8 X( M- Z- {0 i' I
C、JTAG协议无论多么复杂,实际上只有4根线起作用(有时候还有两根鸡肋的nSRST和TRST),他们分别是TMS、TCK、TDI和TDO,他们分别对应SPI协议里面的SS、SCK、MOSI和MISO。在本质上,他们并没有什么不同。即便是ARM的JTAG那么多的引脚,实际上起作用JTAG的也就这4根线而已。 0 N' }* s* D/ n, b5 u7 e
D、JTAG的数据操作都是基于移位寄存器的。
8 a3 Z# B' R9 V8 {! fE、如果JTAG协议在某个下载仿真协议中只是用来发送控制信息和少量的数据,而大量的数据传输是通过额外的其它引脚进行的,即便这个协议被称为JTAG仿真其本质也早已超过JTAG了,严格来说,不应该称之为JTAG。因为JTAG协议中就只有4根线(有时候也算上nSRST和TRST)而已。典型的如NEXUS协议。
. d6 x/ w( i  n3 m9 M# s6 a+ s7 I: W( G
这里面重点理解的是“以物易物”,这个概念,下面是SPI的工作机制以及one-to-one和one-to-many的组织。如下图所示:3 X; K8 Y( R1 }! Y, L9 c

% V) {% h3 A8 l7 I: Q! x, G' J * S/ \/ C3 }1 d- a+ t  G  Y% c
' y: F" ?) E/ F6 b- W1 P

  K7 ^- r4 e, y. E2,jtag_tap& n  C% q# l% s0 n, V6 ^! t

( Q; z5 o: A3 r6 |3 G# ~, t  @$ O- U7 O# h* p' P" ^
1>architecture) g+ l; D" [; v* v$ S

7 w7 Q: c- W2 \" o/ STAP(test access port)的作用是提供adv_dbg_if模块和外部JTAG cable之间的桥梁作用,负责将jtag cable传来的数据传给tap支持的所有device,并将来自device的数据shift out到tdo上。% R1 e3 y" J7 H  \( ?7 f2 Q
下面是adv_debug_sys系统硬件部分的结构:
; l' n  V; g3 E6 t# c) m* _4 C, |9 I, n8 {- K7 @0 q- j

& A, T, F" y& [' q6 ^( m# K 6 f: ^4 ]4 ]6 \- v. i' u
jtag_tap一共支持4个chain(相当于SPI中SPI总线上挂有4个device),其中IDCODEchain在jtag_tap模块内部,其它3个在外部,如上图所示。$ k3 @/ S, c/ l* F2 j: t( Q

( {4 R" w. {/ l/ |) ~( g2>tap fsm% d6 [0 r0 U! R  I
IEEE 1149.1中定义了FSM,所以几乎所有的JTAG模块都会实现相同的FSM。如下所示:& S. j) ~8 K6 A* j  ?" _/ V
) V2 G$ [. Y, M# n: A5 G8 D! o# t

# @1 d3 q; l% t0 `! k# N. b; e6 [( {4 n

+ Z& p3 Y3 L4 V+ n# D+ S说明:
! _( j: i) m3 y* C" Z整个状态机分为三个部分:信道选择部分、数据信道和指令信道。所谓的信道选择,就是图中最顶上由四个状态组成的矩形,分别对应着四个状态:
0 X6 a3 D8 Z" G% U- ha,JTAG TAP状态机复位状态  8 y. D5 j  ?- {6 v0 \. v; n
顾名思义,就是进入该状态,将导致整个硬件TAP控制器复位,所有的寄存器都将被初始化。在TCK的上升沿,TMS为低电平时,进入下一个状态;否则保持不变。
2 ?6 t* R  a! a# N4 y/ g! sb,JTAG TAP的Run-Test/Idle状态  ' q' d. O0 V% a; R
其实就是“开工”和“休息”的选择分支点。在TCK的上升沿,TMS的高电平将导致状态切换,进入数据信道的通讯状态;否则保持不变。
+ G7 f6 P- b7 f7 c3 {2 Hc,JTAG TAP的Select-DR Scan状态  
2 }8 P' f& W1 _( ISelect DR Scan,就是当我们在该状态下,TCK的上升沿读取到了TMS的低电平将直接进入数据信道的操作子状态机;在TCK的上升沿读取到了TMS的高电平,将切换到指令信道的通讯状态。  3 L% O/ f+ Y$ L4 |$ J. ~8 s
d,JTAG TAP的Select-IR Scan状态  
/ h, G3 x/ t% m$ v1 f3 d, K5 B# FSelect-IR Scan,就是当我们在该状态下,TCK的上升沿读取到了TMS的低电平将直接进入指令信道的操作状态机;在TCK的上升沿读取到了TMS的高电平,将重新回到JTAG的复位状态。  - a1 Z9 K5 h$ L" y
数据信道和指令信道对应着两个子状态机,从本质上数据和指令并没有任何不同,只是习惯上,指令的长度固定为4个二进制位(AVR32的JTAG是5个),而数据则随着不同的指令选择了不同长度的指令寄存器,这个就需要具体查阅相关的协议说明了,比如JTAG IDCODE的长度固定为32位,而AVR32的复位指令却有5位。下面,只就常见的几个状态进行解释(以数据信道为例)。
: H# _) z$ v' ]7 S6 T% V# [. T' d' R% q, O
3 S$ y# h* H' @8 p
a,Capture DR状态  
5 G' P  X  Y  k3 A3 I* @7 \$ tJTAG协议是基于移位寄存器的,其通讯具有“以物易物”的特性,在我们进入真正的数据传输之前,需要告知JTAG“准备通讯了哦?你有没有东西要给我哈?”,于是Capture DR就是一个给JTAG机会将需要传达给我们的数据放入指定的移位寄存器中的状态。  
4 g+ d/ i! r4 _: f' ?b,Shift DR状态  
$ \2 a2 t" T  ?0 p  _! z+ Y% F这个状态就是通过TDI和TDO进行数据传输的状态。需要说明的是,即便进入了该状态,TMS上的电平在TCK的上升沿也是会被读取的,从图中看到,一旦在TMS上读取到高电平,系统就会跳出Shift DR状态  
% _6 a; [! A4 t, o$ M9 a  v7 e* V如果此时数据没有传输完成,造成的后果是不确定的。请大家注意,我所说的是不确定,而不是“很严重”:同样是因为移位寄存的传输特性,有时候并不要求一定要将所有的数据都完整的进行传输,比如在AVR32中,针对SAB的数据操作,往往只需要进行最关键的部分,详细地内容可以参照相关的数据手册;
! Q3 {& y/ L2 C- i$ ~2 k% x但有的时候,数据的不完整传输则会导致很严重的后果,这取决于具体的JTAG通讯协议。所以,为了保险起见,一旦进入Shift DR状态,在发送最后一个数据之前,请保持TMS为低电平,当要发送最后一个数据时,应该将TMS设置为高电平,这样,当TCK跳变为上升沿时,系统既完成了最后一个数据的传输,也成功的退出了Shift DR状态。  + n+ {4 |! z; b# h
c,Exit1 DR状态  
3 x  i! _: E& o; Z" n该状态提供了我们一个在刚才输入的数据生效前,重新修改的机会。一般情况下,我们直接保持TMS的高电平,并在TCK的上升沿驱动TAP状态机,直接进入Update-DR状态。  
8 W, {2 u+ k* @0 X8 Z8 S/ u# J; Fd,Update-DR状态  $ O7 v: Q+ p+ C8 C$ s9 D% V: j; O
顾名思义,就是使我们输入的数据生效——一般JTAG内部的动作就是触发一个锁存信号,将移位寄存器中的内容并行的读取到对应的寄存器中。Update-DR有两个出口,一个是,TMS的低电平对应Run-test/ Idle,还有一个是TMS的高电平对应的Select-DR Scan。这两个操作看似区别不大,但是意义非凡。前者往往会导致JTAG内部产生额外的时序(比如发生一个信号,表示完成了一个特定的周期操作,在AVR的JTAG下载中有此实例);后者则表示完成了一次数据操作,将进行下一个数据的操作,但是这些操作属于同一个操作周期。当然有些情况下,这两种方法是没有区别的。
& ]6 p! y- y# a! ]+ w% d+ h% a. E+ h1 L; h& A2 V
3,RTL分析
9 t  v% L, I  l8 s3 \& M! P& Ejtag_tap是advanced debug system项目的一部分,整个advanced debug system我们之前已经介绍过,如有疑问请参考。
5 K# I* ^* }" g1 Ujtag_tap模块包含两个RTL文件:tap_defines.v和tap_top.v两个文件。
4 q! }. w1 n, _" [& w  M) D* M在了解了JTAG的一般知识之后,我们下面就分析jtag_tap的RTL。
: C* K) N7 K. g3 f, I5 q  F: N7 X2 W. U
1>tap_defines.v
/ k  {. C9 [6 G首先,其内容如下:
* {7 s& o# e( L2 P& F; T; ^0 F; _3 }* b' m! X5 F
  z, t; W% ]) @7 v* q) I$ f
// Define IDCODE Value
/ J9 n7 d+ j" ]  `# Y# O' Q7 e`define IDCODE_VALUE  32'h149511c3" R7 E' U" w; s: d9 I" N
// 0001             version: ?+ c- O7 a$ x4 Z) f$ w2 R( i
// 0100100101010001 part number (IQ)2 {/ Y. [0 V6 Y3 t2 v1 [
// 00011100001      manufacturer id (flextronics)
. `7 O+ j6 S5 o6 }* P" V// 1                required by standard
, Z7 W9 X$ E2 N4 d. ]( g" j9 W2 i; ` ; V. D1 a" n/ ]" h$ ^
// Length of the Instruction register
  _; a! e0 P6 P% {`define        IR_LENGTH        4
6 U4 G3 `  A, P/ k3 l$ s . X  K" H2 H) X6 e8 y2 P2 {; I5 h
// Supported Instructions5 N9 {4 q' D# C5 i; n/ q# k" C+ K) }
`define EXTEST          4'b0000
" \. V; W' Z# c! @`define SAMPLE_PRELOAD  4'b0001
, o1 p( o# a& t6 x& w3 N`define IDCODE          4'b0010- u" v, R# |) a9 j( c% O) O
`define DEBUG           4'b1000
8 G( s, |, h' f; \+ y0 s4 n9 ^`define MBIST           4'b1001% a& g: b' l4 @9 G! C5 ]
`define BYPASS          4'b1111
% z  r8 d: S0 q" k4 Q! P9 `
& p+ w. g, \9 I* f! |( ~文件包含三部分内容,IDCODE,IR_LENGTH,和instruction定义。
( O- h" f! _. e) K/ @) O( D& D  u$ Z  ha,一般情况下,每个jtag device对应唯一的一个IDCODE,就像人的名字一样,这个名字用来jtag chain建立的时候‘点名’用的。jtag chain初始化时,读取所有的device的IDCODE,和BSDL文件中的IDCODE比较,获得device name,显示出来。+ e; f& _7 f; D4 b% W7 W2 @& X+ Q

3 b! `( h  Z4 ob,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 R" b, x) f8 @4 v% n' o

1 h3 }/ a4 {: u, I4 Jc,instruction就是操作jtag tap模块支持的指令,这个参数,不同的tap大不相同,具体支持什么指令,可从bsdl文件中获得。下面就是jtag_tap的bsdl文件:2 A8 a" C8 R1 c3 G
( R$ W4 }: ?- Q+ _- J0 s
; n4 }2 W- _* }# Q  p. {- N  {
-- This is a minimal BSDL file describing the particulars, N8 k/ n) u, H) t4 n/ T! f  T
-- of the OpenCores standard / native TAP.  It is designed6 X6 G$ U: y7 i# s1 B. ?
-- only to be used by the adv_jtag_bridge program.  This
6 J+ X9 D- p; T+ n-- file almost certainly lacks key entries and attributes
: y  g" J' k4 a. Y2 V-- required by other JTAG / BSDL systems.
$ h1 }9 @/ t- [3 F9 j7 Y--- n" Y: Q8 S6 @. F+ r
-- by Nathan Yawn (nathan.yawn@opencores.org)
1 j- }/ i6 X  a-- Copyright: This file is released into the public domain.
( T6 ?7 q2 ?( I( W4 f--/ C, S. P6 ?0 z  m4 U+ o
- c4 P* @/ K3 A% L* z9 \

" J. \$ _* V, w) D2 Mentity OC_TAP is, m/ |5 ?" {; z' d6 |0 [% p6 R3 r

3 q+ I: L% x) n4 k9 t  Q6 l3 o & _2 ]2 W6 P# Z+ R# c
attribute INSTRUCTION_LENGTH of OC_TAP : entity is 4;" y# N9 c, t& [6 x

' g6 g+ E9 }' R: T/ V. n 0 w& C& p7 k' H9 v) ?9 K1 T, e7 f
attribute INSTRUCTION_OPCODE of OC_TAP : entity is
; |" P- i+ N+ y0 T- ]: ^" ?        "EXTEST            (0000)," &: W  ~/ _# ^9 t, y" R
        "SAMPLE_PRELOAD    (0001)," &
5 z( S* \/ _3 a( e- D        "IDCODE            (0010)," &5 ?$ }" Q" ]9 ?, j6 x2 ~6 p8 S3 Q
        "MBIST             (1001)," &
+ y8 V3 M2 e; g7 E' Z0 E, c        "DEBUG             (1000)," &
# Y/ Y2 J1 t4 F7 F        "BYPASS            (1111),";
- o4 R7 V0 h' \! @0 G8 v/ C2 Q $ c) x' @. \3 g3 }0 a" E
9 K. \( s  o; k: i/ U. Y
attribute IDCODE_REGISTER of OC_TAP : entity is: a& ~3 ~0 c& A: w8 o
        "0001" &        -- version
' V' c* m" j+ t8 k5 \        "0100100101010001" &        -- part number3 X, Z+ J) ]+ M$ [1 p
        "00011100001" &        -- manufacturer (flextronics)9 `5 F5 C( x9 D' I
        "1";                -- required by 1149.1' `9 Z6 X* ]( t6 T

! f8 i) V9 _: t3 _ ; _3 l; ]$ l8 Q) W) O
end OC_TAP;
& n- e5 M+ z  q- ]0 l) c8 |3 W8 T2 Y! u0 e0 U" ]; ?! e% f0 l
1 d) ^! w+ _# m
2>tap_top.v  Q# [+ w6 X/ t1 e! b+ w
这个文件是jtag_tap模块的具体逻辑实现文件。
" j" k9 i- m& D1 \) @. n; k4 m$ ma,接口定义  b# j1 [: o6 i

& c0 A0 G' Y1 ~5 |
0 i# D) N! f* T* s// Top module  [( o% ]+ O, j
module tap_top(
! H2 P3 o  Q* V2 R                // JTAG pads
6 v) g: H" l; ^% f+ I" }  ?& p                tms_pad_i,
/ F7 B9 H+ ~+ Q6 q& n) V1 O                tck_pad_i, . F+ b' z- E. b  n2 o4 y
                trstn_pad_i,
2 A! u# ?! N* A' w8 r/ }                tdi_pad_i, 5 `2 S) V* e# a! K0 ~: E2 s  V
                tdo_pad_o,
) `  ~& D0 Y& T, U% _$ g                tdo_padoe_o,7 k/ P! P+ Q" K# H$ U  A
' H# l$ g9 \% m. u2 `+ [7 T
                // TAP states' Y" x2 {) s' n, s# n8 Z
                                test_logic_reset_o,
8 H% l- r0 }8 z2 _# c) H                                run_test_idle_o,
- C$ O( ^3 v2 K0 p" H$ I                shift_dr_o,
9 D' W  ^0 N( v9 c4 e                pause_dr_o,
6 J) \# [: E5 `' F* \, s% ~% P                update_dr_o,$ ^5 q: }2 N' D- K
                capture_dr_o,8 O' }0 F4 w% \" D! o+ X3 M
               
2 u: E. c% i' F+ n9 p. u+ A+ u8 d5 M: W                // Select signals for boundary scan or mbist
$ ?5 |; E/ ^- [+ `0 c' H                extest_select_o, # W* ?! g+ M( L( j, Y$ ?
                sample_preload_select_o,
8 @" f2 d$ H* w# c# s; t: ~4 E7 U                mbist_select_o,5 O5 W, K  B  `5 c" P
                debug_select_o,' W  h: C& s) Y. ~' N6 X- J
               
: n7 ?5 w' \* O. W                // TDO signal that is connected to TDI of sub-modules.
. z& Q6 u0 C, i% {                tdi_o,
# r' n5 i8 ]- Y- b% X- D% m                3 C: H) y3 w, |( Z" u
                // TDI signals from sub-modules' |- M+ H: D, b8 ^
                debug_tdo_i,    // from debug module% i+ {2 k  N/ P) z. d' |0 o
                bs_chain_tdo_i, // from Boundary Scan Chain3 E8 E5 H8 a' G8 X
                mbist_tdo_i     // from Mbist Chain
: k: P0 w  ^7 ]- t              );: h1 w3 n3 n0 ^2 O& _, _; ^
, ~% R$ P- _; K1 r
jtag_tap接口可分成5个部分:jtag信号,tap states信号,片选信号,tdi_o,以及从device来的数据信号。4 Z  q* m7 g8 j" q5 |
1》首先是jtag信号,除了我们常见的tms,tck,tdi,tdo之外还有两个鸡肋信号:trstn,tdo_oe,前者用来复位tap,后者用来使能tdo。其实这两个信号有没有都可以,tap的复位可以通过tms来实现,tdo使能也可不用。
9 M4 B3 T5 o. i说到这里,有一个小问题,如果不用trstn信号,上电之后tap的状态是随机的,那么有没有一个固定的tms序列来实现tap的复位呢?答案就在本小节中,如果有疑问的话就找找看吧。( b2 s, P6 q8 B! v8 [$ Z  E$ D
2》其次是ap states信号,给device用的,指示tap的当前状态,device根据这个状态来完成某些操作。
9 @" F7 K/ R! O( @. z: c! y4 `3》片选信号,这个就不用多说了。对于jtag_tap来说,片选信号时根据IR reg中的不同位来决定片选的。% W- i% N; h6 ^% M- T# c
代码如下:
1 V4 d! k0 P3 i' U0 Q3 Q/ I! ~6 W  p4 n" h/ i

' b( `# E( U5 J6 l! Z5 x2 R0 a2 d/**********************************************************************************
, C' D. t1 D4 r% \: l. ^*                                                                                 *
( \; C& X6 u6 L! a  A  @4 ?*   Selecting active data register                                                *0 l% Q1 c2 E, M4 o  R6 C2 E
*                                                                                 *1 R; |! N2 M5 P! a3 a6 `3 F
**********************************************************************************/
7 l6 a% A* Z% balways @ (latched_jtag_ir)
; r& J7 x( F9 Ibegin
* j. W% Y0 }1 o: c6 k  extest_select           = 1'b0;
) M9 V1 s. E6 U* `% w  sample_preload_select   = 1'b0;$ g6 |  q2 v* Q  t: {
  idcode_select           = 1'b0;
& @' ?+ P: E# C' l  mbist_select            = 1'b0;* H' z/ K* N: J* c0 s% S
  debug_select            = 1'b0;- i) e; c: N" L! F
  bypass_select           = 1'b0;
3 u3 N/ m. y) H9 T& w4 A
. k2 g3 J- i6 S" j- g/ {  case(latched_jtag_ir)    /* synthesis parallel_case */
8 C" D4 D! G0 C: |, \- K7 b    `EXTEST:            extest_select           = 1'b1;    // External test: p7 D- v( h1 Z6 k9 ~: y9 H! }
    `SAMPLE_PRELOAD:    sample_preload_select   = 1'b1;    // Sample preload
+ @& Y% m9 q6 j4 ?/ A$ r3 v" w    `IDCODE:            idcode_select           = 1'b1;    // ID Code
6 l$ t+ s5 O: J% G9 g+ z    `MBIST:             mbist_select            = 1'b1;    // Mbist test
/ z: }# ?  o, @. D' s# @( A    `DEBUG:             debug_select            = 1'b1;    // Debug7 @. G% U" j/ F5 A8 ]; j
    `BYPASS:            bypass_select           = 1'b1;    // BYPASS
& V3 ^& M+ _0 i1 C    default:            bypass_select           = 1'b1;    // BYPASS3 p$ {' o* R8 _* c
  endcase% Q- o1 Y0 L0 a' g- n
end
! i2 d) P) z5 A7 v. E; H$ A" t& O. `+ {! O6 B

" h) I0 ^. |" `1 v1 P( u8 N, a4 C1 k" a, f7 t; m$ E7 l3 q
b,fsm
: G) T9 J+ G9 t6 s0 ljtag_tap的核心就是tap controller的FSM了,常见的三段式风格:; Z- N* w& ?; r7 [/ s- ]
& h( L# E  X4 L9 a" K- \
& k( t. T8 O) Y' ~3 g6 l3 d) @
/**********************************************************************************/ {6 {0 L+ ?; O. B, q7 s* _, }
*                                                                                 *
" x0 c# M5 e  ]8 k*   TAP State Machine: Fully JTAG compliant                                       *+ n2 p" x7 e; p; t( o$ O
*                                                                                 *1 u% y9 D( d' a2 I, h
**********************************************************************************/
8 Y- `# }7 M$ Y0 d+ Z// Definition of machine state values.  We could one-hot encode this, and use 16
7 V& b! m2 m6 A6 g2 p) y// registers, but this uses binary encoding for the minimum of 4 DFF's instead., r4 i$ N! V% K& O
`define STATE_test_logic_reset 4'hF$ H, C3 K& u; i+ p$ ^% H
`define STATE_run_test_idle    4'hC9 Y$ |% f% i$ W. [7 z0 B
`define STATE_select_dr_scan   4'h7  R# j% {5 {; ~' }; l% n
`define STATE_capture_dr       4'h6
/ E) _: T9 A4 e' ~: U7 {`define STATE_shift_dr         4'h2& S  [9 p6 V, q& \7 S
`define STATE_exit1_dr         4'h19 ^7 O$ Z4 ?) @; p: {
`define STATE_pause_dr         4'h39 w- F# H0 O7 v, L/ G
`define STATE_exit2_dr         4'h0
6 L+ x7 h) H8 P6 x6 |- B7 X$ _`define STATE_update_dr        4'h5) S8 i- m# S$ z; j
`define STATE_select_ir_scan   4'h4
3 e3 @/ [$ Z( M2 L$ Y2 U`define STATE_capture_ir       4'hE
7 U6 g4 w% [, H" L' q0 ~1 ~" w`define STATE_shift_ir         4'hA5 B2 N) J/ d: R$ L3 ^
`define STATE_exit1_ir         4'h9
5 y9 }1 B9 I$ v4 W! n4 H`define STATE_pause_ir         4'hB: X7 y3 B3 ?8 x  v; I
`define STATE_exit2_ir         4'h88 ]; H' f" h, g8 f8 b0 C; Z
`define STATE_update_ir        4'hD
' Q) ?6 a4 d* e: d3 w, d ( W; Q- g; n% E
reg [3:0] TAP_state = `STATE_test_logic_reset;  // current state of the TAP controller, }3 }9 U0 U$ i6 z
reg [3:0] next_TAP_state;  // state TAP will take at next rising TCK, combinational signal
0 q- W0 j+ Y. Z' L
7 B$ }! |" s& p8 _- u, v// sequential part of the FSM4 _* _* q% D7 g4 M9 ^6 e
always @ (posedge tck_pad_i or negedge trstn_pad_i). q; `& F) B/ ^/ M' A
begin
# {- ?  W3 _3 ?3 N- Y        if(trstn_pad_i == 0)( {( ]$ P( f) D; E8 U5 P
                TAP_state = `STATE_test_logic_reset;- C7 W7 d: B# h) W; d- [
        else- i. ^7 A+ i! `% w* v- c$ N: w
                TAP_state = next_TAP_state;+ j' y+ ?6 G& J$ Q7 m: [" j: e
end) F9 g) B7 |" k/ e8 i& F
# |. {2 V# ^5 Z7 P7 k

8 Q, Q, c& G+ [0 p. t1 l6 `// Determination of next state; purely combinatorial
" D2 f" X' ]  s( D# e3 Balways @ (TAP_state or tms_pad_i)1 `/ G- a0 }9 U  Q& q! u$ g% T: ]
begin; [6 g8 W  F' d) O) p! m
        case(TAP_state)
& d6 {* p1 x/ h9 b9 g                `STATE_test_logic_reset:1 e# x/ ]! I. i9 }
                        begin
$ M% {) z+ t) U  ?/ e' w                        if(tms_pad_i) next_TAP_state = `STATE_test_logic_reset;
1 [  t9 p, G( b. t" y- B                        else next_TAP_state = `STATE_run_test_idle;4 \' Z+ C1 Y+ b
                        end5 Q  x2 m# p7 L/ p( @
                `STATE_run_test_idle:
7 {, Z3 D- j2 N2 \! u                        begin) `) ]; \8 i5 y3 g3 s
                        if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan;
  p* p$ I" ^( ~. W2 Z                        else next_TAP_state = `STATE_run_test_idle;/ {. _$ T$ v, Y2 g4 A4 F7 g0 m8 C
                        end! }" E( H5 x' C
                `STATE_select_dr_scan:! S$ H. h! j3 g% ^+ a
                        begin
. x- {- t. J  F- S) R                        if(tms_pad_i) next_TAP_state = `STATE_select_ir_scan;
6 l# C9 |- J. [- {                        else next_TAP_state = `STATE_capture_dr;) q# o$ }; Q. \& k1 ?" d
                        end
) O( [: x; J! A. U: p* K                `STATE_capture_dr:2 T6 F* l, E$ J9 S1 e! Z7 w1 J
                        begin
0 E! `& q5 T. \) Z                        if(tms_pad_i) next_TAP_state = `STATE_exit1_dr; % v" M7 Y0 X. o7 I0 b$ T* n! _
                        else next_TAP_state = `STATE_shift_dr;! |. F9 ~5 T1 ]( L/ F: G$ S
                        end
: j! S+ P, E$ Y; v4 C0 z                `STATE_shift_dr:) p2 G8 G9 v: H/ ]4 k" ^: p* [% X
                        begin
& @6 h, k, o) |; [" F                        if(tms_pad_i) next_TAP_state = `STATE_exit1_dr;
4 Q6 D# k7 H) I- ~, B, a  L7 z                        else next_TAP_state = `STATE_shift_dr;
6 I" {7 `. L" I' Z                        end
" }9 M: \7 X; g# [6 d% K  B                `STATE_exit1_dr:3 i" Z& h8 n( {' A+ G8 t
                        begin- Q5 E4 C9 l3 k1 U0 L9 j
                        if(tms_pad_i) next_TAP_state = `STATE_update_dr; 2 L! w0 e6 ~8 Y0 D: X8 a2 o
                        else next_TAP_state = `STATE_pause_dr;" v) c* P- {) K3 Y
                        end& d1 l, ]/ Y7 {7 e
                `STATE_pause_dr:$ N5 a# K/ c( }
                        begin
7 |3 O9 y; m# O- t: n! X                        if(tms_pad_i) next_TAP_state = `STATE_exit2_dr;
8 I; y1 c( m) P4 I, w% p6 z                        else next_TAP_state = `STATE_pause_dr;% r, x' \+ E, D5 T
                        end: a5 t$ ~5 _' ~
                `STATE_exit2_dr:: Y6 v. m+ U5 ]0 Y6 E" d
                        begin
+ ?& ?* M) j/ T( K                        if(tms_pad_i) next_TAP_state = `STATE_update_dr;
2 j: o, ~' f5 [5 f                        else next_TAP_state = `STATE_shift_dr;
" n& x. {, `1 A# J) l, \                        end7 o3 Z1 n5 C3 |
                `STATE_update_dr:
3 Y  o" `+ V: L6 L                        begin
9 ]5 a+ A  w" z5 }( ?+ F                        if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan; $ G0 t9 n4 T& i' b
                        else next_TAP_state = `STATE_run_test_idle;$ n. O) c) k6 C
                        end; X3 W. c9 f" Y. E
                `STATE_select_ir_scan:
, n- O- O" q+ g+ _% b' e                        begin) _, I1 u( k. i$ j$ p- @
                        if(tms_pad_i) next_TAP_state = `STATE_test_logic_reset;- R6 x, C- ?7 v) i6 V, T' H
                        else next_TAP_state = `STATE_capture_ir;
6 @0 ]$ W2 l3 ]9 o$ d% B                        end
4 a9 t8 F4 P+ }3 m; S8 S% ], P                `STATE_capture_ir:
0 O$ w5 F1 B/ z7 T1 G' u3 V- D                        begin
, }  K5 @% m' A$ V                        if(tms_pad_i) next_TAP_state = `STATE_exit1_ir; 9 w2 J  ~' O, a! X- g+ r
                        else next_TAP_state = `STATE_shift_ir;
  i5 V& x8 C: x# r! b4 U( w9 f. U                        end
. T* }( Y3 W; B8 X! U5 C! p6 s; F                `STATE_shift_ir:
3 t  A# [0 r" `& r: M                        begin
! S0 f7 B- d8 p2 }% L: S                        if(tms_pad_i) next_TAP_state = `STATE_exit1_ir;
' |2 X' I- q' a0 j                        else next_TAP_state = `STATE_shift_ir;' `5 N/ U1 W* X6 K
                        end
. \( m! C  l3 a/ D5 H                `STATE_exit1_ir:
5 q7 c; ?* F. T" x* |3 n: f' k                        begin1 g: c/ b3 t( l" B* @! b3 n5 u, R: I; ]
                        if(tms_pad_i) next_TAP_state = `STATE_update_ir;
. }2 F. |5 k/ Y9 }; x( t3 {7 C                        else next_TAP_state = `STATE_pause_ir;/ E, s2 c+ _* H6 ^
                        end
5 f$ o) u" u* F: M: f( p                `STATE_pause_ir:
2 w, K0 R" y& N- b. ]1 C                        begin" ^: b+ |( l7 k+ M
                        if(tms_pad_i) next_TAP_state = `STATE_exit2_ir;
# l! B- `, C: G$ F& C1 k                        else next_TAP_state = `STATE_pause_ir;
, y! E7 j8 C  s* U6 V9 K                        end
# s& ]2 t% A" C( M8 |                `STATE_exit2_ir:% M% J% f9 [* q) ~$ w
                        begin
- c6 ?9 q2 t& N9 M& B8 X                        if(tms_pad_i) next_TAP_state = `STATE_update_ir;
' e) J3 I# }: Z4 O$ i2 L                        else next_TAP_state = `STATE_shift_ir;
7 r1 i3 B8 C* W                        end- `9 f( D: b+ |' |0 I- c
                `STATE_update_ir:& B$ {8 q( _! y* O3 e+ z. V2 Y- @
                        begin
# n0 L$ W; W3 K- q* }1 e: r4 }                        if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan;
9 [4 _8 e. {' d  U$ y; C0 a; ]+ F                        else next_TAP_state = `STATE_run_test_idle;
# ^/ G! e1 x0 D) ], p6 r                        end
+ q/ g9 F5 g8 Y+ k* U                default: next_TAP_state = `STATE_test_logic_reset;  // can't actually happen8 d% g5 `, [2 q5 ?
        endcase* @/ F& ~! p1 y' b$ ]4 u: q
end
3 p0 r9 |$ M( ~/ C6 k
3 ^0 a7 M4 ~. A1 q- {! O! |$ s
8 o5 r9 W. E* Z// Outputs of state machine, pure combinatorial
2 V& h$ D( k) q- u* {always @ (TAP_state)
3 Y( Y( S+ i6 s' f# Zbegin
$ P: \; p2 y# P7 D        // Default everything to 0, keeps the case statement simple9 i( E. s* I. r) E/ }
        test_logic_reset = 1'b0;1 }/ e. @) K( H# _+ e
        run_test_idle = 1'b0;
, o& M3 R9 p7 z- ^7 D4 i        select_dr_scan = 1'b0;
. @4 Z$ B( V. E( p+ W1 X8 c        capture_dr = 1'b0;
. G7 j( Z3 t. w  P4 v* H        shift_dr = 1'b0;0 T7 U- d/ _& V! N: u4 [
        exit1_dr = 1'b0;2 C, b5 C8 F4 V0 ?: a4 u" n( O
        pause_dr = 1'b0;
/ B' o; O! c3 Z9 i/ n+ o        exit2_dr = 1'b0;& Y/ X$ o$ f7 _: e; ?
        update_dr = 1'b0;! H% G: S% R+ f/ [8 v3 Y& _
        select_ir_scan = 1'b0;
6 v) E" j& k4 D  c3 }        capture_ir = 1'b0;! d( X/ h4 N# p& v2 w
        shift_ir = 1'b0;4 u2 `' Z4 i& L" S7 X8 Y+ R$ M
        exit1_ir = 1'b0;% p+ A5 B8 P' h: D7 l
        pause_ir = 1'b0;
$ Q) ]  ^0 O* b  K% P3 ^8 z, b- H        exit2_ir = 1'b0;
( G' }& i0 ?( q, j2 e        update_ir = 1'b0;
: h- N# o+ w, x' a1 O% F& n2 j$ r ; u9 z, J8 M+ q" d8 b& n
        case(TAP_state)' ~4 a  c2 U4 G
                `STATE_test_logic_reset: test_logic_reset = 1'b1;8 R/ t- q/ M. C0 t" \
                `STATE_run_test_idle:    run_test_idle = 1'b1;7 I! E, y4 S4 `9 @" @5 A, n
                `STATE_select_dr_scan:   select_dr_scan = 1'b1;
# r" k/ H1 }: T3 b/ i7 C8 b                `STATE_capture_dr:       capture_dr = 1'b1;! q6 K4 a8 l4 K0 h+ T1 q1 u4 s; }
                `STATE_shift_dr:         shift_dr = 1'b1;; |# ]. O4 v% T. M. `
                `STATE_exit1_dr:         exit1_dr = 1'b1;; ?, N6 K0 ]& ^6 K& v( p
                `STATE_pause_dr:         pause_dr = 1'b1;5 f- X% n* U2 l6 b. P) S
                `STATE_exit2_dr:         exit2_dr = 1'b1;6 o1 ^. ?$ e6 Z- ?: w% h+ d
                `STATE_update_dr:        update_dr = 1'b1;; u6 N/ S1 f3 m/ w& E
                `STATE_select_ir_scan:   select_ir_scan = 1'b1;
: J. ~' I9 }* ?" n$ s                `STATE_capture_ir:       capture_ir = 1'b1;# O  ?) h0 C- d( [5 N+ o% z% \
                `STATE_shift_ir:         shift_ir = 1'b1;
1 P, x" y' X  g) c2 \& |- f: o                `STATE_exit1_ir:         exit1_ir = 1'b1;
; X: R2 Y5 G( f1 m; Q' g                `STATE_pause_ir:         pause_ir = 1'b1;, ^) @6 N% V1 l" e
                `STATE_exit2_ir:         exit2_ir = 1'b1;3 o% s$ C, n3 z7 i9 X! O# ^
                `STATE_update_ir:        update_ir = 1'b1;
  ^6 X  A* I/ l$ b7 I( F                default: ;
% |$ n$ u0 _$ J1 V% `; B! N        endcase
/ Z7 a: _6 j. q: Y1 |end. X% F" T; Q5 \8 s" ]

# \5 h. b# D, o4 i) X1 o5 j/**********************************************************************************
& E' t3 `5 I- K*                                                                                 *
3 P: v  _* Q5 \5 Z. ]) f" R9 i*   End: TAP State Machine                                                        *
, z0 K* Z  L6 B7 k5 K1 k- c*                                                                                 *
+ O6 q! W& m  O8 ^3 q**********************************************************************************/7 W. r/ H/ R9 ^
& E+ |  U' R4 q3 k9 W0 {3 Y8 \

/ |- @  B/ ~& k( kc,shift reg0 `$ `6 M1 ]5 \+ B
上面说过,JTAG的本质和SPI相同,都是基于shift register的,也就是“以物易物”的思想。那么,如何操作tap呢?通过向tap中写入相应的指令。那么如何将指令写入tap呢?向tap移入任何IR_LENGTH的支持的指令,tap就会移出等长的数据,这个数据没用,直接舍弃即可。
' d) ^: V# s) j# f5 _8 c* T, n- M" l整个过程非常简单,代码如下:! f# O0 z8 W5 K

! P$ O1 B# m0 P6 m  y& H5 W/**********************************************************************************6 n4 e) o9 N- i9 g( z7 d8 L( j- ?
*                                                                                 *4 U* g4 c( B3 E% Q4 }' \( |- w# ]
*   jtag_ir:  JTAG Instruction Register                                           ** X3 a/ J& T1 s
*                                                                                 *9 S! b9 u; A4 c$ q3 B
**********************************************************************************/
8 G" Q8 ?; H% greg [`IR_LENGTH-1:0]  jtag_ir;          // Instruction register
8 E8 h8 L4 {1 S( t6 @* Treg [`IR_LENGTH-1:0]  latched_jtag_ir; //, latched_jtag_ir_neg;+ [- O- O( Q* B* ]  ~9 N3 R
wire                  instruction_tdo;
1 S5 H& t2 E$ i
; x+ ?# f7 M. \+ W9 @7 e. Qalways @ (posedge tck_pad_i or negedge trstn_pad_i)
8 h9 s- ^. s5 b- mbegin
; y+ B' H) E- {8 I  if(trstn_pad_i == 0)1 r5 V1 C- [3 n, {9 H" i
    jtag_ir[`IR_LENGTH-1:0] <= `IR_LENGTH'b0;7 e5 D. {- b$ A; y% n' R
  else if (test_logic_reset == 1)
+ m: X1 e( p6 `, W( E3 a        jtag_ir[`IR_LENGTH-1:0] <= `IR_LENGTH'b0;2 N. x# R0 t( `$ o5 R% Y+ ~
  else if(capture_ir)
6 W8 ]- o' D! O' i4 \& ?( o    jtag_ir <= 4'b0101;          // This value is fixed for easier fault detection
3 }1 K6 \8 m0 e  else if(shift_ir)+ R" O$ @( H  |2 ?% U
    jtag_ir[`IR_LENGTH-1:0] <= {tdi_pad_i, jtag_ir[`IR_LENGTH-1:1]};* c) E* y/ Z" |0 b+ y; O1 [
end+ h( K6 m( C0 y9 z. M; [/ J9 S
assign instruction_tdo = jtag_ir[0];  // This is latched on a negative TCK edge after the output MUX
. N2 q+ \, c! `7 \% w' d/ A// Updating jtag_ir (Instruction Register): D3 C& v  k" B, v
// jtag_ir should be latched on FALLING EDGE of TCK when capture_ir == 1+ {0 N% m7 `! ~6 K7 B; I* R
always @ (negedge tck_pad_i or negedge trstn_pad_i)
: h5 l2 E0 J) w: k0 }0 mbegin8 p" J6 f* ^- Q! r1 E2 z
  if(trstn_pad_i == 0)
) x' X$ h# m: R6 v- g( q6 \3 B) l2 N    latched_jtag_ir <= `IDCODE;   // IDCODE selected after reset: U. e( Y4 q8 i7 t
  else if (test_logic_reset)
- f8 P6 o* W) d# B) a, A: K" t6 g1 w$ t    latched_jtag_ir <= `IDCODE;   // IDCODE selected after reset
/ p/ m3 g9 S# a! t  F1 P3 P. E  else if(update_ir)  u& W( k  e* l8 g
    latched_jtag_ir <= jtag_ir;4 _0 k$ Z3 J' L  N- I4 f. q' M/ N: F
end
# m) {- L' V- q& l) F. D! X/**********************************************************************************# q4 `. a6 b- X; z) G- e
*                                                                                 *
0 R$ O3 y( @: f( u+ q% h# e*   End: jtag_ir                                                                  *& a9 W$ |3 y, l3 h7 r8 O) h
*                                                                                 *
- H4 y6 h: T, u  ?( W% X**********************************************************************************/( r+ T# t; e$ z9 l1 {6 g2 p

7 p( l8 j. G5 b% r" j上面的代码可分成三部分来看,指令移入,指令移出,指令生效。需要注意的地方有以下几点:( w" u1 {9 P' }( ~/ }; h5 S
首先,在移出之前,如果想读指令的话(进入 capture_ir状态),移出的将是0101。
1 t! y; b( d  V$ @$ D; g0 {其次,从jtag cable移进来的数据放在jtag_ir寄存器里面,实际生效以后存放在latched_jtag_ir中。
+ O) V) U7 d9 v9 [' C( K) Z最后,移出的数据来自jtag_ir,而不是latched_jtag_ir。所以说latched_jtag_ir是送给device的,而从device来的数据是放在jtag_ir中的。但是,需要移出的数据暂时存放在instruction_tdo中,最终移到tap外面的数据(tdo)并不一定是instruction_tdo,还有其他很多来源。这个后面会看清楚。! y$ c6 V. B- `! F- _
1 b( w/ @9 {. A

  }1 O/ v4 m1 _- E' ~$ o. o: o' Vd,read IDCODE) ~$ M$ d3 a" D5 Z
上面,我们解释过IDCODE的作用,那么怎么才能读到IDCODE呢,还是“以物易物”的思想,代码如下:
3 V* O  @; K. F" K& L9 U4 u! Q$ F% D) ]" P: H0 Q

& T' d1 S% w( w) r9 n/ ^/**********************************************************************************
7 `( M) ^- |" _*                                                                                 *
' z. H, h3 ?5 k5 u( m- h*   idcode logic                                                                  *( v& Y: R) v. D
*                                                                                 *
) U! T2 x* d0 e6 n( r' O**********************************************************************************/
: X8 R- c# E1 b- treg [31:0] idcode_reg;
) X1 j5 J+ T- i3 G+ U7 Cwire        idcode_tdo;
) Z2 {8 V( w4 E 0 K* C: K0 D  N9 \0 v) _
always @ (posedge tck_pad_i or negedge trstn_pad_i)& r" V8 ]* N8 y" y
begin8 U& u! x* `* w0 Q* P) |, K
  if(trstn_pad_i == 0)$ N. V# }2 m  |+ b" N
    idcode_reg <= `IDCODE_VALUE;   // IDCODE selected after reset
6 T! T% X; k0 R6 ?  else if (test_logic_reset)9 ~  `- D, P* o  x" S
    idcode_reg <= `IDCODE_VALUE;   // IDCODE selected after reset- n' H+ x1 E, t0 Q/ J
  else if(idcode_select & capture_dr)
" R4 \8 ?) N5 H  R0 M# J    idcode_reg <=  `IDCODE_VALUE;. L$ p: F1 Z# `# l' f
  else if(idcode_select & shift_dr)
" ]8 X" s0 K5 t# g( p7 O    idcode_reg <=  {tdi_pad_i, idcode_reg[31:1]};
: L, b' P3 ]: l& Mend; v# N. ^8 H! Y2 Y. F8 ~6 V
assign idcode_tdo = idcode_reg[0];   // This is latched on a negative TCK edge after the output MUX
8 s+ N* B8 Z+ i4 f' [/**********************************************************************************& f# W. ~, I0 p+ ?" J9 Z) G
*                                                                                 *
) a) m3 p4 L5 C$ b0 F*   End: idcode logic                                                             *5 Y! U, \' }; [: ]' }" }0 L' {! |
*                                                                                 *
" t3 w2 e5 b! {) s8 e) {**********************************************************************************/
1 h( N6 D3 G, |$ K4 g
% i+ L; {; s# a: J6 J0 `3 q# K读IDCODE的过程和写指令的过程相同,不同在于向tap写指令是不用关心tap移出的内容(0101),但读IDCODE,不用关心向tap移入的内容,关心的是tap移出的内容(IDCODE)。
4 D# N% Z4 a$ Q8 U
& h5 }8 H$ d/ u. T2 A- F
2 s6 x# n. R0 [, C! q9 w# _4 c4 H2 N  I4 D* Q. J9 A- o, z
e,bypass. i( D8 j5 w7 }0 @1 p, k
adv_dbg_if在使用时,和他在一条jtag chain上的设备必须全部bypass,否则,数据就到不了adv_dbg_if,也就无法工作。这个很好理解,jtag chain,顾名思义,就是一条链,就好像打电话时的总机和分机。如果你想给某个分机打电话的话,那么总机肯定不能接,也就是总机bypass。
4 W0 H1 Y& t  j0 ujtag_tap 工作在bypass模式是时,一个耳朵进,一个耳朵出,唯一的影响是会造成1个cycle的延迟。
; D$ p5 b4 ?% T/ m代码如下:
( q, y6 R/ _$ J, j( X. k( ?1 _4 s# o

+ A* K% Y% `* e' U% e/*********************************************************************************** K# B. l0 g5 h* F) k4 d/ F$ x
*                                                                                 *
( n! w; a- I6 G& V, e% q) ~/ g*   Bypass logic                                                                  *
( u8 P- t" V1 A- z7 s*                                                                                 *
$ I5 }2 F  ]/ u% s8 F/ G. R4 F**********************************************************************************/6 T$ y# q8 ]4 _/ E
wire  bypassed_tdo;1 `, R/ p: M, `" w9 J
reg   bypass_reg;  // This is a 1-bit register1 w6 ~' X% M7 e8 T, A

8 s0 S2 z7 n+ |( B0 @7 A; `8 y% T( oalways @ (posedge tck_pad_i or negedge trstn_pad_i)
" p3 F0 ^  g- z3 C$ F; @begin, G# E2 @& g% A
  if (trstn_pad_i == 0)
% \5 |! X' d( t$ _. X+ m     bypass_reg <=  1'b0;
5 @$ Y- p* Z9 o" ^$ k$ L  else if (test_logic_reset == 1)/ v- m4 c) |8 f) e3 x3 Y3 J
     bypass_reg <=  1'b0;! G' h6 K- ~$ M8 ?9 u/ S3 H3 H" ?
  else if (bypass_select & capture_dr)
* y4 z- w8 o4 V& E5 o/ R- F    bypass_reg<= 1'b0;
" }1 }, ~& c1 c9 l  else if(bypass_select & shift_dr)% s+ v% {+ L3 U9 Q! v$ X- r* i; |
    bypass_reg<= tdi_pad_i;
; A* }1 h. z  @! t5 A4 ?end: D' s) x" a7 V4 W0 Q5 w
assign bypassed_tdo = bypass_reg;   // This is latched on a negative TCK edge after the output MUX  w" j5 q+ q6 j1 d1 B* s
/**********************************************************************************
8 t. l9 g0 W& ], D; B. m  q*                                                                                 *
) R2 M# |% d- p# Y8 O! |) A*   End: Bypass logic                                                             *
3 f" e) ^% U& t/ r- m" Z6 Q6 ?- f*                                                                                 *
5 H7 L3 `- i) @( s" Z( C**********************************************************************************/0 \4 R! n# Q, r4 k; ]4 J; R

7 |' P1 F' y  d: ^* k2 H0 ef,mux output9 @* N+ L: K, `2 @
tap扮演着多个device(分机)的总机的角色。当公司内部的分机有很多,但总机只有一个。所以总机需要有多路选择器的功能。
8 A8 y$ f* u5 ]. S$ W# T* \代码如下:* `' L# V. N) u7 K% a# G# R( ?  ^
' [( f5 e% ~: [4 D
7 @& y) t- F$ d7 \
/**********************************************************************************. z) ]8 W5 `& w0 O
*                                                                                 *6 |- x& d5 n( w) |
*   Multiplexing TDO data                                                         *
, f: D* t# F- Q7 J: x0 A4 u" I7 Y*                                                                                 *
5 `3 r# C2 _& u3 L+ q**********************************************************************************/  o4 B: u% I  m3 I- X5 Z6 k
reg tdo_mux_out;  // really just a wire% u# M# [. E, c
  K2 d4 J! C8 g3 g; o: H
always @ (shift_ir or instruction_tdo or latched_jtag_ir or idcode_tdo or
, |" g5 @0 M: Q* H9 B2 ?          debug_tdo_i or bs_chain_tdo_i or mbist_tdo_i or bypassed_tdo or
* x: E! f9 C7 @                        bs_chain_tdo_i)
0 O- @& G6 |8 s* X" s' x  W+ Rbegin
: S! @7 E6 y3 D+ n$ m6 M  if(shift_ir)! b: E$ }6 Z9 I6 j  b4 R, H+ X& p
    tdo_mux_out = instruction_tdo;. K0 w6 ~3 h) I6 @' ^
  else
' s# B0 q, `; f% y, I$ u    begin# e, _  O, N$ F7 Y1 B, f
      case(latched_jtag_ir)    // synthesis parallel_case  N; _; P8 B+ {/ H" R# t, H3 O7 q
        `IDCODE:            tdo_mux_out = idcode_tdo;       // Reading ID code( j4 i& e- G  H( d5 ^
        `DEBUG:             tdo_mux_out = debug_tdo_i;      // Debug+ H# x2 e3 R2 K! c1 E" \
        `SAMPLE_PRELOAD:    tdo_mux_out = bs_chain_tdo_i;   // Sampling/Preloading
- B" E5 v2 Y6 F4 S: i$ P        `EXTEST:            tdo_mux_out = bs_chain_tdo_i;   // External test: q* D% V* S3 \' p
        `MBIST:             tdo_mux_out = mbist_tdo_i;      // Mbist test
+ [1 Z% X. d" [0 L( i  V4 m9 @9 i2 b        default:            tdo_mux_out = bypassed_tdo;     // BYPASS instruction8 \& [( x, _. ~
      endcase, K. e) w/ y6 |4 _& Y# \
    end2 y1 W* [" E9 V
end
& w. V* V0 I- j0 Z + N9 Q' g( D' y/ q- X( h3 P4 T
: b9 [# m  M. T: i$ z
// TDO changes state at negative edge of TCK3 n5 G+ U9 z" s% ^# x. c
always @ (negedge tck_pad_i)8 _8 v; T; N& j- p& ?% d' A
begin( H+ b) [  q4 N- M3 L
        tdo_pad_o = tdo_mux_out;% i: K2 S4 P$ f
end
6 ~" g1 `/ d; x8 [) S# ?  i. T 0 G+ h- l/ B5 j1 ]

+ I% F- f' r) T9 d( v// Tristate control for tdo_pad_o pin. Z4 z8 m$ K% n9 Z
always @ (posedge tck_pad_i)$ _* z% T# t7 e, L7 t" a  \3 _2 E" \- I8 X
begin
% ?' c7 q: ]1 X  tdo_padoe_o <= shift_ir | shift_dr;+ C' V3 h. Q8 i% j9 J/ A
end
- e& T# v& ^7 f+ L/**********************************************************************************' R, w6 p. S6 B
*                                                                                 ** P- }% L+ s. [& Z
*   End: Multiplexing TDO data                                                    *
+ D  S4 |2 C# V  f, }*                                                                                 *+ M- u8 C2 _+ ~2 @$ w4 e
**********************************************************************************/* m7 `( E( T' g; z: x

; s1 S- a/ {8 ~8 V5 P0 c! x3 q4,jtag_tap的使用
( Z. J4 o+ B; u' D6 Q: f; x要想使用jtag_tap,需要相应的驱动程序,才行。驱动的作用就是根据FSM的定义,以及命令格式,以及adv_dbg_if的实现,来操作jtag的4根线,达到某种目的。在adv_debug_sys的adv_jtag_bridge中的chain_commamds.c中有相关函数,代码如下:/ ~$ M2 B! K: @

  W# G( m% [8 e2 J7 H$ W% `4 O8 n5 z+ |
//: [3 Q( S2 e( b' C
// Functions which operate on the JTAG TAP! B) Q3 {9 ?2 v

/ K- N1 p" |  }0 B* N ( ]3 N/ T7 Z3 c; \: r( s
/* Resets JTAG - Writes TRST=1, and TRST=0.  Sends 8 TMS to put the TAP/ R* V) t  ^2 h3 L2 w4 V" b( ?6 e. C
* in test_logic_reset mode, for good measure.( ]6 }8 t' ]' e# I; X) L
*/
9 Y) H" R' A' dint tap_reset(void) {" N  G) C% L; F: @
  int i;
* Y; _% p' I5 A0 M1 K# }  int err = APP_ERR_NONE;
6 ^. G3 f! I+ ]: z& F ! s8 j1 s  V0 ]8 b( m6 [+ Q
  debug("\nreset(");
9 J1 r1 V; E, i, F  err |= jtag_write_bit(0);
! ?1 G! Q% a4 Z5 ?+ X' X6 j  JTAG_RETRY_WAIT();' k) b) B5 M! P9 k2 T0 N8 D4 a
  /* In case we don't have TRST reset it manually */
: T( `% h! n4 H  for(i = 0; i < 8; i++) err |= jtag_write_bit(TMS);$ o0 @9 a# o- C" n. Y, K0 V) I& ?
  err |= jtag_write_bit(TRST);  // if TRST not supported, this puts us in test logic/reset
. [+ z& _* S  G, h2 q* @1 E  JTAG_RETRY_WAIT();2 f4 \" ]# b6 M
  err |= jtag_write_bit(0);  // run test / idle' v3 {7 ?& O& l) F+ `" A* b  a. T1 r
  debug(")\n");( E) h3 r; v* r7 s7 r% e2 h5 P+ c1 t
  // Reset data on current module/register selections
9 _' i, W5 L# z* \  current_chain = -1;8 K8 Z! J7 m2 U1 b( t
  // (this is only for the adv. debug i/f...bit of a kludge)
& i8 E& w' \+ D: C' d) x$ V6 a5 T7 C  for(i = 0; i < DBG_MAX_MODULES; i++)
0 S- e6 C) r. ?" s; w3 I- Q    current_reg_idx = -1;9 Q- q+ i+ d+ q, V7 }( J; R
  return err;% @& z" U: g3 e
}. b" ]: M0 ^2 W+ u
  // Set the IR with the DEBUG command, one way or the other( B! U& D1 C1 R" U! w5 |, T& X# Y
int tap_enable_debug_module(void)
5 O2 l5 o5 U; |{, C2 r6 Q7 s: p( ^7 t. l! N
  uint32_t data;7 \6 t0 c7 n3 Y3 {2 P
int err = APP_ERR_NONE;: {3 g" y; c- K$ x4 x! g4 M
  if(global_altera_virtual_jtag) {
0 w, C+ I6 b# Z    /* Set for virtual IR shift */" H" _9 W' E7 L! B7 @. [
    err |= tap_set_ir(vjtag_cmd_vir);  // This is the altera virtual IR scan command: N% F+ U- L: y1 }5 \8 N8 ~
    err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
6 h) O. Q* O2 H0 @; h4 d    err |= jtag_write_bit(0); /* CAPTURE_DR */
9 B7 k. l' W, B/ s9 B    err |= jtag_write_bit(0); /* SHIFT_DR */0 O4 ^1 m9 i3 h; Z
    0 d  A2 I- `% @, r. |- g9 \% f
    /* Select debug scan chain in  virtual IR */, W2 V3 A4 s3 ?. Y$ H( p: L8 x
    data = (0x1<<ALT_VJTAG_IR_SIZE)|ALT_VJTAG_CMD_DEBUG;+ c% E- g$ l+ ?
    err |= jtag_write_stream(&data, (ALT_VJTAG_IR_SIZE+1), 1);  // EXIT1_DR
" [" [* K% H5 T0 f7 J# f. ~/ C    err |= jtag_write_bit(TMS); /* UPDATE_DR */
" R4 r7 G* G; w3 ?    err |= jtag_write_bit(0); /* IDLE */
4 ?. @0 o  V( k7 p5 s    // This is a command to set an altera device to the "virtual DR shift" command
) A# _7 @. Z) ^3 H    err |= tap_set_ir(vjtag_cmd_vdr);2 L4 @. O1 S. o$ a/ s
  }$ \! H3 ~4 d; a0 V$ p( P7 C
  else {
& D* r* h: g9 b6 j; E    /* select debug scan chain and stay in it forever */" t1 Y2 F' J$ w3 g. x) a- s! p
    err |= tap_set_ir(global_jtag_cmd_debug);2 R$ P8 B  m, p* H
  }
7 @/ U% c% V; k! O6 q+ Y5 I5 V  return err;- g5 C; H( P4 {; }, i3 O
}
2 H- ^1 I9 R# E. w# @& x9 E  _/* Moves a value into the TAP instruction register (IR)
& U# D& i7 q3 ~$ ]9 f% B$ Z * Includes adjustment for scan chain IR length.
6 p( i, ~0 V4 U5 p */
; P& ~: J* \6 c9 C7 ]1 iuint32_t *ir_chain = NULL;" I& b- Z; G: ^6 j" K  D* y
int tap_set_ir(int ir) {
3 C% x" D; {0 Y# ^) A9 I  int chain_size;
4 C, H! Q6 P  h3 ^  int chain_size_words;. k8 M: T+ y6 U  d* R
  int i;* C6 ^$ P3 M9 W
  int startoffset, startshift;1 C4 T3 [- i6 H# |3 Y* M* d
  int err = APP_ERR_NONE;
$ Y8 @1 Z, t+ i5 h# n  
4 Y; H/ \5 X, S: c8 Z( ?  // Adjust desired IR with prefix, postfix bits to set other devices in the chain to BYPASS
7 _0 @. H& D* p$ \  chain_size = global_IR_size + global_IR_prefix_bits + global_IR_postfix_bits;* X  q9 \4 o! Y
  chain_size_words = (chain_size/32)+1;
! h# h# a( `3 }7 c  if(ir_chain == NULL)  { // We have no way to know in advance how many bits there are in the combined IR register- B/ J+ }: `+ @  J: F
    ir_chain = (uint32_t *) malloc(chain_size_words * sizeof(uint32_t));
3 z# ]1 S% Y2 h( Q& y    if(ir_chain == NULL)8 g3 m2 b+ n0 L/ _4 a" d
      return APP_ERR_MALLOC;
5 v. d; i: f: x; }  S6 L8 ]  }
1 P0 o( z" x1 o2 {  for(i = 0; i < chain_size_words; i++)
" P; J2 ^. F: e, N& e/ U    ir_chain = 0xFFFFFFFF;  // Set all other devices to BYPASS
7 ?' O% E$ }. F3 `3 q  // Copy the IR value into the output stream7 s: Y+ ]& A( c$ v/ |6 |9 |
  startoffset = global_IR_postfix_bits/32;
7 T2 o  G5 p7 M5 ^  startshift = (global_IR_postfix_bits - (startoffset*32));% F0 D. V3 f2 A: O. k
  ir_chain[startoffset] &= (ir << startshift);
' z0 p2 q5 Q! z" n! l* B  ir_chain[startoffset] |= ~(0xFFFFFFFF << startshift);  // Put the 1's back in the LSB positions& M8 k  u( x& N! H
  ir_chain[startoffset] |= (0xFFFFFFFF << (startshift + global_IR_size));  // Put 1's back in MSB positions, if any
' p  d$ Y! Q/ r" H# l% J  if((startshift + global_IR_size) > 32) { // Deal with spill into the next word
: o; ~  |  {) {  {7 ~; i    ir_chain[startoffset+1] &= ir >> (32-startshift);& r! ~" C6 E- E& I
    ir_chain[startoffset+1] |= (0xFFFFFFFF << (global_IR_size - (32-startshift)));  // Put the 1's back in the MSB positions
% N, W( c2 Z( _3 r7 _8 {  }3 ?( A: P6 n* H/ M$ c! p5 Z6 r! o
  // Do the actual JTAG transaction% g; @5 i# w, }- M' w# g* y4 A
  debug("Set IR 0x%X\n", ir);
) s% p& k+ j  I- O) n  err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
) r) a+ R( U/ d! c: @! Q* n! n  err |= jtag_write_bit(TMS); /* SELECT_IR SCAN */1 L( Y, A) R' z! d% N2 @
  err |= jtag_write_bit(0); /* CAPTURE_IR */
/ g! f0 y1 r- d# t8 P8 p3 u  err |= jtag_write_bit(0); /* SHIFT_IR */     i# m& z1 W& R0 r8 z' K3 D; B
  /* write data, EXIT1_IR */
6 |) e9 R, o# X- a: c  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);
. Q" U' F. a) v5 ^6 n  err |= cable_write_stream(ir_chain, chain_size, 1);  // Use cable_ call directly (not jtag_), so we don't add DR prefix bits
/ j4 `  G# ~3 A3 Q  debug("Done setting IR\n");
5 Y8 k! Q+ P. u9 ^/ Z  y* h  err |= jtag_write_bit(TMS); /* UPDATE_IR */
, s; \* @" x; G3 J3 c! o5 k  err |= jtag_write_bit(0); /* IDLE */  
0 p* p+ S- P% `+ @$ K7 y1 J5 f  current_chain = -1;3 u) l4 r6 B/ K; N9 v( t& A5 R
  return err;6 s7 E) A4 ]/ y# k
}
9 _, g, d. o; ^/ f6 j+ g// This assumes we are in the IDLE state, and we want to be in the SHIFT_DR state.2 d; Y- K8 \( Q
int tap_set_shift_dr(void)& M' h* c) F8 u" a
{
; f" W: \8 P- }* \  int err = APP_ERR_NONE;# I/ |+ J1 N2 Y6 M9 S9 T0 s/ e
  err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
0 `* B9 d. U+ ]2 c  err |= jtag_write_bit(0); /* CAPTURE_DR */
( {" U8 H* @% R3 C! Z" n; i  err |= jtag_write_bit(0); /* SHIFT_DR */
& Y$ p) J& U- k% E: h% {- m) o  return err;0 Z3 z9 a: s1 W& |* {
}, F3 T$ K0 Q$ R- K
// This transitions from EXIT1 to IDLE.  It should be the last thing called
* [' s/ M+ I9 F3 N8 ]  E// in any debug unit transaction.
7 b8 i8 h; V0 ~  h/ x9 K) _. Jint tap_exit_to_idle(void)
9 x! C- q2 Z# P* U7 E) T! B{
: \- i% A4 H+ j* u: ^3 T  int err = APP_ERR_NONE;: {  q$ J, {8 E2 m
  err |= jtag_write_bit(TMS); /* UPDATE_DR */( B- Y, B6 A' H5 C; O
  err |= jtag_write_bit(0); /* IDLE */
4 W4 q' F. E& Q$ M$ ~7 g! R4 {  return err;
. P2 l! \5 [) x) m}( i. p& r& R3 V: S3 V3 w. T
// Operations to read / write data over JTAG. N' L/ _1 @; x' {- M
/* Writes TCLK=0, TRST=1, TMS=bit1, TDI=bit0; `: P. D' G( F" Z$ R, Y4 t: i. O
   and    TCLK=1, TRST=1, TMS=bit1, TDI=bit0
) a* a; ?5 E7 s# Y7 g: k- I*/! F1 X7 m0 F  w5 i5 N) j, Q) y
int jtag_write_bit(uint8_t packet) {
2 J1 U+ X* H% i  debug("Wbit(%i)\n", packet);+ t% P+ Y& ~4 J7 g+ C. e3 @2 N/ Y
  return cable_write_bit(packet);
3 K, ~$ c. D  D, K4 [+ q& z  Y- U}
- D6 T+ h- j) C2 z8 Pint jtag_read_write_bit(uint8_t packet, uint8_t *in_bit) {! l# |' T/ K7 q# ~2 x+ s
  int retval = cable_read_write_bit(packet, in_bit);: @7 }5 F& K. j8 E2 g: N6 t: s: E
  debug("RWbit(%i,%i)", packet, *in_bit);
) Q/ U" O# B0 Q: Q  return retval;
/ X& y- i" X7 [1 w- C}
2 o; \1 ?6 a& l' g* Q1 T9 s  n4 d0 V// This automatically adjusts for the DR length (other devices on scan chain): o3 h; z- y! I- v2 P. u
// when the set_TMS flag is true.& {/ g5 R- ^9 R% c# B% b! u. O% p! [
int jtag_write_stream(uint32_t *out_data, int length_bits, unsigned char set_TMS)
# a5 g) A' j- }+ h9 K{
2 Y) }4 M3 Z1 S( d+ I+ J5 k% i  int i;
7 Z" H! Z, A' Z8 r2 E- j  int err = APP_ERR_NONE;9 X' K' N1 [" x& V
  if(!set_TMS)/ q. i7 a3 L/ D/ p5 q( ~
    err |= cable_write_stream(out_data, length_bits, 0);5 H; f( M3 x) r6 \$ f3 i
  else if(global_DR_prefix_bits == 0)* C8 E: f  B0 g8 m! l
    err |= cable_write_stream(out_data, length_bits, 1);4 h/ H5 q/ Z4 ]) e, B9 g* S; Z
  else {! E% W! Z6 r, J" T9 A- f
    err |= cable_write_stream(out_data, length_bits, 0);- E1 F0 Z# @( W
    // It could be faster to do a cable_write_stream for all the prefix bits (if >= 8 bits),
" r0 g- u& w  e) V) ?3 A    // but we'd need a data array of unknown (and theoretically unlimited)6 @0 b+ b$ S: g) ^
    // size to hold the 0 bits to write.  TODO:  alloc/realloc one.
: f7 U1 \# I0 e, b. X4 }1 G' E    for(i = 0; i < (global_DR_prefix_bits-1); i++)
$ v6 f( w- n' X1 k" B      err |= jtag_write_bit(0);- x: u4 k4 w1 }+ A1 Y7 }
    err |= jtag_write_bit(TMS);9 y8 n; F: Q( x1 m, |7 z5 B. g
  }
+ t' R/ ]2 y  c. F- M3 i7 ~! O* l  return err;4 J8 f6 d, k0 F
}
; B1 e1 h6 p& w, h2 m4 ?- Q// When set_TMS is true, this function insures the written data is in the desired position (past prefix bits): S  F4 u. J0 p
// before sending TMS.  When 'adjust' is true, this function insures that the data read in accounts for postfix7 ~+ H9 x0 Y& U$ z1 c
// bits (they are shifted through before the read starts).0 _" j4 j- U- O) \; V  _6 c
int jtag_read_write_stream(uint32_t *out_data, uint32_t *in_data, int length_bits, unsigned char adjust, unsigned char set_TMS), v; Z" Y2 {* ?2 @1 ~
{
& [! e5 q$ u8 m, S  int i;
& ~0 C" M9 d* z  I9 C" N8 d4 w  r$ K  int err = APP_ERR_NONE;
# y# t$ a' Q6 `  H" E7 N, W- ?  if(adjust && (global_DR_postfix_bits > 0)) {$ B- K& ~/ J8 x
    // It would be faster to do a cable_write_stream for all the postfix bits,* P; m: Q+ }+ q& e. c" i
    // but we'd need a data array of unknown (and theoretically unlimited)
2 a4 u3 R8 [1 s( J" P* f! I6 l" r    // size to hold the '0' bits to write./ j/ S  b7 N" W# _& y
    for(i = 0; i < global_DR_postfix_bits; i++). M2 w) w8 f& E+ K4 Z
      err |= cable_write_bit(0);
5 a& m8 a! v/ K9 v! _  }
$ O$ W$ S& w( n! k  // If there are both prefix and postfix bits, we may shift more bits than strictly necessary.$ o3 F+ ?& [8 @, T
  // If we shifted out the data while burning through the postfix bits, these shifts could be subtracted( A7 c9 f' z2 J7 e
  // from the number of prefix shifts.  However, that way leads to madness., v  H8 j: Y  G) Q) ?3 k
  if(!set_TMS)6 \. _7 |" j- S; W  z" @- [" t$ M7 z+ K
    err |= cable_read_write_stream(out_data, in_data, length_bits, 0);  
& F1 u) p+ x. X4 d+ t7 _+ ?' [  else if(global_DR_prefix_bits == 0)
( C" `. x+ w/ o! F9 s8 F    err |= cable_read_write_stream(out_data, in_data, length_bits, 1);  
2 G- u% g7 A. l$ i* Q, M  else {
0 E/ r) @) n2 q# ^4 D& P- }    err |= cable_read_write_stream(out_data, in_data, length_bits, 0);
8 g+ L: c& Y7 Q/ {    // It would be faster to do a cable_write_stream for all the prefix bits,
* @% r3 L' x) E, w) T. ?    // but we'd need a data array of unknown (and theoretically unlimited)
, y% M+ R' @: I# e7 g( F    // size to hold the '0' bits to write." i7 {! F8 q3 `; h  w
    for(i = 0; i < (global_DR_prefix_bits-1); i++)
9 d' D$ ~; F( F2 F, I# n      err |= jtag_write_bit(0);
; I) k: Q1 ^; Z! \2 q    err |= jtag_write_bit(TMS);5 V; t0 s4 ]8 b) v' K- n. R
  }6 H6 }' T/ V5 F: V" H
  return err;. g  j6 F" w6 q
}& x) Q; |! Y9 ^7 g4 C( k# M0 B
// This function attempts to determine the structure of the JTAG chain
5 u- v$ ?- P8 _" A// It can determine how many devices are present.( I* S, b/ `* n5 i7 z
// If the devices support the IDCODE command, it will be read and stored.
9 E- {( x4 l) r0 l3 J// There is no way to automatically determine the length of the IR registers -
- u  S/ k% H# U0 K7 `, b' d! V// this must be read from a BSDL file, if IDCODE is supported.; N8 _) I# a* y' D5 @2 b7 ~) o1 f+ e  N
// When IDCODE is not supported, IR length of the target device must be entered on the command line.
! E8 n0 d& |9 l/ V( s# C( z; @#define ALLOC_SIZE 64
) y& M& N3 P9 P3 c#define MAX_DEVICES 1024
' {# @5 T. ~. Z) e2 Yint jtag_enumerate_chain(uint32_t **id_array, int *num_devices)  w, O$ F6 y, i# I
{
2 |* \' U  c3 @5 w* q8 G  uint32_t invalid_code = 0x7f;  // Shift this out, we know we're done when we get it back4 }7 \2 j: @: m) d# r% v
  const unsigned int done_code = 0x3f;  // invalid_code is altered, we keep this for comparison (minus the start bit)
9 H) V8 n4 W- r4 m  int devindex = 0;  // which device we are currently trying to detect
6 b0 m( g  d* p0 \" P3 z  uint32_t tempID;
( [& ^2 Q  ]% d/ H) {. l: a  uint32_t temp_manuf_code;, u% ^& r) v  m+ A0 [+ ~1 t0 o
  uint32_t temp_rest_code;
: J. f7 I/ F5 M1 v9 g  uint8_t start_bit = 0;
  H; {8 w0 {! U7 y. Y% M. b  uint32_t *idcodes;" s/ }% |( t. E% A# P- K+ Z5 y/ q
  int reallocs = 0;
- ^  i2 C! F6 ^! f- o" w* j8 d  int err = APP_ERR_NONE;: p" v+ P4 l4 }' d$ ]2 T  i
  // Malloc a reasonable number of entries, we'll expand if we must.  Linked lists are overrated.
- ?1 o( k' P9 b- ^! f2 p0 u  idcodes = (uint32_t *) malloc(ALLOC_SIZE*sizeof(uint32_t));/ V; z' P- p% `# T+ S
  if(idcodes == NULL) { ( p% j- G: N9 h. ]
    printf("Failed to allocate memory for device ID codes!\n"); 8 _  Z8 a( j! U" l$ b1 M' q
    return APP_ERR_MALLOC;" j" g: F! q- }6 m4 C
  }
: p; F) c6 B+ P( i0 {2 N  // Put in SHIFT-DR mode
5 M; C" b, M% ^9 K  err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */( f! T8 n9 _7 Q: j6 ]
  err |= jtag_write_bit(0); /* CAPTURE_DR */
% |0 w7 y. m9 p4 O7 ]( v1 [  err |= jtag_write_bit(0); /* SHIFT_DR */9 o( j6 }9 @+ n' f* T9 x" W% R* R
  printf("Enumerating JTAG chain...\n");
& Y, D3 r# C. m2 h  // Putting a limit on the # of devices supported has the useful side effect5 {- B8 c; A3 ~* Y% b& p" v
  // of insuring we still exit in error cases (we never get the 0x7f manuf. id)5 a0 R( |, |  |
  while(devindex < MAX_DEVICES) {2 X+ B  `  m# R) @1 P/ n7 \8 U5 d- e
    // get 1 bit. 0 = BYPASS, 1 = start of IDCODE
1 P" a: C/ u$ e" [$ [( b  Q    err |= jtag_read_write_bit(invalid_code&0x01, &start_bit);) d: U. o9 m/ {- z
    invalid_code >>= 1;
' n$ X9 l* i" _" Q! O0 {) K# c / f' q7 X* \% p% e# {0 E) L
    if(start_bit == 0) {
% z, h& f0 C8 Y' F. T7 @      if(devindex >= (ALLOC_SIZE << reallocs)) {  // Enlarge the memory array if necessary, double the size each time
5 x2 L4 g4 w* z6 ^! o        idcodes = (uint32_t *) realloc(idcodes, (ALLOC_SIZE << ++reallocs)*sizeof(uint32_t));2 U# T. E) n0 U
        if(idcodes == NULL) {
. j5 P/ V$ j) Z          printf("Failed to allocate memory for device ID codes during enumeration!\n");
* g: z# R8 P0 {9 Y7 q$ Z: M% y  b% n          return APP_ERR_MALLOC;
" m" P9 f6 u& K        }& t$ {3 h( G9 H( _
      }
% e4 u  m3 X8 m6 u& y( h% P0 s      idcodes[devindex] = -1;8 I2 ?% [1 I1 U; ?, Y
      devindex++;! w3 N& n* k8 I- O& y/ N" F
    }1 h7 _0 f8 D8 z- l
    else {/ ?  X$ s) c2 X0 C
      // get 11 bit manufacturer code
/ m" N2 }- y2 _8 p$ k      err |= jtag_read_write_stream(&invalid_code, &temp_manuf_code, 11, 0, 0);
/ l2 T$ B7 u0 b' b      invalid_code >>= 11;* r2 J2 ?/ i; {7 C5 A  y& B% @) S
      
- h- [& z3 n( Q" T- \2 W; [      if(temp_manuf_code != done_code) {/ m8 h5 h) s; t' }) M
        // get 20 more bits, rest of ID5 f$ ]- c3 r8 j7 N3 \0 @$ Q& v% K: b
        err |= jtag_read_write_stream(&invalid_code, &temp_rest_code, 20, 0, 0);
  y' G* A9 `; d6 B1 j5 K        invalid_code >>= 20;
7 Z1 W6 H' Y, I+ y$ ?        tempID = (temp_rest_code << 12) | (temp_manuf_code << 1) | 0x01;7 X' Y& t- E( g3 C7 Q+ [0 H
        if(devindex >= (ALLOC_SIZE << reallocs)) {  // Enlarge the memory array if necessary, double the size each time
. U0 Z1 g) T9 W; n8 a7 g          idcodes = (uint32_t *) realloc(idcodes, (ALLOC_SIZE << ++reallocs)*sizeof(unsigned long));) V7 {2 h8 P# b' u  t
          if(idcodes == NULL) {
& ]% w2 n: S( `  v! ^0 n" c) @            printf("Failed to allocate memory for device ID codes during enumeration!\n");
* C/ p3 X; y5 ]7 O/ A- {            return APP_ERR_MALLOC;
  x* `  X6 W% l  S9 h9 h* v          }
  Q' y8 c  Y: U) ]1 [  T        }
, b; h+ E! l  G  m- a' k/ P( Y        idcodes[devindex] = tempID;; b( R. n" V2 |, z$ K
        devindex++;% o1 {# b% K9 F
      } else {( a$ `: }$ J# _; i4 R* c
        break;
8 H. i6 x% \4 h3 R! H      }
# j2 N' w# M  e- K! o9 _" ]+ J+ }    }9 b0 b! A9 R" G
    if(err)  // Don't try to keep probing if we get a comm. error
. a( O" M% M; \- W      return err;3 D- D5 l! ]5 k3 {2 ^$ f2 L  K5 ?. m
  }
3 J, x1 ~% t: J, p  if(devindex >= MAX_DEVICES)
7 E$ Q3 D- x3 C+ ~9 m    printf("WARNING: maximum supported devices on JTAG chain (%i) exceeded.\n", MAX_DEVICES);
; Q& D4 S  u4 [( Z0 d9 _) T( j8 [( ?4 s
# S% v, Q8 s" a' n  // Put in IDLE mode( r. o" K. Y) D, x4 a
  err |= jtag_write_bit(TMS); /* EXIT1_DR */, A( o% v6 O/ T* i3 t5 y2 _
  err |= jtag_write_bit(TMS); /* UPDATE_DR */
5 i8 {1 ]0 R: {& L- S7 S* }" r  err |= jtag_write_bit(0); /* IDLE */ # B: H) |6 Z, i& s1 @' L& T

; R; [7 ]7 y8 C7 M# [  *id_array = idcodes;
- N% u: r* Z5 x0 ~( z3 Y, q* _5 C7 |  *num_devices = devindex;  f# b0 h6 ?- n! b* K; K

: E' m$ z) M8 N/ {& [1 y  return err;
1 _# C3 k& f3 z& L0 y$ w. k}& c& J: Z. t5 ]1 d  M1 n
% C- S# n3 ?% R
$ Q9 h: o$ e2 O8 r' d
& M4 i8 v! c$ G7 F" r" z5 A
int jtag_get_idcode(uint32_t cmd, uint32_t *idcode)5 F# `- F. A, \1 D. a" v) ~
{  P3 h7 H( B4 A: R6 ^0 Y8 s
  uint32_t data_out = 0;7 T) v$ {* P5 K! e# Y
  int err = APP_ERR_NONE;
& K. O0 b6 e7 \  unsigned char saveconfig = global_altera_virtual_jtag;
7 G7 D" N. m8 {. v' ^6 w* }$ W  global_altera_virtual_jtag = 0; // We want the actual IDCODE, not the virtual device IDCODE
8 m6 x7 l8 p6 w4 F' s' l
7 ?7 |- c, d& C( k' f  err |= tap_set_ir(cmd);5 S0 |4 h# s3 P3 l
  err |= tap_set_shift_dr();) S, U  i& l3 T* q
  err |= jtag_read_write_stream(&data_out, idcode, 32, 1, 1);       /* EXIT1_DR */8 N3 Y, j! O) z# j; |

7 `, l! t: U1 l* t  M  if(err)
% v$ x+ C) Z  }' U    printf("Error getting ID code!\n");
( l& }$ X- v( |! r+ ?! H3 v 3 q& j! ?$ a8 E( `4 I* }
  // Put in IDLE mode
& {5 \4 ~( n4 [; x- b/ i  err |= jtag_write_bit(TMS); /* UPDATE_DR */
1 \  F0 l0 a  ^  err |= jtag_write_bit(0); /* IDLE */
' M' ?! T. G+ {5 ]" L) W
- Q* W" d2 `1 Z2 d. i$ h- N% v  global_altera_virtual_jtag = saveconfig;. [+ b5 m& F& q' c6 V9 g( k5 W* U
  return err;0 }+ b( x' `; q/ L
}' H2 k! f/ T( Q2 {$ W& z  c4 t
( j. H+ K8 H9 h4 b

/ s9 ^6 y* Q. n/ p# y/- `  v9 M: M, S7 [1 [
// Helper functions
5 @/ J! i6 a  C
0 J6 H' O% ^+ w3 B& q  {' n/* counts retries and returns zero if we should abort */4 }* b4 r% d7 D2 D: _$ b! Z
/* TODO: dynamically adjust timings *// ^6 b2 ~( o* V: F
int retry_do() {+ @6 n& b* x# e, w6 x7 a
  int err = APP_ERR_NONE;
3 A( [% z, X* J+ e' d ' [1 @& p5 ]$ X1 q# e
  if (soft_retry_no >= NUM_SOFT_RETRIES) {
3 [" Q" V6 q' ]" ~6 d! I! ~' }      return 0;& S" J& U+ x2 p8 T$ F! \
  L7 G) k. H5 n! d0 v- e
      // *** TODO:  Add a 'hard retry', which re-initializes the cable, re-enumerates the bus, etc.( S. C! n$ d" X/ u6 W

3 N! [* V$ B: \  ^/ t2 v  } else { /* quick reset */8 O) ^; V6 I9 Y
    if(err |= tap_reset()) {
& e+ v; t, s/ b7 X# ~& \8 ]      printf("Error %s while resetting for retry.\n", get_err_string(err)); 6 M' ?& K: ?6 [
      return 0;) ^" \2 x+ G: K3 t- _5 G9 U% b1 k
    }/ C6 ^# q7 M7 r' J) }+ h4 H9 i
: ]  [0 E+ U/ h& z+ @+ K
    // Put us back into DEBUG mode
5 P0 R* H2 ]: X4 N" C: f  V    if(err |= tap_enable_debug_module()) {8 |$ a8 l# [( J# u4 G1 i0 P
      printf("Error %s enabling debug module during retry.\n", get_err_string(err));
* b1 Z+ h2 @! T5 N+ m( `      return 0;* @7 }& E; u# a, Z4 O
    }
# O% n! U) G6 I7 k: T) k/ q2 e . M$ \3 F2 P: ?/ [  g2 V4 \
    soft_retry_no++;
/ m2 C! N3 I1 T4 G, X. ~0 b    printf("Retry...\n");
' d" D7 J, a3 r  }. _2 c; p& W4 P4 K! q
% w$ |; ]$ d# c
  return 1;
- F8 a: U: g6 J; t. `; y0 `4 X}
. a+ x  j! ]5 k4 R% z5 G% l6 m
7 W# K# l" f: ]7 f- G
! ^8 {9 \: r- f! J0 d" A
2 m2 S0 y/ }- `
) ]. i: u6 h' q  r3 H$ N! U+ A: G3 K. G
8 v( F- V0 i, x2 v2 \5,小结
' g0 ~3 S* c7 i3 c' m8 _本小节我们分析了advanced debug system中的jtag_tap模块的具体实现。用一句话来概括的话就是,JTAG就是类似SPI总线的一种总线,jtag_tap就相当于SPI总线的arbiter。- a$ p+ m  V) l

作者: youOK    时间: 2020-11-19 14:11
jtag_tap模块分析




欢迎光临 EDA365电子论坛网 (https://bbs.eda365.com/) Powered by Discuz! X3.2