BCD碼(Binary-Coded Decimal)亦稱二進碼十進數或二-十進制代碼。用4位二進制數來表示1位十進制數中的0~9這10個數碼。是一種二進制的數字編碼形式,用二進制編碼的十進制代碼。BCD碼這種編碼形式利用了四個位元來儲存一個十進制的數碼,使二進制和十進制之間的轉換得以快捷進行。(百度百科)
例子:158 以BCD編碼方式編碼就會變成
0 | 0 | 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 | 0 |
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
|
關于邏輯單元:
如果邏輯單元后面的數目為0,那么說明無法實現,說明設計是有問題的。
歡迎光臨 (http://www.zg4o1577.cn/bbs/) | Powered by Discuz! X3.1 |