天天看點

基于FPGA的BCD計數器設計與驗證

部落客福利: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 省略了導緻不行出現如下現象:

基于FPGA的BCD計數器設計與驗證

正常的波形:(其實cout波形也是不對的,數到9就已經是第10個數了,就應該給高電平,可對比IP核)

基于FPGA的BCD計數器設計與驗證

差異在這裡:

基于FPGA的BCD計數器設計與驗證

假如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視窗:

基于FPGA的BCD計數器設計與驗證

将Conuter0、Conuter1、Conuter2、都 【Add Wave】同時也看到可以通過快捷鍵【Ctrl+W】添加。

基于FPGA的BCD計數器設計與驗證

然後傳回到【Wave】視窗在信号視窗中依次按下【Ctrl+A】全選、【Ctrl+G】根據子產品自動分組

基于FPGA的BCD計數器設計與驗證

這是可以看到那些子產品是看不到信号的,需要重新編譯重新運作才能看到:

基于FPGA的BCD計數器設計與驗證

接着就可以看到每個信号的現象了:

基于FPGA的BCD計數器設計與驗證

修複代碼如下:( 目前得到的解釋是:總之非阻塞指派有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      

關于邏輯單元:

繼續閱讀