FPGA学习日志——流水灯water_led

本文介绍了使用Verilog进行流水灯控制器的设计,实验目标是在50MHz时钟下每0.5s依次点亮4个LED。通过计数器和降频技术实现LED灯的循环点亮效果。文章详细解析了always语句的使用,计数器的降频以及移位运算符(包括循环移位)的应用,并提供了仿真代码进行验证。
摘要由CSDN通过智能技术生成

流水灯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. 逻辑右移(>>):1个操作数向右移位,产生的空位用0填充;

  2. 逻辑左移(<<):1个操作数向左移位,产生的空位用0填充;

  3. 算术右移(>>>):1个操作数向右移位。如果是无符号数,则产生的空位用0填充;有符号数则用其符号位填充;

  4. 算数左移(<<<):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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chendy_00

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值