本帖最后由 51黑fan 于 2016-1-31 04:16 編輯
最近學習了Altera的Virtual JTAG工具的使用。下面是我的使用心得。
Altera在Quartus II 6.0中加入了一個sld_virtual_jtag 參數化宏單元模塊,并提供了相應的Tcl程序包。有了這套工具,使用sld_virtual_jtag 和相應的Tcl命令,我們就可以構建自己的虛擬JTAG鏈路,并進行自定義的JTAG調試了。
一、 相關文件
后面的鏈接是Quartus幫助文件中的內容,前兩個是我從Help里拷出來用Word保存的html文件,由于里面的一些鏈接指向本地文件,會被當作危險代碼,不理就是了,不是病毒。
- sld_virtual_jtag 的說明:
sld_virtual_jtag .rar
(8.96 KB, 下載次數: 20)
2016-1-31 04:01 上傳
點擊文件名下載附件
。只給出了VHDL的例化方式,對于各個選項的配置給了說明。 - ::quartus::jtag Tcl命令包的說明:
Tcl命令包.rar
(11.62 KB, 下載次數: 17)
2016-1-31 04:01 上傳
點擊文件名下載附件
。每個命令都給了使用的例子,拷到一個空Tcl文件中,保存后用Quartus的Tcl scripts工具運行即可。 - Altera提供的用戶指南:太大了傳不上來,給個鏈接,自己下吧。里面有JTAG協議的講解和兩個示例。
二、 Virtual JTAG要點解析
澄清一個概念:所謂虛擬JTAG,是Altera用PLD上的硬件JTAG電路和可編程邏輯資源搭建的一個IP core。這個IP core實現了JTAG接口電路的功能,但本身不是硬件JTAG電路的一部分,是用可編程邏輯“虛擬”出來的。
這個IP core有兩個接口:一個接口在布局布線時連接到硬件JTAG電路上,用戶不可見;一個接口由用戶通過電路圖或者HDL例化到代碼中,并通過這個接口自定義JTAG操作。
這個IP core的功能 = JTAG信號hub + JTAG TAP控制器 + IR/DR IO。
用戶看到的是虛擬JTAG電路對內的接口,看不到硬件JTAG電路接口;并且這個接口是從JTAG TAP控制器引出的,信號方向和大家通常從外部對JTAG電路的理解不一樣,所以容易引起混淆,理解上會有一定的困難。
用戶看到(可以利用)的接口有四組:
- JTAG協議中的接口(TDI、TDO、TCK)。TDI是IP core的輸出;TDO是IP core的輸入;沒有TMS,這個引腳的功能被另一組接口解析并代替了;TCK是唯一沒有變化的引腳。
- 命令寄存器接口(IR_IN[]、IR_OUT[])。這組接口是Tcl命令和用戶邏輯交互的接口,很有用。也可用來簡單地傳遞數據。
- 虛擬JTAG TAP控制器狀態接口(virtual_state_...)。這組接口是虛擬JTAG的狀態機輸出,一個狀態對應一個輸出,可以看作狀態信號燈。這組信號就是TMS的解析。如果需要實現復雜地數據傳遞功能,一定要理解這組信號的功能。
- 硬件JTAG TAP控制器狀態接口(jtag_state_...)。這組接口是硬件JTAG的狀態機輸出。這些功能暫時不會用,可能是用來實現更高級控制和數據傳遞功能的。
理解虛擬JTAG概念的關鍵有以下兩點:
- 在IP core的背后有四根看不見的JTAG信號,這四根信號才是我們通常從外部理解的JTAG。
- 看得見的TDI和TDO是兩根等待連接的信號(就像墻上插座里的兩根線),我們通過在這兩根線之間或串接或并接或簡單或復雜的邏輯,實現我們的JTAG鏈路。
理解了這些概念,看懂用戶指南應該不成問題了。然后再看示例程序的verilog代碼。
我的一個空想:如果Altera能夠把用戶不可見的硬件JTAG電路接口開放出來(用戶可見),那么用戶就可以把這個接口上的標準JTAG接口連接到PLD的引腳上,再把這組引腳連接到外部JTAG電路上,那么這個IP core就不再是“虛擬”的了。
三、 Tcl命令的使用。
::quartus::jtag Tcl命令包中的各條命令都有英文注釋,這里就不挨個翻譯了。
下面,把用戶指南里給出的第一個例子逐句分析一下,后面還會給出一個模板。
示例如下。其中只給::quartus::jtag Tcl命令包中的命令加上了綠色,其余簡單的Tcl命令可以按照E文的意義理解,稍微復雜一些的Tcl命令可以參考相關書籍。為了區別原注釋,我的注釋一概用紅色標出。
#### Script begins ######################################################
set loop 3
## 檢測下載電纜,從命令行輸出檢測到的下載電纜名稱。原示例只檢測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";
## 檢測下載電纜對應的jtag鏈路,從命令行輸出檢測到的器件名稱。并選中第一個作為操作對象。##
# 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編號。需要先發送jtag命令--獲取ID,命令值是“6”。然后讀取jtag數據,得到32位的ID值。##
## 由于該步驟需要兩個操作,這兩個操作之間不能插入其他操作,所以需要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 ## 發送jtag命令 6。注意:這里的jtag是真實的jtag ##
puts "IDCODE: 0x[device_dr_shift -length 32 -value_in_hex]" ## 獲取jtag數據 ##
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.
## 循環采樣數據部分 ##
# Send SAMPLE instruction to IR, read captured IR for the sampling number.
# Capture the DR register for the current sampled value.
## 設置循環參數 ##
set run_script 0
while {$run_script != $loop} {
set run_script [expr $run_script +1]
set counter1 0
set counter2 1
## 獲取采樣數據 ##
device_lock -timeout 10000
while {$counter1!=$counter2} {
device_virtual_ir_shift -instance_index 0 -ir_value 1 ## 發送virtual jtag命令 1 ##
set counter1 [device_virtual_dr_shift -instance_index 0 -length 4 -value_in_hex] ## 獲取virtual jtag數據 ##
device_virtual_ir_shift -instance_index 1 -ir_value 1 ## 發送virtual jtag命令 1 ##
set counter2 [device_virtual_dr_shift -instance_index 1 -length 4 -value_in_hex] ## 獲取virtual jtag數據 ##
puts "Value of {counter2,counter1} is <$counter2,$counter1>"
## 設置延時參數 ##
set delay 0
while {$delay != 120000} {
set delay [expr $delay+1]
}
puts ""
}
device_unlock
## 交互輸入,設定FPGA計數器初值部分 ##
## 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 ## 發送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數據 ##
device_virtual_ir_shift -instance_index 1 -ir_value 2 -no_captured_ir_value ## 發送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數據 ##
device_unlock
}
# Close device
close_device
如果要讀懂上述代碼,建議按照用戶指南中的步驟設置好工程,記得一定要把引腳按照自己電路板的情況分配上(可以不用LED)。先把## real jtag operation completed ##之前的代碼運行一下,看看有什么反應。如果反應很好的話,那么祝賀你,你已經克服了對jtag和Tcl的恐懼心理。
上述代碼的組織結構如下:
1. 真實jtag操作。
1.1 檢測電纜。(如果你用的是并口下載線,并且沒有修改原代碼的話,在這一步你就會遇到攔路虎)
1.2 查找器件。(如果你的電路板上串接了不只一個jtag器件的話,你要修改你的代碼,否則這一步也是過不去的)
1.3 打開器件。(前兩關過去了,這一步應該不成問題)
1.4 獲得器件的jtag編號。(IDCODE命令)(個人覺得沒有什么大用處,也許可以起到初始化IR的作用)
2. 虛擬jtag操作。
2.1 循環采樣計數器值。(SAMPLE命令)(通過jtag鏈路從FPGA讀數據)
2.2 設置計數器初值。(FEED命令)(通過jtag鏈路向FPGA發數據)
需要說明的是,2中的SAMPLE命令(2'b01)和FEED命令(2'b10)是用戶自定義的virtual jtag命令(隨便定,只要你的verilog代碼中是對應譯碼的就可以),1中的IDCODE命令(在Cyclone器件中是10'b0000000110)是由Altera定義的,返回值是一組32位的二進制數。
把上面的代碼結合著::quartus::jtag Tcl命令包的幫助文件(我已經給了)逐條分析一下,對于::quartus::jtag Tcl命令包的使用就沒有問題了。
我做的一個Tcl模板
Tcl模板.rar
(1.03 KB, 下載次數: 12)
2016-1-31 03:55 上傳
點擊文件名下載附件
,根據上面代碼改的,可以檢查jtag鏈路,并由用戶選擇使用哪個器件,可以讀取一次jtag數據,然后由用戶輸入一次jtag數據。如果需要循環功能,還要根據上面代碼加入Tcl命令。以后有更好的再傳上來。
四、 verilog代碼分析
先把關鍵的verilog代碼寫在下面。完整的代碼在最后,按照我的理解和習慣,對原示例代碼的寫法作了修改。
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) // 針對SAMPLE指令的處理
if ( sample && cdr )
offload_reg <= counter1;
else if ( sample && sdr )
offload_reg <= {tdi, offload_reg[3:1]}; // 典型的移位寄存器操作(MSB to LSB),移位輸出counter1的當前值
/* 2. Feed Instruction Handler */
always @ (posedge tck) // 針對FEED指令的處理
if ( feed && sdr )
feed_reg <= {tdi, feed_reg[3:1]}; // 典型的移位寄存器操作(MSB to LSB),移位輸入要賦給counter1的初始值
/* 3. Bypass register */ // 旁路寄存器,沒有針對這個virtual_jtag的操作就旁路
always @ (posedge tck)
bypass_reg = tdi;
/* 4. Node TDO Output */ // TDO輸出選擇器,根據IR的不同,選擇不同的信號輸出
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. // 在移位輸入時,也要保證jtag鏈路有輸出
else
tdo <= bypass_reg;
end
上面代碼中,/* Sample Instruction Handler */之后的代碼是需要用戶自己編寫的代碼,是使用virtual_jtag的精髓所在,一定要讀懂。
sld_virtual_jtag 的說明中把TAP控制器的狀態信號分為High level和Low level。示例代碼中使用的是High level部分的信號。High level部分的信號又可以分為DR寄存器操作對應的一組狀態信號(6個)和IR寄存器操作對應的一組狀態信號(2個)。
1. DR寄存器操作對應的一組狀態是:Capture_DR -> Shift_DR -> Exit1_DR -> Pause_DR -> Exit2_DR -> Update_DR。每一個狀態對應一個*dr信號。
對于數據輸出操作(此例中是SAMPLE命令,并行加載,串行輸出),需要在Capture_DR狀態把被采樣的信號并行加載到DR寄存器中,在Shift_DR狀態把DR寄存器串行輸出到virtual jtag鏈路的tdo引腳上(同時串行載入tdi引腳的數據),然后遍歷余下的狀態并不做任何操作。這一過程就是代碼中對offload_reg寄存器的操作。
對于數據輸出操作(此例中是FEED命令,串行輸入,并行加載),在Capture_DR狀態不進行任何操作直接跳轉到下一個狀態,在Shift_DR狀態串行載入virtual jtag輸入的數據,在Exit1_DR狀態把獲得的數據并行加載到目標寄存器上,然后遍歷余下的狀態并不做任何操作。這一過程就是代碼中對feed_reg寄存器的操作。
2. IR寄存器操作對應的一組狀態是:Capture_IR -> Shift_IR -> Exit1_IR -> Pause_IR -> Exit2_IR -> Update_IR。由于virtual jtag模塊提供了并行的ir_in端口,簡化了操作,所以只有首尾兩個狀態對應*ir信號。
在這個例子中,只用到了ir_in端口,沒有用到IR操作對應的狀態及輸出信號。這也可以看作是virtual jtag的方便之處。
3. bypass_reg寄存器提供了jtag鏈路的第三條通路,在沒有針對當前virtual jtag操作的情況下,tdi數據經過一個tck周期的延時輸出到tdo引腳。
4. tdo引腳輸出時,需要根據ir_in的取值選擇三條通路中的一條,所以這部分代碼也是需要用戶設計的。
看懂了這個例子,對virtual jtag的基本輸出和輸入功能就掌握了。
下圖是用lpm宏單元替換各個功能塊后編譯得到的RTL視圖。相應的代碼可以在此
相應的代碼.rar
(4.92 KB, 下載次數: 21)
2016-1-31 04:10 上傳
點擊文件名下載附件
下載。
|