FC2カウンター FPGAの部屋 2011年10月30日
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

システム開発入門[Altera偏]を試してみる4(2.4アップダウン・カウンタのシミュレーション2)

システム開発入門[Altera偏]を試してみる4(2.4アップダウン・カウンタのシミュレーション1)”の続き。

今回は、btn_in.vの修正点と新たに作成したテストベンチ、updown_tb.v について説明する。

最初にbtn_in.v の変更点から説明する。
btn_in.v の50MHzを1250000分周して40Hzを作るen40hzの書き方をparameterを使用した書き方に変更させていただいた。
元のbtn_in.v の一部を下に示す。

reg [20:0] cnt;

wire en40hz = (cnt==1250000-1);


下にparameterを使用した書き方を示す。

parameter DIV_VAL = 1250000;

reg [20:0] cnt;

wire en40hz = (cnt==DIV_VAL-1);


これで、テストベンチから、DIV_VALの値を変更することができる。
updown.v とbtn_in.v を通常にシミュレーションしたのでは、en40hzが1になるまでに、1250000クロックかかってしまう。これではシミュレーション時間がとても長くなってしまう。そこで、シミュレーションの時だけ、DIV_VALを2に設定して、2クロック間スイッチに1を入れておけば動作するようにした。このテクニックはVerilogだけ使えて、VHDLは使用できない。でも、シミュレーション時に再上位のテストベンチからシミュレーションしやすいようにのみ使うのが妥当だ。通常の論理合成可能な記述で使うと、何処でオーバーライトされているかわからずにバグの温床を抱えることになってしまう。

