本帖最后由 51黑fan 于 2016-1-31 04:16 編輯
最近學(xué)習(xí)了Altera的Virtual JTAG工具的使用。下面是我的使用心得。
Altera在Quartus II 6.0中加入了一個(gè)sld_virtual_jtag 參數(shù)化宏單元模塊,并提供了相應(yīng)的Tcl程序包。有了這套工具,使用sld_virtual_jtag 和相應(yīng)的Tcl命令,我們就可以構(gòu)建自己的虛擬JTAG鏈路,并進(jìn)行自定義的JTAG調(diào)試了。
一、 相關(guān)文件
后面的鏈接是Quartus幫助文件中的內(nèi)容,前兩個(gè)是我從Help里拷出來用Word保存的html文件,由于里面的一些鏈接指向本地文件,會(huì)被當(dāng)作危險(xiǎn)代碼,不理就是了,不是病毒。
- sld_virtual_jtag 的說明:
sld_virtual_jtag .rar
(8.96 KB, 下載次數(shù): 20)
2016-1-31 04:01 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
。只給出了VHDL的例化方式,對(duì)于各個(gè)選項(xiàng)的配置給了說明。 - ::quartus::jtag Tcl命令包的說明:
Tcl命令包.rar
(11.62 KB, 下載次數(shù): 17)
2016-1-31 04:01 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
。每個(gè)命令都給了使用的例子,拷到一個(gè)空Tcl文件中,保存后用Quartus的Tcl scripts工具運(yùn)行即可。 - Altera提供的用戶指南:太大了傳不上來,給個(gè)鏈接,自己下吧。里面有JTAG協(xié)議的講解和兩個(gè)示例。
二、 Virtual JTAG要點(diǎn)解析
澄清一個(gè)概念:所謂虛擬JTAG,是Altera用PLD上的硬件JTAG電路和可編程邏輯資源搭建的一個(gè)IP core。這個(gè)IP core實(shí)現(xiàn)了JTAG接口電路的功能,但本身不是硬件JTAG電路的一部分,是用可編程邏輯“虛擬”出來的。
這個(gè)IP core有兩個(gè)接口:一個(gè)接口在布局布線時(shí)連接到硬件JTAG電路上,用戶不可見;一個(gè)接口由用戶通過電路圖或者HDL例化到代碼中,并通過這個(gè)接口自定義JTAG操作。
這個(gè)IP core的功能 = JTAG信號(hào)hub + JTAG TAP控制器 + IR/DR IO。
用戶看到的是虛擬JTAG電路對(duì)內(nèi)的接口,看不到硬件JTAG電路接口;并且這個(gè)接口是從JTAG TAP控制器引出的,信號(hào)方向和大家通常從外部對(duì)JTAG電路的理解不一樣,所以容易引起混淆,理解上會(huì)有一定的困難。
用戶看到(可以利用)的接口有四組:
- JTAG協(xié)議中的接口(TDI、TDO、TCK)。TDI是IP core的輸出;TDO是IP core的輸入;沒有TMS,這個(gè)引腳的功能被另一組接口解析并代替了;TCK是唯一沒有變化的引腳。
- 命令寄存器接口(IR_IN[]、IR_OUT[])。這組接口是Tcl命令和用戶邏輯交互的接口,很有用。也可用來簡(jiǎn)單地傳遞數(shù)據(jù)。
- 虛擬JTAG TAP控制器狀態(tài)接口(virtual_state_...)。這組接口是虛擬JTAG的狀態(tài)機(jī)輸出,一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)輸出,可以看作狀態(tài)信號(hào)燈。這組信號(hào)就是TMS的解析。如果需要實(shí)現(xiàn)復(fù)雜地?cái)?shù)據(jù)傳遞功能,一定要理解這組信號(hào)的功能。
- 硬件JTAG TAP控制器狀態(tài)接口(jtag_state_...)。這組接口是硬件JTAG的狀態(tài)機(jī)輸出。這些功能暫時(shí)不會(huì)用,可能是用來實(shí)現(xiàn)更高級(jí)控制和數(shù)據(jù)傳遞功能的。
理解虛擬JTAG概念的關(guān)鍵有以下兩點(diǎn):
- 在IP core的背后有四根看不見的JTAG信號(hào),這四根信號(hào)才是我們通常從外部理解的JTAG。
- 看得見的TDI和TDO是兩根等待連接的信號(hào)(就像墻上插座里的兩根線),我們通過在這兩根線之間或串接或并接或簡(jiǎn)單或復(fù)雜的邏輯,實(shí)現(xiàn)我們的JTAG鏈路。
理解了這些概念,看懂用戶指南應(yīng)該不成問題了。然后再看示例程序的verilog代碼。
我的一個(gè)空想:如果Altera能夠把用戶不可見的硬件JTAG電路接口開放出來(用戶可見),那么用戶就可以把這個(gè)接口上的標(biāo)準(zhǔn)JTAG接口連接到PLD的引腳上,再把這組引腳連接到外部JTAG電路上,那么這個(gè)IP core就不再是“虛擬”的了。
三、 Tcl命令的使用。
::quartus::jtag Tcl命令包中的各條命令都有英文注釋,這里就不挨個(gè)翻譯了。
下面,把用戶指南里給出的第一個(gè)例子逐句分析一下,后面還會(huì)給出一個(gè)模板。
示例如下。其中只給::quartus::jtag Tcl命令包中的命令加上了綠色,其余簡(jiǎn)單的Tcl命令可以按照E文的意義理解,稍微復(fù)雜一些的Tcl命令可以參考相關(guān)書籍。為了區(qū)別原注釋,我的注釋一概用紅色標(biāo)出。
#### Script begins ######################################################
set loop 3
## 檢測(cè)下載電纜,從命令行輸出檢測(cè)到的下載電纜名稱。原示例只檢測(cè)USB下載線,我給改了。 ##
# get hardware names : get download cable name
foreach hardware_name [get_hardware_names] {
puts "\n$hardware_name"
if { [string match "ByteBlasterMV*" $hardware_name] } {
set byteblaster_name $hardware_name
}
}
puts "\nSelect JTAG chain connected to $byteblaster_name.\n";
## 檢測(cè)下載電纜對(duì)應(yīng)的jtag鏈路,從命令行輸出檢測(cè)到的器件名稱。并選中第一個(gè)作為操作對(duì)象。##
# List all devices on the chain, and select the first device on the chain.
puts "\nDevices on the JTAG chain:"
foreach device_name [get_device_names -hardware_name $byteblaster_name] {
puts $device_name
if { [string match "@1*" $device_name] } {
set test_device $device_name
}
}
puts "\nSelect device: $test_device.\n";
## 打開器件 ##
# Open device
open_device -hardware_name $byteblaster_name -device_name $test_device
## 獲得器件的jtag編號(hào)。需要先發(fā)送jtag命令--獲取ID,命令值是“6”。然后讀取jtag數(shù)據(jù),得到32位的ID值。##
## 由于該步驟需要兩個(gè)操作,這兩個(gè)操作之間不能插入其他操作,所以需要lock一下。##
# Retrieve device id code.
# IDCODE instruction value is 6; The ID code is 32 bits long.
# IR and DR shift should be locked together to ensure that other applications
# will not change the instruction register before the id code value is shifted
# out while the instruction register is still holding the IDCODE instruction.
device_lock -timeout 10000
device_ir_shift -ir_value 6 -no_captured_ir_value ## 發(fā)送jtag命令 6。注意:這里的jtag是真實(shí)的jtag ##
puts "IDCODE: 0x[device_dr_shift -length 32 -value_in_hex]" ## 獲取jtag數(shù)據(jù) ##
device_unlock
## real jtag operation completed ##
## 以下是virtual jtag的操作 ##
# SAMPLE instruction samples a 8-bit bus; the captured value shows the number of sample performed.
# FEED instruction supplies a 8-bit value to the logic connected to this instance.
# Both data registers corresponding to the IR are 8 bit wide.
## 循環(huán)采樣數(shù)據(jù)部分 ##
# Send SAMPLE instruction to IR, read captured IR for the sampling number.
# Capture the DR register for the current sampled value.
## 設(shè)置循環(huán)參數(shù) ##
set run_script 0
while {$run_script != $loop} {
set run_script [expr $run_script +1]
set counter1 0
set counter2 1
## 獲取采樣數(shù)據(jù) ##
device_lock -timeout 10000
while {$counter1!=$counter2} {
device_virtual_ir_shift -instance_index 0 -ir_value 1 ## 發(fā)送virtual jtag命令 1 ##
set counter1 [device_virtual_dr_shift -instance_index 0 -length 4 -value_in_hex] ## 獲取virtual jtag數(shù)據(jù) ##
device_virtual_ir_shift -instance_index 1 -ir_value 1 ## 發(fā)送virtual jtag命令 1 ##
set counter2 [device_virtual_dr_shift -instance_index 1 -length 4 -value_in_hex] ## 獲取virtual jtag數(shù)據(jù) ##
puts "Value of {counter2,counter1} is <$counter2,$counter1>"
## 設(shè)置延時(shí)參數(shù) ##
set delay 0
while {$delay != 120000} {
set delay [expr $delay+1]
}
puts ""
}
device_unlock
## 交互輸入,設(shè)定FPGA計(jì)數(shù)器初值部分 ##
## instead of stopping at the equal value, force a value of supplied by the user in both counters and then end.
# Send FEED instruction to IR, read a two-digit hex string from the console,
# then send the new value to the DR register.
puts "\nType in a digit in hexadecimal to update the contents of the counters:"
gets stdin update_value
set update_value2 [expr $update_value+1]
device_lock -timeout 10000
device_virtual_ir_shift -instance_index 0 -ir_value 2 -no_captured_ir_value ## 發(fā)送virtual jtag命令 2 ##
device_virtual_dr_shift -instance_index 0 -length 4 -dr_value $update_value -value_in_hex -no_captured_dr_value ## 獲取virtual jtag數(shù)據(jù) ##
device_virtual_ir_shift -instance_index 1 -ir_value 2 -no_captured_ir_value ## 發(fā)送virtual jtag命令 2 ##
device_virtual_dr_shift -instance_index 1 -length 4 -dr_value $update_value2 -value_in_hex -no_captured_dr_value ## 獲取virtual jtag數(shù)據(jù) ##
device_unlock
}
# Close device
close_device
如果要讀懂上述代碼,建議按照用戶指南中的步驟設(shè)置好工程,記得一定要把引腳按照自己電路板的情況分配上(可以不用LED)。先把## real jtag operation completed ##之前的代碼運(yùn)行一下,看看有什么反應(yīng)。如果反應(yīng)很好的話,那么祝賀你,你已經(jīng)克服了對(duì)jtag和Tcl的恐懼心理。
上述代碼的組織結(jié)構(gòu)如下:
1. 真實(shí)jtag操作。
1.1 檢測(cè)電纜。(如果你用的是并口下載線,并且沒有修改原代碼的話,在這一步你就會(huì)遇到攔路虎)
1.2 查找器件。(如果你的電路板上串接了不只一個(gè)jtag器件的話,你要修改你的代碼,否則這一步也是過不去的)
1.3 打開器件。(前兩關(guān)過去了,這一步應(yīng)該不成問題)
1.4 獲得器件的jtag編號(hào)。(IDCODE命令)(個(gè)人覺得沒有什么大用處,也許可以起到初始化IR的作用)
2. 虛擬jtag操作。
2.1 循環(huán)采樣計(jì)數(shù)器值。(SAMPLE命令)(通過jtag鏈路從FPGA讀數(shù)據(jù))
2.2 設(shè)置計(jì)數(shù)器初值。(FEED命令)(通過jtag鏈路向FPGA發(fā)數(shù)據(jù))
需要說明的是,2中的SAMPLE命令(2'b01)和FEED命令(2'b10)是用戶自定義的virtual jtag命令(隨便定,只要你的verilog代碼中是對(duì)應(yīng)譯碼的就可以),1中的IDCODE命令(在Cyclone器件中是10'b0000000110)是由Altera定義的,返回值是一組32位的二進(jìn)制數(shù)。
把上面的代碼結(jié)合著::quartus::jtag Tcl命令包的幫助文件(我已經(jīng)給了)逐條分析一下,對(duì)于::quartus::jtag Tcl命令包的使用就沒有問題了。
我做的一個(gè)Tcl模板
Tcl模板.rar
(1.03 KB, 下載次數(shù): 12)
2016-1-31 03:55 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
,根據(jù)上面代碼改的,可以檢查jtag鏈路,并由用戶選擇使用哪個(gè)器件,可以讀取一次jtag數(shù)據(jù),然后由用戶輸入一次jtag數(shù)據(jù)。如果需要循環(huán)功能,還要根據(jù)上面代碼加入Tcl命令。以后有更好的再傳上來。
四、 verilog代碼分析
先把關(guān)鍵的verilog代碼寫在下面。完整的代碼在最后,按照我的理解和習(xí)慣,對(duì)原示例代碼的寫法作了修改。
wire [3:0] counter1;
reg [3:0] feed_reg; // 四位的DR寄存器,用于加載輸入值
wire tdi, tck, cdr, cir, e1dr, e2dr, pdr, sdr, udr, uir;
reg tdo, bypass_reg;
wire [1:0] ir_in; // 兩位的IR寄存器輸出,來自my_vji_a
wire sample = ir_in[0]; // IR譯碼,2'b01表示SAMPLE命令
wire feed = ir_in[1]; // IR譯碼,2'b10表示FEED命令
reg [3:0] offload_reg; // 四位的DR寄存器,用于輸出
/* instantiation of the vji mega functionc */
my_vji_a VJI_INST(
.ir_out (2'b0), // input to megafunction
.tdo (tdo), // input to mega function
.ir_in (ir_in), // output from mega function
.tck (tck), // output from mega function
.tdi (tdi), // output from mgafunction
.virtual_state_cdr (cdr), // output from mega function
.virtual_state_e1dr(e1dr), // "
.virtual_state_e2dr(e2dr), // "
.virtual_state_pdr (pdr), // "
.virtual_state_sdr (sdr), // "
.virtual_state_udr (udr), // "
.virtual_state_uir (uir), // "
.virtual_state_cir (cir)); // "
/* 1. Sample Instruction Handler */
always @ (posedge tck) // 針對(duì)SAMPLE指令的處理
if ( sample && cdr )
offload_reg <= counter1;
else if ( sample && sdr )
offload_reg <= {tdi, offload_reg[3:1]}; // 典型的移位寄存器操作(MSB to LSB),移位輸出counter1的當(dāng)前值
/* 2. Feed Instruction Handler */
always @ (posedge tck) // 針對(duì)FEED指令的處理
if ( feed && sdr )
feed_reg <= {tdi, feed_reg[3:1]}; // 典型的移位寄存器操作(MSB to LSB),移位輸入要賦給counter1的初始值
/* 3. Bypass register */ // 旁路寄存器,沒有針對(duì)這個(gè)virtual_jtag的操作就旁路
always @ (posedge tck)
bypass_reg = tdi;
/* 4. Node TDO Output */ // TDO輸出選擇器,根據(jù)IR的不同,選擇不同的信號(hào)輸出
always @ ( sample, feed, feed_reg[0], offload_reg[0], bypass_reg )
begin
if (sample)
tdo <= offload_reg[0];
else if (feed)
tdo <= feed_reg[0]; // Used to maintain the continuity of the scan chain. // 在移位輸入時(shí),也要保證jtag鏈路有輸出
else
tdo <= bypass_reg;
end
上面代碼中,/* Sample Instruction Handler */之后的代碼是需要用戶自己編寫的代碼,是使用virtual_jtag的精髓所在,一定要讀懂。
sld_virtual_jtag 的說明中把TAP控制器的狀態(tài)信號(hào)分為High level和Low level。示例代碼中使用的是High level部分的信號(hào)。High level部分的信號(hào)又可以分為DR寄存器操作對(duì)應(yīng)的一組狀態(tài)信號(hào)(6個(gè))和IR寄存器操作對(duì)應(yīng)的一組狀態(tài)信號(hào)(2個(gè))。
1. DR寄存器操作對(duì)應(yīng)的一組狀態(tài)是:Capture_DR -> Shift_DR -> Exit1_DR -> Pause_DR -> Exit2_DR -> Update_DR。每一個(gè)狀態(tài)對(duì)應(yīng)一個(gè)*dr信號(hào)。
對(duì)于數(shù)據(jù)輸出操作(此例中是SAMPLE命令,并行加載,串行輸出),需要在Capture_DR狀態(tài)把被采樣的信號(hào)并行加載到DR寄存器中,在Shift_DR狀態(tài)把DR寄存器串行輸出到virtual jtag鏈路的tdo引腳上(同時(shí)串行載入tdi引腳的數(shù)據(jù)),然后遍歷余下的狀態(tài)并不做任何操作。這一過程就是代碼中對(duì)offload_reg寄存器的操作。
對(duì)于數(shù)據(jù)輸出操作(此例中是FEED命令,串行輸入,并行加載),在Capture_DR狀態(tài)不進(jìn)行任何操作直接跳轉(zhuǎn)到下一個(gè)狀態(tài),在Shift_DR狀態(tài)串行載入virtual jtag輸入的數(shù)據(jù),在Exit1_DR狀態(tài)把獲得的數(shù)據(jù)并行加載到目標(biāo)寄存器上,然后遍歷余下的狀態(tài)并不做任何操作。這一過程就是代碼中對(duì)feed_reg寄存器的操作。
2. IR寄存器操作對(duì)應(yīng)的一組狀態(tài)是:Capture_IR -> Shift_IR -> Exit1_IR -> Pause_IR -> Exit2_IR -> Update_IR。由于virtual jtag模塊提供了并行的ir_in端口,簡(jiǎn)化了操作,所以只有首尾兩個(gè)狀態(tài)對(duì)應(yīng)*ir信號(hào)。
在這個(gè)例子中,只用到了ir_in端口,沒有用到IR操作對(duì)應(yīng)的狀態(tài)及輸出信號(hào)。這也可以看作是virtual jtag的方便之處。
3. bypass_reg寄存器提供了jtag鏈路的第三條通路,在沒有針對(duì)當(dāng)前virtual jtag操作的情況下,tdi數(shù)據(jù)經(jīng)過一個(gè)tck周期的延時(shí)輸出到tdo引腳。
4. tdo引腳輸出時(shí),需要根據(jù)ir_in的取值選擇三條通路中的一條,所以這部分代碼也是需要用戶設(shè)計(jì)的。
看懂了這個(gè)例子,對(duì)virtual jtag的基本輸出和輸入功能就掌握了。
下圖是用lpm宏單元替換各個(gè)功能塊后編譯得到的RTL視圖。相應(yīng)的代碼可以在此
相應(yīng)的代碼.rar
(4.92 KB, 下載次數(shù): 21)
2016-1-31 04:10 上傳
點(diǎn)擊文件名下載附件
下載積分: 黑幣 -5
下載。
image.jpg (59.83 KB, 下載次數(shù): 146)
下載附件
2016-1-31 04:10 上傳
|