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