找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 7932|回复: 3
打印 上一主题 下一主题
收起左侧

虚拟JTAG工具在FPGA调试中的应用

[复制链接]
跳转到指定楼层
楼主
本帖最后由 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) 。只给出了VHDL的例化方式,对于各个选项的配置给了说明。  
  •     ::quartus::jtag Tcl命令包的说明: Tcl命令包.rar (11.62 KB, 下载次数: 17) 。每个命令都给了使用的例子,拷到一个空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) ,根据上面代码改的,可以检查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) 下载。



评分

参与人数 1黑币 +5 收起 理由
lucky421 + 5

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:617264 发表于 2019-9-27 23:01 | 只看该作者
好难,,之前已经弄了一周了还是没有搞明白。我现在都快要崩了啊!!!!
回复

使用道具 举报

板凳
ID:83305 发表于 2019-10-13 16:04 | 只看该作者
乖乖的好难哦。不知道学进不
回复

使用道具 举报

地板
ID:446139 发表于 2019-12-13 10:10 | 只看该作者
很有收获谢谢lz
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表