计数器(3):避免多计少计
计数器,FPGA设计中最常用的设计,然而有些地方需要多加注意。
一、勿要多计
打算计10下,所以我计数器就写计到10:cnt==10,程序如下所示。
1、设计文件
1 module jsq 2 //========================< 端口 >========================================== 3 ( 4 input wire clk , 5 input wire rst_n , 6 input wire en , 7 output reg [ 4:0] cnt 8 ); 9 //========================< 信号 >========================================== 10 reg en_flag ; 11 wire add_cnt ; 12 wire end_cnt ; 13 14 //========================< 程序 >========================================== 15 always @(posedge clk or negedge rst_n) begin 16 if(!rst_n) begin 17 en_flag <= 0; 18 end 19 else if(en) begin 20 en_flag <= 1; 21 end 22 else if(end_cnt) begin 23 en_flag <= 0; 24 end 25 end 26 27 always @(posedge clk or negedge rst_n) begin 28 if(!rst_n) 29 cnt <= 0; 30 else if(add_cnt) begin 31 if(end_cnt) 32 cnt <= 0; 33 else 34 cnt <= cnt + 1; 35 end 36 end 37 38 assign add_cnt = en_flag; 39 assign end_cnt = add_cnt && cnt== 10; //我想计10下 40 41 42 43 endmodule
2、仿真文件
1 `timescale 1ns/1ps //时间精度 2 `define Clock 20 //时钟周期 3 4 module jsq_tb; 5 6 //========================< 端口 >========================================== 7 reg clk ; //时钟,50Mhz 8 reg rst_n ; //复位,低电平有效 9 reg en ; 10 wire [ 4:0] cnt ; 11 12 //========================================================================== 13 //== 模块例化 14 //========================================================================== 15 jsq u_jsq 16 ( 17 .clk (clk ), 18 .rst_n (rst_n ), 19 .en (en ), 20 .cnt (cnt ) 21 ); 22 23 //========================================================================== 24 //== 时钟信号和复位信号 25 //========================================================================== 26 initial begin 27 clk = 1; 28 forever 29 #(`Clock/2) clk = ~clk; 30 end 31 32 initial begin 33 rst_n = 0; #(`Clock*20+1); 34 rst_n = 1; 35 end 36 37 //========================================================================== 38 //== 设计输入信号 39 //========================================================================== 40 initial begin 41 en = 0; 42 #(`Clock*20+1); //初始化完成 43 en = 1; 44 #(`Clock*10); 45 en = 0; 46 #(`Clock*100); 47 $stop; 48 end 49 50 51 52 endmodule
3、Modelsim仿真图像
观察图像可以看到,计数器 cnt 计到了 cnt==10 这里,可是当我仔细数数时却发现,计数是从 0 开始计的,从 0 到 10,所以实际计了 11 下!和我之前写代码时脑中所想不一致!所以,如果只想计10下,那实际写代码应该写 9 才对。
总结:计数器想计 n 下,那写代码只能写 cnt==n-1。
二、勿要少记
数据 data 和数据使能 data_en 同时来,我想数一下到底来了多少个数据。
1、代码设计
1 module jsq 2 ( 3 input wire clk , 4 input wire rst_n , 5 input wire [ 3:0] data , 6 input wire data_en , 7 output reg [ 3:0] cnt 8 ); 9 10 always @(posedge clk or negedge rst_n) begin 11 if(!rst_n) begin 12 cnt <= 0; 13 end 14 else if(data_en) begin 15 cnt <= cnt + 1; 16 end 17 else begin 18 cnt <= 0; 19 end 20 end 21 22 23 endmodule
2、仿真设计
1 `timescale 1ns/1ps //时间精度 2 `define Clock 20 //时钟周期 3 4 module jsq_tb; 5 6 //========================< 端口 >========================================== 7 reg clk ; //时钟,50Mhz 8 reg rst_n ; //复位,低电平有效 9 reg [ 3:0] data ; 10 reg data_en ; 11 wire [ 4:0] cnt ; 12 13 //========================================================================== 14 //== 模块例化 15 //========================================================================== 16 jsq u_jsq 17 ( 18 .clk (clk ), 19 .rst_n (rst_n ), 20 .data (data ), 21 .data_en (data_en ), 22 .cnt (cnt ) 23 ); 24 25 //========================================================================== 26 //== 时钟信号和复位信号 27 //========================================================================== 28 initial begin 29 clk = 1; 30 forever 31 #(`Clock/2) clk = ~clk; 32 end 33 34 initial begin 35 rst_n = 0; #(`Clock*20+1); 36 rst_n = 1; 37 end 38 39 //========================================================================== 40 //== 设计输入信号 41 //========================================================================== 42 initial begin 43 data_en = 0; 44 data = 0; 45 #(`Clock*20+1); //初始化完成 46 47 data_en = 1; 48 data = 0; 49 #(`Clock); 50 data = 1; 51 #(`Clock); 52 data = 2; 53 #(`Clock); 54 data = 3; 55 #(`Clock); 56 data = 4; 57 #(`Clock); 58 data = 5; 59 #(`Clock); 60 data = 6; 61 #(`Clock); 62 data = 7; 63 #(`Clock); 64 data = 8; 65 #(`Clock); 66 data = 9; 67 #(`Clock); 68 data = 0; 69 data_en = 0; 70 71 #(`Clock*100); 72 $stop; 73 end 74 75 76 77 endmodule
3、Modelsim仿真图像
观察图像可以看到,计数器 cnt 计到了 cnt==10 这里,而我的 data_en 确实是维持了 10 个周期的。这种情况下,cnt 计到多少就是多少,第0下是不作数的。如果按照上面的情况,以为真实计数是 10-1=9下,反而是错的了。当然如果其他信号需要用到 cnt来搞事情,那还是需要写 cnt==n-1的,这就又回到上面第一种情况了。
此外 cnt 较 data_en 延后了一个周期,当 data_en 都拉低了,而后 cnt 才计到 cnt==10,这是时序逻辑的特点,并非出错。
总结:计数器计算一段波形的持续时间,最终即到 cnt==n,那波形时间就是n,不需要 -1。