(注1:ちなみに、ModelSimであれば、VHDLでもinit_signal_spyを使用して、VHDLの他の階層のsignalをミラーする事ができる)
(注2:VHDLの共有変数を使用すると、似たようなことが書ける

次に、updown_tb.v を下に示す。

// UPDOWN.v用テストベンチ

`default_nettype none
`timescale 1ns/100ps

module UPDOWN_tb;

    reg                clk = 1'b0;
    reg                rst = 1'b1;
    reg        [2:0]    nBUTTON = 3'b000;
    wire    [6:0]    nHEX0;
    integer        j;
    
    // nBUTTON と押しボタンスイッチの対応
    parameter RESET_SW_ENABLE =    3'b100;
    parameter DOWN_SW_ENALBE =    3'b010;
    parameter UP_SW_ENABLE =    3'b001;
    parameter SW_DISABLE =        3'b000;
    
    // スイッチのチャタリング防止用のイネーブルの間隔(シミュレーション用)
    parameter CHATTERING_INTR = 2;
    
    // クロック用のパラメータとクロック生成
    parameter PERIOD = 20;
    parameter real DUTY_CYCLE = 0.5;
    parameter OFFSET = 0;
    
    initial
    begin
        #OFFSET;
        forever
        begin
            clk = 1'b0;
            #(PERIOD-(PERIOD*DUTY_CYCLE)) clk = 1'b1;
            #(PERIOD*DUTY_CYCLE);
        end
    end

    // UPDOWNモジュールのインスタンス
    UPDOWN UPDOWN_inst (
        .CLK(clk),
        .RST(rst),
        .nBUTTON(nBUTTON),
        .nHEX0(nHEX0)
    );
    // btn_in.vのDIV_VALの値をシミュレーションに都合が良いように変更する
    defparam UPDOWN_inst.BTN_IN.DIV_VAL = CHATTERING_INTR; 
    
    // テストベンチ部分、各taskを呼び出してテストを行う
    initial begin
        #(PERIOD*2);    // 2クロック待ってリセットを0にする
        rst = 1'b0;
        
        #1;    // 遅延
        @(posedge clk);    // 次のクロックへ
        #1;    // 遅延
        
        UP_SW(4'd10);    // 10回、UPスイッチを押す
        
        DOWN_SW(4'd7);    // 7回、DOWNスイッチを押す
        
        RESET_SW;    // RESETスイッチを押す
        
        #(PERIOD*5);    // 5クロック待つ
        
        $stop;
    end

    // i回、UP_SWを押すtask (i < 16)
    task UP_SW;
        input [3:0] i;    // バースト回数
        begin
            @(posedge clk);    // 次のクロックへ
            #1;    // 遅延
            while (i>0) begin
                nBUTTON = UP_SW_ENABLE;
                CHATTERING_WAIT;    // チャタリング防止用の期間待つ
                
                nBUTTON = SW_DISABLE;
                CHATTERING_WAIT;    // チャタリング防止用の期間待つ
                
                i=i-4'd1;
            end
        end
    endtask
    
    // i回、DOWN_SWを押すtask (i < 16)
    task DOWN_SW;
        input [3:0] i;    // バースト回数
        begin
            @(posedge clk);    // 次のクロックへ
            #1;    // 遅延
            while (i>0) begin
                nBUTTON = DOWN_SW_ENALBE;
                CHATTERING_WAIT;    // チャタリング防止用の期間待つ
                
                nBUTTON = SW_DISABLE;
                CHATTERING_WAIT;    // チャタリング防止用の期間待つ
                
                i=i-4'd1;
            end
        end
    endtask
    
    // RESET_SW を押すtask
    task RESET_SW;
        begin
            @(posedge clk);    // 次のクロックへ
            #1;    // 遅延
            nBUTTON = RESET_SW_ENABLE;
            CHATTERING_WAIT;    // チャタリング防止用の期間待つ
            nBUTTON = SW_DISABLE;
            CHATTERING_WAIT;    // チャタリング防止用の期間待つ
        end
    endtask
    
    // チャタリング防止用の期間Waitするtask
    task CHATTERING_WAIT;
        begin
            for (j=CHATTERING_INTR; j>0; j=j-1) begin // チャタリング防止用の期間、値を保持する
                @(posedge clk);    // 次のクロックへ
                #1;    // 遅延
            end
        end
    endtask
    
endmodule

`default_nettype wire



部分的な解説

    // クロック用のパラメータとクロック生成
    parameter PERIOD = 20;
    parameter real DUTY_CYCLE = 0.5;
    parameter OFFSET = 0;
    
    initial
    begin
        #OFFSET;
        forever
        begin
            clk = 1'b0;
            #(PERIOD-(PERIOD*DUTY_CYCLE)) clk = 1'b1;
            #(PERIOD*DUTY_CYCLE);
        end
    end


上の部分はクロックを生成する記述だ。確か、何処かで見て、参考にさせていただいている。
これだと、クロックサイクル、デューテー・サイクル、オフセットを指定することができる。注意が必用なのは、時間分解能がns だとしたら、きちんと割り切れる値にする必要が有ることだ。上の場合は例えば、PERIODを奇数にしてしまうと、小数点が出てしまい切り捨てられるので、思った波形にならない。その場合は時間分解能を100ps などに変更する。

下の部分で、defparamを使用して、btn_in.vのDIV_VALに2を代入している。

    // btn_in.vのDIV_VALの値をシミュレーションに都合が良いように変更する
    defparam UPDOWN_inst.BTN_IN.DIV_VAL = CHATTERING_INTR; 



テストベンチの主な部分を下に示す。

    // テストベンチ部分、各taskを呼び出してテストを行う
    initial begin
        #(PERIOD*2);    // 2クロック待ってリセットを0にする
        rst = 1'b0;
        
        #1;    // 遅延
        @(posedge clk);    // 次のクロックへ
        #1;    // 遅延
        
        UP_SW(4'd10);    // 10回、UPスイッチを押す
        
        DOWN_SW(4'd7);    // 7回、DOWNスイッチを押す
        
        RESET_SW;    // RESETスイッチを押す
        
        #(PERIOD*5);    // 5クロック待つ
        
        $stop;
    end


@(posedge clk); は、次のクロックの立ち上がりまで待つ記述となる。その後の#1の遅延は、現在、クロックの立ち上がりの瞬間にいるので、時刻を少しでもずらすために入れてある。デルタ時間も0では、次の@(posedge clk); がうまく動作しない可能性があるからだ。

UP_SWタスクは4ビット幅の引数を1つ持つ。引数にはUP_SWを押す回数を設定する。今回は10を設定している。
DOWN_SWタスクは4ビット幅の引数を1つ持つ。引数にはDOWN_SWを押す回数を設定する。今回は7を設定している。
RESE_SWタスクは引数を持たない。RESE_SWタスクを呼び出すと、RSETスイッチが1回押される。


UP_SWタスクを下に示す。

    // i回、UP_SWを押すtask (i < 16)
    task UP_SW;
        input [3:0] i;    // バースト回数
        begin
            @(posedge clk);    // 次のクロックへ
            #1;    // 遅延
            while (i>0) begin
                nBUTTON = UP_SW_ENABLE;
                CHATTERING_WAIT;    // チャタリング防止用の期間待つ
                
                nBUTTON = SW_DISABLE;
                CHATTERING_WAIT;    // チャタリング防止用の期間待つ
                
                i=i-4'd1;
            end
        end
    endtask


クロックの立ち上がりまで待って、遅延する。
UP_SWを1にしている。CHATTERING_WAITタスクを呼び出して、チャタリングが収まるまで待ってから、UP_SWを0にして、またCHATTERING_WAITタスクを呼び出す。これをi回繰り返している。

疲れてきたので、後の説明は省略する。

  1. 2011年10月30日 05:08 |
  2. FPGAボードで学ぶ組込みシステム開発入門[Altera偏]
  3. | トラックバック:0
  4. | コメント:0