部落客福利:100G+電子設計學習資源包!
http://mp.weixin.qq.com/mp/homepage?__biz=MzU3OTczMzk5Mg==&hid=7&sn=ad5d5d0f15df84f4a92ebf72f88d4ee8&scene=18#wechat_redirect --------------------------------------------------------------------------------------------------------------------------
BCD碼(Binary-Coded Decimal)亦稱二進碼十進數或二-十進制代碼。用4位二進制數來表示1位十進制數中的0~9這10個數位。是一種二進制的數字編碼形式,用二進制編碼的十進制代碼。BCD碼這種編碼形式利用了四個位元來儲存一個十進制的數位,使二進制和十進制之間的轉換得以快捷進行。(百度百科)
例子:158 以BCD編碼方式編碼就會變成
1 | 1 | 1 | 1 |
1 | 5 | 8 |
在C語言中如果不通過位運算,一般都會采用一下方式進行拆分,涉及到除法運算:
A = 158/100 = 1 B = (158 % 100)/10 = 5 C = 158 %10 = 8
在FPGA中可以通過級聯3個4位計數器的方式實作該BCD編碼器。
一、實作單個4位計數器
1、源程式:
/* 實驗名稱:計數器
* 程式功能:In_cin 來一個高電平則計數一次
* 約定俗成:所有需要外部輸入的信号加入字首"In_" ,所有需往外部輸出的信号加入字首"Out_"
*/
module my_Counter(In_clk, In_cin, In_rst_n, Out_cout, Out_q);
input In_clk; // 時鐘輸入
input In_cin; // 觸發計數
input In_rst_n; // 複位信号 低複位
output Out_cout; // 溢出或與預定數值相等時輸出一個時鐘的高電平
output [3:0]Out_q; // 存儲計數的數值
reg[3:0] cnt; // 存儲計數值
// 捕獲 In_clk 上升沿,捕獲 In_rst_n 下降沿
// 計數程式塊
always@(posedge In_clk or negedge In_rst_n)
if(1'b0 == In_rst_n) // 複位信号處理 計數歸零
cnt <= 4'd0;
else if(1'b1 == In_cin) // In_cin 為高時計數
begin
if(4'd9 == cnt) // cnt 等于 9 則歸零
cnt <=4'd0;
else
cnt <= cnt + 1'b1; // cnt 小于 9 則累加
end
else
;
// 捕獲 In_clk 上升沿,捕獲 In_rst_n 下降沿
// 溢出或比對輸出程式塊
always@(posedge In_clk or negedge In_rst_n)
if(1'b0 == In_rst_n) // 複位信号處理 計數歸零
Out_cout <= 1'b0;
else if(1'b1 == In_cin && 4'd9 == cnt) // 注意1'b1 == In_cin 不可省略
Out_cout <= 1'b1; // In_cin 為高并且同時上個計數器計數到9則輸出1
else
Out_cout <= 1'b0; // 反之輸出0
assign Out_q = cnt;
endmodule
問題點:在上述代碼中的我做了一次實驗,将1'b1 == In_cin 省略了導緻不行出現如下現象:

正常的波形:(其實cout波形也是不對的,數到9就已經是第10個數了,就應該給高電平,可對比IP核)
差異在這裡:
假如1'b1 == In_cin省略了,那麼意味着隻要計數一到9,無論In_cin目前狀态是高還是低電平,cout就會輸出高電平,這就導緻cout提前被拉高(波形對比),同時由會延後到計數器複位歸零之後才會拉低。
2、仿真測試代碼
/* 實驗名稱:BCD 計數器驗證 */
`timescale 1ns/1ns
`define clock_period 20
module mytest_tb;
reg clk;
reg cin;
reg rst_n;
wire cout;
wire [3:0]q;
my_Counter BCD_Counter(
.In_clk(clk),
.In_cin(cin),
.In_rst_n(rst_n),
.Out_cout(cout),
.Out_q(q)
);
initial clk = 1'b1;
always #(`clock_period / 2) clk = ~clk;
initial begin
rst_n = 1'b0;
cin = 1'b0;
#(`clock_period * 20);
rst_n = 1'b1;
#(`clock_period * 20);
repeat(30)begin
cin = 1'b1;
#`clock_period;
cin = 1'b0;
#(`clock_period * 5);
end
#(`clock_period * 20);
$stop;
end
endmodule
二、計數器級聯
1、源程式
/* 實驗名稱:級聯計數器
* 程式功能:
* 約定俗成:所有需要外部輸入的信号加入字首"In_",所有需往外部輸出的信号加入字首"Out_"
*/
module mytest(In_clk, In_cin, In_rst_n, Out_cout, Out_q);
input In_clk;
input In_cin;
input In_rst_n;
output Out_cout;
output[11:0] Out_q;
wire Out_line0; // 計數器0的Out_cout端與計數器1的Out_cout端連結
wire Out_line1; // 計數器1的Out_cout端與計數器2的Out_cout端連結
wire[3:0] q0, q1, q2;
// 将三組4位信号合并成一組12位的信号
assign Out_q = {q2, q1, q0};
my_Counter Conuter0(
.In_clk(In_clk),
.In_cin(In_cin), // 重點
.In_rst_n(In_rst_n),
.Out_cout(Out_line0), // 重點
//.Out_q(Out_q[3:0]) // 方式1
.Out_q(q0) // 方式2
);
my_Counter Conuter1(
.In_clk(In_clk),
.In_cin(Out_line0), // 重點 這裡來一次高電平意味這計數器0計滿
.In_rst_n(In_rst_n),
.Out_cout(Out_line1), // 重點
//.Out_q(Out_q[7:4]) // 方式1
.Out_q(q1) // 方式2
);
my_Counter Conuter2(
.In_clk(In_clk),
.In_cin(Out_line1), // 重點 這裡來一次高電平意味這計數器1計滿
.In_rst_n(In_rst_n),
.Out_cout(Out_cout), // 重點
//.Out_q(Out_q[11:8]) // 方式1
.Out_q(q2) // 方式2
);
endmodule
/* 實驗名稱:計數器
* 程式功能:
* 約定俗成:所有需要外部輸入的信号加入字首"In_"
* 所有需往外部輸出的信号加入字首"Out_"
*/
module my_Counter(In_clk, In_cin, In_rst_n, Out_cout, Out_q);
input In_clk;
input In_cin;
input In_rst_n;
output reg Out_cout;
output [3:0]Out_q;
reg[3:0] cnt; // 存儲計數值
// 捕獲 In_clk 上升沿,捕獲 In_rst_n 下降沿
// 計數程式塊
always@(posedge In_clk or negedge In_rst_n)
if(1'b0 == In_rst_n) // 複位信号處理 計數歸零
cnt <= 4'd0;
else if(1'b1 == In_cin) // In_cin 為高時開始計數
begin
if(4'd9 == cnt) // cnt 等于 9 則歸零
cnt <=4'd0;
else
cnt <= cnt + 1'b1; // cnt 小于 9 則累加
end
else
;
// 捕獲 In_clk 上升沿,捕獲 In_rst_n 下降沿
// 溢出輸出程式塊
always@(posedge In_clk or negedge In_rst_n)
if(1'b0 == In_rst_n) // 複位信号處理 計數歸零
Out_cout <= 1'b0;
else if(1'b1 == In_cin && 4'd9 == cnt)
Out_cout <= 1'b1; // In_cin 為高并且同時上個計數器計數到9則輸出1
else
Out_cout <= 1'b0; // 反之輸出0
assign Out_q = cnt; // 與計數器相連輸出
endmodule
2、仿真測試源程式
/* 實驗名稱:BCD 級聯計數器驗證 */
`timescale 1ns/1ns
`define clock_period 20
module mytest_tb;
reg clk;
reg cin;
reg rst_n;
wire cout;
wire [11:0]q;
mytest BCD_Counter(
.In_clk(clk),
.In_cin(cin),
.In_rst_n(rst_n),
.Out_cout(cout),
.Out_q(q)
);
initial clk = 1'b1;
always #(`clock_period / 2) clk = ~clk;
initial begin
// 複位
rst_n = 1'b0;
cin = 1'b0;
#(`clock_period * 200);
rst_n = 1'b1;
#(`clock_period * 20);
// 直接給高電平,讓它在每個時鐘周期都計數
cin = 1'b1;
#(`clock_period * 5000);
$stop;
end
endmodule
那麼該如何解決了:
1、由于是三級級聯計數器,那麼我們要先看每一級計數器的狀态。需要作如下步驟:
在ModelSim找到SIM視窗:
将Conuter0、Conuter1、Conuter2、都 【Add Wave】同時也看到可以通過快捷鍵【Ctrl+W】添加。
然後傳回到【Wave】視窗在信号視窗中依次按下【Ctrl+A】全選、【Ctrl+G】根據子產品自動分組
這是可以看到那些子產品是看不到信号的,需要重新編譯重新運作才能看到:
接着就可以看到每個信号的現象了:
修複代碼如下:( 目前得到的解釋是:總之非阻塞指派有1個時鐘周期的延遲才會生效 )
/* 實驗名稱:級聯計數器
* 程式功能:
* 約定俗成:所有需要外部輸入的信号加入字首"In_",所有需往外部輸出的信号加入字首"Out_"
*/
module mytest(In_clk, In_cin, In_rst_n, Out_cout, Out_q);
input In_clk;
input In_cin;
input In_rst_n;
output Out_cout;
output[11:0] Out_q;
wire Out_line0; // 計數器0的Out_cout端與計數器1的Out_cout端連結
wire Out_line1; // 計數器1的Out_cout端與計數器2的Out_cout端連結
wire[3:0] q0, q1, q2;
// 将三組4位信号合并成一組12位的信号
assign Out_q = {q2, q1, q0};
my_Counter Conuter0(
.In_clk(In_clk),
.In_cin(In_cin), // 重點
.In_rst_n(In_rst_n),
.Out_cout(Out_line0), // 重點
//.Out_q(Out_q[3:0]) // 方式1
.Out_q(q0) // 方式2
);
my_Counter Conuter1(
.In_clk(In_clk),
.In_cin(Out_line0), // 重點 這裡來一次高電平意味這計數器0計滿
.In_rst_n(In_rst_n),
.Out_cout(Out_line1), // 重點
//.Out_q(Out_q[7:4]) // 方式1
.Out_q(q1) // 方式2
);
my_Counter Conuter2(
.In_clk(In_clk),
.In_cin(Out_line1), // 重點 這裡來一次高電平意味這計數器1計滿
.In_rst_n(In_rst_n),
.Out_cout(Out_cout), // 重點
//.Out_q(Out_q[11:8]) // 方式1
.Out_q(q2) // 方式2
);
endmodule
/* 實驗名稱:計數器
* 程式功能:
* 約定俗成:所有需要外部輸入的信号加入字首"In_"
* 所有需往外部輸出的信号加入字首"Out_"
*/
module my_Counter(In_clk, In_cin, In_rst_n, Out_cout, Out_q);
input In_clk;
input In_cin;
input In_rst_n;
output reg Out_cout;
output [3:0]Out_q;
reg[3:0] cnt; // 存儲計數值
// 捕獲 In_clk 上升沿,捕獲 In_rst_n 下降沿
// 計數程式塊
always@(posedge In_clk or negedge In_rst_n)
if(1'b0 == In_rst_n) // 複位信号處理 計數歸零
cnt <= 4'd0;
else if(1'b1 == In_cin) // In_cin 為高時開始計數
begin
if(4'd9 == cnt) // cnt 等于 9 則歸零
cnt <=4'd0;
else
cnt <= cnt + 1'b1; // cnt 小于 9 則累加
end
else
;
/* 這段代碼會導緻在級聯的時候每一級 Out_cout 都會延遲一個時鐘周期
// 捕獲 In_clk 上升沿,捕獲 In_rst_n 下降沿
// 溢出輸出程式塊
always@(posedge In_clk or negedge In_rst_n)
if(1'b0 == In_rst_n) // 複位信号處理 計數歸零
Out_cout <= 1'b0;
else if(1'b1 == In_cin && 4'd9 == cnt)
Out_cout <= 1'b1; // In_cin 為高并且同時上個計數器計數到9則輸出1
else
Out_cout <= 1'b0; // 反之輸出0
*/
// 修改如下
assign Out_cout = (1'b1 == In_cin && 4'd9 == cnt);
assign Out_q = cnt;
endmodule
關于邏輯單元: