[Verilog] カウンタ作成とありがちなバグ

Verilog HDL ネタは、講義とかで使われるようなのを書くとアクセスアップしたりするんだろうか。どんなのがいいかわからないけど。

今回は、カウンタ回路でありがちかもしれないバグの話。

カウント範囲の変更

0~max_count のカウンタ生成で次のような場合、致命的なバグがあります。

gistfile1.v
reg [15:0] count;
// max_count は 入力値など
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        count <= 0; // リセット
    end
    else if (count == max_count) begin
        count <= 0; // カウンタ最大値の場合、0 にリセット
    end
    else begin
        count <= count + 1;
    end
end

max_count でカウンタの範囲を変更できるようにしていますが、count == max_count 時にカウンタ値リセットをしているので、max_count を大きい値から小さい値に変更したとき、例えば count = 100 のとき max_count = 50 に変えると count の値が bit 幅 MAX まで振り切ってしまいます。

count >= max_count のような条件にするか、ダウンカウンタを使えば上記のようなバグは通常発生しません。

gistfile1.v
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        count <= max_count; // 初期値をカウンタ最大値にする
    end
    else if (count == 0) begin
        count <= max_count; // count = 0 でリセット
    end
    else begin
        count <= count - 1;
    end
end

また、ダウンカウンタを使うと「100~0、50~0 の組み合わせで、リセット時以外1ずつしか変化しない」ということも簡単です(アップカウンタは max_count の値を変えた時に、max_count 以外のカウンタ値から 0 にリセットされる場合がある)。

ダウンカウンタの方が、回路規模が小さくなるという話も。

カウンタの有効・無効

普通は、ずっと動いているカウンタを作ることは少なく(意味もなく動いていると電力も喰うし)、動作が有効の条件を付けます。

gistfile1.v
reg enable;
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        count <= max_count;
    end
    else if (enable) begin // enable = 1'b1 の時のみカウント動作
        if (count == 0) begin
            count <= max_count;
        end
        else begin
            count <= count - 1;
        end
    end
end

この enable の条件もよく考えていないとバグることがよく(?)あります。例えば次のように start と finish 信号によってカウント開始・終了を制御している場合、どうでしょう。

gistfile1.v
always @(posedge clk or negedge rst_n) begin
    if (~rst_n) begin
        enable <= 1'b0;
    end
    else if (start) begin
        enable <= 1'b1;
    end
    else if (finish) begin
        enable <= 1'b0;
    end
end

この場合、start と finish が同時に 1 のとき問題ない動作なのか確認が必要です。普通に使っていると start → finish の順で 1 になるけど、start・finish が同時に 1 になる場合もあり考慮されていないと、カウンタが止まっていてほしいときに動き続けることになりかねません。