FPGA小白养成记--流水灯实验

FPGA小白入门学习之流水灯实验

开始

本人是一个FPGA小白,因为兴趣从而开始接触FPGA,我将会把我每次的实验过程和心得分享给大家,希望一起进步,如果有什么错误的地方,还希望批评指正。
下面是我的第一次实验:LED流水灯实验
使用软件:VIVADO

整体思路与方案

LED流水灯闪烁实验:
要求:四个LED灯轮流闪烁,每次间隔0.5S
思路:首先要实现LED灯轮流闪烁,则当第一个0.5S到来时,LED0开始闪烁而其它灯灭;当第二0.5S到来时,LED1开始闪烁,以此类推。
那么我们要如何才能知道时间过了0.5S呢?我们采用计数器计数的方式,当计数器计到某个值时,代表过了0.5S的时间;那么下一个问题就是:计数到多少呢?首先我们要了解板载的时钟频率为50M,又因为公式f=1/T,因此一个周期所耗时间则是50M的倒数,即20ns;至此,我们用0.5S除以20ns就是所需计数的值,即25000000。当计数器计到25000000时,代表过了0.5S。但是,根据我们之前的思路,当第一个0.5S时,LED0闪烁,第二个0.5S时LED1闪烁,以此类推,因此除了计数器,我们还需要定义一个变量,这个变量可以表示过了几个0.5S,从而方便进行判定。因为一整个循环有4个0.5S,因此我们设定一个两位的变量CNT1。总体来说,首先计数器计数,计到25000000时归零,同时CNT1+1,根据CNT1的值,我们进行LED灯闪烁的判定。
至此,大致思路已经完成。
实现:首先我们进行端口的定义,以及变量类型的定义。接着,我们用always语句来表示第一个部分:计数器。每当时钟的上升沿到来或者复位的下降沿到来时触发下列语句,首先当复位时,计数器归零;其次,当计数器计到25000000时,计数器归零;最后,若即没有复位也没有计满,则每次+1,也就是
正常的计数。接下来,我们继续用always语句来表示第二个部分:CNT1,按照与第一部分相似的思路进行。最后,我们用多个always语句来进行LED闪烁的判定,以LED0举例,当复位时,LED0的值为0,也就是灯灭,而当CNT1的值为0时,也就是第一个0.5S内,LED0的值为1,也就是灯亮,最后,若
既没有复位,也不再第一个0.5S的范围内,LED0的值为0。按照这个思路,完成接下来的所有LED灯闪烁的判定。
模板:本次LED灯闪烁的模板可以大致分为三个部分:1.计数器部分,这决定了时间间隔的长度。2.CNT1(名字任意取,不唯一)部分,这显示出过了几个间隔
的时长。3.灯闪烁的判定,通过CNT1的值来进行LED灯闪烁的判定。
最后,实现灯闪烁的方法多种多样,并不唯一,这只是其中一种。

代码部分

首先附上我的源码(再次声明,方法不唯一,这甚至不是最简单的方法,但是确实可以达到要求的方法。)
module led(
input sys_clk,
input sys_rst_n,
output reg led0,
output reg led1,
output reg led2,
output reg led3
);
reg [24:0] cnt;
reg [2:0] cnt1;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n0)
cnt<=0;
else if(cnt
25’d249)
cnt<=0;
else
cnt<=cnt+1;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n0)
cnt1<=0;
else if(cnt
25’d249) begin
if (cnt1<3’d3)
cnt1<=cnt1+1;
else
cnt1<=0;
end
end

always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n0)
led0<=0;
//else if(cnt1
3’d0&&cnt25’d25_000_000) 因为要仿真,为了更直观,首先将这部分注释掉,正式调试的时候再用
else if(cnt1
3’d0)
led0<=1;
else
led0<=0;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n0)
led1<=0;
//else if(cnt1
3’d1&&cnt25’d25_000_000) 与上面是一样的目的
else if(cnt1
3’d1)
led1<=1;
else
led1<=0;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n0)
led2<=0;
//else if(cnt1
3’d2&&cnt25’d25_000_000)
else if(cnt1
3’d2)
led2<=1;
else
led2<=0;
end

always @(posedge sys_clk or negedge sys_rst_n) begin
if(sys_rst_n0)
led3<=0;
//else if(cnt1
3’d3&&cnt25’d25_000_000) 同上
else if(cnt1
3’d3)
led3<=1;
else
led3<=0;
end
endmodule

接下来是testbench代码:
`timescale 1ns/1ps
module tb_led();
reg sys_clk;
reg sys_rst_n;
wire led0;
wire led1;
wire led2;
wire led3;
initial begin
sys_clk=1’b0;
sys_rst_n=1’b0;
#200
sys_rst_n=1’b1;
end

always #10 sys_clk=~sys_clk;
led u_led(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.led0 (led0),
.led1 (led1),
.led2 (led2),
.led3 (led3)
);
endmodule

最后是约束代码:
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
set_property -dict {PACKAGE_PIN XX(因为不同板子不唯一,因此不显示出来) IOSTANDARD LVCMOS33} [get_ports sys_clk]
set_property -dict {PACKAGE_PIN XX IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN XX IOSTANDARD LVCMOS33} [get_ports {led0}]
set_property -dict {PACKAGE_PIN XX IOSTANDARD LVCMOS33} [get_ports {led1}]
set_property -dict {PACKAGE_PIN XX IOSTANDARD LVCMOS33} [get_ports {led2}]
set_property -dict {PACKAGE_PIN XX IOSTANDARD LVCMOS33} [get_ports {led3}]

最后改一下计数器的值,下载到板子上,实验成功!

调试

调试过程确实花了我很长时间(菜鸡落泪),但是我也收获颇丰。
再写的时候,总觉得自己写的一定没有问题,但是真实仿真出来的波形,却告诉你你的逻辑纰漏非常之多(啪啪打脸)。
我认为最直观的还是看波形,看波形就很容易看出问题,从而反推回去,比如我的cnt1的波形全是0,说明我哪一步写错了,导致cnt从来没有进行cnt1<=cnt1+1这个过程,反过去看,就很方便。
总之大概这次的流水灯实验就是这样,我其实很想写我失败的过程,但是因为没有保存图或者代码所以不好描述,因此这次就算了,下次再做。
谢谢大家观看,我们一起进步!

猜你喜欢

转载自blog.csdn.net/m0_49256296/article/details/107623022