计数器设计
如下案例,对时钟CLOCK进行计数,在en使能信号由高电平变换到低电平时,dout开始计数使能高电平,计数周期为10。整个计数器工作过程如下:当en使能信号由低电平变成高电平再变成低电平时(en发出脉冲信号),计数器开始计数,dout输出高电平,同时作为计数器的加一信号,当计数器计满10个时钟信号,达到结束条件时,dout输出低电平,同时计数结束并复位到0或者其他信号(看具体需求)。
这里还可以从另一方面去考虑加一条件,就是当计数器没有加满10个时钟信号就继续加一,这种方式其实和第一种方式一样,因为当计数器没有计满10个信号dout会一直处于高电平。但是第二种方式起始条件、加一条件和结束条件很难统一,所以我们在设计时会采用第一种方式,虽然复杂点,但是以后在设计时会很方便。
图1‑69 对时钟CLOCK进行计数
采用自顶向下的设计流程。
第一步:电路整体结构图;
图1‑70 计数器整体结构图
表1‑29 counter_10clk.v结构图说明
说明:
I/O引脚在以后的章节中,可能不会出现,因为在结构图中很方便就看出来;位宽除非必要和明确情况一般也不会说明;
第二步:时序分析;
见表1‑29。
第三步:计数器结构;
计数器结构如下图所示,这一步主要目的是为了后面分析计数器的起始条件和结束条件做准备,后面本步骤可以省略。
图1‑71 计数器结构
第四步:计数器起始条件、加一条件和结束条件;
计数器最重要的是起始条件、加一条件和结束条件,其中起始条件由时序图中很容易看到就是en发送脉冲信号;加一条件为dout为高电平;结束条件为cnt==9即计满十个时钟信号(cnt由0开始计数)。
表1‑30 计数器工作条件
计数器起始条件加一条件结束条件cnten:HLdout:Hcnt==9引申条件/引申信号en=1/begin_cntdout=1/add_cntcnt==9/end_cnt
说明:H:高电平;L:低电平。
在这一步还需要定义一下特殊信号,比如由起始条件引申出来的起始信号,由结束条件引申出来的结束信号,见表1‑30。在表中可以看到起始条件不需要特殊使用,因为加一条件和起始条件是统一在一起的。
由上表可以写出下列程序:
代码1‑1
parameter counter_value= 4d10;
always @ ( posedge CLOCK or negedge RST_n )
if( !RST_n )
begin
cnt <= 0;
end
else if(add_cnt)
begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
assign add_cnt = dout == 1b1;
assign end_cnt = add_cnt && cnt == counter_value-1;
程序说明:
parameter 定义了计数器的计数终值,在使用的时候只需要在这里修改add_cnt后面的加一条件和end_cnt结束条件,结束条件不需要修改,只需要修改parameter宏定义的值即可。
第五步:功能性代码;
这里主要是dout信号怎么产生,先看一下dout信号的产生条件和结束条件,如下表所示:
表1‑31 dout信号产生条件
信号初始值变化方式/产生条件变化方式/结束条件doutLLH/en:HHL/cnt==9
代码1‑2
module counter_10clk
(
CLOCK, RST_n,
en,
dout
);
input CLOCK, RST_n; //全局时钟信号输入
input en; //计数器使能信号
output reg dout; //计数器输出信号
parameter counter_value= 4d10; //计数时钟个数
reg [7:0] cnt;
wire add_cnt,end_cnt;
always @ ( posedge CLOCK or negedge RST_n )
if( !RST_n )
begin
cnt <= 0;
end
else if(add_cnt)
begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt + 1;
end
always @ ( posedge CLOCK or negedge RST_n )
if( !RST_n )
begin
dout <= 1;
end
else if(en)
begin
dout <= 1;
end
else if(end_cnt)
begin
dout <= 0;
end
assign add_cnt = dout == 1b1;
assign end_cnt = add_cnt && cnt == counter_value-1;
endmodule
这样完整的计数器代码就写完了,后面只需要按照这个模板去修改参数就可以了,并不需要每次都去写程序。
编写TestBench,如下:
代码1‑3
//****************************************************************************//
//# @Author: 碎碎思
//# @Date: 2019-06-12 22:54:31
//# @Last Modified by: zlk
//# @WeChat Official Account: OpenFPGA
//# @Last Modified time: 2019-06-12 23:41:12
//# Description:
//# @Modification History: 2019-06-12 23:41:12
//# Date By Version Change Description:
//# ========================================================================= #
//# 2019-06-12 23:41:12
//# ========================================================================= #
//# | | #
//# | OpenFPGA | #
//****************************************************************************//
`timescale 1 ns/ 1 ps
module counter_10clk_tb; // 申明TestBench名称
reg clock;
reg reset; // 申明信号
reg en;
wire dout;
// 申明设计单元
counter_10clk dut
(
.CLOCK( clock ),
.RST_n( reset ),
.en( en ), //
.dout( dout )
);
initial begin // 建立时钟
clock = 0;
forever #20 clock = ~clock;
end
initial begin // 提供激励
reset = 0;
en = 0;
#200
reset = 1;
en = 1;
#210
en = 0;
#50000 $stop;
end
endmodule
Modelsim仿真结果如下:
图1‑72 10clk计数器仿真结果
更多技术文章及新闻,欢迎关注微信公众号:OpenFPGA