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