模块例化:
前言:
在实际的开发过程中,往往会有许多不同功能的模块,那么如何将他们连在一起使用呢?这里的话就需要进行模块例化,将所需要使用的模块通过例化,连线在一起,最终去实现功能的一个集合。本文将使用之前写过的流水灯和按键来进行演示,完成一个通过按键来控制流水灯的小实验。
按键:
上一篇文章中已经讲解了按键消抖的代码,以及如何将它封装成IP核,那这里的演示我将直接使用封装好的按键IP
双击打开后,出现下图这个界面,这里的参数是之前自定义的20ms的按键消抖时间,有其它需求的可以在这里进行修改。由于后面将以仿真展示,那我这里将缩小至100
连续点击两个ok确认后,出现下图,我这里的话选择的是Global,方便待会展示一下画布的使用,这里点击Generate生成
生成好后左侧会出现IP的名称,然后选择IP
进去后找到需要使用的IP下拉,选择第一个文件下拉后会出现两个文本,一个是VHDL,另一个是verilog,我们这里使用的是verilog语言,就选择第二个,以veo后缀结尾的文件,双击打开
打开后会出现一个文件,并且是只读模式,使用的用户是无法修改的,需要修改IP核的话要找到当时封装的工程文件,在里面进行修改后再重新生成IP核。这里复制红框里的代码
将刚才复制的代码粘贴到需要使用按键的模块里去进行使用
调用IP核例化的步骤大概就是这样,通常的代码例化也是将模块名和括号里定义的端口一起复制到其它模块下面,然后进行连线处理等。
按键控制流水灯verilog代码:
module test(
input wire sysclk ,//系统时钟
input wire rst_n ,//复位
input wire key ,//按键,由于是外部输入信号,因此需要引出来
output reg [3:0] led
);
wire key_flag;//按键按下一次的有效信号,需要引出来使用
reg en ;//使能信号,给一个长信号,由按键控制高低
parameter delay = 50_000_000;//1s内部晶振次数
reg [26:0] cnt;//计数器,记录是否达到一秒
//en使能信号
always @(posedge sysclk)
if(!rst_n)
en <= 0;
else if(key_flag == 1)//按键按下一次有效信号
en <= ~en;
else
en <= en;
//1s计时器——按键按下开始计时
always @(posedge sysclk)
if(!rst_n)
cnt <= 0;
else if(en == 1 )begin
if(cnt == delay - 1)
cnt <= 0;
else
cnt <= cnt + 1;
end
else
cnt <= 0;
//流水灯灯赋值
always @(posedge sysclk)
if(!rst_n)
led <= 4'b0001;
else if(en == 1 && cnt == delay - 1)
led <= {led[0],led[3:1]};
else
led <= led;
key_flag_0 key_flag_0_u (
.clk(sysclk), // input wire clk
.rst_n(rst_n), // input wire rst_n
.key(key), // input wire key
.key_flag(key_flag) // output wire key_flag
);
endmodule
仿真代码:
`timescale 1ns / 1ps
module text_tb();
reg sysclk ;
reg rst_n ;
reg key ;
wire [3:0] led ;
initial begin
sysclk = 0;
rst_n = 0;
key = 1;
#101
rst_n = 1;
#20
key = 0;
#2000
key = 1;
#2000000
key = 0;
end
always #10 sysclk = ~sysclk;
test test_u(
. sysclk (sysclk),//系统时钟
. rst_n (rst_n ),//复位
. key (key ),
. led (led )
);
endmodule
仿真结果:
按键延时:1_000_000也就是20ms,缩短至100也就是2000ns
led灯1s:50_000_000,缩短至500也就是10000ns
在经过101ns过后,复位信号拉高,又过了20ns过后,按键按下,2000ns过后松开,此时采集到按键按下有效,key_flag信号拉高,en使能信号也随之拉高,cnt计时器开始工作
当cnt计时器达到499时,代表1s时间到了,led灯开始跳变
led灯呈现一个流动的方式,每到一秒就发生跳变
当时间达到2_000_000ns时,按键再次按下,在维持了一个2000ns的消抖时间后,key_flag再次拉高,代表按下有效,en使能信号随之拉低。cnt计时器停止工作,led也停止跳变。
画布:
前言:
在FPGA里面除了模块例化可以将不同功能的模块集成在一起使用以外,还有另外一种方法就是画布。模块例化在连接较少模块的时候比较方便,但是一旦功能模块多了起来,都需要互相连线的时候,那时使用例化的话就会非常麻烦。此时,就可以考虑使用画布这种方法了。接下来我将继续以按键控制流水灯去进行展示。
流水灯代码:
在这里的流水灯代码会跟上面的代码有一些不一样,主要是端口的定义不一样,还有就是里面的例化没有了,其它都是一样的。
module test(
input wire sysclk ,//系统时钟
input wire rst_n ,//复位
input wire key_flag ,//按键,由于是外部输入信号,因此需要引出来
output reg [3:0] led
);
reg en ;//使能信号,给一个长信号,由按键控制高低
parameter delay = 50_0;//1s内部晶振次数
reg [26:0] cnt;//计数器,记录是否达到一秒
//en使能信号
always @(posedge sysclk)
if(!rst_n)
en <= 0;
else if(key_flag == 1)//按键按下一次有效信号
en <= ~en;
else
en <= en;
//1s计时器——按键按下开始计时
always @(posedge sysclk)
if(!rst_n)
cnt <= 0;
else if(en == 1 )begin
if(cnt == delay - 1)
cnt <= 0;
else
cnt <= cnt + 1;
end
else
cnt <= 0;
//流水灯灯赋值
always @(posedge sysclk)
if(!rst_n)
led <= 4'b0001;
else if(en == 1 && cnt == delay - 1)
led <= {led[0],led[3:1]};
else
led <= led;
endmodule
创建画布:
在最左侧选择创建画布
单机后出现下图所示,这里的话可以去修改成一个自己想要的名称,然后点击ok,等待创建
创建好后出现下图界面
模块互联:
将1s流水灯的代码拖入到右侧,要注意的是这里的代码里面不能存在模块例化的痕迹,并且要注意定义好端口,方便和后面拖入的其它模块做互联
接下来点击空白处,鼠标右键,选择添加IP
上方搜索框搜索自定义并且已经加载在该工程里的IP核(不知道如何新建或者加载自定义IP核的可以去看看我的上一篇文章)
双击导入按键模块后如下图所示,接下来进行模块连线
鼠标左键放置到引脚上去,点击一下变黄后,快捷键ctrl+t,引出时钟线,下面的复位和按键都需要引出,输出的led灯也需要引出
引出完后如下图所示,接下来进行连线
鼠标左键点击引脚后往外拖,可以引出条线,将其连接到对应的名称上去,完成后如下图所示
完成后先点击上方的刷新一下布局,然后点击左边的检查连线错误
出现下图代表连线没有问题
导出和打包:
选择完后点击Generate,等待加载完
选择第二个,点击OK,等待加载完成
最后生成完后,会出现一个蓝色的文件,画布会出现在它的下方,此时将它置顶,就可以用它去跑综合和生成bit流上板了。双击打开这个文件,其实能够发现就是生成了一个顶层文件里面包含了画布里面做互联的模块。
总结:
本章的话主要是去学习了在遇到模块需要互联时用到的两种方法,例化一般适用于模块较少的情况下,画布一般适用于模块较多的情况,当然也是根据每个人的一个喜好去选择最适合自己的方法。有错的地方希望能够帮忙指出,谢谢!