流水灯water_led
实验目的
在50Mhz的时钟控制下实现4个led灯每隔0.5s依次并循环点亮。
实验框图与波形
实验中需要使用到计数器cnt,对于计数器的取值参考系统时钟和实验目的
50Mhz:1s有50_000_000个时钟周期,则0.5s含25_000_000个周期。计数器最大值CNT_MAX设置为24_999_999。
每当计数器计数到最大值-1时,led_flag拉高一个时钟周期。
led_out设置为4为位宽,每一位代表一个led小灯,同时低电平点亮,所以流水灯呈现的效果为:初始状态1111→1110→1101→1011→0111,依次循环。
实验代码
module water_led
#(
parameter CNT_MAX=22'd24_999_999
)
(
input wire sys_clk,
input wire sys_rst_n,
output reg [3:0] led_out
);
reg [21:0] cnt;
reg led_flag;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
cnt<=22'd0;
else if(cnt==CNT_MAX)
cnt<=22'd0;
else cnt<=cnt+22'd1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
led_flag<=1'b0;
else if(cnt==CNT_MAX-22'd1)
led_flag<=1'b1;
else led_flag<=1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n==1'b0)
led_out<=4'b1110;
else if((led_out==4'b0111)&&(led_flag==1'b1))
led_out<=4'b1110;
else if(led_flag==1'b1)
led_out<=((led_out<<1)+4'd1);
//左移自动补零,即1110变成1100,为满足要求每次加1,实现目的
else led_out<=led_out;
endmodule
always语句
先写初始状态,再写归零条件,再写赋值
降频的利用
这里关于降频的概念和知识点在之前的分频与降频中能找到,这里强调作用效果。
在一开始实验代码中对于led_out的赋值使用的是降频,下面代码使用分频(即使用分频后的标志信号作为时钟源)
//时钟应采用系统时钟,避免在高速电路中出现问题
always@(posedge led_flag or negedge sys_rst_n)
if(sys_rst_n==1'b0)
led_out<=4'b1110;
else if(led_flag==1'b1)
if(led_out==4'b0111)
led_out<=4'b1110;
else
//使用移位运算符
led_out<=((led_out<<1)+4'd1);//左移一位
else led_out<=led_out;
可以看到同样满足实验结果,不过当模块作用于高速电路中往往会出现意料之外的错误。
移位运算符
Verilog中的移位可以分为:算数移位、逻辑移位以及用代码实现的循环移位
-
逻辑右移(>>):1个操作数向右移位,产生的空位用0填充;
-
逻辑左移(<<):1个操作数向左移位,产生的空位用0填充;
-
算术右移(>>>):1个操作数向右移位。如果是无符号数,则产生的空位用0填充;有符号数则用其符号位填充;
-
算数左移(<<<):1个操作数向左移位,产生的空位用0填充;
另外,操作数左移n位相当于乘以2的n次幂(乘法),操作数右移n位相当于除以2的n次幂(除法)
循环移位
正如代码中实现的一样,如果使用简单的左移移位运算符,结果将是这样1110→1100→1000→0000,如何实现这种循环移位1110→1101→1011→0111,只需要在每次移位后再加上1’d1
左循环移位
led_out<=((led_out<<1)+4'd1);
在实验中给出了第二种解决移位补零的方案,
不直接输出led_out,而是输出led_out_reg,将led_out_reg初始状态设置为0001,将该变量直接使用左移移位运算符:0001→0010→0100→1000。再回到初值,这样实现循环,然后led_out对其取反即可。实验代码不复杂就没单独给出了,大家可以自己尝试。
实现循环移位的方式还有很多
led_out<={led_out[3:0],led_out[7]};
仿真代码
`timescale 1ns/1ns
module tb_water_led();
reg sys_clk;
reg sys_rst_n;
wire [3:0] led_out;
initial
begin
sys_clk=1'b0;
sys_rst_n<=1'b0;
#20
sys_rst_n<=1'b1;
end
always #10 sys_clk=~sys_clk;
water_led
#(
.CNT_MAX(25'd24)
)
water_led_inst
(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.led_out(led_out)
);
endmodule