提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
研一从零开始学习verilog!!!此时不学何时学!
第一次写博客,以此激励自己努力学习!
我跟的视频教程是b站的一个up主,小梅哥爱漂流。
vivado是一个编译平台,可运行verilog代码,并且进行模拟仿真。
(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
(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
可以看出,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 ;
④<=为非阻塞赋值,目前还没搞清楚啥意思,后面再说。。。