假设要实现让8个LED灯以每个0.5s的速率进行循环闪烁,首先进行基本程序的编写。
以counter变量作为计数器,Reset_n为低电平复位信号,8位Led为输出,时钟周期为20ns,0.5s/20ns等于25000000,当计数器达,25000000-1=24999999时下一个灯开始闪烁,将该值转化为2进制如图所示,共有25位,因此counter为25位。
假设Led灯高电位点亮,低电位熄灭,8位Led灯从高位到低位依次点亮,则Led的变化情况应该是10000000(7),01000000(6),00100000(5),…………00000100(2),00000010(1),00000001(0),10000000(7),之后循环往复。其中,(7)到(0)的转换可以用右移位实现,从(0)到(7)的转换可以用if语句实现,编写源程序如下。
module led_run(
input Clk,
input Reset_n,
output [7:0]Led
);
reg [24:0]counter;
// parameter MCNT= 25'd24999999; //定义可更改的参数
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
counter <= 0;
else if(counter == 25'd24999999)
// else if(counter == 25'd24999) //仿真使用的计数器
counter <= 0;
else
counter <= counter + 1'b1;
always@(posedge Clk or negedge Reset_n)
if(!Reset_n)
Led <= 8'b0000_0001;
else if(counter == 25'd24999999)begin
// else if(counter == 25'd24999)begin //仿真使用的计数器
if(Led == 8'b1000_0000)
Led <= 8'b0000_0001;
else
Led <= Led << 1;
end
else
Led <= Led;
endmodule
按照流程,进行testbench文件的编写。源文件中全部灯亮一遍需要4s,对于一些配置不高的电脑来说,在vivado中的仿真时间可能达到几分钟,因此为了节省时间,在仿真时将计数器归零时的值设为24999,仿真时间体现在上述源文件代码中的注释行。
实现改变仿真仿真时间的方式有两种:一是事先准备好两行代码注释掉并不需要的一行,二是定义参数MCNT,根据仿真或者板机验证的需求更改数值。
编写测试文件如下所示
`timescale 1ns/1ns
module led_run_tb;
reg Clk;
reg Reset_n;
wire [7:0]Led;
led_run led_run_inst0(
.Clk(Clk),
.Reset_n(Reset_n),
.Led(Led)
);
initial Clk = 1;
always #10 Clk = ~Clk;
initial begin
Reset_n = 0;
#201;
Reset_n = 1;
#40000000;
$stop;
end
endmodule
可以看到,这种写法需要在仿真文件与设计源文件中反复切换并且更改计数器的值,为了简化操作,我们可以在testbench中使用另一种写法。
`timescale 1ns/1ns
module led_run_tb;
reg Clk;
reg Reset_n;
wire [7:0]Led;
led_run2
#(
.MCNT(2499)
)
led_run_inst0(
.Clk(Clk),
.Reset_n(Reset_n),
.Led(Led)
);
initial Clk = 1;
always #10 Clk = ~Clk;
initial begin
Reset_n = 0;
#201;
Reset_n = 1;
#40000000;
$stop;
end
endmodule
在例化模块的过程中,在被例化模块后添加 #( .MCNT(2499)) 其中先声明要改变的变量名称,在之后的括号中标注出要更改为的值,这样就省去了临时更改数值的麻烦。