第一部分:單個BCD計數器
一、BCD計數器原理
- BCD碼的特點:用4位二進制數,來表示一位十進制數(0~9)。
- 類似於4位二進制計數器,但4位二進制計數器需要計數到1111然后才返回0000,而十進制計數器要求計數到1001(十進制的9)就返回0000。BCD計數器是一種常見的十進制計數器。
- 而4位二進制就相當於1位十六進制,因此看十六進制更方便。
二、RTL圖
三、代碼編寫
1、BCD_Counter 模塊
module BCD_Counter(Clk, Cin, Rst_n, Cout, q);//端口首字母最好大寫,各個端口用空格隔開
input Clk; //計數基准時鍾
input Cin; //計數器進位輸入
input Rst_n; //系統復位
output reg Cout; //計數器進位輸出
output[3:0] q; //計數器輸出
reg[3:0] cnt; //定義 計數器寄存器(counter)
//執行計數過程
always@(posedge Clk or negedge Rst_n) begin
if(Rst_n == 1'b0)
cnt <= 4'b0;
else if(Cin == 1'b1)begin
if(cnt == 4'd9)
cnt <= 4'd0;
else
cnt <= cnt + 1'b1;
end
else
cnt <= cnt;
end
//產生進位輸出信號
always@(posedge Clk or negedge Rst_n) begin
if(Rst_n == 1'b0)
Cout <= 4'b0;
else if(Cin == 1'b1 && cnt == 4'd9)
Cout <= 1'b1;
else
Cout <= 1'b0;
end
assign q = cnt;
endmodule
2、testbench 代碼
`timescale 1ns/1ns
`define clock_period 20 //系統時鍾20ns(50MHZ)
module BCD_Counter_tb;
//激勵信號定義,對應連接到待測模塊輸入端口。 reg型
reg Clk;
reg Cin;
reg Rst_n;
//待檢測信號定義,對應連接到待測試模塊的輸出端口。 wire型
wire Cout;
wire[3:0] q;
//例化待測試模塊
BCD_Counter BCD_Counter0(
.Clk(Clk),
.Cin(Cin),
.Rst_n(Rst_n),
.Cout(Cout),
.q(q)
);
//產生時鍾信號
initial Clk = 1'b1;
always#(`clock_period/2) Clk = ~Clk;
//產生 Cin激勵信號 和 Rst_n復位信號
initial begin
Rst_n = 0; //首先系統處於復位狀態
Cin = 1'b0;
#(`clock_period*200); //延時200個系統時鍾周期
Rst_n = 1'b1; //系統開始運行
#(`clock_period*20);
repeat(30) begin //重復執行30次。1個周期高電平,5個周期低電平。
Cin = 1'b1;
#`clock_period;
Cin = 1'b0;
#(`clock_period*5);
end
#(`clock_period*20);
$stop;
end
endmodule
四、波形圖
結果:每計數到9,進位輸出:Cout = 1 ,計數器清零。
五、內部結構
第二部分:三個BCD計數器的級聯
一、RTL圖
testbench:
二、代碼編寫
1、BCD_Counter_top 模塊 (設置為 “頂層文件”)
注意:q 是12位。q0 是低位,q2 是高位。
module BCD_Counter_top(Clk, Cin, Rst_n, Cout, q);
input Clk; //計數基准時鍾
input Cin; //計數器進位輸入
input Rst_n; //系統復位
output Cout; //計數器進位輸出
output[11:0] q; //計數器輸出
wire Cout0,Cout1;
wire[3:0] q0,q1,q2;
assign q = {q2,q1,q0};
BCD_Counter BCD_Counter0(
.Clk(Clk),
.Cin(Cin),
.Rst_n(Rst_n),
.Cout(Cout0),
.q(q0)
);
BCD_Counter BCD_Counter1(
.Clk(Clk),
.Cin(Cout0),
.Rst_n(Rst_n),
.Cout(Cout1),
.q(q1)
);
BCD_Counter BCD_Counter2(
.Clk(Clk),
.Cin(Cout1),
.Rst_n(Rst_n),
.Cout(Cout),
.q(q2)
);
endmodule
2、BCD_Counter_top_tb testbench
`timescale 1ns/1ns
`define clock_period 20
module BCD_Counter_top_tb;
reg Clk;
reg Cin;
reg Rst_n;
wire Cout;
wire[11:0] q;
BCD_Counter_top BCD_Counter_top0(
.Clk(Clk),
.Cin(Cin),
.Rst_n(Rst_n),
.Cout(Cout),
.q(q)
);
//產生時鍾信號
initial Clk = 1'b1;
always#(`clock_period/2) Clk = ~Clk;
//產生 Cin激勵信號 和 Rst_n復位信號
initial begin
Rst_n = 0; //首先系統處於復位狀態
Cin = 1'b0;
#(`clock_period*200); //延時200個系統時鍾周期
Rst_n = 1'b1; //系統開始運行
#(`clock_period*20);
Cin = 1'b1;
//直接就讓 Cin = 1 ,不再設置Cin為周期信號了。
#(`clock_period*3000);
$stop;
end
endmodule
注意:
- 1、頂層的 q 是12位
- 2、直接就讓 Cin = 1 ,不再設置Cin為周期信號了。
對比之前的圖:
現在的 Cin :
三、仿真結果
波形存在錯誤:999 之后 不是 000, 而是999 -> 990 -> 901 -> 002
1. 錯誤分析步驟:
(1)添加子模塊波形
(2)對信號進行分組
Ctrl + A 全選信號
Ctrl + G 對信號智能分組
(3)分組完 restart 重新啟動一下
(4)波形分析
Cout 一直滯后,因為有D觸發器,導致了延遲。
2. 修改代碼
修改方法:把always改成assign
波形圖: