06-BCD计数器设计与应用——小梅哥FPGA设计思想与验证方法视频教程配套文档
芯航線——普利斯隊長精心奉獻
?
實驗目的:1.掌握BCD碼的原理、分類以及優缺點
???????? 2.設計一個多位的8421碼計數器并進行驗證
???????? 3.學會基本的錯誤定位以及修改能力 ????
實驗平臺:無
實驗原理:
????BCD碼(Binary-Coded Decimal)又被稱為二進碼十進數、二-十進制代碼是一種十進制的數字編碼,用4位二進制數來表示十進制數中的0~9個十個數之一。BCD編碼又可以分成有權碼和無權碼兩種,其中有權碼如:8421碼、2421碼以及5421等;無權碼如:余3碼、格雷碼以及余3循環碼等。
????BCD碼中最常用的是8421碼,其四個bit權值分別是8,4,2,1;同理5421碼各位的權依次為5,4,2,1,5421碼特點是最高位連續5個0后連續5個1,故其當計數器采用這種編碼時,最高位可產生對稱方波輸出;余3碼是在8421碼上加加0011的出來的;碼格雷碼的特點是任意兩個相鄰的代碼只有一位二進制數不同,編碼格式不唯一;余3循環具有格雷碼的特點還具有編碼的首尾可以連接來進行循環,這樣可用反饋移位寄存器來實現,硬件實現簡單。下面表6-1給出常見的幾種編碼格式:
| 十進制數 | 8421碼 | 余3碼 | 2421碼 | 5421碼 | 格雷碼 | 余3循環碼 |
| 0 | 0000 | 0011 | 0000 | 0000 | 0000 | 0010 |
| 1 | 0001 | 0100 | 0001 | 0001 | 0001 | 0110 |
| 2 | 0010 | 0101 | 0010 | 0010 | 0011 | 0111 |
| 3 | 0011 | 0110 | 0011 | 0011 | 0010 | 0101 |
| 4 | 0100 | 0111 | 0100 | 0100 | 0110 | 0100 |
| 5 | 0101 | 1000 | 1011 | 1000 | 0111 | 1100 |
| 6 | 0110 | 1001 | 1100 | 1001 | 0101 | 1101 |
| 7 | 0111 | 1010 | 1101 | 1010 | 0100 | 1111 |
| 8 | 1000 | 1011 | 1110 | 1011 | 1100 | 1110 |
| 9 | 1001 | 1100 | 1111 | 1100 | 1101 | 1010 |
表6-1 常見的BCD碼
在實際使用中如不特指BCD碼格式均為代指8421碼。通過以上介紹將十進制895轉換為BCD碼就是1001_1001_0101,同理若將BCD碼1001_0110_0100轉換為十進制數即為964。
BCD碼的運算規則:BCD碼是十進制數,而運算器對數據做加減運算時,都是按二進制運算規則進行處理的。這樣,當將BCD碼傳送給運算器進行運算時,其結果需要修正。修正的規則是:當兩個BCD碼相加,如果和等于或小于 1001(即十進制數9),不需要修正;如果相加之和在 1010 到1111(即十六進制數 0AH~0FH)之間,則需加 'd6也就是'b0110進行修正;如果相加時,本位產生了進位,也需加 6 進行修正。下面舉例說明:計算5+8,將5和8轉換為8421 BCD碼后輸入加法器,則運算如下:0 1 0 1 + 1 0 0 0 = 1 1 0 1 結果大于9,+ 0 1 1 0 即加 6 修正得出1 0 0 1 1,補充高位為0001_0011。5+8=13,結論正確。
BCD碼的主要應用之一就是數碼管,假設我們要將十進制數158顯示,一般解決辦法是先要除法運算158/100= 1得出百位,再取余158%100 = 58后繼續進行除法運算58 / 10 = 5得出十位,再進行一次取余158%10 = 8,得到個位。以上過程可以看出需要除法,但是由于除法運算是比較消耗計算時間導致整體需要的指令周期太久。但是如果我們先將其轉換為BCD碼,則可大幅度減少運算時間。具體例子會在數碼管一講詳細介紹。
實驗步驟:
按照02章所講,建立工程子文件夾后,新建一個以名為BCD_Counter的工程保存在prj下,并在本工程目錄的rtl文件夾下新建verilog file文件在此文件下輸入以下內容并以BCD_Counter.v保存。
| 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; //定義計數器寄存器 ? //執行計數過程 always@(posedge 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@(posedge Clk or negedge Rst_n) if (!Rst_n) Cout <= 1'b0; else if(Cin == 1'b1 && cnt ==4'd9) Cout <= 1'b1; else Cout <= 1'b0; ? assign q = cnt; ? endmodule |
?
進行分析和綜合直至沒有錯誤以及警告。
????為了測試仿真編寫測試激勵文件,新建BCD_Counter_tb.v文件保存到testbench文件夾下,輸入以下內容再次進行分析和綜合直至沒有錯誤以及警告。本激勵文件除產生正常的時鐘以及復位信號外,還生成了重復30次的占空比為1:5周期為120ns的cin信號。
| `timescale 1ns/1ns ? `define clock_period 20 ? module BCD_Counter_tb; ? reg Clk; reg Cin; reg Rst_n; ? 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; ? initial begin Rst_n = 1'b0; Cin = 1'b0; #(`clock_period*200); 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 |
?
設置好仿真腳本后進行功能仿真,可以看到如圖6-1所示的波形文件,可以看出在復位信號置高后,每當進位輸入信號cin為高時計數值輸出q完成一次自加,直到計數值為9后清零重新計數并產生進位信號。
圖6-1 功能仿真波形圖
現在以上面的BCD計數器為基礎設計級聯的多位BCD計數器,這里我們將計數器位數設置為12,即3個BCD計數器級聯既可以實現。新建verilog file文件在此文件下輸入以下內容并以BCD_Counter_top.v保存至rtl文件夾下。本文件實現了例化與調用BCD_counter.v文件并將進位信號根據需要連接。
| 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 |
將上述的文件設置為頂層,并再次進行分析和綜合直至沒有錯誤以及警告。點擊RTL viewer,可以看到圖6-2的模塊結構,可以看出符合預期目的。
圖6-2多級BCD計數器RTL視圖
為了測試仿真編寫測試激勵文件,新建BCD_Counter_top_tb.v文件保存到testbench文件夾下,輸入以下內容再次進行分析和綜合直至沒有錯誤以及警告。本激勵文件為了簡化分析,復位后將cin一直置高,并延遲一定的時間。由于現在為三級BCD計數器,計數器滿值為十六進制的999,特將仿真時間進行了延長至5000個時鐘周期。
| `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; ? 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 |
????設置好仿真腳本后進行功能仿真,可以看到如圖6-3所示的波形文件,可以看到進位輸出信號cout在計數值q變為十六進制999后延遲了兩個系統周期才有輸出,不符合既定設計,即設計存在錯誤。
圖6-3 多級BCD計數器初次功能仿真
????為了定位錯誤,將子模塊的相關信號加入到wave欄,并再次仿真查看內部數據的信息進行分析解決。
????在Modelsim找到Inatance窗口找到頂層文件,如圖6-4-1所示點擊加號后可以看到本頂層設計調用的模塊。如圖6-4-2所示單擊不同的模塊在Object欄可以看到其端口列表,選中需要的右鍵Add Wave,即可將內部信號加入到波形窗口。這里我門將每個模塊的計數值輸出信號q以及進位輸出信號cout加入到波形窗口中。
圖6-4-1 添加內部信號到wave窗口
圖6-4-2 添加內部信號到wave窗口
單擊工具欄中的Restart來復位仿真,在彈出對話框中全選后點擊OK,點擊Run-All來重啟仿真。
圖6-5-1 復位仿真
圖6-5-2 復位仿真
圖6-6 重啟仿真
可以看到仿真后加入內部信號的波形較亂沒有層次,這里介紹一個分組操作,我們首先ctrl+A選中所有wave窗口中的波形,后ctrl+G進行分組。分組后如圖6-7所示,放大局部信號可以看到照成這個原因是由于每一級的BCD計數器的進位輸出信號均延遲了一個時鐘周期,從而導致頂層文件進位輸出cout信號輸出延遲了三個時鐘周期的問題,此時的計數器值已經變為了十六進制的002,而不再是999。
圖6-7 加入內部信號的功能仿真波形
這樣我們就定位了錯誤,只需將修改進位輸出信號cout修改為與計數值信號q計數到9時同時輸出。將進位產生信號的邏輯修改為如下,且將Cout的類型改為普通wire型。
| assign Cout = (Cin == 1'b1 && cnt == 4'd9); |
再次仿真后可以看到在圖6-8-1中十六進制的999時輸出進位信號,符合了原定的要求,我們可以再將顯示格式修改為二進制數,如圖6-8-2中在計數值q為1001_1001_1001產生進位信號,通過前面所講的BCD碼原理將其轉換為BCD碼的格式也是999。
圖6-8-1 修改后的BCD計數器波形圖
圖6-8-2 修改后的BCD計數器波形圖
至此,就完成了一個BCD計數器的設計,并且學會了基本的調試修改能力。具體BCD計數器的板級驗證,可以參考后續文檔關于芯航線數碼管的驅動設計。
?
備注:在修改設計后如果進行單獨的功能仿真,會發現在圖6-9中進位信號存在毛刺(glitch),這里的解決辦法很多,其中之一就是在后續設計中通過預估正常信號與毛刺信號的時間寬度來進行篩選,也可以通過相關約束來解決;硬件方面可以外接濾波電容來消除其影響。
圖6-9 毛刺信號
轉載于:https://www.cnblogs.com/xiaomeige/p/5500950.html
總結
以上是生活随笔為你收集整理的06-BCD计数器设计与应用——小梅哥FPGA设计思想与验证方法视频教程配套文档的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 四十三 常用内建模块 base64
- 下一篇: [Leetcode][第491题][JA