计数器是verilog编程中非常常用的一种技巧,但是我们如果是C等语言的编程思维则极有可能错误地使用计数器。下面举个例子:
module delay (
input clk_sys, //系统时钟
input rst_n,
output reg [31:0] time_cnt
);
parameter DELAY_TIME = 32'd50; //计数总时钟个数
reg [31:0] time_cnt_r;
//组合电路,实现计数器自增1
always @(*) begin
if(time_cnt == DELAY_TIME)
time_cnt_r = 32'h1;
else
time_cnt_r = time_cnt_r + 1;
end
//时序电路,实现同步输出到端口
always @(posedge clk_sys or negedge rst_n) begin
if(!rst_n)
time_cnt <= 32'h0;
else
time_cnt <= time_cnt_r;
end
endmodule
该模块实现对系统时钟的计数,计数到50个时钟个数则清零重新开始计数。代码中
time_cnt_r = time_cnt_r + 1;
这条语句大家并不陌生,在C语言里,典型的计数方式。
我们使用quartus编译综合得出的电路:
是不是感觉有点复杂。我们把代码稍微变一下,把上面那条计数器自增1代码改成:
time_cnt_r = time_cnt + 1;
再次编译综合,得到的RTL电路如下:
电路竟然变得如此简洁,更重要的是节省了很多很多的buf资源:
仔细对比两条计数器自增1代码,发现区别在于:前面一个是计数器+1后赋值给自己,而后面一个是计数器+1后赋值给另外一个临时计数器。
一个小小的改动,结果相差这么大,值得深思。