Altera的FPGA支持多种调试工具,其中最为大家所熟知的是Signaltap,虽然Signaltap支持条件触发、保存波形等强大的功能,但是,它也只能看个波形而已啊。
如果我要跟FPGA内部的逻辑交换数据,是否有个简便的方法?
有的,它就是虚拟JTAG。虚拟JTAG可以通过TCL脚本在线地把PC机的数据写入到FPGA内部或者从FPGA内部读出并传到PC机。这样,在调整算法参数(如PID的参数整定)的时候,无需多次综合。此外,只要你会用TCL的TK图形库,就能利用虚拟JTAG做上位机。
推荐《TCL/TK入门经典》、《虚拟JTAG教学视频》、《虚拟JTAG博客》,还有Altera的《虚拟JTAG用户手册》、《QuartusII脚本参考手册》。
上面这些视频、资料,已经把原理说得非常清楚了,这里不再重复,而是给出一个最简单的例子。
1、新建一个虚拟JTAG的IP核。
建议手动设定虚拟JTAG的索引号(index),这里设为0,记住索引号,后面有用。如下图所示。
下图这一步是设置仿真激励,目前我们不需要仿真,可以直接点finish,生成IP核。
2、建个verilog文件,代码如下。注意例化虚拟JTAG的名称。
module vjtag (
input clock,
output reg [7:0] my_counter='b0
);
always @ (posedge clock)
if (load && e1dr) // decode logic: used to load the counter my_counter
my_counter <= tmp_reg;
else
my_counter <= my_counter;
// Signals and registers declared for VJI instance
wire tck, tdi;
reg tdo='b0;
wire cdr, eldr, e2dr, pdr, sdr, udr, uir, cir;
wire ir_in;
// Instantiation of VJI
vir_jtag my_vji(
.tdo (tdo),
.tck (tck),
.tdi (tdi),
.tms(),
.ir_in(ir_in),
.ir_out(),
.virtual_state_cdr (cdr),
.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)
);
// Declaration of data register
reg [7:0] tmp_reg='b0;
// Deocde Logic Block
// Making some decode logic from ir_in output port of VJI
wire load;
assign load = ir_in;
// Bypass used to maintain the scan chain continuity for
// tdi and tdo ports
reg bypass_reg='b0;
always @ (posedge clock)
bypass_reg <= tdi;
// Data Register Block
always @ (posedge tck)
if ( load && sdr )
tmp_reg <= {tdi, tmp_reg[7:1]};
// tdo Logic Block
always @ (*)
if(load)
tdo <= tmp_reg[0];
else
tdo <= bypass_reg;
endmodule
3、锁好时钟引脚,综合,再调用Signaltap,采集my_counter信号,再综合下载代码。如下图所示。
4、在上面的工程目录下,新建一个tcl脚本,这里是vji_ctl.tcl。代码如下。
# exec "D:\\altera\\13.0sp1\\quartus\\bin64\\quartus_stp.exe" "-t" "G:\\WorkDir\\ProAltera\\ProAVerilog\\vjtag\\vji_ctl.tcl"
set usb [lindex [get_hardware_names] 0]
set device_name [lindex [get_device_names -hardware_name $usb] 0]
proc push {value} {
global device_name usb
if {$value > 256} {
return "value entered exceeds 8 bits"
}
open_device -device_name $device_name -hardware_name $usb
set push_value [int2bits $value]
set diff [expr {8 - [string length $push_value]%8}]
if {$diff != 8} {
set push_value [format %0${diff}d$push_value 0]
}
puts -nonewline "push value : "
puts $push_value
device_lock -timeout 10000
device_virtual_ir_shift -instance_index 0 -ir_value 1
device_virtual_dr_shift -instance_index 0 -dr_value $push_value -length 8
device_unlock
close_device
}
proc int2bits {i} {
set res ""
while {$i>0} {
set res [expr {$i%2}]$res
set i [expr {$i/2}]
}
if {$res==""} {set res 0}
return $res
}
push 2
5、在Quartus里,调出TCL控制台。
因为Quartus 13.0的TCL控制台有bug,不能载入::quartus::project包,所以不能直接在TCL控制台使用该包的命令(如quartus_stp -t a.tcl,会出错),于是,这里使用exec命令,间接调用quartus_stp命令。(网上的例子多数用的是老版本的Quartus,可以载入::quartus::project包,也可以直接使用quartus_stp -t a.tcl命令)
在TCL控制台中,使用 exec "D:\\altera\\13.0sp1\\quartus\\bin64\\quartus_stp.exe" "-t" "G:\\WorkDir\\ProAltera\\ProAVerilog\\vjtag\\vji_ctl.tcl"
D:\\altera\\13.0sp1\\quartus\\bin64\\ , 是我的quartus的安装目录,G:\\WorkDir\\ProAltera\\ProAVerilog\\vjtag\\ , 是上面的工程目录。
这两个目录都要按照自己的实际修改,注意:路径分隔符用\\。
然后,回车,正常的话,会输出以下信息。
其中push命令,可以把数据通过虚拟JTAG发到FPGA,load和e1dr信号有效的话,就把数据装载到my_counter。
最后,可以在Signaltap里,看到my_counter的值,有变化。
6、以上例子的使用流程。
只要修改TCL脚本中的push命令后面的参数,保存,再在Quartus的TCL控制台中调用exec命令(使用方向键↑,可以调用以前的命令),就可以把数据发到FPGA的tmp_reg,再利用load和e1dr装载到my_counter。