??歡迎來到FPGA專欄~BCD計數(shù)器設(shè)計
- ☆* o(≧▽≦)o *☆嗨~我是小夏與酒??
- ?博客主頁:小夏與酒的博客
- ??該系列文章專欄:FPGA學(xué)習(xí)之旅
- 文章作者技術(shù)和水平有限,如果文中出現(xiàn)錯誤,希望大家能指正??
- ?? 歡迎大家關(guān)注! ??
![]()
一、效果演示
頂層模塊中的BCD模塊級聯(lián):
Verilog實現(xiàn):
調(diào)用ip核實現(xiàn):
當(dāng)計數(shù)到12‘h999
時,產(chǎn)生一個進位輸出:
二、BCD碼基礎(chǔ)知識
BCD 碼中最常用的是 8421 碼,其各個 bit 權(quán)值分別是 8d、4d、2d、1d;同理 5421 碼各位的權(quán)依次為 5d、4d、2d、1d。5421 碼特點是最高位連續(xù) 5 個 0 后連續(xù) 5 個 1,故其當(dāng)計數(shù)器采用這種編碼時,最高位可產(chǎn)生對稱方波輸出;余 3 碼是在 8421 碼上加 0011b 得出來的;格雷碼的特點是任意兩個相鄰的代碼只有一位二進制數(shù)不同,編碼格式不唯一;余 3 循環(huán)碼具有格雷碼的特點并且編碼的首尾可以連接來進行循環(huán),這樣可用反饋移位寄存器來實現(xiàn),硬件實現(xiàn)簡單。
在實際使用中如不特指 BCD 碼格式,則均為 8421 碼。通過以上介紹將十進制 895 轉(zhuǎn)換為BCD 碼就是 1000_1001_0101,同理若將 BCD 碼 1001_0110_0100 轉(zhuǎn)換為十進制數(shù)即為 964。
BCD 碼的主要應(yīng)用之一就是數(shù)碼管,假設(shè)要將十進制數(shù) 158 顯示,一般解決辦法是把需要顯示的十進制數(shù)的個、十、百、千位數(shù)等進行拆分,即把 158 拆分出 1、5、8,然后查出對應(yīng)的數(shù)碼管顯示段碼再送去給數(shù)碼管連接的 IO 口。這個過程可以進行下面的運算:先進行除法運算 158/100 得出百位 1,再取余 158%100 = 58 后繼續(xù)進行除法運算 58 / 10 得出十位5,再進行一次取余 158%10 ,得到個位 8。以上過程可以看出需要除法,但是由于除法運算是比較消耗計算時間導(dǎo)致整體需要的指令周期太久。但是如果先將其轉(zhuǎn)換為 BCD 碼,則可大幅度減少運算時間。
三、BCD計數(shù)器Verilog實現(xiàn)
我們使用Verilog HDL實現(xiàn)一個BCD計數(shù)器。
一個BCD計數(shù)模塊結(jié)構(gòu)如下:
其中,各端口信號的含義如下表所示:
端口信號 | I/O | 功能 |
---|---|---|
Clk | input | 50M系統(tǒng)時鐘 |
Rst_n | input | 全局復(fù)位 |
Cin | input | 進位輸入信號 |
Cout | output | 進位輸出信號 |
q | output | 計數(shù)值輸出 |
對于計數(shù)過程,最大計數(shù)值設(shè)定為4‘d9
:
//執(zhí)行計數(shù)過程
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
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;
end
??需要注意的是,只有當(dāng)計數(shù)值達到4’d9
,且Cin信號為高電平時,Cout才會產(chǎn)生一個高電平信號。
Verilog HDL代碼:
module BCD_Counter(
input Clk, //系統(tǒng)時鐘信號
input Rst_n, //系統(tǒng)復(fù)位信號
input Cin, //進位輸入信號
output reg Cout, //進位輸出信號
output [3:0] q //計數(shù)器值
);
reg [3:0]cnt;
//執(zhí)行計數(shù)過程
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
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;
end
//產(chǎn)生進位輸出信號
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
Cout <= 1'b0;
else if(cnt == 4'd9 && Cin == 1'b1)
Cout <= 1'b1;
else
Cout <= 1'b0;
end
assign q = cnt;
endmodule
RTL視圖:
在仿真測試過程中,為了便于觀察,我們產(chǎn)生占空比為1:5
的Cin信號:
repeat(30)begin
Cin = 1'b1;
#(`clock_period*1);
Cin = 1'b0;
#(`clock_period*5);
end
測試激勵文件:
`timescale 1ns/1ns
`define clock_period 20
module BCD_Counter_tb;
reg Clk;
reg Rst_n;
reg Cin;
wire Cout;
wire [3:0]q;
BCD_Counter BCD_Counter0(
.Clk(Clk), //系統(tǒng)時鐘信號
.Rst_n(Rst_n), //系統(tǒng)復(fù)位信號
.Cin(Cin), //進位輸入信號
.Cout(Cout), //進位輸出信號
.q(q) //計數(shù)器值
);
initial Clk = 1;
always#(`clock_period/2) Clk = ~Clk;
initial begin
Rst_n = 1'b0;
Cin = 1'b0;
#(`clock_period*20);
Rst_n = 1;
#(`clock_period*20);
repeat(30)begin
Cin = 1'b1;
#(`clock_period*1);
Cin = 1'b0;
#(`clock_period*5);
end
#(`clock_period*20);
$stop;
end
endmodule
仿真結(jié)果:
四、級聯(lián)BCD計數(shù)器實現(xiàn)
BCD計數(shù)器的級聯(lián)RTL視圖:
在上述設(shè)計過程中,一個BCD計數(shù)器的計數(shù)范圍為:4'd0~4'd9
,即4'h0~4'h9
。
在此使用了三個BCD計數(shù)器進行級聯(lián),因此頂層輸出的hex格式
數(shù)據(jù)范圍為000~999
。
4.1 Verilog實現(xiàn)
在頂層文件中調(diào)用已編寫好的BCD模塊:
module BCD_Counter_top(
input Clk, //系統(tǒng)時鐘信號
input Rst_n, //系統(tǒng)復(fù)位信號
input Cin, //進位輸入信號
output Cout, //進位輸出信號
output [11:0] q //計數(shù)器值
);
wire Cout0;
wire Cout1;
wire Cout2;
wire [3:0]q0;
wire [3:0]q1;
wire [3:0]q2;
BCD_Counter BCD_Counter0(
.Clk(Clk),
.Rst_n(Rst_n),
.Cin(Cin),
.Cout(Cout0),
.q(q0)
);
BCD_Counter BCD_Counter1(
.Clk(Clk),
.Rst_n(Rst_n),
.Cin(Cout0),
.Cout(Cout1),
.q(q1)
);
BCD_Counter BCD_Counter2(
.Clk(Clk),
.Rst_n(Rst_n),
.Cin(Cout1),
.Cout(Cout2),
.q(q2)
);
assign Cout = Cout2;
assign q = {q2,q1,q0};
endmodule
由于級聯(lián)的BCD計數(shù)器計數(shù)范圍較大,在編寫測試激勵文件的時候可以直接把Cin信號置為高電平:
`timescale 1ns/1ns
`define clock_period 20
module BCD_Counter_top_tb;
reg Clk;
reg Rst_n;
reg Cin;
wire Cout;
wire [11:0]q;
BCD_Counter_top BCD_Counter_top0(
.Clk(Clk), //系統(tǒng)時鐘信號
.Rst_n(Rst_n), //系統(tǒng)復(fù)位信號
.Cin(Cin), //進位輸入信號
.Cout(Cout), //進位輸出信號
.q(q) //計數(shù)器值
);
initial Clk = 1;
always#(`clock_period) Clk = ~Clk;
initial begin
Rst_n = 0;
Cin = 1'b0;
#(`clock_period*10)
Rst_n = 1;
#(`clock_period*5)
Cin = 1'b1;
#(`clock_period*5000)
$stop;
end
endmodule
仿真結(jié)果如下圖:
從仿真結(jié)果中可以看出來:Cout信號
延遲了三個系統(tǒng)時鐘周期才出現(xiàn),設(shè)計存在錯誤,不符合設(shè)計要求。
加入具體波形,發(fā)現(xiàn)BCD_Counter0
、BCD_Counter1
和BCD_Counter2
的輸出已經(jīng)出現(xiàn)系統(tǒng)時鐘周期的延遲:
在基礎(chǔ)的BCD_Counter模塊中,Cout信號的產(chǎn)生使用了D觸發(fā)器,所以造成了一拍的延遲。為了消除延遲效果,需要使用組合邏輯電路:
...
output Cout, //進位輸出信號
...
//產(chǎn)生進位輸出信號
assign Cout = (cnt == 4'd9 && Cin == 1'b1);
...
即:
module BCD_Counter(
input Clk, //系統(tǒng)時鐘信號
input Rst_n, //系統(tǒng)復(fù)位信號
input Cin, //進位輸入信號
output Cout, //進位輸出信號
output [3:0] q //計數(shù)器值
);
reg [3:0]cnt;
//執(zhí)行計數(shù)過程
always@(posedge Clk or negedge Rst_n)begin
if(!Rst_n)
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;
end
//產(chǎn)生進位輸出信號
assign Cout = (cnt == 4'd9 && Cin == 1'b1);
assign q = cnt;
endmodule
重新仿真,計數(shù)結(jié)果正確:
4.2 ip核實現(xiàn)
使用ip核創(chuàng)建計數(shù)器的過程:【FPGA零基礎(chǔ)學(xué)習(xí)之旅#6】ip核基礎(chǔ)知識之計數(shù)器。
首先使用ip核創(chuàng)建一個計數(shù)器,在此步驟勾選同步復(fù)位:
ip核生成代碼如下:
// megafunction wizard: %LPM_COUNTER%
// GENERATION: STANDARD
// VERSION: WM1.0
// MODULE: LPM_COUNTER
// ============================================================
// File Name: Counter.v
// Megafunction Name(s):
// LPM_COUNTER
//
// Simulation Library Files(s):
// lpm
// ============================================================
// ************************************************************
// THIS IS A WIZARD-GENERATED FILE. DO NOT EDIT THIS FILE!
//
// 13.0.0 Build 156 04/24/2013 SJ Full Version
// ************************************************************
//Copyright (C) 1991-2013 Altera Corporation
//Your use of Altera Corporation's design tools, logic functions
//and other software and tools, and its AMPP partner logic
//functions, and any output files from any of the foregoing
//(including device programming or simulation files), and any
//associated documentation or information are expressly subject
//to the terms and conditions of the Altera Program License
//Subscription Agreement, Altera MegaCore Function License
//Agreement, or other applicable license agreement, including,
//without limitation, that your use is for the sole purpose of
//programming logic devices manufactured by Altera and sold by
//Altera or its authorized distributors. Please refer to the
//applicable agreement for further details.
// synopsys translate_off
`timescale 1 ps / 1 ps
// synopsys translate_on
module Counter (
cin,
clock,
sclr,
cout,
q);
input cin;
input clock;
input sclr;
output cout;
output [3:0] q;
wire sub_wire0;
wire [3:0] sub_wire1;
wire cout = sub_wire0;
wire [3:0] q = sub_wire1[3:0];
lpm_counter LPM_COUNTER_component (
.cin (cin),
.clock (clock),
.sclr (sclr),
.cout (sub_wire0),
.q (sub_wire1),
.aclr (1'b0),
.aload (1'b0),
.aset (1'b0),
.clk_en (1'b1),
.cnt_en (1'b1),
.data ({4{1'b0}}),
.eq (),
.sload (1'b0),
.sset (1'b0),
.updown (1'b1));
defparam
LPM_COUNTER_component.lpm_direction = "UP",
LPM_COUNTER_component.lpm_modulus = 10,
LPM_COUNTER_component.lpm_port_updown = "PORT_UNUSED",
LPM_COUNTER_component.lpm_type = "LPM_COUNTER",
LPM_COUNTER_component.lpm_width = 4;
endmodule
// ============================================================
// CNX file retrieval info
// ============================================================
// Retrieval info: PRIVATE: ACLR NUMERIC "0"
// Retrieval info: PRIVATE: ALOAD NUMERIC "0"
// Retrieval info: PRIVATE: ASET NUMERIC "0"
// Retrieval info: PRIVATE: ASET_ALL1 NUMERIC "1"
// Retrieval info: PRIVATE: CLK_EN NUMERIC "0"
// Retrieval info: PRIVATE: CNT_EN NUMERIC "0"
// Retrieval info: PRIVATE: CarryIn NUMERIC "1"
// Retrieval info: PRIVATE: CarryOut NUMERIC "1"
// Retrieval info: PRIVATE: Direction NUMERIC "0"
// Retrieval info: PRIVATE: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E"
// Retrieval info: PRIVATE: ModulusCounter NUMERIC "1"
// Retrieval info: PRIVATE: ModulusValue NUMERIC "10"
// Retrieval info: PRIVATE: SCLR NUMERIC "1"
// Retrieval info: PRIVATE: SLOAD NUMERIC "0"
// Retrieval info: PRIVATE: SSET NUMERIC "0"
// Retrieval info: PRIVATE: SSET_ALL1 NUMERIC "1"
// Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
// Retrieval info: PRIVATE: nBit NUMERIC "4"
// Retrieval info: PRIVATE: new_diagram STRING "1"
// Retrieval info: LIBRARY: lpm lpm.lpm_components.all
// Retrieval info: CONSTANT: LPM_DIRECTION STRING "UP"
// Retrieval info: CONSTANT: LPM_MODULUS NUMERIC "10"
// Retrieval info: CONSTANT: LPM_PORT_UPDOWN STRING "PORT_UNUSED"
// Retrieval info: CONSTANT: LPM_TYPE STRING "LPM_COUNTER"
// Retrieval info: CONSTANT: LPM_WIDTH NUMERIC "4"
// Retrieval info: USED_PORT: cin 0 0 0 0 INPUT NODEFVAL "cin"
// Retrieval info: USED_PORT: clock 0 0 0 0 INPUT NODEFVAL "clock"
// Retrieval info: USED_PORT: cout 0 0 0 0 OUTPUT NODEFVAL "cout"
// Retrieval info: USED_PORT: q 0 0 4 0 OUTPUT NODEFVAL "q[3..0]"
// Retrieval info: USED_PORT: sclr 0 0 0 0 INPUT NODEFVAL "sclr"
// Retrieval info: CONNECT: @cin 0 0 0 0 cin 0 0 0 0
// Retrieval info: CONNECT: @clock 0 0 0 0 clock 0 0 0 0
// Retrieval info: CONNECT: @sclr 0 0 0 0 sclr 0 0 0 0
// Retrieval info: CONNECT: cout 0 0 0 0 @cout 0 0 0 0
// Retrieval info: CONNECT: q 0 0 4 0 @q 0 0 4 0
// Retrieval info: GEN_FILE: TYPE_NORMAL Counter.v TRUE
// Retrieval info: GEN_FILE: TYPE_NORMAL Counter.inc FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL Counter.cmp FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL Counter.bsf FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL Counter_inst.v FALSE
// Retrieval info: GEN_FILE: TYPE_NORMAL Counter_bb.v TRUE
// Retrieval info: LIB_FILE: lpm
創(chuàng)建一個頂層文件,并在頂層中調(diào)用ip核實現(xiàn)級聯(lián):
module Counter_ip_top(
input Clk,
input Rst_n,
input Cin,
output Cout,
output [11:0] q
);
wire Cout0;
wire Cout1;
wire Cout2;
wire [3:0]q0;
wire [3:0]q1;
wire [3:0]q2;
Counter Counter0(
.cin(Cin),
.clock(Clk),
.sclr(Rst_n),
.cout(Cout0),
.q(q0)
);
Counter Counter1(
.cin(Cout0),
.clock(Clk),
.sclr(Rst_n),
.cout(Cout1),
.q(q1)
);
Counter Counter2(
.cin(Cout1),
.clock(Clk),
.sclr(Rst_n),
.cout(Cout2),
.q(q2)
);
assign Cout = Cout2;
assign q = {q2,q1,q0};
endmodule
測試激勵文件:
`timescale 1ns/1ns
`define clock_period 20
module Counter_ip_top_tb;
reg Clk;
reg Rst_n;
reg Cin;
wire Cout;
wire [11:0]q;
Counter_ip_top ounter_ip_top(
.Clk(Clk),
.Rst_n(Rst_n),
.Cin(Cin),
.Cout(Cout),
.q(q)
);
initial Clk = 1;
always#(`clock_period) Clk = ~Clk;
initial begin
Rst_n = 0;
Cin = 1'b0;
#(`clock_period*10)
Rst_n = 1;
#(`clock_period*5)
Cin = 1'b1;
#(`clock_period*5000)
$stop;
end
endmodule
運行仿真,看到的結(jié)果如下:Cout
和q
沒有任何變化,說明剛才由ip核生成的計數(shù)器的同步復(fù)位信號是高電平觸發(fā),即高電平觸發(fā)系統(tǒng)復(fù)位。
在一般的系統(tǒng)設(shè)計中,我們習(xí)慣于低電平觸發(fā)系統(tǒng)復(fù)位,我們在頂層模塊中稍加修改:
//使系統(tǒng)復(fù)位信號翻轉(zhuǎn)以匹配ip核Counter的復(fù)位
wire Rst_n0;
assign Rst_n0 = ~Rst_n;
即:
module Counter_ip_top(
input Clk,
input Rst_n,
input Cin,
output Cout,
output [11:0] q
);
wire Cout0;
wire Cout1;
wire Cout2;
wire [3:0]q0;
wire [3:0]q1;
wire [3:0]q2;
wire Rst_n0;
assign Rst_n0 = ~Rst_n;
Counter Counter0(
.cin(Cin),
.clock(Clk),
.sclr(Rst_n0),
.cout(Cout0),
.q(q0)
);
Counter Counter1(
.cin(Cout0),
.clock(Clk),
.sclr(Rst_n0),
.cout(Cout1),
.q(q1)
);
Counter Counter2(
.cin(Cout1),
.clock(Clk),
.sclr(Rst_n0),
.cout(Cout2),
.q(q2)
);
assign Cout = Cout2;
assign q = {q2,q1,q0};
endmodule
仿真結(jié)果符合設(shè)計預(yù)期:
RTL視圖:
文章來源:http://www.zghlxwxcb.cn/news/detail-481985.html
??結(jié)尾文章來源地址http://www.zghlxwxcb.cn/news/detail-481985.html
- ?? 感謝您的支持和鼓勵! ????
- ??您可能感興趣的內(nèi)容:
- 【stm32開發(fā)】stm32+oled最小系統(tǒng)板資料(原理圖、PCB、示例代碼)【六一】
- 【FPGA零基礎(chǔ)學(xué)習(xí)之旅#5】產(chǎn)生非等占空比信號
- 【Arduino TinyGo】【最新】使用Go語言編寫Arduino-環(huán)境搭建和點亮LED燈
- 【全網(wǎng)首發(fā)開源教程】【Labview機器人仿真與控制】Labview與Solidworks多路支配關(guān)系-四足爬行機器人仿真與控制
![]()
到了這里,關(guān)于【FPGA零基礎(chǔ)學(xué)習(xí)之旅#7】BCD計數(shù)器設(shè)計的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!