Show page source of StateMachineVerilog #104279

= ステートマシンの書き方 Verilog HDL編 =

ステートマシンの書き方と、それには直接関係ないけれど知っておくと特になることのまとめ。

=== !`default_nettye none ===
Verilog HDLは、宣言なしで登場した名前を1bit幅のwireとして勝手に解釈しますが、moduleの前に!`default_nettype noneを書いておけばそれを禁止できます。
代入の左辺のbit幅が足りず桁あふれするせいでまともに動かない、という厄介なバグを、コンパイルの段階で検出できるありがたいおまじないです。
なお、XilinxのIPコアのソースなどは!`default_nettypeがwireであることを前提に作られているものがあるので、endmoduleの後に!`default_nettype wireを
書いておくのも忘れないようにしましょう(Verilog HDLのプリプロセッサ制御文は、ほかのソースファイルの記述にまで影響を与えます)。

=== 命名規則 ===
一例として、ここに載せてあるソースの命名規則を以下に示します。
||種類||命名||備考||
|| 入力ポート || I_~ ||ポートは全部大文字||
||出力ポート||O_~||||
||入出力ポート||IO_~||||
||パラメータ||G_~||VHDLのGenericに由来||
||localparam||C_~||VHDLのConstantに由来||
||FFを推論するもの||r_~||||
||組み合わせ出力を推論するもの||w_~||wireのみでなく、reg w_~というパターンもあり||
||負論理||~_N||||
||イネーブル信号||~_ENA||少し長いが、_ENだと負論理と区別しにくいので||

=== ラッチが推論されないようにする ===
ステートマシンを書くとき、always @(*)節の中に少しでも書き忘れがあるとラッチが推論される回路になってしまいます。
組み合わせ回路になるようにするには、以下のようにする必要があります。
 * beginのすぐあとに「なにも作用しない」ときの値を列挙する
 * 現在の状態を維持する(遷移しない)場合にもw_next_stへの代入をする
 * default文を書く
ラッチが推論されていないことを確かめるには、ソースを眺めるよりもツールで合成をしてWarningを調べるほうが確実です。
以下に、各ツールでラッチが生成されたとき出るWarningをまとめます。
|| ツール || メッセージ ||
|| Xilinx XST || WARNING:Xst:737 - Found n-bit latch for signal <信号名> ||
|| Xilinx Vivado || ||
|| Altera QuartusII || ||

== ソース ==
[[Embed(statemachine_20151104.zip)]]

{{{ code verilog
/*
I_KICK入力があってからG_COUNTクロック後にO_KICKを出すステートマシン。 
*/

`default_nettype none

module DELAY_SM #(
    parameter G_COUNT = 10
) (
    input  wire I_CLK,
    input  wire I_RST,
    input  wire I_KICK,
    output wire O_KICK
);

localparam C_IDLE = 0; // ステート数が増えたときに付け直すのが面倒なので、
localparam C_WAIT = 1; // 4'd0のような幅をつける記述はしない。

// Verilog2005の$clog2の代わり
function integer clog2;
    input integer value;
begin
    value = value - 1;
    for (clog2 = 0; value > 0; clog2 = clog2 + 1)
        value = value>>1;
end
endfunction

// ステート数が少ないため幅は4も必要ないが、ビット割付はシンセサイザが勝手に
// やるので幅が大きすぎる分には問題ない。
reg  [3:0] r_current_st;
reg  [3:0] w_next_st;

reg        w_start;
wire       w_finish;
reg        w_kick;
reg        r_kick;

// clog2(value)は、valueの「要素数」を表現できる最小限のビット幅を返す。
// valueまでの「値」を表現したいなら+1した値を与えなければならない。
// たとえば0~16までを表現したいなら、clog2(16+1)ビットが必要になる。
reg  [clog2(G_COUNT+1)-1:0] r_count;

// ステートマシンの核はこれだけ。
always @(posedge I_CLK or posedge I_RST) begin
    if (I_RST) begin
        r_current_st <= C_IDLE; // リセットで初期状態へ遷移
    end
    else begin
        r_current_st <= w_next_st;
    end
end

// このalways節の中はすべてブロッキング代入
// ノンブロッキング代入を使ってしまうと、同時刻に同一変数への代入が行われる
// ため結果が処理系依存になってしまう。
always @(*) begin // へたにセンシティビティ・リストを書くより*がよい
    w_start = 1'b0; // ここらへんには「なにも作用しない」ときの値を
    w_kick  = 1'b0; // 列挙する。記述がもれるとラッチが生成される。

    case (r_current_st)
        C_IDLE : begin
            if (I_KICK) begin
                w_start   = 1'b1; // こうするとミーリ型になる
                w_next_st = C_WAIT;
            end
            else begin
                // 遷移しない場合もw_next_stへの代入を書く
                // そうしないとラッチ生成
                w_next_st = C_IDLE;
            end
        end
        C_WAIT : begin
            if (w_finish) begin
                w_kick    = 1'b1;
                w_next_st = C_IDLE;
            end
            else begin
                w_next_st = C_WAIT;
            end
        end
        default : begin // defaultでw_next_stへの代入を忘れるとラッチ生成
            w_next_st = C_IDLE;
        end
    endcase
end

// 組み合わせ出力からフリップフロップ出力に直す
always @(posedge I_CLK or posedge I_RST) begin
    if (I_RST) begin
        r_kick <= 1'b0;
    end
    else begin
        r_kick <= w_kick;
    end
end
assign O_KICK = r_kick;

// 値域は0~G_COUNT
always @(posedge I_CLK or posedge I_RST) begin
    if (I_RST) begin
        r_count <= 0;
    end
    else begin
        if (w_start) begin
            r_count <= 1;
        end
        else if (r_count == 0) begin
            r_count <= r_count;
        end
        else if (r_count < G_COUNT) begin
            r_count <= r_count + 1'b1;
        end
        else begin
            r_count <= r_count;
        end
    end
end
assign w_finish = (r_count == G_COUNT-1) ? 1'b1 : 1'b0;

endmodule

`default_nettype wire
}}}