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