使用Verilog语言描述计数器——脉动计数器。

内容说明:

本次设计的计数器属于脉动计数器。使用Verilog语言设计,并且设计方法采用模块设计和简单的行为级设计。会有这两种设计的对比测试。最后,会有对这次设计计数器过程中的一些小心得。

计数器

什么是计数器?

计数是一种最简单基本的运算。计数器就是实现这种运算的逻辑电路。计数器在数字系统中主要是对脉冲的个数进行计数,以实现测量、计数和控制的功能,同时兼有分频功能。(百度)

自己对计数器的理解。

计数器就是一个简简单单具有计数功能的模块(module)。不过,这个原理简单的计数模块在芯片和计算机中却是不可或缺的部分。计数器虽然是对脉冲个数进行计数,但是如果把脉冲个数进行某种运算,而且这种运算可以具有某种含义,那么“计数”便可以统计“某种关键量”的次数(数量)。

计数器分类:

按照触发条件:同步计数器和异步计数器。
按照功能:加法计数器、减法计数器,移位计数器、可以记录固定数值的计数器。
本次设计的脉动计数器属于减法计数器。

脉动计数器

脉动计数器是由4个T触发器组成的计数模块。具体逻辑图如图1

脉动阵列架构 verilog脉动阵列_脉动阵列架构


图1:脉动计数器逻辑图

设计思想:

这次设计给出了电路的逻辑图。在这个图中,我们可以看出需要调用4个T触发器,用时钟信号和复位信号把4个T触发器连接起来,4个T触发器的输出组成了最终的输出结果。需要先设计出T触发器,一个T触发器是由一个D触发器和一个非门构成,而D触发器之前设计过。这样设计逻辑就很简单了,先设计D触发器,之后和非门组成T触发器,最后构成脉动计数器。
这次的设计方面继续采用模块化设计。

设计代码:

D触发器设计代码:
这个D触发器功能比较完善,具有复位、置数功能。

module dff (clk,rst,load,d,q);
input	clk,rst,load;
input	d;
output	q;
reg	q;

always @ (posedge clk or negedge rst or negedge load)
	if(!rst)
		q<=0;
	else if (!load)
		q<=1;
	else
		q<=d;

endmodule

T触发器设计代码:

module	tff (clk,rst,q);
input	clk,rst;
output	q;
//wire	a;
//not (a,q);
//dff u1 (clk,rst,1'b1,a,q);
dff u1 (clk,rst,1'b1,~q,q);

endmodule

T触发器设计时,可以有两种想法。一种是如同注释一样,调用一个非门;另一种是在模块调用总直接取非。两种各自选取。
注:在dff u1 (clk,rst,1'b1,~q,q);中,1'b1不可偷懒写成1。因为,前者代表1位二进制的1;后者代表十进制的1,具体几位看自己的计算机,一般是32位。在赋值的时候,有时会忽略数字的位数和进制,这样只要你自己可以理解不出错误,都可以(开心就好)。但是模块调用是“很严肃”的事情,这样“无所谓”会让模块调用(实例化)出现问题。

顶层模块设计代码:

module counter (clk,rst,q);
input		clk,rst;
output	[3:0]	q;
tff u1 (.clk(clk),.rst(rst),.q(q[0]));
tff u2 (.clk(q[0]),.rst(rst),.q(q[1]));
tff u3 (.clk(q[1]),.rst(rst),.q(q[2]));
tff u4 (.clk(q[2]),.rst(rst),.q(q[3]));
endmodule

在使用模块设计之后,使用行为级编写一个简单的计数器,功能和上面设计的脉动计数器一样。不过,代码长度和难度完全不一样。
代码如下:

module counter_1 (clk,rst,q);
input		clk,rst;
output	[3:0]	q;
reg	[3:0]	q;
always @ (posedge clk or negedge rst)
begin
	if(!rst)
		q<=0;
	else
		q<=q-1;
end
endmodule

测试代码:

module counter_tb;
reg		clk,rst;
wire	[3:0]	q1,q2;
counter   t1 (clk,rst,q1);
counter_1 t2 (clk,rst,q2);
initial
begin
	clk=0;rst=0;
#10	rst=1;
#50	rst=0;
#10	rst=1;
#200	rst=0;
#10	$stop;
#10	$finish;
end

always	#6 clk=~clk;

endmodule

运行结果:

两中不同设计方法的计数器测试结果如图2所示。

十进制输出结果如图3所示。

异步复位部分如图4所示。

脉动阵列架构 verilog脉动阵列_十进制_02


图2:两中不同设计方法的计数器测试结果

注:图上显示的数值是把二进制转换为了无符号的数值。就是输出没有负数。但是如果改变输出数值的格式,比如改成十进制,结果如图3所示。

脉动阵列架构 verilog脉动阵列_脉动阵列架构_03


图3:十进制输出结果

对于为什么会有-8后面是7这个现象的解释。

脉动阵列架构 verilog脉动阵列_脉动阵列架构_04


可以这样理解,0000减1,但是减不了。向“远方”借1,最后变成了1111。这种理解方式只是让你“强行”明白减法。这里的标准回答是补码,十进制中的4位二进制数中规定最高一位是标志位。“1”表示负数;“0”表示正数。有关补码的知识之后再说。

这样也可以解释(-8)的下一位为什么是(7)。

脉动阵列架构 verilog脉动阵列_脉动阵列架构_05

脉动阵列架构 verilog脉动阵列_Verilog_06


图4:异步复位部分

可以看出异步复位不需要等待时钟的到来,就可以把输出结果复位(置零)。但是新的输出的出现需要等待时钟。

总结

本次主要介绍计数器和如何使用Verilog编写一个计数器。两种设计方法的区别,使用分模块(复杂)设计可以控制模块内部功能,自主性强一些,通过修改内部代码可以实现功能的“微调”;但是编写代码难度加大,对设计者素质要求高。使用行为级(容易)设计最大的优点简单、方便;同时缺点无法做到“窥看”内部。

预告

其他计数器的设计。计数器也有新花样。

感想

不入其行,不知其难。
入了其行,方知世界之大!!!