计数器是时序电路中比较简单的模型,但对于Verilog语法的训练和复习起到不可或缺的作用。计数器设计中有16进制计数器,10进制计数器(BCD码计数器),60进制计数器,30进制计数器等不同的模型,这些模型在数字电路中学习中已经有较为详细的介绍。本节内容主要是利用Verilog描述语言对这些计数器进行描述,从而学会Verilog基础语法在时序电路设计中的应用。
1.二进制计数器
二进制计数器是指将reg类型的向量按照整体使用,向量的权值都是2的幂次方。对于4位二进制组成的向量可以表达的范围0-9 a,b,c,d,e,f 总共16个数值,因此也把二进制计数器称为16进制计数器。
例1:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
module count16 ( input rst, input clk, output reg [15:0] count_hex ); always@(posedge clk or posedge rst) if(rst) begin // 异步复位 count_hex <= 0; end else //同步释放 count_hex <= count_hex + 1; endmodule |
在时钟的使用及触发器模型 中我们介绍了在时钟边沿的驱动下,reg型变量可以通过触发器模型来分析。对于D触发器,有D端,Q端,时钟端。如图1:

图 1
(1) 触发器的时钟端
时钟端比较好理解,因为在always过程中有posedge 关键字,所以我们知道所有寄存器(count_hex[15:0])对应触发器的clk端全部连在一起组成同步时序电路。
(2) 触发器的D端和Q端
对于count_hex<=count_hex+1; 这样的赋值语句,左边和右边都有寄存器变量,到底哪一边是触发器的D端,哪一边是Q端?
我们可以这样分析,在赋值语句的左边count_hex是被赋值变量,而触发器的Q端是输出端,如果左端是Q,就会给输出端赋值,这不合理,因此赋值语句左边的count_hex只能是触发器的D端。赋值语句右边希望能在稳定输出的前提下评估组合逻辑运算,将运算的结果赋给D端,从这一点看赋值语句右边count_hex只能是Q端,这和数字电路中现态次态分析是一致的。
因次结论如下:对于类似的count_hex<=count_hex+1;赋值语句,赋值语句左边对应的触发器的D端,赋值语句右边对应的是触发器的Q端的现态。
另外,如果在仿真波形上或signal_tap,ILA等调试工具上看到的寄存器的值(触发器模型),都是触发器的Q端的值。
因此例1的程序可以用图2-1的模型来理解。在Vivado中经过analysis & elaboration 后的模型如图2-2所示。
图2-1

图2-2
2.十进制计数器(BCD码计数器)
十进制计数器就是逢9进1,当多位十进制计数器级联时,处在不同位置的寄存器向量的权值不同。如例2中的向量a,b,c,d, a代表个位,b为十位,c为百位,d为千位。因此如果b要实现加1,a需要产生进位,即当a==9时,如果a再出现加1的情况下,a将回归到0,而b将实现加1操作。b到c,c到d的计数及进位操作与a到b的情况类似。与2进制计数器不同,进位按照2进制的规律(16进制的规律)可以自动实现,因此各级的回归于进位需要if或case等分支判断语句帮助实现。
例2:
十进制计数器程序
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
module BCD_counter ( input clk, input rst, output reg [3:0] a, output reg [3:0] b, output reg [3:0] c, output reg [3:0] d ); always@(posedge clk or posedge rst) if(rst) begin a <= 0; b <= 0; c <= 0; d <= 0; end else begin if(a == 9) begin a <= 0; if(b == 9) begin b <= 0; if(c == 9) begin c <= 0; if(d == 9) d <= 0; else d <= d + 1; end else c <= c + 1; end else b <= b + 1; end else a <= a + 1; end endmodule |
testbench 仿真程序 tb.v
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
`timescale 1ns/1ps module tb ( ); parameter PERIOD = 10; reg clk; initial begin clk = 1'b0; # (PERIOD/2) forever # (PERIOD/2) clk=~clk; end wire [3:0] a, b, c, d; reg rst; initial begin rst = 1; #20 rst = 0; end BCD_counter BCD_counter_inst ( .rst (rst), .clk (clk), .a (a), .b (b), .c (c), .d (d) ); endmodule |
仿真波形如下:

图3

图4
在10进制计数器中,if(a==9)的语句中,a的模型如果为D触发器,那么这里a是D端还是Q端?由于D端接收的是组合逻辑,在整个周期内D的是不稳定的。而Q值在上升沿后到下一个上升沿之前都是稳定的,因此这里的a指的是a的Q端。
3. 60进制计数器
上面解决了D触发器模型的问题后,60进制计数器就显得比较简单了。
例4:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
module counter60 ( input clk, input rst, output reg [3:0] a, output reg [3:0] b ); always@(posedge clk or posedge rst) if(rst) begin a <= 0; b <= 0; end else begin if(a == 9) begin a <= 0; if(b == 5) b <= 0; else b <= b + 1; end else a <= a + 1; end endmodule |
4. 12进制计数器
12进制计数器由两个部分构成:
- 在a, b 组合中不满足十进制的12,即不满足(a==2)&&(b==1)的情况下,a,b组合仍然满足10进制的计数规律。
- 当满足(a==2)&&(b==1)条件时,高位清零,低位置1。即b<=0; a<=1;
12进制的Verilog程序如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
module counter12 ( input clk, input rst, output reg [3:0] a, output reg [3:0] b ); always@(posedge clk or posedge rst) if(rst) begin a <= 1; b <= 0; end else begin if((a == 2)&&(b == 1)) begin a <= 1; b <= 0; end else if(a == 9) begin a <= 0; b <= b + 1; end else a <= a + 1; end endmodule |

图5
5. 24进制计数器
24进制计数器与12进制计数器非常相似,可以直接12进制的方式进行分析和设计,24进制的Verilog程序如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
module counter24 ( input clk, input rst, output reg [3:0] a, output reg [3:0] b ); always@(posedge clk or posedge rst) if(rst) begin a <= 0; b <= 0; end else begin if((a == 3)&&(b == 2)) begin a <= 0; b <= 0; end else if(a == 9) begin a <= 0; b <= b + 1; end else a <= a + 1; end endmodule |

图6
其它的计数器如30进制,31进制,28进制,29进制,7进制等,这里就不一一介绍了,可以参照复杂数字钟设计部分内容。
思考题:试分析以下语句有何区别?(a为4位 reg类型)
a <= a + 1;
a <= a + 1’b1;
a <= a + 4’b1;
a <= a + 4’h1;
a <= a + 32’b1;
a <= a + ‘b1;
a <= a + ‘h1;
