基本理论
计数是一种最简单基本的运算,计数器就是实现这种运算的逻辑电路,计数器在数字 系统中主要是对脉冲的个数进行计数,以实现测量、计数和控制的功能,同时兼有分频功 能。计数器在数字系统中应用广泛,如在电子计算机的控制器中对指令地址进行计数,以 便顺序取出下一条指令,在运算器中作乘法、除法运算时记下加法、减法次数,又如在数 字仪器中对脉冲的计数等等。 计数器也是在 FPGA 设计中最常用的一种时序逻辑电路,根据计数器的计数值我们可 以精确的计算出 FPGA 内部各种信号之间的时间关系,每个信号何时拉高、何时拉低、拉 高多久、拉低多久都可以由计数器实现精确的控制。而让计数器计数的是由外部晶振产生 的时钟,所以可以比较精准的控制具体需要计数的时间。计数器一般都是从 0 开始计数, 计数到我们需要的值或者计数满溢出后清零,并可以进行不断的循环,3 位数的十进制计 数器最大可以计数到 999,4 位数的最大可以计数到 9999;3 位数的二进制计数器最大可以 计数到 111(7),4 位数的最大可以计数到 1111(15)。
实例及验证
实例:使用计数器1s时间间隔,让led灯闪烁一次
模块框图
其中计数器计数的计算过程:
系统时钟频率为:f=50Mhz=5*10^7hz
系统的时钟周期为;T=1/f=2*10^-8s=20ns
1s需要多少时钟周期1s/20ns=5*10^7
所以,计数器从0开始所以需要计数(5*10^7)-1次才是1s。
波形绘制
方式一:计数到最大值二分之一减一,然后给led_out取反
方式二:由于是计数到最大值二分之一减一时led_out取反,所以最大值可以设置为(5*10^7)/2-1、
方式三:引入脉冲标志信号cnt_flag,当计数到二分之一最大值减一时产生一个周期的高脉冲信号,led_out在检测到cnt_flag拉高后产生一次翻转。
方式一代码实现(不带标志信号)
module counter
#(
parameter CNT_MAX = 25'd24_999_999 )
(
input wire sys_clk , //系统时钟50MHz
input wire sys_rst_n , //全局复位
output reg led_out //输出控制led灯
);
reg [24:0] cnt; //经计算得需要25位宽的寄存器才够500ms
//cnt:计数器计数,当计数到CNT_MAX的值时清零
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'b0;
else if(cnt == CNT_MAX)
cnt <= 25'b0;
else
cnt <= cnt + 1'b1;
//led_out:输出控制一个LED灯,每当计数满标志信号有效时取反
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out <= 1'b0;
else if(cnt == CNT_MAX)
led_out <= ~led_out;
endmodule
方式一的RTL视图(不带标志信号)
如图最左边为一个加法器ADDER,加法器输入端为寄存器反馈值和1,输出接到了选择器MUX2_1输入,选择器输入为加法器输入,选择信号为比较器输出,当计数到最大值,则选择器输出为DATAB,起到了计数值清零的作用,与此同时触发器led_out取反输出一次。
方式二代码实现(带标志信号)
module counter
#(
parameter CNT_MAX = 25'd24_999_999
)
(
input wire sys_clk ,
input wire sys_rst_n,
output reg led_out //在非阻塞赋值只能用于对寄存器类型变量进行赋值
);
reg [24:0] cnt;
reg cnt_flag;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 25'd0;
else if(cnt == CNT_MAX)//计数到最大之清零
cnt <= 25'd0;
else //未到最大加一
cnt <= cnt + 25'd1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_flag <= 1'b0;
else if(cnt == (CNT_MAX - 25'd1))//计数到最大值减一产生一个高脉冲信号
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
led_out <= 1'b0;
else if(cnt_flag == 1'b1)//检测到高脉冲led_out反转一次
led_out <= ~led_out;
else
led_out <= led_out;
endmodule
方式二的RTL视图(带标志信号)
与方式一相比较,增加了一个比较器EQUAL用于判断是否计数到最大值减一,还增加了一个D触发器cnt_flag。
parameter与localparam的区别
*parameter可以写在端口列表进行参数传递
//parameter CNT_MAX = 24_999_999;
//localparam CNT_MAX = 24_999_999;
例如
module counter
#(
parameter CNT_MAX = 24_999_999,
)
(
input wire sys_clk ,
input wire sys_rst_n,
output wire led_out
);
例化时可以修改参数,注意实例化名称位置
counter
#(
.CNT_MAX(100),
)
counter_inst0
(
.sys_clk (),
.sys_rst_n(),
.led_out (),
);
counter
#(
.CNT_MAX(10000),
)
counter_inst1
(
.sys_clk (),
.sys_rst_n(),
.led_out (),
);
*/