【QuartusII入門】LED点灯回路の作成3(寄り道:Verilogの解説1, LED_BLINK)

前回までに使ったVerilogの解説をしていきます。

まずはLED_BLINKから。全体はこんな感じ。

`timescale 1ns/1ps
module led_blink(
    clk         , //クロック50MHz
    reset_n     , //リセット信号(ローアクティブ)
    led           //LED
    );
    input  wire        clk     ; //クロック50MHz
    input  wire        reset_n ; //リセット信号(ローアクティブ)
    output reg  [ 7:0] led     ; //LED

    reg  [31:0] ctr; //時間計測用カウンタ

    always @(posedge clk)
    begin
        if(!reset_n)//リセットボタンが押された時
        begin
            ctr <= 32'd1; //カウンタの値を1にリセット
        end
        else if(ctr == 32'd25000) //25000回クロックをカウント(=0.5ms経過)した時
        begin
            ctr <= 32'd1;//カウンタの値を1にリセット
        end
        else  begin
            ctr <= ctr + 32'd1; //それ以外の時はカウンタを加算
        end
    end

    always @(posedge clk)
    begin
        if(!reset_n)
        begin
            led <= 8'hFF;
        end
        else if(ctr == 32'd25000) //0.5ms経った時、LEDを反転
        begin
            if(led == 8'hFF)
            begin
                led <= 8'h00;
            end
            else begin
                led <= 8'hFF;
            end
        end
    end
endmodule


 

上から順番に行きましょう。

`timescale 1ns / 1ps

シミュレーションの時間の単位と精度を設定します。
この場合シミュレーション単位は1ns, 精度は1psとなります。
後述しますがテストベンチで波形を作る際にシミュレーション単位を基に計算します。
精度はどこまで細かい時間で遅延の計算をするか、と言う設定かと。
RTLシミュレーションの場合はあまり意識してないです。
 

module led_blink(
    clk         , //クロック50MHz
    reset_n     , //リセット信号(ローアクティブ)
    led           //LED
    );
    input  wire        clk     ; //クロック50MHz
    input  wire        reset_n ; //リセット信号(ローアクティブ)
    output reg  [ 7:0] led     ; //LED

~中略~ 

endmodule

モジュールの入出力ポートの宣言をします。

 

 

module <モジュールの名前> (
 ポート名,
 ...
);

この部分でまずどんな名前のポートがあるのかを宣言し、
 

    input  wire           ポート名 ;
    output reg  [ポートの幅] ポート名 ;

と書くことで入出力の方向とビット幅を定義します。
 

ビット幅というのは1つのポートが何本の信号を持っているか、と言う事です。
例えば


output reg [ 7:0] led ;

と表記すると、ledと言う信号は7から0の8本の線で1セットの信号となります。
 

    output reg  [ 0:7] led     ;

と書いても8本の信号線になりますが、数字の方向が逆の線はそのままでは接続できません。
慣習的に大きい方が左、小さい方が右になるように記述しています。
一番左のビットを最上位ビット(MSB), 一番右のビットを最下位ビット(LSB)と呼びます。

 

inputが入力、outputが出力になります。

 

wire , reg と言うのは信号のタイプです。

wireは接続されているだけの線で、値を記憶しておくことはできません。
接続にはassign文を使います。

wire [7:0] val;
assign val = 8'd10;

と書くと、valと言う信号線には常に10進数で10を表す8bitの2進数が接続されます。

 

論理式を接続することもできます。

wire A;
wire B;
wire C;
assign A = B | C;

こう書くとAはBとCの論理和を取った値になります。
BやCが変化するとAも変化します。

 

reg に値を保存するには、always文でタイミングを指定してやる必要があります。

   always @(posedge clk) <- clk の立ち上がり(posedge) のタイミングでctrの値を更新
    begin
        if(!reset_n)//リセットボタンが押された時
        begin
            ctr <= 32'd1; //カウンタの値を1にリセット
        end
        else if(ctr == 32'd25000) //25000回クロックをカウント(=0.5ms経過)した時
        begin
            ctr <= 32'd1;//カウンタの値を1にリセット
        end
        else  begin
            ctr <= ctr + 32'd1; //それ以外の時はカウンタを加算
        end
    end

クロックの立ち上がり時点における信号の状態で、ctrに保存する値を決めます。
 

        if(!reset_n)//リセットボタンが押された時
        begin
            ctr <= 32'd1; //カウンタの値を1にリセット
        end
        else if(ctr == 32'd25000) //25000回クロックをカウント(=0.5ms経過)した時
        begin
            ctr <= 32'd1;//カウンタの値を1にリセット
        end

if文で条件を指定します。
最初のif文ではreset_nを反転(!)させたものが1,つまりreset_nの状態が0(L)ならctrの値を32bit10進数(32’d)の1にします。
その条件が成り立たない場合(else), ctrの値が32bit10進数の25000(32’d25000)と等しければ、ctrの値を同じく32’d1にします。

 

 

上記2つの条件が成り立たない場合は

        else  begin
            ctr <= ctr + 32'd1; //それ以外の時はカウンタを加算
        end

最後のこの文で、ctrの値を今のctrの値に1足したものに更新します。
 

後半のledに対する文もほぼ同様の書き方ですね。

    always @(posedge clk)
    begin
        if(!reset_n)
        begin
            led <= 8'hFF;
        end
        else if(ctr == 32'd25000) //0.5ms経った時、LEDを反転
        begin
            if(led == 8'hFF)
            begin
                led <= 8'h00;
            end
            else begin
                led <= 8'hFF;
            end
        end
    end

 

以下の部分でledの値を見て反転させています。

            if(led == 8'hFF)
            begin
                led <= 8'h00;
            end
            else begin
                led <= 8'hFF;
            end

ちなみにctrの値が25000と言うのはクロックが50MHzであることを想定した値です。
シミュレーションであまり長い時間間隔にしてしまうとシミュレーション自体に時間がかかってしまうため、この値にしてあります。
実際にハードウェア上で確認する場合は1000倍して25000000にすると0.5秒周期でLEDが反転します。


次回はテストベンチ側の解説をします。
分からない部分があればコメントにどうぞ。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です