【IC设计】Verilog线性序列机点灯案例(二)(小梅哥课程)

案例和代码来自小梅哥课程,本人仅对知识点做做笔记,如有学习需要请支持官方正版。

该系列目录:

Verilog线性序列机点灯案例(一)
Verilog线性序列机点灯案例(二)
Verilog线性序列机点灯案例(三)
Verilog线性序列机点灯案例(四)

设计目标

我们的FPGA的时钟频率为50MHz,即每个周期20ns。
因此,在该时钟下时间和周期数的对应关系为:

持续时间对应周期数
0.25s12,500,000 cycles
0.5s25,000,000 cycles
0.75s37,500,000 cycles
1s50,000,000 cycles

我们的目标是让LED以**【亮0.25秒->灭0.5秒->亮0.75秒->灭1秒】**的规律,持续循环闪烁。

设计思路

在这里插入图片描述
为了完成这样的规律性闪烁,需要一个计数器,计数满2.5秒归零,即:当上升沿采样到125,000,000-1时,计数器归零。
然后,led灯根据当前计数器的数值,设置led的亮灭,图中已经标注了led跳变时的counter数值。下面直接上代码

RTL 及 Testbench

led_ctrl1.v 是RTL代码

module led_ctrl1(
    clk,
    rst_n,
    led_out
);
    input clk;
    input rst_n;
    output reg led_out;
    
    reg [26:0] counter;
    
    //第一个always负责counter计数器的逻辑
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            counter <= 0;
        end else if(counter == 125_000_000-1) begin
            counter <= 0;
        end else begin
            counter <= counter + 1;
        end
    end
    
    //第二个always负责led_out闪烁的逻辑
    //亮0.25s->灭0.5秒->亮0.75秒->灭1秒
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            led_out <= 0;
        end else if(counter == 0) begin
            led_out <= 1;
        end else if(counter == 12_500_000) begin
            led_out <= 0;
        end else if(counter == 37_500_000) begin
            led_out <= 1;
        end else if(counter == 75_000_000) begin
            led_out <= 0;
        end 
    end
    
endmodule

tb_led_ctrl1.v是testbench代码

`timescale 1ns / 1ns

module tb_led_ctrl1();
    reg clk;
    reg rst_n;
    wire led_out;
    initial clk = 1;
    always #10 clk = ~clk;
    
    led_ctrl1 led_ctrl1_inst0(
        .clk(clk),
        .rst_n(rst_n),
        .led_out(led_out)
    );
    
    initial begin
        rst_n = 0;
        #201;
        rst_n = 1;
        #250_000_000
        $stop;
    end
endmodule

仿真结果

从图中黄色marker标注下的时间间隔可以看出,仿真结果和预期目标一致。
在这里插入图片描述

存在的问题?

如果到此就结束了,那么案例(二)和(一)并没有多大区别。
实际上,按照刚才的实现方式可以完成功能,但存在如下问题:

  1. 仿真时间过长
    为了在实际上板时观察到led闪烁的效果,我们的闪烁都是秒级的,vivado仿真一秒时间几乎需要十几秒才能完成,能否减少仿真时间,不影响功能?
  2. 可读性较差
    在我们的代码中0.25s,0.75s这些时间尺度都是用具体的计数器的周期数来表示的,数字太大,不好理解,如何解决?

针对以上问题,观察我们的需求是让LED以**【亮0.25秒->灭0.5秒->亮0.75秒->灭1秒】**循环,那么最基本的单位可以视为0.25秒,我们可以使用两个计数器,第一个计数器计数到0.25秒(12500_000 - 1个cycles)时第二个计数器加1。按照这个思路,我们在设置led时只需要关注好第二个计数器即可,1亮,2、3灭,4、5、6亮,7、8、9、10灭,显然可读性是比0亮,12_500_000灭好多了。
此外,针对仿真时间过长的问题,我们可以在RTL模块中定义一个parameter时间单元,而在testbench仿真中重新缩小该时间单元1000倍,实际上板时只会烧录RTL模块,这样既节省了仿真时间,又不影响功能。
在这里插入图片描述

改善后的代码

RTL代码

module led_ctrl1(
    clk,
    rst_n,
    led_out
);
    input clk;
    input rst_n;
    output reg led_out;
    
    parameter MCNT = 12500_000 - 1;
    reg [26:0] counter0;
    
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            counter0 <= 0 ;
        else if(counter0 == MCNT)
            counter0 <= 0;
        else
            counter0 <= counter0 + 1'd1;
    end
    
    reg [3:0] counter1;
    always@(posedge clk or negedge rst_n) begin
        if(!rst_n)
            counter1 <= 0 ;
        else if(counter0 == MCNT) begin
           if(counter1 == 9)
              counter1 <= 0;
           else
              counter1 <= counter1 + 1'd1;
        end
        else
            counter1 <= counter1;
    end
    
    always@(posedge clk or negedge rst_n)
    if(!rst_n)
        led_out <= 0;
    else begin
        case(counter1)
            0:led_out <= 1'd1;
            1:led_out <= 1'd0;
            2:led_out <= 1'd0;
            3:led_out <= 1'd1;
            4:led_out <= 1'd1;
            5:led_out <= 1'd1;
            6:led_out <= 1'd0;
            7:led_out <= 1'd0;
            8:led_out <= 1'd0;
            9:led_out <= 1'd0;
            default:led_out <= led_out;
        endcase
    end
    
endmodule

testbench代码

`timescale 1ns / 1ns

module tb_led_ctrl1();
    reg clk;
    reg rst_n;
    wire led_out;
    initial clk = 1;
    always #10 clk = ~clk;
    
    led_ctrl1 led_ctrl1_inst0(
        .clk(clk),
        .rst_n(rst_n),
        .led_out(led_out)
    );
    defparam led_ctrl1.MCNT = 12500 - 1; 
    
    initial begin
        rst_n = 0;
        #201;
        rst_n = 1;
        #20_000_000;
        $stop;
    end
endmodule

仿真结果

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

农民真快落

我琢磨着也没人给我打赏呀。。

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

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

打赏作者

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

抵扣说明:

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

余额充值