用FPGA做项目的时候,大部分时间都花在调试上了,调试除了板子实际跑看结果和用ILA进行调试外,还可以用软件进行仿真,本文就简单演示下 XILINX自带的仿真功能。
本文在 vivado2018.3版本上 演示, 其他版本请自行研究
仿真在我们的设计中是非常重要的, 当我们编写的逻辑比较复杂时,用vivado 布线综合动辄几十分钟甚至几个小时,有时候好不容综合好,生成bit下载进去,又发现我们的逻辑有问题,这时候再修改,再下载可能又要花去很多时间,甚至还不一定找到问题点, 这个时候仿真的重要性就凸显出来了。
仿真功能和ila 不同,ila功能可以理解为是FPGA内部的逻辑分析仪,相当于对FPGA内部的信号波形进行采集并且上传到软件进行显示,而仿真功能相当于是根据我们所写的逻辑模拟仿真出结果以及各个中间的信号波形,通过仿真我们可以在程序设计的过程中就判断出各个模块工作是否符合预期,是否有意想不到的问题等。
仿真还有个优势是,仿真可以从0时刻就开始观察波形,而且可以添加大量的观察信号,这个是下载到板子上观察结果,或者用ila抓波形无法比拟的。
新手会比较抗拒仿真,不过如果熟练掌握仿真技能,可以事半功倍。
仿真实际演示
本文用一个计数器的简单工程来演示下 ILA功能模块的使用。
本文在 vivado2018.3版本上 演示, 其他版本请自行研究
1、具体步骤
1) 具体步骤 新建一个VIVADO 工程,打开软件 选中Create Project, 如下图所示
2)点击NEXT ,在出现的第二个对话框“Project name”中输入工程名;在“Project location”中选择保存路径;勾选“Create project subdirectory”,最后点击“Next” 备注,所有的路径均不能出现中文名称
3)点击 RTL PROJECT 选项,点击NEXT
4) 第四步Add Sources 选项直接留空,NEXT
5)第五步Add Constraints 选项直接留空,NEXT
6)选择芯片型号 我们板子上用的芯片是XC7Z010 ,并在列表栏中选择对应的封装型号,完整型号是XC7Z010CLG400-1 如下所示,选中后点NEXT
7)确认所选信息 点击“Finish”,完成vivado的工程创建
2 增加我们的verilog逻辑
1)在主界面点击左侧 Add Sources ,点击 复选框的Add or create design sources 选项 并点击NEXT
2)在出现的Add Sources 中 选择创建新文件 Create FILE 如下图所示,并在弹出的窗口中 选择类别为Verilog ,在FILE name中填写文件的名称,这里用”count_module” 代替,点击OK 并点击FINISH
3)在跳出的窗口中可以填写模块的输入输出信号,由于这部分工作在代码中可以完成,所以这里直接点OK 完成VERILOG 文件的创建
1.2 代码:这里我们代码上做一个简单的功能,FPGA芯片内部计数器time_count每满100次计数(0-99)result寄存器翻转一次(从1变成0,或者从0变成1) , 程序和工程一很像,但是为了演示,所以缩短了计数器的时间
`timescale 1ns / 1ps module count_module( input clk, input rst_n ); parameter T1MS = 7'd99; //50M晶振时钟 reg [7:0]time_count;//时钟计数器 reg result; always@(posedge clk or negedge rst_n) if(!rst_n)begin time_count<=26'd0; result<=1'b0; end else if(time_count>=T1MS)begin time_count<=26'd0; result<=~result; end else time_count<=time_count+1'b1; endmodule
3.创建仿真文件testbench
1)回到主界面点击左侧 Add Sources ,点击 复选框的Add or createsimulation sources 选项 并点击NEXT
2)点选Create File 在弹出的对话框里输入 testbench 的名称(这里用count_tb代替,其中tb代表testbench的缩写),随后点OK
3)在下一个对话框选择OK 并在弹出的对黄框里选择YES
4)此时就能在工程的仿真目录里找到我们刚才创建的count_tb.v文件了
双击打开 count_tb.v 然后在里面添加testbench 的激励代码
`timescale 1ns / 1ps module count_tb( ); reg clk_i; reg rst_n_i; initial begin clk_i = 0; rst_n_i = 0; #200; rst_n_i = 'b1; end always #5 clk_i = ~clk_i; count_module u_test( .clk(clk_i), .rst_n(rst_n_i) ); endmodule
备注 :testbench 的写法和标准的逻辑有点区别,并且大部分的testbench代码都是不可综合的
testbench 内容注释: initial 模块 是从0时刻开始就工作的, 只执行一次, #200相当于延迟200个单位周期(备注 这句是不可综合语句)
#200; 后再跟 rst_n_i=1’b1; 代表 200个单位时间后 rst由0置位到1
always #5 clk_i = ~clk_i; 代表每5个单位时间 翻转一次,也就是10个单位时间为一个周期
至于仿真的时间单位 则在程序的最顶端 描述 `timescale 1ns / 1ps //仿真时间单位/仿真时间精度
count_module u_test()相当于是对count_module的程序实例化并且命名为u_test
5)仿真代码编写完成后,保存该文件
也可以按下绿色箭头 预编译一下看看 代码有没有报错
进行仿真
1) 点选 Run Behavioral simulation启动仿真
备注:其他几个选项分别代表 Run Behavior Simulation 功能仿真 ,Run Post-Systhesis Functional Simulation 综合后的功能仿真,Run Post-Systhesis Timing Simulation 综合后的时序仿真,Run Post-Implementation Functional Simulation 实现后/布局布线后的功能仿真,Run Post-Implementation Timing Simulation 实现后/布局布线后的时序仿真 一般我们选择功能仿真即可。
2)接下来就出现了我们的仿真主界面
其中 Scope 窗口 我们设计的层次划分, 当我们选中Scope窗口中的任意模块,对应模块的信号寄存器都会出现在 Objects窗口中
3)这里我们右键我们例化的 u_test模块,并且选择Add to Wave Window 将u_test下的所有信号添加到观察窗口
这样观察窗口就会出现我们需要的信号和寄存器了
4)因为上面的信号是新添加的,所以这里需要rerun 一下仿真,再点Run all(如果不rerun的话,新添加的信号线将不会有波形)
用鼠标缩小 ,并将波形像左拉到最开始的地方可以看到
我们的仿真已经正常开始工作了, 并且可以看到 rst_n是在第200ns 开始由低变高,跟我们设定的相同, 而clk_i则是一直以10ns为周期在工作
展开波形 右键点time_count计数器 在Radix 中将我们数据格式改成unsigned Decimal ,无符号十进制
可以观察到我们的计数器 从复位信号拉高后的第二个上升沿开始,计数器从0-1开始自增,并且每来一个上升沿自增一次
当计数器自增到第99之后,下一个上升沿,计数器从99变为0,并且result信号翻转一次
缩小波形可以看到,result寄存器正在以1us(100个10ns,因为计数器是计数0-99的)的间隔 在0-1之间来回振荡 周期是2us
当然我们也可以右键选中我们的time_count计数器点选Waveform Style –> Analog 来显示数值对应的模拟波形(如正弦波三角波等)
这里因为count是从0-99 再到0,所以外形看上去有点类似三角波 如下图所示
以下是完整工程:
以上是 vivado 自带的仿真功能的简单调用和介绍,当然vivado 仿真的功能远不止我介绍的这些,需要各位自行去挖掘了,不作展开