基于Verilog HDL语言设计移位计数器和模50(十进制)计数器。
修改:
修改时间:2020/08/19
修改内容:这次的模50计数器生产了锁存器,修改代码见文章
内容说明:
介绍移位计数器的设计思想并提供设计代码和测试代码。介绍模50计数器的设计思想(也可以是任意两位数)并提供设计代码和测试代码。
移位计数器
移位计数器?
移位计数器就是输出结果实现移位功能,但是移动的方式由设计者自己设计。现在设计两种移位计数器,使用同一测试代码测试。最后,观察结果。
移位计数器设计代码:
移位方式:0001——0010——0100——1000
module counter_3 (clk,rst,q);
input clk,rst;
output [3:0] q;
reg [3:0] q;
always @ (posedge clk) //同步
//always @ (posedge clk or negedge rst) //异步
begin
if(!rst)
q<=4'b0001;
else
begin
q<=q<<1;
q[0]<=q[3];
end
end
endmodule
移位方式:0000——0001——0011——0111——1111——1110——···——0000
module counter_4 (clk,rst,q);
input clk,rst;
output [3:0] q;
reg [3:0] q;
always @ (posedge clk)
//always @ (posedge clk or negedge rst)
begin
if(!rst)
q<=4'b0000;
else
begin
q<=q<<1;
q[0]<=~q[3];
end
end
endmodule
测试代码:
module counter_3_4_tb;
reg clk,rst;
wire [3:0] q1,q2;
counter_3 t1 (clk,rst,q1);
counter_4 t2 (clk,rst,q2);
initial
begin
clk=0;rst=0;
#10 rst=1;
#45 rst=0;
//#30 rst=1;
#10 rst=1;
#100 rst=0;
#10 $stop;
end
always #6 clk=~clk;
endmodule
说明:
#45 rst=0;
//#30 rst=1; //如果加上这个语句,可以采集到复位信号
#10 rst=1;
由于时钟周期是16个单位,如果复位信号低电平时间只能维持10个单位,那么是无法采集到这个复位信号。所以,如果想要采集到复位信号,必须要超过16个单位的时间。
仿真结果:
同步移位计数器复位信号没有采集到结果见图1-1;同步移位计数器复位信号采集到结果见图1-2;异步移位计数器结果见图1-3;异步移位计数器结果局部放大结果见图1-4。
图1-1:同步移位计数器复位信号没有采集到结果
图1-2:同步移位计数器复位信号采集到结果
图1-3: 异步移位计数器结果
图1-4:异步移位计数器结果局部放大结果
模50计数器
模50计数器介绍:
模50计数器是使计数器实现0-9-19-29-39-49的计数功能,并且在计数达到49时输出一个标志位。同时具有同步复位和同步置数的功能。
注:数字电路的输入和输出在计算机中都是二进制,结果当然也是以二进制的形式存在,这个“模50”就是将二进制转换为十六进制。出现的“0-9-19-29-39-49”均是十六进制。
模50计数器设计代码:
module counter_5 (clk,rst,run,load,data,q,cout);
input clk,rst,run,load;
input [7:0] data;
output cout;
output [7:0] q;
reg [7:0] q;
always @ (posedge clk)
begin
if(rst) q<=0;
else if (load) q<=data;
else if (run)
begin
if (q[3:0]==9)
begin
q[3:0]<=0;
if(q[7:4]==4)
q[7:4]<=0;
else
q[7:4]<=q[7:4]+1;
end
else
q[3:0]<=q[3:0]+1;
end
end
assign cout = ((q==8'h49)&run) ? 1:0;
endmodule
修改代码:
module counter_5 (clk,rst,run,load,data,q,cout);
input clk,rst,run,load;
input [7:0] data;
output cout;
output [7:0] q;
reg [7:0] q;
always @ (posedge clk)
begin
if(rst) q<=0;
else if (load) q<=data;
else if (run)
begin
if (q[3:0]==9)
begin
q[3:0]<=0;
if(q[7:4]==4)
q[7:4]<=0;
else
q[7:4]<=q[7:4]+1;
end
else
q[3:0]<=q[3:0]+1;
end
else
q[7:0] <= 8'h00;
end
assign cout = ((q==8'h49)&run) ? 1:0;
endmodule
注:虽然两次代码对仿真没有什么影响,但是当电路复杂时,这种错误是致命的!!!
测试模块:
module counter_5_tb;
reg clk,rst,run,load;
reg [7:0] data;
wire cout;
wire [7:0] q;
counter_5 t1 (clk,rst,run,load,data,q,cout);
initial
clk=0;
always #5 clk=~clk;
initial
begin
rst=1;
#15 rst=0;
end
initial
begin
load=0;data=8'h30;run=1;
#600 load=1;
#10 load=0;
#100 $stop;
end
endmodule
仿真结果:
实现0-49计数结果见图2-1;实现置数功能结果见图2-2。
图2-1:实现0-49计数结果
图2-2:实现置数功能结果
总结:
移位计数器的关键在设计者如何实现自己的“移位想法”编程方法比较简单。十进制计数器的难点如何把“通俗语言”写成“Verilog代码”。无论是什么学科,都会有通俗易懂的描述和确定标准的描述,可以“表述”这些东西就是进步。
感想:
无