BCD计数器
158 将158拆解成百位十位个位
C中
158/100 = 1 百位
(158/10)%10 = 5 十位
158%100 = 8 个位
在FPGA中理论也是可以使用这样的算法 但是过于耗费逻辑资源
BCD:每四位表示一个数,158需要十二个
0001 | 0101 | 1000 |
---|---|---|
1 | 5 | 8 |
创建工程文件夹——》 创建工程(选路径起名字) ——》 代码选择——》 型号选择——》 仿真工具及语言选择
BCD计数器
新建Verilog HDL File 名字为 BCD_counter
module BCD_counter (Clk,Cin,Rst_n,Cout,q);
input Clk; //计数器 基准时钟
input Cin; //进位输入 与普通计数器一样 这个为高电平时
//计数器的值才会+1
input Rst_n; //系统复位
output reg Cout; //进位输出 //凡是在always中赋值的
//都应该是reg类型
output [3:0]q; //计数值输出
reg [3:0]cnt; //凡是在always中赋值的都应该是reg类型
//定义一个计数器
//计数过程
always@(possdge Clk or negedge Rst_n)
if (Rst_n == 1'b0)
cnt <= 4'd0;
else if (Cin == 1'b1) begin
if (cnt == 4'd9)
cnt <= 4'd0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= cnt ; //可写可不写
//产生进位输出信号
always@(possdge Clk or negedge Rst_n)
if (Rst_n == 1'b0)
Cout <= 1'b0;
else if (cin == 1'b1 && cnt == 4'd9)
Cout <= 1'b1;
else
Cout <= 1'b0;
assign q = cnt; //把cnt的值传给q 实时输出到外部
endmodule
对刚才写的计数器进行testbench仿真
`timecale 1ns/1ns
`define xitongshizhong 20 //(20ns)
module BCD_counter_tb;
reg clk1;
reg cin1;
reg ret_n1;
wire cout1;
wire [3:0]q1;
BCD_counter u1(
.Clk(clk1),
.Cin(cin1),
.Rst_n(rst_n1),
.Cout(cout1),
.q(q1)
);
initial clk1 = 1'b1;
always#(`shizhongzhouqi/2)
clk1 = ~ clk1;
initial begin
rst_n1 = 1'b0;
cin1 = 1'b0;
#(`shizhongzhouqi*200);
rst_n1 = 1'b1;
#(`shizhongzhouqi*20);
repeat(30)begin
cin1 = 1'b1;
#(`shizhongzhouqi*1);
cin1 = 1'b0;
#(`shizhongzhouqi*5);
end
#(`shizhongzhouqi*20);
$stop;
end
endmodule
——》进行分析与综合 ——》链接——》仿真
计数器设计完成 仿真验证完成 下一步
进行级联
moudle BCD_counter_top(Clk,Cin,Rst_n,Cout,q);
input Clk;
input Cin;
input Rst_n;
output Cout;
output [11:0]q; //3个计数器级联
wire cout0;
wire cout1;
wire [3:0]q0;
wire [3:0]q1;
wire [3:0]q2;
assign q = {q2,q1,q0}; //花括号进行位拼接
BCD_counter C0(
.Clk(Clk),//共用
.Cin(Cin),
.Rst_n(Rst_n),
.Cout(cout0),//共用
.q(q0)
);
BCD_counter C1(
.Clk(Clk),//共用
.Cin(cout0),
.Rst_n(Rst_n),//共用
.Cout(cout1),
.q(q1)
);
BCD_counter C2(
.Clk(Clk),//共用
.Cin(cout1),
.Rst_n(Rst_n),//共用
.Cout(Cout),
.q(q2)
);
endmodule
——》分析与综合 ——》将其设置为顶层——》分析与综合
对刚才写的顶层计数器进行testbench仿真
`timecale 1ns/1ns
`define xitongshizhong 20 //(20ns)
module BCD_counter_top_tb;
reg clk1;
reg cin1;
reg ret_n1;
wire cout1;
wire [11:0]q1;
BCD_counter_top u0(
.Clk(clk1),
.Cin(cin1),
.Rst_n(rst_n1),
.Cout(cout1),
.q(q1)
);
initial clk1 = 1'b1;
always#(`shizhongzhouqi/2)
clk1 = ~ clk1;
initial begin
rst_n1 = 1'b0;
cin1 = 1'b0;
#(`shizhongzhouqi*200);
rst_n1 = 1'b1;
#(`shizhongzhouqi*20);
// repeat(50)begin
// cin1 = 1'b1;
// #(`shizhongzhouqi*1);
// cin1 = 1'b0;
// #(`shizhongzhouqi*5);
// end
cin1 = 1'b1;
//三个(最大10次)计数器 至少要cin有效 999次 所以我们直接让cin一直有效
//那么就是每一次 时钟上沿来临都会计数一次
#(`shizhongzhouqi*2000);//延时2000的时钟周期 大于999 保证能看到
$stop;
end
endmodule
分析与综合——》链接——》仿真
这说明我们的进位量出现了问题 把所有模块的图都调出来
这里可以看到 由于时序逻辑 D触发器的特性 会滞后输出 (cin有效但是需要下次时钟上升沿的时候 这个有效电平才会被捕获
代码修改 基础模块 BCD_counter
将Cout的输出由时序逻辑 改为组合逻辑 组合逻辑无D触发器
module BCD_counter (Clk,Cin,Rst_n,Cout,q);
input Clk; //计数器 基准时钟
input Cin; //进位输入 与普通计数器一样 这个为高电平时
//计数器的值才会+1
input Rst_n; //系统复位
output Cout; //进位输出
output [3:0]q; //计数值输出
reg [3:0]cnt; //凡是在always中赋值的都应该是reg类型
//定义一个计数器
//计数过程
always@(possdge Clk or negedge Rst_n)
if (Rst_n == 1'b0)
cnt <= 4'd0;
else if (Cin == 1'b1) begin
if (cnt == 4'd9)
cnt <= 4'd0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= cnt ; //可写可不写
//产生进位输出信号
assign Cout = (cin == 1'b1 && cnt == 4'd9) ;
//当计数器计数值为9 并且 cin为1时, Cout=1
assign q = cnt; //把cnt的值传给q 实时输出到外部
endmodule
再次仿真
可以看到 这次没有滞后 都是直接产生的