当FPGA设计中复杂度慢慢变高的时候,仿真的手段也要增加,目前我们仿真的手段都是在ModelSim中配置相应的testbench,给模块发送需要的信号。这种软件仿真的方式有几个缺点:
- 一个是软件仿真速度很慢,一般都不会仿真超过1秒,对于一些需要等待一段时间的逻辑就只好暂时降低等待时间;
- 二是软件只能仿真逻辑,不会考虑电路内的逻辑延迟、时钟问题(其实可以设置延迟,但具体数字很难确定);
- 三是软件不会考虑引脚配置是否正确。
这时引入第二种仿真方式,多一种选择,少一点debug时间。这就是在FPGA内部集成一个逻辑分析器,Xilinx的Vivado中叫做ChipScope,相应Altera的叫做SignalTap,基本上是同一类。这种方式是在芯片内部调用逻辑和BRAM抓取特定信号并保存下来,通过JTAG和电脑实时通信,将捕捉到的信号在Vivado中显示。
这篇教程以上一篇的数码管project作为基础
FPGA基础入门【6】ChipScope的使用
设置ChipScope
设置ChipScope的前提条件是完成Synthesis,这样Vivado能够确认project中的所有信号名称信息。在左侧的Flow Navigator中展开Synthesis -> Open Synthesized Design -> Set Up Debug
系统会打开新界面开始为ChipScope添加需要的信号:
点击Find Nets to Add添加想要的信号。将数码管project中的top level代码拿出来看看:
// Verilog code for segment project
module segment(
input clock,
input reset,
input button,
output [7:0] segment_out,
output [7:0] digit
);
// Pushdown detection
wire pushdown;
pushdown_detect pd(
.clock(clock),
.button(button),
.pushdown(pushdown)
);
// Decimal counter digit0-3
wire [3:0] counter0;
wire [3:0] counter1;
wire [3:0] counter2;
wire [3:0] counter3;
wire [2:0] carry;
decimal_counter d0(
.clock (clock),
.reset (reset),
.carryin (pushdown),
.carryout (carry[0]),
.result (counter0)
);
decimal_counter d1(
.clock (clock),
.reset (reset),
.carryin (carry[0]),
.carryout (carry[1]),
.result (counter1)
);
decimal_counter d2(
.clock (clock),
.reset (reset),
.carryin (carry[1]),
.carryout (carry[2]),
.result (counter2)
);
decimal_counter d3(
.clock (clock),
.reset (reset),
.carryin (carry[2]),
.carryout (),
.result (counter3)
);
// Segment translation
segment_trans trans(
.clock (clock),
.reset (reset),
.counter0 (counter0),
.counter1 (counter1),
.counter2 (counter2),
.counter3 (counter3),
.digit (digit[3:0]),
.segment_out (segment_out)
);
assign digit[7:4] = 4'b1111;
endmodule
比较重要的几个信号分别是输入button,消抖后的信号pushdown,十进制的4位数,和译码之后的输出digit和segment_out。我们分别输入搜索后加入相应信号如下
将需要的信号一一搜索加入后如下
到下一步中,sample of data depth是采样深度,芯片内部的储存空间是有限的,不会无限捕捉信号。
Input pipe stages设置信号和储存采样之间的缓冲寄存器层数,这在时钟频率高的时候会用上,在那时缓冲有助于减少时钟冲突,给系统更多的空间来布线。在时钟不高的情况下就不用配置了。
Capture control和Advanced trigger是和信号触发条件和储存方式有关,为了简化教程不细讲。
除了这种初始化配置方式,在Netlist窗口中,也可以选中自己需要看的信号,右键后点击Mark Debug加入到debug清单中,之后可以在下方Debug窗口中点击Set up Debug(小虫标志),一路next下去就可以将标记好的信号配置到chipscope中。
编译烧写
ChipScope配置好之后就可以再点击Flow Navigator中Run Implementation,编译好后再generate bitstream生成image文件,就和上一篇教程一模一样,只是系统已经在相应的位置添加了模块用来保存你需要的信号。
需要注意的是ChipScope在内部添加了自己的成分,因此增加了编译需要的时间,对于初始project较小的情况尤其明显,需要耐心等待。
编译完成后烧写入编译好的bitstream。这一步可以看到Debug probes file就自动填入了刚刚配置好的ltx文件
在烧写完之后就会自动打开waveform,到这就可以实际使用chipscope了
实测
这时如果去按按键,可以发现逻辑没有变化,原来是怎样工作的现在还是怎样,不同的是,配置好trigger之后,waveform就可以在满足trigger条件时开始捕捉信号。配置trigger如下:
在第一次使用chipscope时这个窗口应该是空着的,点击绿圈加号Add probes添加trigger,出现一个小菜单,选择自己需要的信号即可,这里我选择了pushdown。
在这之后需要选择operator,radix和value。operator是操作符,有等于和不等于,顾名思义就是选择符合条件时开始捕捉或者不符合条件时开始捕捉。radix是数字进制,可以选择二进制、八进制、十六进制、带符号十进制和无符号十进制,影响后面Value的形式。value就是实际数值,不过不只有数字可以选择,还有X无所谓,R上升沿,F下降沿,B上升或者下降沿,和N不动这些选项,这里选择上升沿,就是消抖后探测到上升沿时触发。
在绿圈加号右边还有个与门符号,这是当你有多个trigger触发条件时的逻辑关系,比如默认是AND,意思是多个条件都满足时才触发;也可以改为OR,当有一个条件满足时触发;NAND都不满足时才触发;NOR只要有一个不满足就触发。
这里可以看到旁边还有个capture setup窗口,这个是在前面选择了capture control才有内容。
配置好后点击绿圈,带触发条件开启chipscope,在你按下按键后就会出现相应waveform,可以看到十进制计数器counter0-3的计数跟实际的输出是相符的(顺序有点乱)。但digit刷新是每1ms的事情,在采样1024和时钟100MHz的条件下,最多只能看10us的数据,看到digit变化的可能性很低,如果想看就要设置digit的相应trigger。
好了写到这相信差不多会用了,工具部分到这一章已经基本都介绍过了,下一篇就集中于开发板上了。