计数器
1、设计定义:
- 实验现象:LED在计数器的控制下,每500ms状态翻转一次,即LED亮灭的一个周期为1s。
- FPGA所用的时钟为50mHZ或100mHZ,频率较高,对应的周期为20ns或10ns,而要想得到500ms的时间,则需要对系统时钟进行计数。
-LED每500ms翻转一次,所以让计数器每记满500ms就使能一次,LED翻转,500ms记完后,计数器清零,之后重新计数。- 500ms=500_000_000ns / 20=25_000_000 个时钟周期。
2、设计输入:
//使用时序逻辑来进行设计
//module 模块名(端口列表:时钟信号(50M),复位信号(低电平有效),输出(led))
module counter(Clk50M,Res_n,led);
//对端口属性进行定义
input Clk50M;//系统时钟50M
input Res_n;//全局复位,_n表示低电平复位
output reg led;//led作为寄存器类型的输出,led低电平有效
//一共需要计数25_000_000次,为了表达这个数,将其转化为16进制位17D7840,则需要4*6+1=25位的寄存器才能完全表达
//最高位为1,所以只需要1位来表示
reg [24:0]cnt;
//逻辑描述,计数器的计数进程
always@(posedge Clk50M or negedge Res_n)//时序逻辑常见写法,分别以时钟信号的上升沿和复位信号的下降沿为敏感信号
if(Res_n == 1'b0)//开发板复位键默认高电平,当按键按下时变为低电平,即判断当复位键按下时
cnt <= 25'd0;//时序逻辑中的赋值号"<=",复位键按下,计数器cnt赋值为25位宽的十进制的0
else if(cnt == 25'd24_999_999)//实际计数是从0开始的,所以从0到24_999_999,此时计数记满,将计数器清零
cnt <= 25'd0;
else
cnt <= cnt +1'b1;//在没有复位或者记满的情况下,每来一个时钟的上升沿,计数器自加1
//led输出控制进程
always@(posedge Clk50M or negedge Res_n)
if(Res_n == 1'b0)//同上
led <= 1'b1;//复位时,LED输出高电平
else if(cnt == 25'd24_999_999)
led <= ~led;//LED翻转,"~":按位取反,当下一次记满500后,再次取反
else
led <= led;//在没有复位或者记满是,保持现有状态,这两行可以省略
endmodule
3、分析与综合
//声明仿真步进和精度
`timescale 1ns/1ns
`define clock_period 20//为了让代码具有通用性,所以定义一个时钟周期参数。表明时钟周期为20ns
module counter_tb;
//声明激励信号源
reg clk;//故意将激励信号源写成小写,好和下面端口名区分
reg res_n;
wire led;
//声明端口名
counter counter0(
.Clk50M(clk),
.Res_n(res_n),
.led(led)
);
initial clk = 1;//习惯性让时钟在一开始位于1(有好处),位于0也可以
//系统时钟为50M,则周期为20ns,让clk每延时10ns翻转一次,即得到20ns的时钟信号
always #(`clock_period/2)//"`"表示调用一个宏定义参数,同开头的"`"
clk = ~clk;
//产生复位信号
initial begin
res_n = 1'b0;//低电平,系统处于复位状态
#(`clock_period * 200);//延时200个系统周期
res_n = 1'b1;//复位释放
//没有控制逻辑,只有时钟和复位信号,其他由系统内部自己运行,所以只需要让系统运行一段时间后停下来即可
#2000000000;//设置的LED亮灭一次为1s,所以要让仿真的时间大于1s才能看清实验现象
$stop;
end
endmodule
- 如果出现编译没有问题,而不能仿真的情况,再通过仿真报错文件修改quarters的代码后,记得重新编译后再进行仿真操作。
- 后仿真会很慢,为了更早的观察到实验现象,可以将计数次数减小。待到后仿真无误之后再将其更正。但要记得重新进行全编译。
4、电路模型-RTL视图
-
左侧窗口:
-
- Analysis & Synthesis —> Netlist Viewer ----> RT Viewer
- Analysis & Synthesis —> Netlist Viewer ----> RT Viewer