虚拟JTAG工具在FPGA调试中的应用(续二)
0赞四、 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下载。