从零开始学习vivado——day 3 时序逻辑设计之计数器

发布时间:2023年12月21日

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

研一从零开始学习verilog!!!此时不学何时学!
第一次写博客,以此激励自己努力学习!
我跟的视频教程是b站的一个up主,小梅哥爱漂流。


一、一、vivado是什么?

vivado是一个编译平台,可运行verilog代码,并且进行模拟仿真。


二、计数器的设计

1.创建新的工程文件并编译代码

(1)定义端口

module led_flash(
    Clk,
    Reset_n,
    Led
);
    input Clk;//时钟
    input Reset_n;//复位端
    output reg Led;//灯
    reg [24:0]counter;//计数变量

(2)计数与Led的变化

    always@(posedge Clk or negedge Reset_n)//posedge Clk代表时钟的上升沿,negedge Reset_n代表复位端下降沿
    if(!Reset_n)   //!代表取非,即如果Reset_n为低电平,则counter归零
        counter <=0;//<=为非阻塞赋值,此处可以理解为等于
    else if(counter == 25000000-1)//要计数25000000次,则需要减1,因为清零也算一次
        counter <=0 ;//清零,计数从头开始
    else 
        counter <= counter + 1'd1   ;
    
    always@(posedge Clk or negedge Reset_n)//posedge Clk代表时钟的上升沿,negedge Reset_n代表复位端下降沿
    if(!Reset_n)    //!代表取非,Reset_n为低电平时,Led也为低电平
        Led <=0;
    else if(counter == 25000000-1)
        Led <= !Led;

首先,解读一下代码。
其实注释的都很清楚了,计数器是利用D触发器进行设计的,即在时钟上升沿的时候触发,or negedge Reset_n这段代码其实不加也可以,因为代码里面并没有让Reset_n变为高电平之后再变为低电平。对于counter变量,需要用到if ,else if,else,因为它有三种情况,复位端为低电平时肯定是清零的,因为计数器没有工作,还有当计数器计满的时候也要清零,否则counter会加1.
而Led可以直接利用取反符号达到亮灯和灭灯的目的,因为要设计在1s内,有500ms是亮灯状态,500ms是熄灯状态,即从Led亮的一瞬间开始计时,到下一次灯亮的时间,应该刚好是1s。
时钟的周期为20ns,所以500ms/20ns=25000000次,也就是说counter需要计数25000000之后,Led灯的状态才会翻转。而25000000换算成二进制,有25位,所以定义的时候需要加个[24:0]。
再一点,计数的时候,当counter达到25000000-1的时候,就需要清零和翻转了,因为从25000000-1到0也算一次运行过程,即总共也是计数了25000000次。
因为Led和counter的变化条件都一样,嫌麻烦的话可以利用begin end语句将Led和counter的变化括起来即可。

(3)代码整合

module led_flash(
   Clk,
   Reset_n,
   Led
);
   input Clk;
   input Reset_n;
   output reg Led;
   reg [24:0]counter;
   
   always@(posedge Clk or negedge Reset_n)//posedge Clk代表时钟的上升沿,negedge Reset_n代表复位端下降沿
   if(!Reset_n)   //!代表取非,即如果Reset_n为低电平,则counter归零
       counter <=0;//<=为非阻塞赋值,此处可以理解为等于
   else if(counter == 25000000-1)//要计数25000000次,则需要减1,因为清零也算一次
       counter <=0 ;//清零,计数从头开始
   else 
       counter <= counter + 1'd1   ;
   
   always@(posedge Clk or negedge Reset_n)//posedge Clk代表时钟的上升沿,negedge Reset_n代表复位端下降沿
   if(!Reset_n)    //!代表取非,Reset_n为低电平时,Led也为低电平
       Led <=0;
   else if(counter == 25000000-1)
       Led <= !Led;
//led和counter两个变量分开进行会好一点,如果要合在一起写的话,需要用begin end括起来
//
endmodule

2.创建激励文件

(1)时间尺度

`timescale 1ns/1ns

依旧默认1ns
(2)例化

module led_flash_tb;
    reg Clk;
    reg Reset_n;
    wire Led;//输出为wire型

    led_flash led_flash(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .Led(Led)
);

(3)时钟信号编写

    initial Clk =1;
    always#10 Clk=!Clk;//!是逻辑取反,`是按位取反,对于单信号来说是等价的   整句话就是延时10ns之后,Clk总是取反

10ns让时钟翻转一次即可,因为要设计的计数器是1s的间隔,所以也可以把是时钟的周期延长一点,这个都可以自己修改。
(4)复位端信号编写

    initial begin 
     Reset_n=0;//初始复位端为低电平
     #101;//避开时钟上升沿
     Reset_n=1;//101ns之后复位端变高电平
     #1000000000;
     $stop;//1s之后停止
    end

复位端初始为低电平,Led处于清零状态,错开时钟上升沿之后,将复位端变为高电平,为防止一直运行下去,可以使用stop停止。
(4)代码整合

`timescale 1ns/1ns

module led_flash_tb;
    reg Clk;
    reg Reset_n;
    wire Led;//输出为wire型

    led_flash led_flash(
    .Clk(Clk),
    .Reset_n(Reset_n),
    .Led(Led)
);
 
    initial Clk =1;
    always#10 Clk=!Clk;//!是逻辑取反,`是按位取反,对于单信号来说是等价的   整句话就是延时10ns之后,Clk总是取反
    
    initial begin 
     Reset_n=0;//初始复位端为低电平
     #101;//避开时钟上升沿
     Reset_n=1;//101ns之后复位端变高电平
     #1000000000;
     $stop;//1s之后停止
    end



endmodule

3.仿真结果展示

在这里插入图片描述

可以看出,Led第一次翻转到第二次翻转的间隔为500ms,成功完成设计。


总结

今天学习的是基于D触发器设计的计数器。
新学的语法有:

①上升沿,下降沿

always@(posedge Clk or negedge Reset_n)

poseedge是沿上升沿触发,negedge是沿下降沿触发;
②时钟信号的产生

    initial Clk =1;
    always#10 Clk=!Clk;

③if else内嵌一个if else

    if(!Reset_n)   
        counter <=0;
    else if(counter == 25000000-1)
        counter <=0 ;
    else 
        counter <= counter + 1'd1   ;

④<=为非阻塞赋值,目前还没搞清楚啥意思,后面再说。。。

文章来源:https://blog.csdn.net/weixin_46087443/article/details/132778069
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。