FPGA 学习分享-- 05 例化与仿真

avatar
作者
筋斗云
阅读量:0

目录

一. 实验内容

  通过具体例程,学习vivado软件的下述功能:   1.例化:04节fifo核的使用   2.仿真:01节流水灯 

二. 例化

2.1 概论

依我看,例化其实就是C语言的函数调用。这样做方便整体代码修改,以及模块化编写程序。咱们就带着函数调用的思想去学习例化的语法规则就好。

2.2 例化框架

例化的大体框架如下:

引用的外部模块名字  此模块的新名字 (   .外部参数1					(对应的内部参数1),            .外部参数2					(对应的内部参数2),   		   .外部参数3					(对应的内部参数3),        .外部参数4					(对应的内部参数4),     ......   ......   .外部参数n					(对应的内部参数n)              //注意:最后一个参数的括号后面没有逗号 ); 

这是fifo核的例化。

fifo_generator_0 u_fifo_generator_0 (   .clk					(clk),            .srst					(~rst_n),   		   .din					(wr_data),        .wr_en				(wr_en),     .rd_en				(rd_en),     .dout					(rd_data),       .full					(full),       .empty				(empty)   ); 

外部模块名为fifo_generator_0,
一般的,新名字都是 “u_模块名” ,即u_fifo_generator_0

再来看看参数。

外部参数名内部参数名
clkclk
srst~rst_n
dinwr_data
doutrd_data

没有列举完哈,这几个参数比较典型,拉出来说说。


fifo核自带的时钟名字为clk,我们定义的时钟名为clk
fifo核自带的复位名字为srst,我们定义的复位名为rst_n
(但因为srst为高电平有效,rst_n为低电平有效,所以取反)
fifo核自带的写数据名字为din,我们定义的写数据为wr_data
fifo核自带的读数据名字为dout,我们定义的读数据为rd_data


由此可见,外部参数和内部参数的名字不要求一致,只要他们代表的意义一样即可。
但内部参数对应的数据类型一般都是wire类型,这点还希望大家注意一下。

同理,ILA例化也是一样的框架。

ila_0 u_ila_0 ( 	.clk             (clk), 	// input wire clk 	.probe0          (probe0) 	// input wire [26:0] probe0 ); 

值得一提的是,probe0参数有27位,但只是一个参数!
所以只占用一个外部参数名。

wire   [26:0]  probe0;  assign probe0[0] = clk;           assign probe0[1] = rst_n; assign probe0[2] = wr_rd_flag; assign probe0[3] = wr_en; assign probe0[4] = rd_en; assign probe0[12:5] = wr_data[7:0]; assign probe0[20:13] = rd_data[7:0]; assign probe0[21] = full; assign probe0[22] = empty; assign probe0[26:23] = cnt_dly[3:0]; 

三. 仿真

3.1 概论

选择仿真大致有两个情况:

  1. 不上电,先行验证代码的正确性
  2. 没有板子,只能通过仿真来进行观看结果

据我所知,一般在实验或者工程上电之前,都会进行仿真,一旦连仿真结果都是失败的错误的,那么也就不必去上电了。所以学会仿真是必要的。

3.2 建立仿真文件

我在01节仿真部分已经带着大家一起建立过仿真文件了,在此处就不耽误大家时间了,有需要的同学可以去01节看看喔。

FPGA学习分享–01 led流水灯的实现

3.3 编写仿真代码

仿真仿真,就是要模仿真的。真的具有什么,放出来的结果应该也具有什么特点。
所以,我们在仿真中需要实现
1.系统时钟的编写
2.复位信号的切换
3.结合具体情况的一些其他功能
当然,也要确保仿真模块的代码语法的正确性。下面我们就以01节仿真代码为例,来看看这一必要的仿真模块到底长什么样。

`timescale 1ns / 1ps     module tb_led_stream();  reg         sys_clk;        reg         sys_rst_n;      wire  [3:0] led;                initial begin         //初始化                         	sys_clk = 1'b0; 	sys_rst_n = 1'b0; 	#1 sys_rst_n = 1'b1; end   always #10 sys_clk = ~sys_clk;    //定义一个50M赫兹的时钟  led_stream u_led_stream(      //例化 .clk                  (sys_clk),         .rst_n                (sys_rst_n),        .led                  (led)    );  endmodule  

咱们一个部分一个部分的讲解:

  1. 单位时间:
`timescale 1ns / 1ps    

这个代表了在仿真模块中的单位时间为1ns,精度为1ps。
举个例子,"#10"就代表经过10ns.

  1. 仿真变量:
reg         sys_clk;        reg         sys_rst_n;     

我要实现时钟和复位信号,就必然要先定义他们。

  1. 初始化:
initial begin         //初始化                         	sys_clk = 1'b0; 	sys_rst_n = 1'b0; 	#1 sys_rst_n = 1'b1; end  

代码翻译如下:
最开始,时钟信号为低电平,复位信号为低电平,1ns之后复位信号拉高。
这和真正的板子是一致的。板子真正的复位信号,是上电瞬间为低电平有效,之后就拉高为1。时钟也是相似的道理(详见下一模块)。

  1. 时钟模块
always #10 sys_clk = ~sys_clk;    //定义一个50M赫兹的时钟 

先来讲讲真正的系统时钟。
时钟信号就是不断地拉高拉低,通过记录时钟信号的上升沿即可得到时间。那么一个上升沿是多少时间呢。这就和系统的晶振有关系了。博宸电子的ZYNQ7020DEV开发板采用的是50M的晶振,即1s震动50M次。也就是时钟信号1s翻转50M次。
在这里插入图片描述
也就是说,时钟信号每隔10ns都是前一个瞬间的取反。
在这里插入图片描述
在仿真模块中,10ns后取反是
#10 sys_clk = ~sys_clk;
而时钟信号是一直每隔10ns就取反,所以必然有always语句。

always #10 sys_clk = ~sys_clk;    //定义一个50M赫兹的时钟 
  1. 例化模块
    现在该把module模块例化到仿真模块中了,让仿真模块实现它的功能。
led_stream u_led_stream(      //例化 .clk                  (sys_clk),         .rst_n                (sys_rst_n),        .led                  (led)    ); 

led_stream是module模块的模块名,直接从module中复制过来就行。剩下的例化知识就和上面讲例化是相同的。大家可以自行理解。

3.4 启动仿真

点击vivado左边的Run Simulation后就可以看到仿真图啦!

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!