HDL初心者ながらにVerilogで簡単な回路を記述して、FPGAに回路を合成したときにソフトウェアとの違いに悩まされた時のメモ

症状としては、回路を記述した後に、波形シミュレーションを行い信号線の動きを丁寧に確かめたにも関わらず、実際に回路に実装してみると、まったく動作しないという現象に陥りました。

結論としては、Verilogの記述方法が合成ツール的に良くないことが原因となっていたようです。再度同じ問題にはまらないために以下でこの事例をまとめておきます。




回路合成の環境とツール



今回動作しなかったものは、汎用的なカウンターの記述です。

コンパイラ&波形シミュレータVivado
Xcelium
合成ツールVivado v2023.1
評価ボードDigilent Arty A7
FPGAArty A7-100T(XC7A100TCSG324-1)

回路の合成ツールの種類や世代、対象ボードによって差があるのかもしれませんが、今回は以上の構成で試しています。



正常に動作する回路記述例



まず、正常に動作する場合のVerilogによる回路記述例を示します。

動作したVerilog記述


module counter #(
    parameter P_BASE = 'd32 //- counter max
,   parameter P_BIT  = 'd32 //- counter bit
) (
    input   wire                    clk     //- clock
,   input   wire                    resetn  //- reset negative logic
,   input   wire                    enable  //- enable
,   input   wire                    up_dw   //- up:1, down:0
,   output  reg     [(P_BIT-1):0]   count   //- count value
,   output  wire                    carry   //- carry
);
    // count up down
    wire w_upflag = ~(count+1 < P_BASE);
    wire w_dwflag = (count == 'd0);

    always @(posedge clk or negedge resetn) begin
        if (!resetn) begin
            count   <=  {P_BIT{1'b0}};
        end else if (enable) begin
            if (up_dw & w_upflag) begin
                count   <=  {P_BIT{1'b0}};
            end else if (up_dw) begin
                count   <=  count + 'd1;
            end else if (!up_dw & w_dwflag) begin
                count   <=  (P_BASE - 'd1);
            end else if (!up_dw) begin
                count   <=  count - 'd1;
            end
        end
    end

    // carry
    assign carry = ((w_upflag & up_dw) | (w_dwflag & !up_dw)) & enable;

endmodule // counter

アップ/ダウンを切り替えられるようにするための信号線「up_dw」の入力があるだけの、簡単なカウンタの記述です。

「enable」信号が入力するたびに「count」レジスタの値を1加算、もしくは1減算しています。また、一定値以上、もしくは0より小さくなる場合は、特定の値(0もしくはP_BASE-1)に値を更新しています。


動作記述の波形シミュレーション



sig_iの値が立ち上がりを検出し、カウンタにenableを入力してカウンタの値を増加させています。上の波形シミュレーションより、正しくカウンタの値が足し算され、それに応じたledの位置がワンホットで出力される回路の動作が確認できました。

動作記述の生成実装回路



Vivadoのimplementation Schematicの出力を上に示します。

ここでは、全体として左から以下のような動作フローとなっています。
  1. ボタン入力
  2. ボタン入力信号のチャタリング(ノイズ除去)
  3. レベル信号とパルス信号の変換
  4. カウンタの値をパルス信号(enable)に応じて増加
  5. カウンタの値に応じた位置のLEDをワンホットで点灯
ブロック図より、左から3番目にcounterというブロックがあり、シミュレーションで意図した通りの回路が生成されていることが分かります。



正常に動作しない回路記述



次に、先ほどと同じ機能を実現したつもりで、FPGA上で動作しなかったVerilogによるカウンタの記述例を示します。

動作しなかったVerilog記述


module counter #(
    parameter P_BASE = 'd32 //- counter max
,   parameter P_BIT  = 'd32 //- counter bit
) (
    input   wire                    clk     //- clock
,   input   wire                    resetn  //- reset negative logic
,   input   wire                    enable  //- enable
,   input   wire                    up_dw   //- up:1, down:0
,   output  reg     [(P_BIT-1):0]   count   //- count value
,   output  wire                    carry   //- carry
);
    // count up
    wire w_upflag = ~(count+1 < P_BASE);

    always @(posedge clk or negedge resetn) begin
        if (!resetn) begin
            count   <=  {P_BIT{1'b0}};
        end else if (enable) begin
            if (up_dw & w_upflag) begin
                count   <=  {P_BIT{1'b0}};
            end else if (up_dw) begin
                count   <=  count + 'd1;
            end
        end
    end

    // count down
    wire w_dwflag = (count == 'd0);

    always@(posedge clk or negedge resetn) begin
        if (!resetn) begin
            count   <=  {P_BIT{1'b0}};
        end else if (enable) begin
            if (!up_dw & w_dwflag) begin
                count   <=  (P_BASE - 'd1);
            end else if (!up_dw) begin
                count   <=  count - 'd1;
            end
        end
    end

    // carry
    assign carry = ((w_upflag & up_dw) | (w_dwflag & !up_dw)) & enable;

endmodule // counter

先ほどの正常動作の記述との違いは、カウンタのアップ機能とダウン機能の記述をalways文を2つ用いて分割したことです。

countの値が同じリセット信号(resetn)で同じようにリセットされる記述が2回書かれているのが動作しない原因かと思い、これを片方削除ましたが、同じように動作しませんでした。


動作記述の波形シミュレーション



波形シミュレーションでは正常に動作したVerilogのカウンタ記述の場合と全く同じ結果となりました。記述として文法エラーになるわけでもなく、シミュレーションは難なく通っているので、これまたたちが悪いです。

VivadoでもXceliumでも試しましたが、ソフトウェアによる波形シミュレーションは問題なく実行され、同じ結果となります。

こうなるとFPGA上に実装したうえで、任意の信号線を外にだしつつ、LEDなどの外部機器を用いて少しずつ、以上な動作箇所を絞り込んでいくしかないような気もします。


動作記述の生成実装回路



Vivadoのimplementation Schematicの出力は以上のようになっていました。ブロック図にもなっておらず、電源に1つのLEDが接続されているだけの回路になっているという解釈であっているのでしょうか?

いずれにしても、思い描いているような回路になっていなさそうなことだけはわかりますが、これだけ見ても、回路中でなにが原因でこうなっているのかよくわからないような気がします。回路設計経験の長い方ならわかるんですかね?



対策および防止策



このRTL記述の傾向が原因となった現象の対処法としては
  • エラーにはなっていない、クリティカルなwarningはできるだけ排除
  • 回路イメージの付くRTLの記述を心掛ける
  • 回路記述に規則性を持たせる
などが挙げられると思います。

LintをかけるとErrorとして出てくるので、見つけることができます。

今回の場合だと、動作しない記述では以下のようなクリティカルワーニングが出てはいました。
[Synth 8-6859] multi-driven net on pin Q with 1st driver pin 'counter/count_reg[0]/Q'
[Synth 8-6858] multi-driven net Q is connected to at least one constant driver which has been preserved, other driver is ignores

ワーニングが出ていても動くことも多いので、所詮警告だと思って、いつものごとく気にしていなかったら半日ほど動かず悩まされることになりました。






今回でRTLはソフトウェアのプログラム言語と違い、改めて回路を記述しているということを意識させられましたね。

改めて考えると、記述から具体的に回路をイメージしたときに違和感を感じなくもないですが、もうちょっと合成ツールさんが頑張ってくれてもいいんですよ;;

このエントリーをはてなブックマークに追加
コメントを閉じる

コメント

コメントフォーム
記事の評価
  • リセット
  • リセット