前面已经学习了时序逻辑电路中的基本单元:触发器,这次就用其来整点活,实现计数器的设计,计数器可以说是任何和时序有关的设计都会用到他。
一. 计数器原理
计数器的功能主要就是计数,在数字系统中主要对脉冲的个数进行计数,以实现测量,计数和控制的功能,同时也可以做到分频等功能。 同时,其也是FPGA设计中最常用的一种时序逻辑电路,根据计数器的计数值可以精确控制各个信号的状态。
二. 实用
用计数器计数1s间隔,实现led灯每隔1s闪烁的结果。
本设计十分简单,用一个模块附带简单的输入输出便可以完成。 如下所示:
sys_clk:输入时钟,为系统时钟
sys_rst_n:复位信号,低电平有效
led_out:输出信号,控制LED灯的亮灭
我们的逻辑是这样:计数器进行计数,计数到1s便给LED灯赋值,让其发亮,就可以达到1s亮灭的结果。 所以需要计算一下,计1s需要计数多少次,如果时钟是50MHz,也就是0.000_000_02s,计数一秒需要计数50_000_000个数才可以,不过在设计时刻视为从0开始计数,所以是49_999_999个。
接下来的设计时,为了节省寄存器资源,只计数24_999_999个,也就是1s内闪烁一次(0.5秒一次)。 这需要25位宽的计数器。
下面介绍两种方式的计数器; 带标志位和不带标志位。
不带标志位 :设置一个cnt信号位,时钟的每个上升沿到来时刻,cnt便自动+1,当计数器计到24_999_999的时候,清0,led_out信号取反,只要不复位,变一直计数下去。
带标志位: 在上面方案的基础上,添加一个cnt_flag信号,当计数器计数到24_999_999的时候先不取反,而是让cnt_flag产生一个高脉冲,当led_out 的信号检测到cnt_flag拉高的时候取反。
关于标志位有什么用,先挖个坑,以后说。
图1
图2
注意一下上面两个图(取自野火教材),标志位拉高的时刻并不一样,下面的图在N-1时刻拉高,led_out的状态是刚刚好在0时刻转换(慢一拍),上面的图实际上到后来是计数计多了。 对于这种简单的设计这个问题不痛不痒,但是对于一些要求比较高的设计来说,一秒也不能差,所以这个地方需要注意。
下面给出代码(带信号标志位)
module counter
#(
parameter CNT_MAX = 25'd24_999_999
)
(
input wire sys_clk , //系统时钟 50Mh
input wire sys_rst_n , //全局复位
output reg led_out //输出控制 led 灯
);
//reg define
reg [24:0] cnt ; //经计算得需要 25 位宽的寄存器才够 500ms
reg cnt_flag;
//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;
//cnt_flag:计数到最大值产生的标志信号,每当计数满标志信号有效时取反
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'b1)
cnt_flag <= 1'b1;
else
cnt_flag <= 1'b0;
//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_flag == 1'b1)
led_out <= ~led_out;
endmodule
测试平台:
module tb_counter(
);
reg sys_clk;
reg sys_rst_n;
wire led_out;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
counter
#(
.CNT_MAX (25'd24) //实例化带参数的模块时候,当我们想要修改常数在此模块的值的时候,直接实例化参数名后面的括号中修改即可
)
counter_inst(
.sys_clk(sys_clk),
.sys_rst_n(sys_rst_n),
.led_out(led_out)
);
endmodule
仿真波形:
到这里,问题还没解决,在7系列的FPGA的板上,并不输出单端口的时钟,其是200MHz的差分时钟。
AD11和AD12差分时钟输出
经过查阅,调用PLL锁相环的IP核进行差分时钟的单端口时钟输出。
调用时钟IP核,并且选择PLL,在下面选择差分时钟接口。 output时钟设置50MHz。
重新写顶层代码,例化时钟。
module counter
(
//input wire sys_clk,
input wire sys_rst_n,
input clk_p,
input clk_n,
output reg led_out
);
reg [24:0] cnt;
reg cnt_flag;
parameter CNT_MAX = 25'd24_999_999;
wire locked;
wire sys_clk;
clk_wiz_0 diff_2_single
(
.clk_out1(sys_clk),
.reset(1'b0),
.locked(locked),
.clk_in1_p(clk_p),
.clk_in1_n(clk_n)
);
//cnt:计数器计数,当计数到cnt_max的时候值清0
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;
//cnt_flag:计数到最大值产生的标志位信号,每当计数满标志信号有效时取反
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'b1)
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;
endmodule
RTL电路如下所示:
关于kintex7差分时钟引脚约束的问题需要注意:
这个地方只需要约束其中之一即可,另外一个不用管,如果是不清楚电平标准可以在Tcl控制台输入指令查询:
-
FPGA
+关注
关注
1626文章
21665浏览量
601793 -
led
+关注
关注
240文章
23134浏览量
658401 -
计数器
+关注
关注
32文章
2253浏览量
94351 -
触发器
+关注
关注
14文章
1995浏览量
61047 -
时序逻辑电路
+关注
关注
2文章
94浏览量
16527
发布评论请先 登录
相关推荐
评论