|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
$ h8 J; Y% j% ^) k! _7 _( v* M
引言* o, k: X. Z5 M) p6 M
“知其然,还要知其所以然”,在搭建好ORPSoC的仿真环境和调试环境之后,我们有必要对仿真和调试系统中扮演重要角色的jtag_tap模块和adv_dbg_if模块进行进一步的分析,以了解其工作机制。
2 q/ P4 l3 x3 {3 R5 W4 z. D7 p
, H: \8 {5 ^% q% {本小节就来分析advanced debug system中的tap_top模块。
, m2 P5 Y& N s( \7 P M! L" T5 c1 j5 m6 k7 ^4 A$ q
0 K: w* |7 D- u1 T P' A
1,from SPI to JTAG6 Q" _- ?6 D/ n2 [: x
在分析JTAG的具体实现之前,我们先了解一下JTAGF的基本知识。
, W; l- G' R2 |; n U8 E @9 D9 V N5 G9 z4 T
A、JTAG协议的本质与SPI协议并没有什么不同,它等于一个复杂的SS状态机+变长的MOSI和MISO数据移位操作。不过所谓的变长,都是事先约定好的。 # C- k' N. r. @* H- }: Q
B、JTAG协议是一个同步通讯协议,它是全双工的。它的通讯原则是“以物易物”——即你如果想得到某些东西,你必须先给与相同长度的内容;你如果只是想发送一些数据,也会自动获取相同长度的内容,至于交换的内容是否有意义,这是另外一回事了。
% [ Q8 B' y3 F" oC、JTAG协议无论多么复杂,实际上只有4根线起作用(有时候还有两根鸡肋的nSRST和TRST),他们分别是TMS、TCK、TDI和TDO,他们分别对应SPI协议里面的SS、SCK、MOSI和MISO。在本质上,他们并没有什么不同。即便是ARM的JTAG那么多的引脚,实际上起作用JTAG的也就这4根线而已。 7 V) W, I }! \& `" y7 n
D、JTAG的数据操作都是基于移位寄存器的。 * H% O" W* A/ o! y) a i
E、如果JTAG协议在某个下载仿真协议中只是用来发送控制信息和少量的数据,而大量的数据传输是通过额外的其它引脚进行的,即便这个协议被称为JTAG仿真其本质也早已超过JTAG了,严格来说,不应该称之为JTAG。因为JTAG协议中就只有4根线(有时候也算上nSRST和TRST)而已。典型的如NEXUS协议。 % i; c/ D1 F5 ^: ~0 a, y2 l' h8 c
9 t1 T% s% B) ?4 C$ g
这里面重点理解的是“以物易物”,这个概念,下面是SPI的工作机制以及one-to-one和one-to-many的组织。如下图所示:
* j: @. o# {" {5 J: B: @: P- m$ c
( n, `! y7 b$ X: d& F6 o7 [
: N0 J) l+ d9 C" T+ U) W: Y# o* `6 o) R, D% R- ~
0 L7 ^7 W C& Y0 y2 J* m
2,jtag_tap
& i" _- D0 d3 r V' @ }% I+ f4 c6 _. ?& X- F! a! t2 V
8 g8 x3 d5 ]& d7 \8 T
1>architecture
x8 e7 Y: s: M2 ]2 j9 _
4 E. K3 ], t n0 PTAP(test access port)的作用是提供adv_dbg_if模块和外部JTAG cable之间的桥梁作用,负责将jtag cable传来的数据传给tap支持的所有device,并将来自device的数据shift out到tdo上。
% q3 N2 }- O+ b F9 ]- F/ X1 O下面是adv_debug_sys系统硬件部分的结构:
+ c* t1 e& n* t( d. U. w ]" `1 a S2 q8 ^: A
+ @. p5 k% W6 L) o! X3 O" T
) {+ v0 ?; y* rjtag_tap一共支持4个chain(相当于SPI中SPI总线上挂有4个device),其中IDCODEchain在jtag_tap模块内部,其它3个在外部,如上图所示。. D+ S1 S( ^! ]# y. \" D4 l
; k) c" H# J& Z3 Z, k! x
2>tap fsm
0 _8 I- p4 w g; U3 l- K; C' AIEEE 1149.1中定义了FSM,所以几乎所有的JTAG模块都会实现相同的FSM。如下所示:' _! F, `. T* k: n; R
0 d- D# W% ?# u; f# X% f4 h
3 T0 z" X9 t9 f \
, S1 z& E! Z G8 ~. N( R
/ ]6 k- k- E2 B- V; Y' Z' P说明:$ x: C& b+ C4 q5 P0 B" o
整个状态机分为三个部分:信道选择部分、数据信道和指令信道。所谓的信道选择,就是图中最顶上由四个状态组成的矩形,分别对应着四个状态:
7 G, Q" J; M; K6 \6 ua,JTAG TAP状态机复位状态
/ O+ p) | S1 G; |1 T顾名思义,就是进入该状态,将导致整个硬件TAP控制器复位,所有的寄存器都将被初始化。在TCK的上升沿,TMS为低电平时,进入下一个状态;否则保持不变。 ) r0 J% q% G/ y! s" Y; l4 |/ e
b,JTAG TAP的Run-Test/Idle状态 1 C+ l2 T/ \" I7 c; ?" ^
其实就是“开工”和“休息”的选择分支点。在TCK的上升沿,TMS的高电平将导致状态切换,进入数据信道的通讯状态;否则保持不变。 * ?& V$ n5 x/ }) x3 Y9 P' Y W' h$ u
c,JTAG TAP的Select-DR Scan状态
# P9 ?* I' ^& F) p1 n. s2 U8 BSelect DR Scan,就是当我们在该状态下,TCK的上升沿读取到了TMS的低电平将直接进入数据信道的操作子状态机;在TCK的上升沿读取到了TMS的高电平,将切换到指令信道的通讯状态。 , [, J4 e2 u G- W# P
d,JTAG TAP的Select-IR Scan状态
! }8 |& t" y; _: G8 h+ U: U' RSelect-IR Scan,就是当我们在该状态下,TCK的上升沿读取到了TMS的低电平将直接进入指令信道的操作状态机;在TCK的上升沿读取到了TMS的高电平,将重新回到JTAG的复位状态。 8 |. t3 {8 k- M
数据信道和指令信道对应着两个子状态机,从本质上数据和指令并没有任何不同,只是习惯上,指令的长度固定为4个二进制位(AVR32的JTAG是5个),而数据则随着不同的指令选择了不同长度的指令寄存器,这个就需要具体查阅相关的协议说明了,比如JTAG IDCODE的长度固定为32位,而AVR32的复位指令却有5位。下面,只就常见的几个状态进行解释(以数据信道为例)。
$ T; h/ x' A1 O& P+ g; M
' l( w' B6 H4 Y; N. d$ T9 [+ E4 S Q; `; c- ^+ _1 |
a,Capture DR状态
5 j% T( l+ L- {8 h+ D$ }* |5 {JTAG协议是基于移位寄存器的,其通讯具有“以物易物”的特性,在我们进入真正的数据传输之前,需要告知JTAG“准备通讯了哦?你有没有东西要给我哈?”,于是Capture DR就是一个给JTAG机会将需要传达给我们的数据放入指定的移位寄存器中的状态。 ' j- G. Y% r! U$ k$ D; @; V6 j
b,Shift DR状态 0 t. g- p6 F# z1 L2 C K
这个状态就是通过TDI和TDO进行数据传输的状态。需要说明的是,即便进入了该状态,TMS上的电平在TCK的上升沿也是会被读取的,从图中看到,一旦在TMS上读取到高电平,系统就会跳出Shift DR状态
( S9 H% v$ @' X/ p- H4 }% [如果此时数据没有传输完成,造成的后果是不确定的。请大家注意,我所说的是不确定,而不是“很严重”:同样是因为移位寄存的传输特性,有时候并不要求一定要将所有的数据都完整的进行传输,比如在AVR32中,针对SAB的数据操作,往往只需要进行最关键的部分,详细地内容可以参照相关的数据手册; , j0 I2 i. E* V1 Z, I; G: e
但有的时候,数据的不完整传输则会导致很严重的后果,这取决于具体的JTAG通讯协议。所以,为了保险起见,一旦进入Shift DR状态,在发送最后一个数据之前,请保持TMS为低电平,当要发送最后一个数据时,应该将TMS设置为高电平,这样,当TCK跳变为上升沿时,系统既完成了最后一个数据的传输,也成功的退出了Shift DR状态。
1 o; k U- J* f9 ?3 C$ ^7 Yc,Exit1 DR状态
9 T# K; w( L2 A+ g该状态提供了我们一个在刚才输入的数据生效前,重新修改的机会。一般情况下,我们直接保持TMS的高电平,并在TCK的上升沿驱动TAP状态机,直接进入Update-DR状态。 , r8 a" u5 y- m4 k7 f5 O
d,Update-DR状态 1 o" `' j# L- K' ^. L
顾名思义,就是使我们输入的数据生效——一般JTAG内部的动作就是触发一个锁存信号,将移位寄存器中的内容并行的读取到对应的寄存器中。Update-DR有两个出口,一个是,TMS的低电平对应Run-test/ Idle,还有一个是TMS的高电平对应的Select-DR Scan。这两个操作看似区别不大,但是意义非凡。前者往往会导致JTAG内部产生额外的时序(比如发生一个信号,表示完成了一个特定的周期操作,在AVR的JTAG下载中有此实例);后者则表示完成了一次数据操作,将进行下一个数据的操作,但是这些操作属于同一个操作周期。当然有些情况下,这两种方法是没有区别的。 3 Z; \& T- ?' Y( ] u* Z7 e2 |
6 F$ r4 v0 ?8 s+ O+ M/ f
3,RTL分析7 T+ g, v7 f; U1 r0 i
jtag_tap是advanced debug system项目的一部分,整个advanced debug system我们之前已经介绍过,如有疑问请参考。
/ L) l n& ^6 [- L5 Qjtag_tap模块包含两个RTL文件:tap_defines.v和tap_top.v两个文件。4 ~! t; @7 h0 E$ ~0 p& _. {" Z
在了解了JTAG的一般知识之后,我们下面就分析jtag_tap的RTL。
; k7 \, F+ T6 {3 J6 K* g4 f+ R) q& y" H/ \9 v- U/ T
1>tap_defines.v0 U* k( j; }0 V! h3 N0 y* D" w
首先,其内容如下:
' Y, w/ p$ Z; \9 f! x1 c2 N2 a8 I7 {+ G" z8 W
1 m- L$ f8 n. J6 n// Define IDCODE Value
8 i3 }6 i& Y7 s1 C; k`define IDCODE_VALUE 32'h149511c31 x* k' e8 j" v* F: t
// 0001 version: E9 \; M/ G: C
// 0100100101010001 part number (IQ)
z% x) T, }- B5 q! c7 ^5 \$ f// 00011100001 manufacturer id (flextronics)
0 M0 I5 a% q8 v) l. c// 1 required by standard
' m6 b1 e' S, b& B; Y' n $ r5 A4 Y( ?4 e! u: ^
// Length of the Instruction register/ S1 _* W* d) \: G' \
`define IR_LENGTH 4: Q! B7 T/ O+ c4 l# b
" j" O& \( d" g9 [2 u! X// Supported Instructions8 B9 h/ v* W* C) u
`define EXTEST 4'b0000* B- B+ W" I& @; J: q; y2 s0 a- A0 e
`define SAMPLE_PRELOAD 4'b0001
5 `' Y. y" \. X7 N" i9 ]; b1 ?`define IDCODE 4'b0010
+ a- |: H* {0 v1 X* n" |) _`define DEBUG 4'b10007 ]% t" Y* t* ^/ ?2 V
`define MBIST 4'b1001
- Z; P0 s" _* g6 i+ U4 U$ f`define BYPASS 4'b1111
/ i5 |- }& K% ?& @; v9 [9 q. z' B0 S
文件包含三部分内容,IDCODE,IR_LENGTH,和instruction定义。( ]" r" i6 ]4 q F. Y0 J* Z
a,一般情况下,每个jtag device对应唯一的一个IDCODE,就像人的名字一样,这个名字用来jtag chain建立的时候‘点名’用的。jtag chain初始化时,读取所有的device的IDCODE,和BSDL文件中的IDCODE比较,获得device name,显示出来。8 p# V' F, l+ v5 N" D+ k0 P
+ g2 V1 M$ Y. B5 pb,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。8 q0 C1 q* U9 A; x5 {% l0 H
$ y6 T4 e. Q+ G+ t( @2 _+ j; M
c,instruction就是操作jtag tap模块支持的指令,这个参数,不同的tap大不相同,具体支持什么指令,可从bsdl文件中获得。下面就是jtag_tap的bsdl文件:
; d. ]1 W7 Z4 ]2 r3 j7 W& B
5 ]0 Y C$ z* Y' F/ J
7 p% B" N: @# i- F-- This is a minimal BSDL file describing the particulars
( j, d; n9 G+ X* q. Q-- of the OpenCores standard / native TAP. It is designed6 `# k6 p8 M! x0 T2 W3 M" u
-- only to be used by the adv_jtag_bridge program. This
: N ~1 Y1 [# s! f& K9 P+ b* Y-- file almost certainly lacks key entries and attributes. {" U5 m$ c4 T2 m$ B( E: @
-- required by other JTAG / BSDL systems.: n. y2 u2 ~3 x( S9 t' B* [
--4 _* c/ _: L& a6 D2 m
-- by Nathan Yawn (nathan.yawn@opencores.org)# G4 K0 q& Q9 S; |3 v8 h; ~
-- Copyright: This file is released into the public domain.
4 h# k) c7 u% u2 C3 A4 Q+ z--
1 X* _/ u) U I$ d) x+ `
& |: V1 a( Z/ H H5 o % L1 A% _3 W! \8 H
entity OC_TAP is
) H! V! [; }; p( `
0 I. j9 P* B! ]4 \; S
; B- Z. p3 z8 u* M0 H" @/ q+ E) Zattribute INSTRUCTION_LENGTH of OC_TAP : entity is 4;
; G3 n. P0 j5 ^" O& s8 m
2 }, J9 w. Y! X" J3 }+ L, G
- d6 N H# c7 f7 [7 h3 dattribute INSTRUCTION_OPCODE of OC_TAP : entity is
! N# ~: J+ Y: ^; c "EXTEST (0000)," &- b! c0 m! f" k+ s( M
"SAMPLE_PRELOAD (0001)," &' k L8 z5 g! {. T- f- @; S, l* E
"IDCODE (0010)," &( P1 T0 [! @7 }" b
"MBIST (1001)," &
& @% b. |! M; S "DEBUG (1000)," &
2 D' e2 A2 \/ k! [: G! T2 d "BYPASS (1111),";7 M, Z* V- D6 l
) V$ @: q7 o6 J4 |2 i 1 o2 F1 ~( c# E6 k1 W+ G
attribute IDCODE_REGISTER of OC_TAP : entity is
, S3 t( g8 Y) \% k& J! B* _6 a# E "0001" & -- version
& w3 C1 y9 f; S "0100100101010001" & -- part number
8 l% X1 w% C! z "00011100001" & -- manufacturer (flextronics)
o3 ~ @+ o& A [, K% n: N2 ` "1"; -- required by 1149.10 A+ A) b, V+ I( M
! Q% H! f2 s7 w3 M: K& {/ I G 2 q) z" O7 O! a7 }
end OC_TAP;
1 E+ Z% s- E" p3 q" Z1 d; n z5 y/ p7 l! \ z# K
: I+ \9 J# y3 c: d1 n
2>tap_top.v! |% r( u9 w1 G$ y1 Y4 t" a
这个文件是jtag_tap模块的具体逻辑实现文件。( x% e: j: [9 _3 p; B' O
a,接口定义+ b, T2 \+ V$ i
8 A: W+ v6 g6 U, l- T% G
7 ]: W4 j# }1 v6 ^: Y3 j; I
// Top module
4 a4 v& T" f; r& \' B' X( pmodule tap_top(
+ e6 y# ?0 U0 }$ h# I // JTAG pads
0 {( Y5 s' Y. B4 }2 ^ tms_pad_i, ) A2 |7 V1 d' D, C0 M
tck_pad_i,
' k9 q' H3 I B9 ~ trstn_pad_i,
. {3 D% L1 |, {9 l% o4 [9 a+ J tdi_pad_i, + _ h: W! j" G5 r
tdo_pad_o, , r4 b8 w* R8 n' e1 [! m
tdo_padoe_o,
2 `5 O0 V) u( }& d' q a A ' W/ i) F9 Q' R u( \
// TAP states# }( A2 i- H' _+ ~
test_logic_reset_o,
8 v; u' O" ^ G run_test_idle_o,/ W1 \ P8 e* t
shift_dr_o,( e- x3 X+ x, W3 ]# S4 p5 K1 L
pause_dr_o,
6 m6 b# h* K$ j; s0 D update_dr_o,
g* W' D6 g" S capture_dr_o,$ m/ { u& f; l! J! w/ O6 R
4 ^1 \$ |# G1 ] Y% H$ ]: H6 z5 Y6 C
// Select signals for boundary scan or mbist6 ?1 M) R3 ~5 Z d7 y4 v8 D
extest_select_o, 9 B: P8 M& k4 w [! ~
sample_preload_select_o,* ]- h2 U" m3 ^! y7 {
mbist_select_o,
$ V. t+ k# ]/ b y H5 G& |# M; C4 S debug_select_o,
/ |/ y1 C1 n! C2 O; h9 U- N3 E
6 ~6 n6 C: A- i2 C) a/ z1 z/ Q) m q9 i' c // TDO signal that is connected to TDI of sub-modules.- r9 m. W" q3 Y3 m
tdi_o, ) Z7 D; f* S0 b( a' Q8 W# K
. a5 N$ w4 {: _1 P7 m: j // TDI signals from sub-modules
4 W# }; M, k0 h6 r9 ^) q( ^- H debug_tdo_i, // from debug module
* I# a8 w( C: N( Y8 I bs_chain_tdo_i, // from Boundary Scan Chain
c. @7 c& |2 u( n mbist_tdo_i // from Mbist Chain2 v# k5 T# Z( w9 V4 P
);
# Q: \" z3 W+ q6 L+ k- ?" _" V6 F
jtag_tap接口可分成5个部分:jtag信号,tap states信号,片选信号,tdi_o,以及从device来的数据信号。; Y+ X& v5 W3 C. U
1》首先是jtag信号,除了我们常见的tms,tck,tdi,tdo之外还有两个鸡肋信号:trstn,tdo_oe,前者用来复位tap,后者用来使能tdo。其实这两个信号有没有都可以,tap的复位可以通过tms来实现,tdo使能也可不用。' Y- d/ g% z! V+ n' z- T
说到这里,有一个小问题,如果不用trstn信号,上电之后tap的状态是随机的,那么有没有一个固定的tms序列来实现tap的复位呢?答案就在本小节中,如果有疑问的话就找找看吧。. Q0 G+ n, f, z" @) e5 Y5 h
2》其次是ap states信号,给device用的,指示tap的当前状态,device根据这个状态来完成某些操作。: K, A+ B+ i6 ?) t/ z) ?
3》片选信号,这个就不用多说了。对于jtag_tap来说,片选信号时根据IR reg中的不同位来决定片选的。5 p. i& n( e' P5 p, n
代码如下:
2 C# P3 L# Y: ]9 Z4 p( f G+ `2 \
9 ^0 }* t9 |# ^) _# q( t$ H4 y0 R' _- H5 J
/**********************************************************************************- T* ~8 J* C+ t8 T- N! i
* *& ~9 |( x0 d) ? |- }
* Selecting active data register *( [2 C+ p6 o, {8 z% L% q g5 k2 v
* *3 D. k$ H) M/ c1 s
**********************************************************************************/
' Q0 W- k7 D7 ~4 C/ ]always @ (latched_jtag_ir)
1 t9 J2 ?: r! `' P, rbegin2 F. o% i7 ?0 N
extest_select = 1'b0;
8 ~, ~+ k; C+ H$ l: O0 M% U. s& {7 S sample_preload_select = 1'b0;
' @! y- p2 ^0 o1 {7 w1 x" T idcode_select = 1'b0;
* N5 y$ v+ u8 C, R% V1 X mbist_select = 1'b0;" K7 _9 U* L) F4 M) v& v v
debug_select = 1'b0;0 U, I e/ A. r' l
bypass_select = 1'b0;9 W0 u! O/ E$ Y: u9 R
: B" r! w" R* t% L8 x case(latched_jtag_ir) /* synthesis parallel_case */
) q( d) Z7 ^; ?7 d7 b; |6 ?. h `EXTEST: extest_select = 1'b1; // External test
6 |1 ~8 L* M% \& E7 p `SAMPLE_PRELOAD: sample_preload_select = 1'b1; // Sample preload& v3 Z- y/ g& [: q
`IDCODE: idcode_select = 1'b1; // ID Code
x, e, J; j! t7 c r" P `MBIST: mbist_select = 1'b1; // Mbist test4 h1 g( h; s! n6 C$ [
`DEBUG: debug_select = 1'b1; // Debug
; {# x; X% T- a3 m1 s `BYPASS: bypass_select = 1'b1; // BYPASS
' {& r% \- v" {, ]' t( y7 _ default: bypass_select = 1'b1; // BYPASS
# X" G' }- }9 a endcase
* y- i9 F% Z/ W- l) ~% R0 iend, H/ f& O+ y) `# S
K& |) `& j' f# \
+ F- S7 [. A# d6 Y; A$ n
8 B& `6 f1 q6 T% r8 ub,fsm/ T6 L. M; }2 h; a
jtag_tap的核心就是tap controller的FSM了,常见的三段式风格:4 w& W3 X2 [' l; m
5 O6 X# L: c) l, I1 L
1 E# w9 U/ Z' z7 X1 F
/**********************************************************************************( I' s W, X: V. B& \
* *% T0 H7 l4 d- L$ i* ~/ T
* TAP State Machine: Fully JTAG compliant *
: t. l4 ?0 r1 w0 i' V% _' z( z* *
s1 j& C, I# W O2 D**********************************************************************************/
" }. i7 ^+ {2 _$ C/ ]// Definition of machine state values. We could one-hot encode this, and use 16
F* U* C: }: r4 _5 w! f f; e// registers, but this uses binary encoding for the minimum of 4 DFF's instead.
b4 v( l8 G+ k' O ~`define STATE_test_logic_reset 4'hF
; r: h; d1 i# P$ E`define STATE_run_test_idle 4'hC
. h/ e1 C! x b- w+ X V5 S`define STATE_select_dr_scan 4'h7) `( C U1 a% o
`define STATE_capture_dr 4'h6
$ I; ]" h2 K; E: Y# U9 i2 Y`define STATE_shift_dr 4'h2
7 L0 m7 ]% y+ m, i`define STATE_exit1_dr 4'h1
. ]. k; _1 x: s0 U`define STATE_pause_dr 4'h3
, S9 ]$ d" d) @. ?`define STATE_exit2_dr 4'h0& T H. c: S( j( N: L
`define STATE_update_dr 4'h59 m; K5 x x# V0 D4 C+ W
`define STATE_select_ir_scan 4'h41 G1 y2 ]2 S, l/ L
`define STATE_capture_ir 4'hE
- H! R' K5 R7 G' ?, |9 N`define STATE_shift_ir 4'hA* ?6 [) ]* }, K5 y: V! B* f7 j
`define STATE_exit1_ir 4'h9: |1 V- z- r0 ] K
`define STATE_pause_ir 4'hB8 s/ x( w0 ^& {. M) y3 W2 o# k
`define STATE_exit2_ir 4'h8
3 W# z0 o( a* r& _* w+ z$ b" Q& [`define STATE_update_ir 4'hD
% C8 I1 I) z$ L4 L
- A0 n* y% a# |& mreg [3:0] TAP_state = `STATE_test_logic_reset; // current state of the TAP controller6 t, K: H; v9 D% P5 A
reg [3:0] next_TAP_state; // state TAP will take at next rising TCK, combinational signal. z9 z& ?7 @* k$ Z
& ?6 Z, ]; `/ `4 U4 r% N- P3 X$ | K' R
// sequential part of the FSM& k! o0 D. i" c8 r# [2 |
always @ (posedge tck_pad_i or negedge trstn_pad_i). F4 \, @1 G/ P
begin
! V |* S. j- u; i if(trstn_pad_i == 0)5 [1 H* X1 F' p
TAP_state = `STATE_test_logic_reset;! U# a9 U6 U0 K' x5 E7 _
else' e3 a% K5 E$ T
TAP_state = next_TAP_state;( {# i2 J' U1 x
end
1 ?% L; b# }2 N! F1 Z2 _: l3 C% g - m7 m* O, ~' H( n
9 A( q! |! A" G7 R9 C/ O
// Determination of next state; purely combinatorial y! h' p& J9 a& c+ V- G, X
always @ (TAP_state or tms_pad_i)
& a& W6 `( Z4 Q, Rbegin
; i' g4 K) G7 W3 f& m3 @8 \ case(TAP_state)
& }" H f5 f) H `STATE_test_logic_reset:$ L/ }& k! D7 A* @
begin2 ?3 F5 D9 E. F* C3 h" o+ r
if(tms_pad_i) next_TAP_state = `STATE_test_logic_reset;
+ A4 C @; y! e& U, F0 N else next_TAP_state = `STATE_run_test_idle;' O8 ?; B" H& z
end
! }- s! T, W- Z' [3 U `STATE_run_test_idle:4 K1 U. W7 |; ~" X" H$ g+ w
begin
0 ~' R' }; }( _0 F if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan;
, B1 Y' m" C3 e4 e' Z else next_TAP_state = `STATE_run_test_idle;
7 b( ]# a5 ^5 Z$ i5 t end( t/ i! m1 F1 j7 {! F' \
`STATE_select_dr_scan:3 a# O. T; i( S6 A2 \: B# @
begin
9 N Z1 i! k2 b2 X if(tms_pad_i) next_TAP_state = `STATE_select_ir_scan;
3 o8 m v) S) [9 C% _ else next_TAP_state = `STATE_capture_dr;" `) y' T* g2 R/ s
end% g+ U" B' n' \
`STATE_capture_dr:) r+ D3 N. y0 a* G
begin
* Q, q* B7 w# m+ t7 W. ~! T if(tms_pad_i) next_TAP_state = `STATE_exit1_dr;
8 u- X3 Z( y9 I0 q& d. Z& i else next_TAP_state = `STATE_shift_dr;% I$ W; Y6 k; w- R. l0 e) ^5 o4 z& s
end. ?0 r, V5 G$ V
`STATE_shift_dr:
/ ~0 A* K9 h& c; q* `4 c begin
" p5 x+ N; e9 m5 Z9 i if(tms_pad_i) next_TAP_state = `STATE_exit1_dr; / i5 C: q+ Q* Q8 S; H) h& [$ |
else next_TAP_state = `STATE_shift_dr;- B9 D% ?4 T2 ?
end
4 w; h* e) [5 W( r; _/ \ `STATE_exit1_dr:+ i2 s; U' @# ^/ p) u
begin
& ^2 `# U! e. D if(tms_pad_i) next_TAP_state = `STATE_update_dr; ' R$ b% j8 Y+ l) L1 S0 t
else next_TAP_state = `STATE_pause_dr;
% M2 I% I$ u! ?- Q end0 M. @7 Q z0 e& V$ N( o# S
`STATE_pause_dr:& O4 v3 o; S7 M# {; Z; i- P; A) `
begin
8 i7 [2 ?% P8 o) G; b* |4 ^ if(tms_pad_i) next_TAP_state = `STATE_exit2_dr;
8 b1 Q V) u; a3 H4 z. A: c! h% j else next_TAP_state = `STATE_pause_dr;
5 o. X- F S ]% ` end* E" U6 Y. L- [+ A) R" [2 t$ b* \* l
`STATE_exit2_dr: S% @7 |( _% ]; G& q
begin
3 h( E6 |8 K1 g3 v' }# v if(tms_pad_i) next_TAP_state = `STATE_update_dr; 9 Y4 r, X. s6 H: I" f$ ~
else next_TAP_state = `STATE_shift_dr;
5 X/ s9 ^. }/ E' ^$ P" r end8 i# d7 { m, N0 z; O, y. [0 s
`STATE_update_dr:
* G- r7 @* H! L0 D% s7 n begin
' ~2 @8 k, Q/ e- Y8 w! U if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan; + O2 l) o: O! |, a
else next_TAP_state = `STATE_run_test_idle;( X8 W5 t" k C: O
end
6 U- ^: X5 }+ n: c `STATE_select_ir_scan:
9 l' K9 L$ x7 ?0 _5 C begin
3 k4 j: h( |& h if(tms_pad_i) next_TAP_state = `STATE_test_logic_reset;
1 ]4 n- F% l/ c N else next_TAP_state = `STATE_capture_ir;+ Q0 ~7 o2 I9 Q5 P
end( R3 Y f8 p; F+ }& u# h: ~1 J
`STATE_capture_ir:: `1 o0 v- `0 B4 `- G- L. J
begin8 p) I2 j, {! J2 _
if(tms_pad_i) next_TAP_state = `STATE_exit1_ir; 0 ]2 ^0 X1 b2 u$ N, Z
else next_TAP_state = `STATE_shift_ir;0 P* q7 ]5 f4 @! F; W
end* Y' S, n" _- o6 H h
`STATE_shift_ir:' |9 w2 g4 |! y
begin, t# ]8 N2 {- ]7 M/ H2 J4 ]7 E
if(tms_pad_i) next_TAP_state = `STATE_exit1_ir; - W; B, V$ l$ s+ [( ^0 V% g
else next_TAP_state = `STATE_shift_ir;
" `0 r2 ^* x! D6 Q end
: O8 b+ |8 J7 y: _4 T `STATE_exit1_ir:; f, q& u4 z( d$ T* V
begin" v& @) `! H a- x/ d6 p
if(tms_pad_i) next_TAP_state = `STATE_update_ir;
4 b2 h( A% W1 n5 O6 ?5 J- V; `5 o else next_TAP_state = `STATE_pause_ir;
( _" N) \; o+ z4 w, h- u end
( U$ c l6 F; y/ H8 M$ Y% X5 I `STATE_pause_ir:, L1 G% J' Y! p& m9 ]1 }
begin; o* g4 `5 w7 k" m
if(tms_pad_i) next_TAP_state = `STATE_exit2_ir;; U% ]5 N1 R" Z( O9 ?# F+ m
else next_TAP_state = `STATE_pause_ir;
- Q: u. s6 s" O end
3 w8 ], o. k4 G* }4 B1 [6 H9 E `STATE_exit2_ir:
b+ u! p7 h0 U! [; B+ N- S6 V4 s) ? begin1 V7 S- s n4 S/ ^* C, J @
if(tms_pad_i) next_TAP_state = `STATE_update_ir;# B" k) i* T6 t& ]# O% e' X
else next_TAP_state = `STATE_shift_ir;
5 m' ^+ N- \1 \* g2 j% M$ M) Q end
8 k" f, x, K. x1 N0 H/ h, I. C `STATE_update_ir:
4 ~7 K' |% i) O, G begin
: I4 q) s% K! A$ W if(tms_pad_i) next_TAP_state = `STATE_select_dr_scan;* T, j, M, w, l; B" M
else next_TAP_state = `STATE_run_test_idle;
1 u" C: Z) P! T3 p6 H end
9 h( M! M# V! b3 M4 T& z default: next_TAP_state = `STATE_test_logic_reset; // can't actually happen) |4 a4 b2 @) R, Z3 C9 I
endcase( D+ ^/ Q' d' D: L' G
end
' L) T" ? }/ x
S% q6 o; U3 I" _% `, D 0 V" [/ q. F) U
// Outputs of state machine, pure combinatorial' v1 M4 F4 t. x* m) m# G" x
always @ (TAP_state)
4 Q5 i- j% ^; C- c' Y6 Gbegin
, O* h1 k: r1 t+ R# z0 o& u6 j4 J // Default everything to 0, keeps the case statement simple; i7 P0 p% O- z
test_logic_reset = 1'b0;: f1 S9 Q$ b9 W" n q
run_test_idle = 1'b0;
2 l b# G( Z3 S3 P; x select_dr_scan = 1'b0;
' D7 ?2 k* a+ [% V7 W. y capture_dr = 1'b0;
% u2 V6 m5 `2 F5 C0 H7 p, s/ b* F shift_dr = 1'b0;0 B* o( ?: ]& ]- g
exit1_dr = 1'b0;
9 D4 v% g- _# v2 P pause_dr = 1'b0;1 `$ e. L& o1 H! w, x, @
exit2_dr = 1'b0;
5 g, j$ m' L1 \% `$ }; ~( T update_dr = 1'b0;; C1 Z. Z* @# t3 D' a8 Z" i
select_ir_scan = 1'b0;
h* s1 b5 q; y) q) d/ A capture_ir = 1'b0;
9 S. D9 i4 w. G% j shift_ir = 1'b0;
# H( Q! }1 R4 N3 T2 Z) A$ G! ] exit1_ir = 1'b0;
0 w: y/ X z; V) j' I6 i7 U pause_ir = 1'b0;
' @2 X5 K O0 V) W f exit2_ir = 1'b0;
3 r% d% w3 k! V# P) N6 | update_ir = 1'b0;
: z% {, S& I+ m9 p: J* m * Y# t+ b+ b' W2 n2 V R8 {/ ]. X/ k
case(TAP_state)& X9 s6 b3 F0 M( K* F
`STATE_test_logic_reset: test_logic_reset = 1'b1;
3 k C4 G% E% D9 s' N: J `STATE_run_test_idle: run_test_idle = 1'b1;
4 W8 ?3 {7 @+ k: S( Z! R6 j# w: L `STATE_select_dr_scan: select_dr_scan = 1'b1;# z- v- l+ q0 I, t# ^7 @
`STATE_capture_dr: capture_dr = 1'b1;/ q- A, D: q9 j- w; H& Y
`STATE_shift_dr: shift_dr = 1'b1;
, S# l8 @0 i8 W/ K `STATE_exit1_dr: exit1_dr = 1'b1;
5 E! U; `3 f: Q; Q ]- y# U `STATE_pause_dr: pause_dr = 1'b1;
- j' A* O2 d& J `STATE_exit2_dr: exit2_dr = 1'b1;" Q- @) S* Q. a( [/ O8 c- D4 ~6 O
`STATE_update_dr: update_dr = 1'b1;3 x W/ ~/ j; M) y; a
`STATE_select_ir_scan: select_ir_scan = 1'b1;
6 d% y1 H1 J5 U7 s' o `STATE_capture_ir: capture_ir = 1'b1;1 E9 \7 _' G5 u* _6 x3 s6 r( n
`STATE_shift_ir: shift_ir = 1'b1;
" ?9 C3 t" Q$ d2 b. a9 W `STATE_exit1_ir: exit1_ir = 1'b1;
+ O8 u( Q, S5 g0 r1 T5 d1 m# l q `STATE_pause_ir: pause_ir = 1'b1;* d: Y2 O! m: v, m# U
`STATE_exit2_ir: exit2_ir = 1'b1;- x: ^' G1 R1 f* {; t- y
`STATE_update_ir: update_ir = 1'b1;
& E$ U ?. \2 D6 P7 F/ k" d/ Q default: ;0 P; P+ J9 x, {# L# \2 U9 h
endcase
6 P% K8 |# |: {end, \' W4 a0 }9 Q, U* L
9 _0 j; i$ r4 `6 ~/**********************************************************************************
; p; z) _- {4 W+ F* *
# W0 i6 U( c! @2 t! \* End: TAP State Machine *
# |+ u. H' _: Y: V1 Y) z/ [* *
+ c9 C5 r, n: X0 M1 z2 k8 G6 @**********************************************************************************/
7 z- w/ G4 j9 V
+ o! r9 O& g6 T+ f$ M9 Q; h1 O, @6 ^4 q) f2 P2 P8 S/ D
c,shift reg, f9 I8 u7 I9 C/ h$ X$ Q4 K7 f3 q. F
上面说过,JTAG的本质和SPI相同,都是基于shift register的,也就是“以物易物”的思想。那么,如何操作tap呢?通过向tap中写入相应的指令。那么如何将指令写入tap呢?向tap移入任何IR_LENGTH的支持的指令,tap就会移出等长的数据,这个数据没用,直接舍弃即可。
4 f7 _1 Z# o: Y" h+ \/ `; j; U整个过程非常简单,代码如下:, U* j: L6 M! b8 O8 |
+ Q( u/ @& P: \
/**********************************************************************************" N6 Z- o9 K5 r6 ^8 {5 @
* *4 c$ a2 R F/ u- y1 ^. _9 t
* jtag_ir: JTAG Instruction Register *1 x# X& f' D4 Z4 `2 ?
* *
5 s$ T' N$ m- t+ W! |0 p2 Y**********************************************************************************/( p( b; n. Q0 U+ E* [' a$ \2 _
reg [`IR_LENGTH-1:0] jtag_ir; // Instruction register
0 q; s) S, T9 Q4 h3 K. K" ?8 H. mreg [`IR_LENGTH-1:0] latched_jtag_ir; //, latched_jtag_ir_neg;% J" a: Z+ l% R2 l9 {$ S
wire instruction_tdo;" Y: l( t$ v* B# X. R& J% t* @. y
7 p) j1 h' Q0 `: h
always @ (posedge tck_pad_i or negedge trstn_pad_i)
! o; @4 ]5 u/ {: J( m& cbegin! c3 |. `% Y& \" Q* o; R& I
if(trstn_pad_i == 0)
8 u' I; N& _3 R( w% t% C jtag_ir[`IR_LENGTH-1:0] <= `IR_LENGTH'b0;4 e7 e, z* V* P5 X, C/ D
else if (test_logic_reset == 1)$ k# u8 x! ~* U% D0 b
jtag_ir[`IR_LENGTH-1:0] <= `IR_LENGTH'b0;( s7 \8 t6 ]3 _1 \; w& O
else if(capture_ir)
6 D V1 a) d3 K. V1 C! k1 l jtag_ir <= 4'b0101; // This value is fixed for easier fault detection
6 C0 w2 l! r: C( E' \( F" j9 r, o else if(shift_ir)
! t( q, |1 G! j E- o jtag_ir[`IR_LENGTH-1:0] <= {tdi_pad_i, jtag_ir[`IR_LENGTH-1:1]};8 s8 T+ Q0 _% H* [5 Y& V5 b# t
end
- t- k2 k3 k1 @- tassign instruction_tdo = jtag_ir[0]; // This is latched on a negative TCK edge after the output MUX C3 W6 e, Y* r: g
// Updating jtag_ir (Instruction Register)
5 M0 N3 L) x: m$ U& d, X* M* w, |// jtag_ir should be latched on FALLING EDGE of TCK when capture_ir == 1
* {# S( V0 J- ? ialways @ (negedge tck_pad_i or negedge trstn_pad_i)
" K2 T/ u5 ~# m8 j6 M% Ebegin6 O; J7 ?+ P" j; Y0 v" c' z2 x
if(trstn_pad_i == 0)! Y0 [) N h* g8 }. ~# p
latched_jtag_ir <= `IDCODE; // IDCODE selected after reset
3 V2 g5 q- B% U( o else if (test_logic_reset)8 B# ^! C9 w9 Y- k7 r5 [
latched_jtag_ir <= `IDCODE; // IDCODE selected after reset+ x7 J8 s% `( F
else if(update_ir)
; m' L! c8 M) g0 O* F/ m: n% ? latched_jtag_ir <= jtag_ir;
" r6 m, [* V! R+ X/ aend; E3 z1 `1 A# d% |( W) U/ L$ k% k
/**********************************************************************************
7 y9 V% ?5 m3 f4 ~* *
" k" _+ T6 R* A) ^0 z" `+ e' w* End: jtag_ir *
4 @7 e1 l& c- Q6 l6 b* *' ]- D# t% E4 ^% d( L) |$ ]7 |/ d
**********************************************************************************/
5 v9 G' G- h$ z4 x0 j/ f8 p- M" i% v+ h3 [* M: I$ t* t
上面的代码可分成三部分来看,指令移入,指令移出,指令生效。需要注意的地方有以下几点:3 l* k0 c* L i1 w) X
首先,在移出之前,如果想读指令的话(进入 capture_ir状态),移出的将是0101。; ^" s ^$ v! D9 N; l0 }$ c
其次,从jtag cable移进来的数据放在jtag_ir寄存器里面,实际生效以后存放在latched_jtag_ir中。; b- K7 ]3 Y5 t+ F1 c+ ~# b# L! z0 d, K
最后,移出的数据来自jtag_ir,而不是latched_jtag_ir。所以说latched_jtag_ir是送给device的,而从device来的数据是放在jtag_ir中的。但是,需要移出的数据暂时存放在instruction_tdo中,最终移到tap外面的数据(tdo)并不一定是instruction_tdo,还有其他很多来源。这个后面会看清楚。
+ _8 C0 ]: w3 R/ _: O; [4 L1 ? l- G# Y- F
* ~1 ^# H6 ?8 l3 G, |/ @d,read IDCODE5 I: r+ o* S: Y" ]8 D& X
上面,我们解释过IDCODE的作用,那么怎么才能读到IDCODE呢,还是“以物易物”的思想,代码如下:* e0 G" J" V7 I+ o2 T' r9 f
: g) q8 x8 W9 m* x2 E
% p3 k$ |! {( C
/**********************************************************************************
# H( I( ]) P( d* *
- ~; u1 ^* f$ @8 M9 I6 C, H* idcode logic *! b% s$ a% a6 m7 P. r
* *
; N* [, R2 p+ I' h# Q**********************************************************************************/- P- v/ [! T7 N8 A
reg [31:0] idcode_reg;; f' ?& q/ s3 z! ^% a, T l0 v* t
wire idcode_tdo;
& h$ C; a9 V" p! G( i
+ w( s O |4 I balways @ (posedge tck_pad_i or negedge trstn_pad_i)
, w$ r! x0 x$ F- qbegin
- A7 Z2 ?- @, r! r% | if(trstn_pad_i == 0)
4 i0 r$ o- D. W- ? idcode_reg <= `IDCODE_VALUE; // IDCODE selected after reset, q: _/ J# r$ }0 h9 J9 b! @3 A* J
else if (test_logic_reset)! _- T1 } o1 f# h- G( ?. ~
idcode_reg <= `IDCODE_VALUE; // IDCODE selected after reset
0 X: b% k) c+ x5 H8 N. l else if(idcode_select & capture_dr)
5 H5 Q. o8 D$ A& q idcode_reg <= `IDCODE_VALUE;" j0 t/ d% ?8 R/ }
else if(idcode_select & shift_dr): U$ X9 w# S/ M
idcode_reg <= {tdi_pad_i, idcode_reg[31:1]};6 g# H4 z. J# I# Z, n1 w% L$ m: p
end- _2 E' g& Y0 V! v2 w
assign idcode_tdo = idcode_reg[0]; // This is latched on a negative TCK edge after the output MUX) @1 E& F4 l7 Z# R8 M1 z
/**********************************************************************************
2 P( s& e# r. L4 H0 e* *
, f3 }1 b9 z7 |6 e- m7 g* End: idcode logic *
) ]6 w6 }- ~% C; N% X* *8 D2 F; x* X, @! j! |8 o
**********************************************************************************/
$ Y+ V) ?' O! v* R& u
5 `/ ]8 g5 P+ V1 Y F) |读IDCODE的过程和写指令的过程相同,不同在于向tap写指令是不用关心tap移出的内容(0101),但读IDCODE,不用关心向tap移入的内容,关心的是tap移出的内容(IDCODE)。
* x; J: [0 o$ Z' g L5 D- E( d$ c+ B+ ?4 u* i& ]
* ]( d2 X' T1 z& \+ D/ p7 Z0 I H" d& u
e,bypass
, U9 v2 X' v' {1 ]# r, [' n, X& R! Tadv_dbg_if在使用时,和他在一条jtag chain上的设备必须全部bypass,否则,数据就到不了adv_dbg_if,也就无法工作。这个很好理解,jtag chain,顾名思义,就是一条链,就好像打电话时的总机和分机。如果你想给某个分机打电话的话,那么总机肯定不能接,也就是总机bypass。
! Q c8 Y% F) q' ?- _; u& Mjtag_tap 工作在bypass模式是时,一个耳朵进,一个耳朵出,唯一的影响是会造成1个cycle的延迟。
9 |& J) f$ l" {8 @- K9 |代码如下:& x7 ? e# K1 e' `. r
) c7 i8 ]" R* |& p
- C7 v* d$ v" F) N' m; {+ N/**********************************************************************************6 Y. A0 T4 V5 F* W% v
* *+ e" o! x' E9 ?. _2 Q
* Bypass logic *
! G7 x& E+ R' {* *' m' _ i6 K& U) f8 U4 r: t
**********************************************************************************/- r: y ?( B9 @
wire bypassed_tdo;
) }1 p' Y1 }+ R) k/ Q% e+ ~reg bypass_reg; // This is a 1-bit register
; S/ h5 n4 |) K, R+ m & U8 f/ `; ]1 h% Y1 c
always @ (posedge tck_pad_i or negedge trstn_pad_i)/ ?1 w1 x9 `0 A7 o5 ^
begin
* P7 L. i4 ?6 K( b8 ], N if (trstn_pad_i == 0)
* D+ a, v8 Y- U# o) u bypass_reg <= 1'b0;
* s) a! z2 H7 n9 N else if (test_logic_reset == 1)
% Y6 o; ^3 G/ k8 H' d) } bypass_reg <= 1'b0;
0 z4 K5 U5 L4 [/ C ?1 m$ j else if (bypass_select & capture_dr)
; d- }* K; B! D bypass_reg<= 1'b0;+ \/ z1 l& J& l
else if(bypass_select & shift_dr) @& s7 K# Q% h5 g T
bypass_reg<= tdi_pad_i;( _, x ?! Q( `5 k' u2 p- O
end
1 p% ^7 i2 Q+ aassign bypassed_tdo = bypass_reg; // This is latched on a negative TCK edge after the output MUX
. c1 C. O/ A: w; }/**********************************************************************************
1 I8 v" g7 V( K1 j) j5 }+ X; r* */ m9 N" X1 \, @- d8 f# |& A
* End: Bypass logic *
" @1 b% K) w+ M2 _ F* *
3 ~" [$ d! ^3 }. N7 Q**********************************************************************************/
! t9 d' @# }3 Y3 g/ a; H5 A ?3 S6 q6 C7 n% P( f0 y8 [
f,mux output8 [1 w \, U2 H: o& O
tap扮演着多个device(分机)的总机的角色。当公司内部的分机有很多,但总机只有一个。所以总机需要有多路选择器的功能。- N& Q. K) q8 H- U) `7 }7 @
代码如下:- T, `! Q9 A5 ?
- J% B0 ]/ W" W0 |9 ]/ q+ R* ?, u, k- O
/**********************************************************************************8 _' X8 _, Y; m7 l
* *4 ~0 ?+ T8 B8 o: a/ P; l
* Multiplexing TDO data *- Y4 w7 i5 w$ J
* *
. J3 [, N: E6 G4 I4 f**********************************************************************************/$ n( Q& a. c* [5 ?3 l5 m$ y! e
reg tdo_mux_out; // really just a wire
$ a Q4 u$ d) L$ `. D
6 N9 X9 f. D5 z' g; o6 M0 walways @ (shift_ir or instruction_tdo or latched_jtag_ir or idcode_tdo or
9 Z" Y: _: r- B5 U6 E debug_tdo_i or bs_chain_tdo_i or mbist_tdo_i or bypassed_tdo or
; R9 R9 n: ^$ K9 c bs_chain_tdo_i)4 e2 `, I7 Z+ j7 {& f
begin+ }% S+ g/ F: c# c
if(shift_ir)
$ x+ b. u) m& k1 r( K tdo_mux_out = instruction_tdo;4 X5 J& a7 L8 s" k3 Q- p
else
! j' |9 Z) t- } begin* |0 s" S6 u7 l- M/ E! c
case(latched_jtag_ir) // synthesis parallel_case
2 E1 A8 d$ M6 ?# j, s `IDCODE: tdo_mux_out = idcode_tdo; // Reading ID code
2 h' e- u! r5 t- Z# Y- K `DEBUG: tdo_mux_out = debug_tdo_i; // Debug8 ~' Q# [( P# m& J! F" ~& R( t- y
`SAMPLE_PRELOAD: tdo_mux_out = bs_chain_tdo_i; // Sampling/Preloading
& z* L! w) Z& L" }1 A6 L, u5 Z `EXTEST: tdo_mux_out = bs_chain_tdo_i; // External test
. G8 I i! o. ?' C1 C `MBIST: tdo_mux_out = mbist_tdo_i; // Mbist test
+ I8 P) n$ r% ^ ^$ n default: tdo_mux_out = bypassed_tdo; // BYPASS instruction
, y- p- t5 z8 X' z endcase& t7 u4 g4 y# R* Z H- N f
end+ Z! g0 s; H9 E2 F
end
" r5 W& `5 Z& l- u4 `7 }3 q
& j- _/ D- ?: Q. j5 f
4 G: A& |, B" |& i// TDO changes state at negative edge of TCK
& w6 q' l& S1 F0 i4 Xalways @ (negedge tck_pad_i); z# [6 p5 }6 B) @
begin
! G4 H- c) h; M* n tdo_pad_o = tdo_mux_out;
+ b* M' z5 U+ }/ D4 Hend6 U8 l/ u! F6 O5 B# \- y9 ^5 S- M" e
+ M3 a" h5 N5 M' G) |7 b
6 _( w& y" Z, T# X- |4 ]// Tristate control for tdo_pad_o pin
* H0 f3 T' w0 D0 R, Yalways @ (posedge tck_pad_i)
3 |9 X- ^0 I# m, A, W/ c* |! Sbegin
L$ e6 O4 {3 f6 x, ~ tdo_padoe_o <= shift_ir | shift_dr;) e6 x8 {; Y3 A% u
end
, r$ i5 @& t, V$ ]: ]9 v/**********************************************************************************
W: i2 {1 V- u5 I C E7 [7 A* *
* r$ L/ Z! z6 H4 r( y. p( b* End: Multiplexing TDO data *
! }+ N' X/ n- H4 A# x* *, p% S1 ], @3 s) U2 ~# G
**********************************************************************************/% w5 ^4 w3 ~5 w, l5 Q
& J, m7 h5 M* a! G% E9 D7 ^% j4,jtag_tap的使用0 F% ~% `! q- {2 A# Y, h) F3 z
要想使用jtag_tap,需要相应的驱动程序,才行。驱动的作用就是根据FSM的定义,以及命令格式,以及adv_dbg_if的实现,来操作jtag的4根线,达到某种目的。在adv_debug_sys的adv_jtag_bridge中的chain_commamds.c中有相关函数,代码如下:
8 r2 l0 S% c. h( l" }7 g1 ^4 e; {
1 ]" ?0 Y c# c
) S# U6 A9 l; u& Z1 X# E//. X4 I0 e' e' ]8 E4 y7 X8 n/ q
// Functions which operate on the JTAG TAP% a j J& Z, b+ i
& b! ]9 X. d/ D( o/ I: b ) d* P5 v, ` b' \1 I
/* Resets JTAG - Writes TRST=1, and TRST=0. Sends 8 TMS to put the TAP2 p2 }7 y: e2 C) M
* in test_logic_reset mode, for good measure.& o% H) @8 E0 b) b; H1 p
*/* b& u1 V* ]0 ^* X, y. U
int tap_reset(void) {
1 i% r* n# i0 }( Q" r int i;
9 `0 y8 q K+ P1 ? int err = APP_ERR_NONE;* P+ x! i5 _8 z3 ?. _+ ]
& x( b9 N- J5 t7 U debug("\nreset(");
7 F% G9 x5 E2 u/ V8 [ err |= jtag_write_bit(0);. L! L: M+ q# T
JTAG_RETRY_WAIT();
: t& `9 d$ `) a /* In case we don't have TRST reset it manually */$ o4 K/ v1 T* {3 R' }5 p
for(i = 0; i < 8; i++) err |= jtag_write_bit(TMS);( [- ?# N% l6 @+ y! [
err |= jtag_write_bit(TRST); // if TRST not supported, this puts us in test logic/reset
0 t3 c# U' S# G JTAG_RETRY_WAIT();
0 O& _$ X/ @6 b5 L( h: U4 a c err |= jtag_write_bit(0); // run test / idle, G3 R5 q/ G! ]1 j& U- I( w" n; d+ n
debug(")\n");. Z6 o p" a8 h
// Reset data on current module/register selections. }# s! Y) [" V" f9 ]5 v6 c* j7 C
current_chain = -1;
& d+ x( n8 z+ E // (this is only for the adv. debug i/f...bit of a kludge). f) n3 Y6 w/ e6 {
for(i = 0; i < DBG_MAX_MODULES; i++)1 S0 _2 P J" g2 }, u; z+ S* c
current_reg_idx = -1;/ l6 d1 o# M( t! _" g2 A, A4 n
return err;
7 u% F2 Q& i% N( }& J( _1 `}3 a4 I" J7 ~& {
// Set the IR with the DEBUG command, one way or the other t" T8 H% D0 ]* P9 H1 }# n6 ]
int tap_enable_debug_module(void)% r: h/ H8 u M8 p0 w
{& z& ]& A0 D( X
uint32_t data;/ v* } i8 I* v) z
int err = APP_ERR_NONE;2 R; y; d% R/ P0 g- {
if(global_altera_virtual_jtag) {2 z) X* V! y% c" K" }
/* Set for virtual IR shift */8 F% ~1 k# D; K( V# X Y* `3 r
err |= tap_set_ir(vjtag_cmd_vir); // This is the altera virtual IR scan command- b3 L, D/ h7 t% s. S
err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
i- z- t' i) ] X. {8 n+ m err |= jtag_write_bit(0); /* CAPTURE_DR */
# B" t' G% J, S2 S8 B err |= jtag_write_bit(0); /* SHIFT_DR */
$ C; k" V6 {. f( W1 u3 n 8 B" _0 l/ n# R% W/ V
/* Select debug scan chain in virtual IR */
1 V+ m7 ?, y/ K. h1 L7 t, n data = (0x1<<ALT_VJTAG_IR_SIZE)|ALT_VJTAG_CMD_DEBUG;9 H* C( d2 s* K& F. r
err |= jtag_write_stream(&data, (ALT_VJTAG_IR_SIZE+1), 1); // EXIT1_DR
2 {& E. o* S% c4 f4 {; K4 C& w err |= jtag_write_bit(TMS); /* UPDATE_DR */
' M* \8 `7 ]* B7 T2 z/ \ err |= jtag_write_bit(0); /* IDLE */ . ?% i3 j9 _3 }# I' C: v( j9 y
// This is a command to set an altera device to the "virtual DR shift" command
1 R$ {7 `7 z Z: ~" c err |= tap_set_ir(vjtag_cmd_vdr);$ p$ b) t y3 l. z: M6 V( U
}5 \7 p \. X$ m; e) Y5 B
else {4 ~" M5 |& g$ Y9 k, W5 \; s' |( X, E
/* select debug scan chain and stay in it forever */4 O9 p$ ^* ?' C' f2 q+ P; V# g9 p" \
err |= tap_set_ir(global_jtag_cmd_debug);5 V. V# d: j, z
}: v9 L0 I7 r+ H
return err;. V. }3 d, W- D% K
}
) f8 a3 q4 s$ r3 P7 u/* Moves a value into the TAP instruction register (IR), v/ J8 t* R" ^2 ?, l
* Includes adjustment for scan chain IR length./ Y' c+ e3 o# ~: B* f
*/
/ {+ n i8 x4 Huint32_t *ir_chain = NULL;
& x+ N6 H! r$ D6 x! fint tap_set_ir(int ir) {
% n; v& o/ W" |0 d1 o int chain_size;
4 T' Q, @3 W& ^8 _ int chain_size_words;
6 Y# U& T' L( E/ {7 S# j int i;- L% W" C& A+ [# b: W3 d& X7 D
int startoffset, startshift;
8 D0 x0 W6 T: y+ v7 v int err = APP_ERR_NONE;
5 U. b" n7 ]: I# O3 O4 x/ E
0 E1 w- w# G6 x9 J' ?3 Q I // Adjust desired IR with prefix, postfix bits to set other devices in the chain to BYPASS
3 A( j- S) }5 U& M! q- ?1 Q chain_size = global_IR_size + global_IR_prefix_bits + global_IR_postfix_bits;
; N3 t. ~0 ?$ w9 J- O chain_size_words = (chain_size/32)+1;6 t& K1 @ O$ F9 o$ w' S% g! \' n
if(ir_chain == NULL) { // We have no way to know in advance how many bits there are in the combined IR register
' R, e- M* p& ` ir_chain = (uint32_t *) malloc(chain_size_words * sizeof(uint32_t));" }0 `: M3 Q* ^$ V' h7 E5 C4 a$ I
if(ir_chain == NULL)
3 a. @# E" _4 h/ A return APP_ERR_MALLOC;
# ?, S0 i ]+ I: ?! T5 ~: ~' t }; X' q4 L" G- j
for(i = 0; i < chain_size_words; i++)" @8 ~6 K: W' ?- d' Y
ir_chain = 0xFFFFFFFF; // Set all other devices to BYPASS# _8 Q" O }- f
// Copy the IR value into the output stream
w' O, ~6 J0 V5 C& f: a8 W/ G9 X startoffset = global_IR_postfix_bits/32;* Q# d" V) T J; G
startshift = (global_IR_postfix_bits - (startoffset*32)); m# O1 t8 L# p! k. A" v- e7 _8 u
ir_chain[startoffset] &= (ir << startshift);" B7 d' v5 R: {/ p8 J0 I) C7 |
ir_chain[startoffset] |= ~(0xFFFFFFFF << startshift); // Put the 1's back in the LSB positions- N" h, n7 c8 G7 ~# Q
ir_chain[startoffset] |= (0xFFFFFFFF << (startshift + global_IR_size)); // Put 1's back in MSB positions, if any 2 Y* z) i" i+ x+ r9 Y) y G& y9 u
if((startshift + global_IR_size) > 32) { // Deal with spill into the next word
" ]4 S$ u5 ^0 _ ir_chain[startoffset+1] &= ir >> (32-startshift);
7 Y* c+ n, e B ir_chain[startoffset+1] |= (0xFFFFFFFF << (global_IR_size - (32-startshift))); // Put the 1's back in the MSB positions
! s2 b$ T# _* T b5 |( } }7 S; Q3 N; d$ l1 L! Q# V5 Y9 m
// Do the actual JTAG transaction4 Z" Q8 |; h. Q$ D' \- q# y& n
debug("Set IR 0x%X\n", ir);7 n2 A! V# Y& z' h
err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
* P$ h3 _: N& ]+ p err |= jtag_write_bit(TMS); /* SELECT_IR SCAN */1 c( R# ]/ t4 L4 z( G( S9 u1 D4 e
err |= jtag_write_bit(0); /* CAPTURE_IR */
! y- `( ^$ i: V: x$ s( C err |= jtag_write_bit(0); /* SHIFT_IR */ ( p6 J7 ^" i( U7 u1 h2 v( D% {% ~
/* write data, EXIT1_IR */4 `: Y; M+ s0 A% }
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);; E% F8 d5 o% J4 Y
err |= cable_write_stream(ir_chain, chain_size, 1); // Use cable_ call directly (not jtag_), so we don't add DR prefix bits
- G% [/ K; ]# q! o0 I- `, g0 O debug("Done setting IR\n");
7 b( N: e7 d: L- f err |= jtag_write_bit(TMS); /* UPDATE_IR */
6 R( {" V4 P- F- J err |= jtag_write_bit(0); /* IDLE */
) e8 g W6 |0 T3 W7 Z current_chain = -1;
2 q( F& G* W3 [ return err;
1 j) P! b4 C% `}
* L. f9 }7 r! H; `1 y// This assumes we are in the IDLE state, and we want to be in the SHIFT_DR state.
2 i+ i! a8 u7 a) |int tap_set_shift_dr(void)
3 F+ l! G' R4 I! f{
/ v2 f6 K, G$ Z8 }+ q int err = APP_ERR_NONE;
+ b% ~% V4 W, M* ?- t err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
3 I4 G# j; Q( E9 l err |= jtag_write_bit(0); /* CAPTURE_DR */9 Z2 H7 b" g% k7 {; o0 O: O/ e
err |= jtag_write_bit(0); /* SHIFT_DR */* H a5 G0 M& j7 h. W2 _) R: s
return err;
+ P. }# O" e, X}
6 ~* z0 b9 F) I& ^3 p" b// This transitions from EXIT1 to IDLE. It should be the last thing called
4 x$ ~7 f+ f7 ]) ?2 f7 x// in any debug unit transaction.
, V, W# A6 h3 X3 s8 h' F: _- U6 T" Yint tap_exit_to_idle(void)
4 a% e4 |5 V7 e" I% v{+ B8 v9 [8 F8 M+ u! ?7 F F
int err = APP_ERR_NONE;( q! t" I, }+ Z& Y+ `
err |= jtag_write_bit(TMS); /* UPDATE_DR */% x% e6 v' Z: q$ o: L* B* ?
err |= jtag_write_bit(0); /* IDLE */( |- [5 V. f- R s
return err;+ @( p7 f/ N3 X( y
}# K3 E) Q( C/ p% w9 J5 J
// Operations to read / write data over JTAG) ~$ H d, P |! G# @8 z6 O2 l
/* Writes TCLK=0, TRST=1, TMS=bit1, TDI=bit0
) T9 a# E7 ~" x7 }5 V# M- d and TCLK=1, TRST=1, TMS=bit1, TDI=bit0( y. j* ?2 ~( ~) D% c
*/
9 ]% t+ a. b! Kint jtag_write_bit(uint8_t packet) {
% C. J- \$ b3 J# n debug("Wbit(%i)\n", packet);' B$ h5 e0 F7 ~# k" j
return cable_write_bit(packet);: \* R& {. Z# s' X
}
8 U% U6 M7 q. h* B% O8 g0 X& @9 rint jtag_read_write_bit(uint8_t packet, uint8_t *in_bit) {
6 X3 f+ _' j0 j int retval = cable_read_write_bit(packet, in_bit); \ p7 U* A; a, G: C" W
debug("RWbit(%i,%i)", packet, *in_bit);9 ~" R. f I& s1 T1 {, ?% Q8 F
return retval;; h) u+ R% ?) }. n
}
- a: C, s/ e K" C; d6 w// This automatically adjusts for the DR length (other devices on scan chain)/ r) Q% j) p3 O$ E( _4 m% g; J
// when the set_TMS flag is true.+ c5 t" c3 s# b! o
int jtag_write_stream(uint32_t *out_data, int length_bits, unsigned char set_TMS). w1 r) j; V, C7 g
{" `, c* f6 w( G
int i;( P l% B! p* |7 z
int err = APP_ERR_NONE;. ]/ i' s0 G2 w4 \) o$ J2 `. ^
if(!set_TMS)) M0 Y; G8 j) c
err |= cable_write_stream(out_data, length_bits, 0); {( o9 z8 i% d
else if(global_DR_prefix_bits == 0)
/ @4 M3 A2 U/ L2 P3 s, E+ V err |= cable_write_stream(out_data, length_bits, 1);3 K5 _9 i- o$ r: w" \, a+ \ F
else {8 r! u& i" U9 x6 l! x1 I
err |= cable_write_stream(out_data, length_bits, 0);0 _( r( X& |# P6 O# P
// It could be faster to do a cable_write_stream for all the prefix bits (if >= 8 bits),
9 K7 E! |# _' ]) z4 l! P // but we'd need a data array of unknown (and theoretically unlimited): p3 i$ ^! s" S0 M; r5 |
// size to hold the 0 bits to write. TODO: alloc/realloc one.
3 X4 Y4 W. t" s* c for(i = 0; i < (global_DR_prefix_bits-1); i++), ]! ^ C7 T# v1 y
err |= jtag_write_bit(0);0 m6 p k' [9 a2 A6 J% ]4 X5 W
err |= jtag_write_bit(TMS);5 x7 W3 @6 B. W- g
}* x% X4 M, v5 N _
return err;0 `$ E0 }6 i/ m8 C: z+ i2 o
}- h% t- G3 Q# j1 y; ~! D( B
// When set_TMS is true, this function insures the written data is in the desired position (past prefix bits)* J9 s4 O; c& c1 j
// before sending TMS. When 'adjust' is true, this function insures that the data read in accounts for postfix
2 Q! M0 w7 Q# s8 \// bits (they are shifted through before the read starts).( \+ t+ X; i0 J3 T6 s7 a
int jtag_read_write_stream(uint32_t *out_data, uint32_t *in_data, int length_bits, unsigned char adjust, unsigned char set_TMS)1 ?4 N2 [& J$ f. E8 z/ z
{
9 E2 g! p# w+ [9 f, g int i;
% b6 ]% v' V$ r. {* G* {/ P& R int err = APP_ERR_NONE;
0 @0 z* E. C9 {3 F2 D# o4 w; W( w1 S1 s if(adjust && (global_DR_postfix_bits > 0)) {6 ]( ` y' b; v% z, v; j
// It would be faster to do a cable_write_stream for all the postfix bits,$ n5 I' X1 r, k$ o$ O' l+ `3 W
// but we'd need a data array of unknown (and theoretically unlimited)
- ~ J2 b6 I- N+ f* ~7 p // size to hold the '0' bits to write.; F& ^* k$ L; i4 r. l
for(i = 0; i < global_DR_postfix_bits; i++)
; o1 Q" `7 H. R7 ~: _+ m1 g7 z err |= cable_write_bit(0);2 s9 a( O7 \4 `2 S% M7 j: Z3 u
}6 j! s8 E/ j# K* b* f$ F
// If there are both prefix and postfix bits, we may shift more bits than strictly necessary.. e* F% K; p0 ~1 A+ f
// If we shifted out the data while burning through the postfix bits, these shifts could be subtracted
- E4 G e% A1 k% L6 M // from the number of prefix shifts. However, that way leads to madness.8 S, E3 S6 o. c
if(!set_TMS)& A4 b$ M" D1 p o; y) p
err |= cable_read_write_stream(out_data, in_data, length_bits, 0); 9 X+ C8 |- d0 A5 ]/ w
else if(global_DR_prefix_bits == 0)$ H6 X; @0 Z! P U: s
err |= cable_read_write_stream(out_data, in_data, length_bits, 1); $ B! L+ E# L# l. \: \' z1 d
else {0 D6 }" q, r( C0 Q
err |= cable_read_write_stream(out_data, in_data, length_bits, 0); / Q. j$ I3 }/ k
// It would be faster to do a cable_write_stream for all the prefix bits,
8 p# ^1 L: |; ^, f; P/ G // but we'd need a data array of unknown (and theoretically unlimited)6 v, ]/ U$ R3 X" u- O6 W
// size to hold the '0' bits to write.
8 R, C3 j% P# ^7 O6 i( o for(i = 0; i < (global_DR_prefix_bits-1); i++)9 a1 | S! }" ?5 h @
err |= jtag_write_bit(0);& t( _ ^2 p2 }! ], O$ p
err |= jtag_write_bit(TMS);
* Z( T( m5 P4 l2 s7 `/ j* g }% z5 q' T t( D/ F" |
return err;
' q0 X& F2 v/ G" M}
: P4 t4 p% @& y+ V// This function attempts to determine the structure of the JTAG chain* ^# l" ^' v/ _/ p' |$ N. I) G
// It can determine how many devices are present.
. p2 Z: L3 e8 t/ ~: k# S7 F// If the devices support the IDCODE command, it will be read and stored.& \! ^- E" C$ _) m# b$ s
// There is no way to automatically determine the length of the IR registers -
" j" D3 c! a. p4 _) e. Y// this must be read from a BSDL file, if IDCODE is supported.
( c; ~- B$ G4 R* ]( G( \1 G% P4 h// When IDCODE is not supported, IR length of the target device must be entered on the command line.
4 U+ a9 C, E, v6 o6 j% H#define ALLOC_SIZE 64
1 A- t8 _+ g, Q! z#define MAX_DEVICES 1024
8 B' P9 o+ N6 z0 sint jtag_enumerate_chain(uint32_t **id_array, int *num_devices)
; G& _, H8 R1 P: }/ G{% ^3 y7 w% |2 f
uint32_t invalid_code = 0x7f; // Shift this out, we know we're done when we get it back8 S# G+ O* |+ ]' K9 t6 U' k
const unsigned int done_code = 0x3f; // invalid_code is altered, we keep this for comparison (minus the start bit)
% X) B& _" Q7 K; X int devindex = 0; // which device we are currently trying to detect6 t9 F/ J/ X- W9 G+ A' ~4 z
uint32_t tempID;
\- a7 v) z2 C1 q5 p uint32_t temp_manuf_code;, N* y, J: }' l7 w( ?. y, v" j
uint32_t temp_rest_code; h6 U+ Z! b. F2 s% c3 A) E
uint8_t start_bit = 0;
" c$ ?: g3 X: U$ \. Y; b& ^9 z uint32_t *idcodes;6 G' f i/ |/ R0 N3 s1 n2 T+ }1 Z
int reallocs = 0;
0 u" ~1 B" g5 f( t7 @. S int err = APP_ERR_NONE;8 I5 G! T6 P" w5 R* X; T
// Malloc a reasonable number of entries, we'll expand if we must. Linked lists are overrated.' Z, c Y8 I% w* K2 m( n) g
idcodes = (uint32_t *) malloc(ALLOC_SIZE*sizeof(uint32_t));% R2 F4 \4 n/ Z3 e4 o5 ^, I: M f
if(idcodes == NULL) {
4 R0 D) X. x0 B4 f# P) n8 j printf("Failed to allocate memory for device ID codes!\n");
& f) e8 ~) j& `( R return APP_ERR_MALLOC;
* o! w' m+ y2 \) m, J }2 ?3 p w0 ]7 _
// Put in SHIFT-DR mode5 k, G6 x% ^5 u6 O* B$ K w- d8 ^+ K3 e
err |= jtag_write_bit(TMS); /* SELECT_DR SCAN */
) g# _9 u3 A3 i2 ^8 | E2 x6 J$ H' _ err |= jtag_write_bit(0); /* CAPTURE_DR */5 H! r% j+ H) s: @+ Z' W* m1 x* B2 N
err |= jtag_write_bit(0); /* SHIFT_DR */
0 c2 @% E" E) C) p0 u printf("Enumerating JTAG chain...\n");
+ q6 `# y+ ]. D$ U# k // Putting a limit on the # of devices supported has the useful side effect8 h% K+ Z; ]1 P' _% E
// of insuring we still exit in error cases (we never get the 0x7f manuf. id)
5 O3 k& y6 o {3 g while(devindex < MAX_DEVICES) {
7 ] X5 F& n0 O7 j7 M6 R: { // get 1 bit. 0 = BYPASS, 1 = start of IDCODE
" H& w" E- ] E9 m5 a err |= jtag_read_write_bit(invalid_code&0x01, &start_bit);
% \8 A1 _. ~) j# T2 a( g invalid_code >>= 1; t) Y5 S( @$ H' Y; Q% W; G( b
$ p' a5 a3 l: @- Z" O" e if(start_bit == 0) { F+ f U m, D/ e1 G3 e- w( U
if(devindex >= (ALLOC_SIZE << reallocs)) { // Enlarge the memory array if necessary, double the size each time
+ H* v7 S9 ^! Y6 S8 J; Y! Z idcodes = (uint32_t *) realloc(idcodes, (ALLOC_SIZE << ++reallocs)*sizeof(uint32_t));
5 C0 N8 Y: C8 \+ E; O if(idcodes == NULL) {
: y7 _" g: h+ F7 S+ _ printf("Failed to allocate memory for device ID codes during enumeration!\n"); 9 J; {. A5 U+ X/ C& y* [
return APP_ERR_MALLOC;
9 X2 P3 l: B) M" x# m* q }. C7 E+ v( d( G- P7 }. A: Q8 \4 W
}) l0 }- H1 H+ x( h
idcodes[devindex] = -1;
! Q( @6 ?4 b6 }/ v7 _9 ?& ` X devindex++;
. F* R p. s% c8 | }
/ T+ D. B% E1 w) t. c else {
" ^" l O3 d# j$ J // get 11 bit manufacturer code
8 T0 u( k# @6 |* i err |= jtag_read_write_stream(&invalid_code, &temp_manuf_code, 11, 0, 0);, X, \; H+ Z# Z; f6 z; {
invalid_code >>= 11;. F) [/ L$ y% V3 ]
; o, ], y" n) U; x
if(temp_manuf_code != done_code) {. T# J; ?7 z5 V* M
// get 20 more bits, rest of ID
. }, y0 u' }0 T; H, f err |= jtag_read_write_stream(&invalid_code, &temp_rest_code, 20, 0, 0);# } V ]& c+ }1 g0 r& _5 o k
invalid_code >>= 20;
9 D3 z; E2 b6 z3 W tempID = (temp_rest_code << 12) | (temp_manuf_code << 1) | 0x01;7 z" S2 g$ J3 [: R9 ]/ i% W
if(devindex >= (ALLOC_SIZE << reallocs)) { // Enlarge the memory array if necessary, double the size each time8 {) c0 r& ?3 `" e, c5 T
idcodes = (uint32_t *) realloc(idcodes, (ALLOC_SIZE << ++reallocs)*sizeof(unsigned long));; ], |( R5 N h% N& o7 J5 s8 O/ k
if(idcodes == NULL) {
: `5 B5 d7 M" C# n% o0 w+ b printf("Failed to allocate memory for device ID codes during enumeration!\n");
1 C2 C M( |1 o/ }2 |. r return APP_ERR_MALLOC;
. n4 W$ | C9 A3 X2 ^ }0 m% O2 `, E1 E0 [- A$ Z8 d
}
6 {! U9 f4 Y/ h, m% ] idcodes[devindex] = tempID;
( d5 u" ], ~ X2 B devindex++;
" ^4 p6 B5 W6 o } else {
1 B/ t) d6 w4 @2 u break;& }2 J! X* f* z- F! e
}
4 E: b5 C) W2 x( `; G3 H }4 o& }7 c; N) o; _
if(err) // Don't try to keep probing if we get a comm. error
( ^3 E2 {! t- G1 E return err;9 a9 a! K, z8 S9 U& d6 k; m
}
* d7 h+ S8 Y8 Q) P4 L if(devindex >= MAX_DEVICES)" W1 |& N- \$ V* Q
printf("WARNING: maximum supported devices on JTAG chain (%i) exceeded.\n", MAX_DEVICES);
- Z: N( v1 X7 _" \* H! X , C% j0 D& g H$ o* T, b+ r/ d
// Put in IDLE mode9 k/ R5 p& l0 ~# b
err |= jtag_write_bit(TMS); /* EXIT1_DR */
, G$ O; \$ o K D7 F) o! F3 i9 P err |= jtag_write_bit(TMS); /* UPDATE_DR */. f" d2 _8 X9 s- a( }+ p
err |= jtag_write_bit(0); /* IDLE */
0 I! X n# p, a; K+ ^ {/ }" y - S$ I1 q5 ]7 c( x, {2 L/ ?
*id_array = idcodes;$ o8 \$ Z7 U0 |2 u4 c0 @
*num_devices = devindex;
1 f9 } _% @- x \4 ^ / L- f1 }! [+ p1 x4 j; }% R
return err;
0 }6 n9 x6 O) X9 n}
$ ~# u6 r- W3 M5 W5 ]0 [ 3 Y, W4 J( w& m* l
9 I# Q# E- d }7 A3 J; K 6 z8 F2 e8 s; V/ T3 T D, ]. |
int jtag_get_idcode(uint32_t cmd, uint32_t *idcode)- p) S6 k# L9 K: n) {
{/ h( T9 s$ J7 [; E/ A
uint32_t data_out = 0;" r+ t* k, E. i% o
int err = APP_ERR_NONE;
( ~/ r) b' q- M4 k* X; U unsigned char saveconfig = global_altera_virtual_jtag;
; a1 b4 \3 N9 E) i0 a global_altera_virtual_jtag = 0; // We want the actual IDCODE, not the virtual device IDCODE
, _/ T/ ^7 m( t8 I+ Q. F0 g
. C! p& J! B% S6 L+ b err |= tap_set_ir(cmd);
: F$ r4 K8 c7 _6 _7 P& u ]; Z err |= tap_set_shift_dr();# y* I, z9 U, \& ?
err |= jtag_read_write_stream(&data_out, idcode, 32, 1, 1); /* EXIT1_DR */" t) I3 L9 o# a' {5 ^0 F
5 P$ {+ T, B3 h4 P if(err)
0 u) t. ?9 B4 a; o( y, F1 e printf("Error getting ID code!\n");8 ]4 o* ~3 K- l; H% m! Y* ]
' \5 F# |4 a% x% ]
// Put in IDLE mode: A2 _% D: ~ W/ d1 t8 H
err |= jtag_write_bit(TMS); /* UPDATE_DR */
& J h g5 ?/ z0 V/ R% p* K( A: V K err |= jtag_write_bit(0); /* IDLE */
2 B0 x0 _4 f% g
( u% ^. e" [* y& J V8 k. q7 G3 R global_altera_virtual_jtag = saveconfig;' @0 u" J" u* U [
return err;
' f a$ ?0 l5 z, L+ {0 b N) a}
$ R( z# ]8 Q8 \+ M6 x$ i7 E2 X7 l * \) Y! a" z% N* i! c( T" x
/ y3 l/ {) y' o/
$ ~) W4 J0 \) w1 `* F' ~5 p' h// Helper functions
3 o/ c( u$ v: |& Q% D
/ k/ n; x/ e k/* counts retries and returns zero if we should abort */
; K: V. |2 a& C# U9 V/* TODO: dynamically adjust timings */
. ]0 g; ]& Z9 W* d7 S. q* Iint retry_do() {
( v( A: C0 z9 N3 z: H6 I int err = APP_ERR_NONE;4 B: ]& o2 P8 k
+ W% O0 I: a3 m, x if (soft_retry_no >= NUM_SOFT_RETRIES) {0 ]; |9 I5 {2 s, W7 o
return 0;2 e: D2 j0 R% J6 h
# y% }0 \. G5 _, n
// *** TODO: Add a 'hard retry', which re-initializes the cable, re-enumerates the bus, etc.
+ Z& y+ i* N+ a/ q2 C; `, }
+ j+ E& H4 T& `+ `3 T$ @- i; I } else { /* quick reset */
# C3 w9 j! O" c# X if(err |= tap_reset()) {2 R/ c7 ~0 P7 I% G: ^( B! A
printf("Error %s while resetting for retry.\n", get_err_string(err));
$ s$ y$ Q8 c% K6 B% K* U. H return 0;
g# k' A3 ^- x9 u4 P$ O8 L j }
* d8 W: Q, n$ `( |7 r }) N" p 3 I+ p. p( a( }
// Put us back into DEBUG mode" |7 w2 D) r c' b
if(err |= tap_enable_debug_module()) {
: U E, h( k# V9 o% f$ `: T' A: R; O printf("Error %s enabling debug module during retry.\n", get_err_string(err));
, k& G" f6 e. U return 0;
2 ]+ C5 n( q4 ^5 @6 L0 P8 _1 C }3 `: N6 Q1 a& j3 B \3 O! J h
2 k" ?- F8 d" ?0 w2 h soft_retry_no++;
' t- |) H: E2 ~# r2 I printf("Retry...\n");- W6 \8 _# R! j" ?4 E. @; d
}
) h+ w1 P1 B9 R3 S: ?/ ^ ) P' y/ ^8 {# z; a
return 1;8 H" z. A4 K" \% |* h! o; d
}! ^! b0 ?3 e" p* T* k/ C- U
% M/ `+ d/ i3 B) M' F
5 D9 v7 D* T) b7 M3 l
- ]7 f+ D/ u( n5 B6 u2 }
! D- k; y! w& g8 {+ Z P
4 J9 G8 u# D3 J5,小结6 [% Z& t9 {2 b: |
本小节我们分析了advanced debug system中的jtag_tap模块的具体实现。用一句话来概括的话就是,JTAG就是类似SPI总线的一种总线,jtag_tap就相当于SPI总线的arbiter。8 G9 g0 }/ r& ^: F) R1 j
|
|