【设计经验】2、ISE中ChipScope使用教程
一、软件与硬件平台
软件平台:
操作系统:Windows 8.1
开发套件:ISE14.7
硬件平台:
FPGA型号:XC6SLX45-CSG324
二、ChipScope介绍
ChipScope是Xilinx提供的一个校验FPGA设计的工具。它的本质是一个虚拟的逻辑分析仪,能调用FPGA内部的逻辑资源对代码中的各个变量进行抓取分析。与ModelSim等一些其他的FPGA仿真工具不同的是,ChipScope可以直接反映代码在实际硬件上的执行情况,从而能够更加有效的定位设计中的问题。
三、目标任务
本文会以一个4-bit的计数器为例来给大家演示如何使用ChipScope来校验这个计数器的功能。对于一个4-bit的计数器来说,计数器的最低位bit0是时钟信号的2分频,bit1是时钟信号的4分频,bit2是时钟信号的8分频,最高位bit3是时钟信号的16分频。接下来,我们就利用ChipScope来验证这个逻辑的正确性。
我的开发板上有四个LED灯和四个按键,所以我会把计数器的4-bit分别绑定到四个LED灯上面,然后在逻辑里面设计一个异步复位按钮用来复位这个计数器,同时设计这个异步复位还有一个目的就是用来设置初始的触发条件,这一点后面会详细介绍。
四、待测代码
module led_top ( input I_clk , input I_rst_n , output reg [3:0] O_led_out ); always @(posedge I_clk or negedge I_rst_n) begin if(!I_rst_n) O_led_out <= 4'd0 ; else O_led_out <= O_led_out + 1'b1 ; end endmodule
写好待测代码,并添加物理约束文件绑定好管脚,我的开发板上的约束文件如下
NET I_clk LOC = V10 | TNM_NET = sys_clk_pin | IOSTANDARD = "LVCMOS33"; TIMESPEC TS_sys_clk_pin = PERIOD sys_clk_pin 50000 kHz; NET I_rst_n LOC = N4 | IOSTANDARD = "LVCMOS15"; ## SW2 pushbutton NET O_led_out<0> LOC = V5 | IOSTANDARD = "LVCMOS33"; ## LED1 NET O_led_out<1> LOC = R3 | IOSTANDARD = "LVCMOS33"; ## LED2 NET O_led_out<2> LOC = T3 | IOSTANDARD = "LVCMOS33"; ## LED3 NET O_led_out<3> LOC = T4 | IOSTANDARD = "LVCMOS33"; ## LED4
五、ChipScope使用完整流程
1、利用上面的待测代码和约束文件在ISE14.7中建立一个新工程。然后点击Synthesize-XST把整个工程综合一遍。
2、选中顶层模块名led_top,然后鼠标右键选择New Source选项,在弹出的New Source Wizard界面中选择第二个ChipScope Definition and Connection File选项,并取名字ChipScope_LED(名字可以随便取),然后点击Next
3、点击Next以后在弹出的一下对话框中点击Finish
4、Finish点击完毕以后,等一段时间,工程的层次目录就多出来了一个后缀为.cdc的ChipScope文件
5、双击ChipScope_LED.cdc文件,会弹出下面的界面,使用默认的设置保证Use SRLs和Use RPMs处于选中状态
Use SRLs(SRL = Shift Register LUT)选项使编译器用移位寄存器的查找表(Shift Register LUTs)代替触发器(flip flops)和乘法器,因此它能有效的减少FPGA内部资源,提高ChipScope性能
Use RPMs(RPM = Relationally Placed Macros)包含RLOC约束,RLOC定义了潜在设计原语的顺序与结构。Use RPMs让编译器用FMAP,HMAP,ROM,RAM等相关联的宏模块,使逻辑块布局的更加合理,可以有效的提高速度与性能,并节省FPGA资源
6、上一步选择完并点击Next后弹出如下界面,这个界面保持默认设置
7、继续点击Next弹出如下界面。在这个界面中选择触发端口(Trigger Ports)的数目和它们各自的宽度(Trigger Width),
说明:
a、这里建议先把触发宽度(Trigger Width)选择为最大值256,后面等信号添加完毕以后在回来修改为信号的总宽度。这么做是因为在一个比较复杂的设计中,你事先根本不知道自己要抓多少信号,所以等你把要抓的信号都设置好了以后在回来修改这个参数是比较好的选择。
b、Match Type的类型一共有6种,每种支持的值类型与功能见下表
Match Type |
Bit Values |
Functions |
Basic |
0,1,X |
=,<> |
Basic w/edges |
0,1,X,R,F,B,N |
=,<> |
Extended |
0,1,X |
=,<>,>,>=,<,<= |
Extended w/edges |
0,1,X,R,F,B,N |
=,<>,>,>=,<,<= |
Range |
0,1,X |
=,<>,>,>=,<,<=,in range,not in range |
Range w/edges |
0,1,X,R,F,B,N |
=,<>,>,>=,<,<=,in range,not in range |
表中各参数的含义如下:
参数 |
含义 |
0 |
低电平 |
1 |
高电平 |
X |
不定值 |
R |
上升沿(Rising) |
F |
下降沿(Falling) |
B |
双边沿(Both Edge) |
N |
非边沿(No Edge) |
= |
等于 |
<> |
不等于 |
< |
小于 |
<= |
小于等于 |
> |
大于 |
>= |
大于等于 |
in range |
在某个范围内 |
not in range |
不在某个范围内 |
由于本例比较简单,所以Match Type选择为Basic就可以了
c、Enable Trigger Sequencer选项指定了触发队列的级数,这个值保持默认即可。触发队列的结构图如下图所示
d、Enable Storage Qualification选项可以通过用户自定义的条件去过滤捕获的数据,这个选项一般让它默认选上
8、点击Next进入Capture Parameter选项卡,这一页在这个例子中保持默认参数即可
说明:
a、Data Depth指的是当触发条件满足以后,采样的数据的个数。这个值设置的越大,那么你得到的信息就越多,但相应的占用的FPGA内部的资源也越多。如果你想抓取UART和IIC的数据,由于UART和IIC协议的传输速度较慢,所以这个值必须设置大一点才能看到比较多的信息,而对于SPI这种传输速度比较快的协议,这个值可以设置的小一些。
b、Data Same As Trigger选项的意思是你抓取的信号既可以触发信号,也可以作为数据信号。这里给大家一个建议,对于inout信号(双向信号),最好选择只作为数据信号,不作为触发信号。
9、点击Next,进入Net Connections选项页
10、双击红色的CLOCK PORT选项,在弹出的界面中按照下图1、2、3的顺序把时钟信号连上
11、连好时钟信号以后点击OK,返回到Net Connections界面,发现CLOCK PORT已经由红色变成了黑色,说明时钟设置成功
12、接着双击红色的TRIGGER PORTS,在弹出的界面中按照下图1、2、3的顺序设置要抓取的信号
13、要抓取的信号设置完毕以后点击OK返回Net Connections选项页,发现TRIGGER PORTS还是红色的,这是因为我们先前设置的要抓取信号的总宽度是256,而这里我们只抓了5个信号,所以需要返回Trigger Parameter页把Trigger Width设置为5
14、设置正确的Trigger Width以后,在回到Net Connections选项页可以发现TRIGGER PORTS已经变成黑色的了,说明信号连接全部设置正确。然后点击Return To Project Navigator
15、在弹出的Save Project对话框中选择“是”
至此,整个.cdc文件的设置过程全部设置完毕。
16、接着双击Generate Programming File生成bit文件
17、连接好开发板的Jtag线并上电。双击上张图片最后一个选项Analyze Design Using ChipScope打开ChipScope界面如下所示
18、如果Jtag连接正常的话,会弹出下面的窗口,这个窗口表示找到了开发板使用的FPGA型号为XC6SLX45
19、点击OK,左上角的P标签变成了绿色,Jtag Chain多出了FPGA型号这个标签
20、左键单击选中DEV:0 MyDevice0 (XC6SLX45),然后右键弹出菜单栏,在菜单栏中选择Configure..
21、在弹出的界面中勾选Import Design-level CDC File和Auto-create Buses两个选项
22、点击OK,bit文件就被下载到了开发板中,然后进入以下界面
23、双击Trigger Setup和Waveform分别打开设置触发的窗口和波形窗口
触发窗口中的Depth就是我们之前在.cdc文件中设置要抓取的数据深度,Position这个参数默认情况是0,但是建议最好设置一个偏移量,这里我设置的是100,它表示的是把触发条件满足之前的100个数据和触发条件满足之后的924(1024-100)个数据显示到波形窗口中,这样我们就可以捕捉到在触发之前的一部分信息,更有助于问题的定位。Storage Qualification这个参数就是之前在.cdc文件勾选了Enable Storage Qualification这个选项后才有的,一般选择默认的All Data。
波形窗口是显示要抓取的波形的窗口,这里还没开始抓取,所以显示为空。
24、点击右上角的1号图标,把Trigger Setup窗口放大,然后设置触发条件为I_rst_n为R(R表示上升沿触发),接着点击3号图标进入等待触发状态
25、等待触发状态
26、按一下开发板上I_rst_n信号绑定的按钮,使触发条件满足,波形窗口出现了抓到的波形
上图中红色的T标尺代表的触发的起始位置,它的位置不能移动;绿色的O和蓝色的X是两个可移动标尺,他们两个的绝对位置以及差值均显示在波形窗口的右下角,有时候需要测量两个关键点的差值就可以拖动这两个标尺来直接得出结果
27、按住鼠标左键在波形窗口中画一个矩形框可以把波形窗口放大,可以看到更多的波形细节
28、如果我想让O_led_out为4’b0010的时候触发,那么修改触发条件为如下图所示,然后点击触发按钮
29、由于我们设置的O_led_out一直在自增,所以不需要按按键,等O_led_out为4’b0010触发条件满足就可以触发了,触发以后的波形如下图所示
显然触发位置在O_led_out为4’b0010的位置,和之前设置的相同。
30、如果你想查看O_led_out总线每一个bit的波形情况,可以点击一下信号名前面的小图标展开
31、如果你想看O_led_out总线的模拟波形,那么你可以双击Bus Plot标签,在弹出的界面中选中O_led_out总线就可以了
32、更多的功能大家有空可以多多尝试。至此ChipScope的教程就完毕了。
六、如何防止信号被优化
在一个复杂的设计中,我们往往会抓大量的信号,而ISE14.7编译代码的时候会把一些有相同逻辑的信号给优化掉,这会导致我们在选择信号的时候找不到想要抓取的信号,针对这种情况给大家提供两个解决办法。
方法一:
1、在你想要抓取的所有信号前面加上(*KEEP = “TRUE”*)
(*KEEP = "TRUE"*)reg [3:0] R_cnt;
2、选中Synthesize-XST,鼠标右键在弹出的菜单栏中选择Process Properties...
3、在弹出的窗口中选择-keep_hierarchy为Soft,(Yes和Soft的区别我暂时不清楚,但是我习惯于选择Soft,希望有网友能提供答案以及来源,谢谢)
4、设置完毕以后重新综合,然后重新在.cdc选择要抓取的信号
方法二:
上面一种方法根据我的经验并不能100%的保证信号不被综合掉,所以还有另外一种方法是写一段冗余逻辑把信号进行运算然后赋值给一个输出,并把输出引到顶层绑定一个空闲的管脚,这种情况我的处理方法是:
假设要抓取的信号是(*KEEP = "TRUE"*)reg [3:0] R_cnt;
1、定义1个输出信号O_test;
Output O_test;
2、把要抓取的信号各位相或然后赋值给O_test
assign O_test = | R_cnt ;
R_cnt前面的“|”表示把R_cnt的每一bit按位相或,这行代码和
assign O_test = R_cnt[0] | R_cnt[1] | R_cnt[2] ;
综合出来的逻辑是一模一样的
3、把O_test引到顶层并在约束文件中分配一个空余管脚
通过这种增加冗余逻辑的方式,R_cnt信号一般不会被综合掉,我自己在平时的使用中会先使用第一种方法,如果发现第一种方法还是把我想看的关键信号综合掉了的话就采用第二种方法。
七、采用例化ILA核的方式抓信号
除了采用.cdc文件抓取信号以外,还有一种方式是采用例化ILA核的方式抓信号的时序。这种方式的详细操作流程如下:
1、添加一个新的ICON IP核
2、一般情况下保持所有的参数默认就可以了
3、再添加一个ILA 的IP核
4、在第一页设置好相关的参数,这些参数的含义核.cdc文件中参数的含义一模一样,这里不再过多解释
5、第二页主要是设置要抓取的信号宽度,值得注意的是这里可以设置大一点没关系,因为这种方法不要求信号宽度和要抓的信号数目完全相同
6、生成这个两个IP核以后,把这两个IP核例化到代码中
module led_top ( input I_clk , input I_rst_n , output reg [3:0] O_led_out ); always @(posedge I_clk or negedge I_rst_n) begin if(!I_rst_n) O_led_out <= 4'd0 ; else O_led_out <= O_led_out + 1'b1 ; end wire [35:0] CONTROL0; wire [7:0] TRIG; icon icon_debug ( .CONTROL0(CONTROL0) // INOUT BUS [35:0] ); ila ila_debug ( .CONTROL(CONTROL0), // INOUT BUS [35:0] .CLK(I_clk), // IN .TRIG0(TRIG) // IN BUS [7:0] ); assign TRIG[0]=I_rst_n; assign TRIG[4:1]=O_led_out; endmodule
接下来就是生成bit文件并用ChipScope抓取信号了,和前一种方法一样。
这种方法和前一种方法的区别在于这种方法只能抓取一个.v文件中的信号,而且速度生成IP核的比较慢,进入ChipScope中以后还需要自己修改端口的名字,比较浪费时间;好处就是可以100%保证抓到想抓的信号,所以对于逻辑不太复杂的单文件代码可以采用这种方式。不过我个人还是比较喜欢用.cdc文件的方式来抓信号。
八、总结
1、用ILA逻辑分析仪抓信号有两种方式:.cdc文件方式(推荐)和例化ILA核方式
2、在cdc文件中防止信号被优化有两种方式:keep_hierarchy选为Soft,增加冗余逻辑
欢迎关注我的微信公众号:FPGA之禅