Verilog HDL高度なF.A.Q.

Verilog HDL は、謎めいた難解な言語です。ときとして、言語に仕掛けられた呪文、罠により壮大な迷宮にはまり込んでしまうことがあるかもしれません。ささやかながらお手伝いできたら幸いです。

Q1.Verification IPを設計するにあたり信号強度の評価を行えないか?
Q2.$writememh $sprintfってないのでしょうか?
Q3.$random の動きがおかしい?
Q4 verilog generate 文について
Q5. function/taskとmoduleの動作の違いについて
Q6. real 変数をモジュールポートを通じて受け渡しするには
Q7. $signedの挙動について
Q8. バイナリファイルのReadWrite
Q9. 演算途中に値が極端に大きくなった場合はどうなるのでしょうか?
Q10.慣性遅延とは?
Q11.Verilogはビット幅間違ってもエラーやワーニングはでないのでしょうか?
Q12. break とcontinue は、どうやって書けばいいのでしょう?
Q13. ビットを逆順にしたい


Q1.Verification IPを設計するにあたり信号強度の評価を行えないか?
PCB基板上のバス状態がPullかStrongかで通信状態が異なるため、if文などで処理結果を切り分けたいと考えております。

A1.難問です。

フローを制御するためには、何らかの方法で、Strengthを取り出す記述が必要になります。直接的にこれを取り出す記述はverilogHDLではありません。($monitor %vでは、当該タイムスロットの一番最後です。読み取れますが、処理を変えることができません。)

そこで、間接的に二つの方向が考えられます。
1)VPIでCallback
vpi_register_cb(),vpiValueChangeでcallback, vpiStrengthValで値を取り出します。

2)スイッチレベルで値に変換する
MOS SWでstrengthを伝播させる方法があります。このデバイスは測定対象物に影響を与えずにstrengthを伝播させることができます。期待レベル分のMOS SWを用意し、期待レベルとマージしてXになるかどうかで判断させることができます。

今回は、2)の方法で記述してみました。


実行結果

Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。
------------- シミュレーションを開始します。--------------------

wire_a=Pu1 st1=St1 pl1=Pu1 st0=St0 pl0=PuX
信号強度は pull1 です。

---------- シミュレーションを終了します。time=1----------

記述のダウンロード


Q2. $writememh $sprintfってないのでしょうか?
 どちらもLRM(言語規格LRM 1364-2001)にはありません。Veritakは、独自の実装です。なおSystemVerilog(P1800)にはあります


Q3. $random の動きがおかしい?

 $randomは、符号付32ビット整数で返ります。符号なしにするには、$unsigned($random)とするか連接{$random}で囲むと、符号なしにします。
なお、$randomの生成シーケンスは、シミュレータに依存しません。

Q3.1 $random のseedについて
  val=$random(seed); で順序だった(?)ランダムが生成できます。V2001以降のシミュレータでしたらシミュレータに依存しません。
  このseedは、reg/integer 等のvariable でなければなりません。というのは、シミュレータの動きは、

  1. 引数seedに対して一意に決まるreturn 値val を生成
  2. seed 値に対して書き込む

  という風に、return値を戻す他、seed変数に対しても書き込みます。この書き込んだseed値に対して次のreturn 値が決まります。seedを保持する信号が定数だと書き込みができないのです。
  

 次のベンチでは、ランダム値ではなく、常に同じ値が表示されてしまいます。parameter は、実行時定数なので、値を書き戻すことができません。seedが同じなので常に同じvalになってしまいます。また、引数なしでもランダムシーケンスは生成することはできますが、シミュレータ内部の値が用いられるのでユーザの指定するSEEDで機能させることはできません。

module random_test6;
        reg [31:0] value;
        parameter seed = 33;

        initial begin
                repeat(10) begin
                        value=$random(seed);//seed値は、定数は不可 reg 型/integer 型/time型のいずれかにする 
                        $display("value=%h", value);
                end
                $display("");
        end



endmodule

参考(英語)
なお、$randomについては、$dist_normal等統計システムタスク郡を含む、CソースがIEEE-1364-2005に掲載されています。(LRM2001にも載っていますが2001の方はバグがあります。)

Q3.2 AWGNについて
  $dist_normalで生成できます.引数はinteger 仕様です。


Q4 verilog generate 文について
 Verilog 2001の generateについては、こちらのチュートリアルをご参照ください。


Q5. function/taskとmoduleの動作の違いについて
確かにVerilogの場合、

 ・信号の代入なのか、ハードウェアの接続なのか

が紛らわしいと思います。基本的には、module間の接続は、wireによるハードの接続になります。例外は、outputポートだけregが置けることです。接続には、バスの衝突や、信号強度(簡易的なインピーダンスのモデリング)を表現できる必要がありますが、regには、その機能がありませんので、その機能のあるwireで接続することが基本になります。受ける側(input/inout)で、regで受けられないのはその為です。(inoutポートでreg宣言ができないのは、SystemVerilogでも同じです。)

ところが、task/functionは、通常のプログラミング言語と同様でwireではなく、regによる値渡しになり、仮引数、実引数という概念が存在します。taskの内外との通信には、記憶のないwireではなく、記憶のあるregが必要です。そのためtask のoutputもtask内のregから外のregへの値渡しのCOPYが行われます。





Q6. real 変数をモジュールポートを通じて受け渡しするには
verilog のモジュールポートは、VHDLのそれと違い、基本的には、ハードの接続のイメージです。なので、バスコンフリクトや、プルアップ等の信号強度のモデリングのために暗黙的にwire4値でつながります。(内部的には64ビット宣言だと128ビット使います)ところが、verilog のrealは、C言語のdouble相当の2値64ビットです。このマッピングをどうするかというのは、多分実装依存でそれを吸収するために$xxのシステムタスクがあります。

Q7.$signedの挙動について
 Q. k = (($signed(3'b110) >>> 1) === 3'b111));//kが1にならない。

 A. 符号付き演算になるのは、全項が符号付きの場合だけです (ただし、Self Determined 項を除く。Self Determined項とは、コンテキストの影響を受けない、たとえば、>>>の右辺 1の項になります。)。左辺(k)の符号は関係なく右辺項の符号だけで決まります。>>>は確かに符号付シフトですが、 ===項の右辺が3'b111で符号付きではありません。従い全体の演算として符号なしの演算になります。k=1を期待される場合は、3'b111を符号付きの3'sb111にするか、符号の解釈変更し$signed(3'b111)にします。参考(英語)

 Q. Veritak result is 2328816 but I am expecting -423696. please note if I defined test2 as signed, then it works but I wanted test2 to
be unsigned. there is something not right about a defined signed multiply by a defined unsigned number.
wire signed [14-1:0] test1 = -2522;
wire [12-1:0] test2 = 168;
wire signed [23-1:0] bx_full4 = test1 * test2;

  乗算の符号は、左辺の符号に関係せず、右辺だけで決まります。この場合test1は、符号付き、test2は、符号なしですから乗算は、符号なしになります。順序としては、test1,test2共に[22:0]にビット拡張され、それから符号なし乗算になります。符号付き乗算にするには、test1 *$signed(test2) とします。

Q8.バイナリファイルのRead/Write
 Windowsの世界は、特殊です。(テキストモード、バイナリモードについてはたとえばこちら) ファイルのOpenには、$fopen("ファイル名","rb"); または、$fopen("ファイル名","wb");を使います。また書式%cについては、0の書き込みについて処理系によるがあるようです。(VeritakではRAW BINARYとしています。)

Q.9 verilogについて、演算途中に値が極端に大きくなった場合はどうなるのでしょうか?
  reg [7:0] input1,input2,output;
  output <= input1 * input2 >> 8;
  Verilog HDLのルールについては、こちらの8.3.9章演算子 ビット幅について をご覧ください。この場合は、評価幅は、
  

    左辺右辺とも8ビットですので、全体として8ビットとしての評価になります。この場合、全てのビット幅が同じですので明快でinput1*input2の評価はあくまで8ビットになります。もし、input1*input2を16ビットにしたいということでしたら、(お勧めしませんが、)
 ({8'h00,input1}* {8'h00,input2}) >> 8;
とするか、あるいは、つぎのようなfunctionを用意しておいてそれを呼び出す(<=mul(input1,input2);)方法があります。 特にビット幅拡張は、C言語のそれとは全く異なります。(言語の設計意図としては、早い段階でビット拡張してなるべくオーバフローを防止する意図があるそうです。) ルールをいちいち参照するのは面倒なことですし、明示的にしてビット処理幅を一致させるようにした方が、あとあとよろしいかと思います。  

 function [7:0] mul(input [7:0] a,b);
        reg [15:0] temp1,temp2,temp3;
        begin
                temp1={8'h00,a};//符号なし拡張 temp1=a;と等価
                temp2={8'h00,b};//符号なし拡張 temp2=b;と等価
                temp3=temp1*temp2;
                mul=temp3>>8;
        end
endfunction

function [7:0] mul_signed(input signed [7:0] a,b);
        reg signed [15:0] temp1,temp2;
        reg [15:0] temp3;
        begin
                temp1=a;//RHS全項がsignedなのでsigned 拡張が行われる
                temp2=b;//RHS全項がsignedなのでsigned 拡張が行われる
                temp3=temp1*temp2;//RHS全項がsignedなのでsigned乗算
                mul_signed=temp3 >>8;
        end
endfunction

Q.10慣性遅延 とは?
 たとえば、assign #(1) a=b; と記述すると bの変化は1ns(timescaleが1nsのとき)遅れて伝播しますが、1ns以下のグリッチは伝播しません。このような遅延を慣性遅延と呼んでいます。これに対して、グリッチもそのまま伝播する遅延をトランスポートディレイ(伝播遅延)と呼んでいます。詳しくはこちらの6章をご参照ください。

Q11.Verilogはビット幅間違ってもエラーやワーニングはでないのでしょうか?
VHDLから移行してきた人のF.A.Q.ですね。出ません。それが言語仕様なんです。

ただVeritakの場合は、

1)ポートのビット幅が一致しない場合:
 コンパイラステータス画面でWarningが出ているはずです。
2)LHS/RHSの不一致は、プロジェクト設定でLINT BIT_WIDTH、LINT Floating..をチェックするとコンパイル時にWarningが出ると思います。役に立たないかもしれませんが..
3)宣言していないWireは、SCOPETREEVIEWで見るとwire の"W"では、なく白べたになっています。

VHDLは、常にビット幅の一致を求められますが、Verilogは、勝手にビット拡張します。C言語のルールのそれとは全く違うので思わぬバグの温床になりえます。言語の設計意図としては、なるべく早くビット拡張してオーバフロー、アンダーフローを抑制することにあるのだそうです。
いずれにしてもビット幅は一致させる癖をつけておいたほうがよさそうです。。(bit幅が一致していればビット拡張されることがありません。)

  Q.質問なのですが、wp, rpが4ビットのときに、
  reg [3:0] wp, rp;
  fullの論理は下のようになっています。
  assign fifo_full = (rp-1==wp) ? 1'b1: 1'b0;
  これだと、rp=4'h0、wp=4'hFの時にfifo_fullが1になっていないようなのですが、この論理ではだめでしょうか?

  Ans.結論から申しますとrp-4'b0001にすればOKです。

これもビット幅の問題なのですが、"1"は、32ビット以上の整数になります。なので、==の評価時は、両辺同じ32ビット幅に拡張されます。LHSは、32'hffff_ffff RHSは、32'h0000_000fで同じではありません。なのでFalseになってしまいます。 特に演算系では、ビット幅は一致させるにした方がよろしいと思います。

Q12. break とcontinue は、どうやって書けばいいのでしょう?
 begin: loop_exit
  for (i=0;i< MAX;i=i+1) begin :loop_continue
    ....
    if (i<5) disable loop_continue;
    else if (i>=5) disable loop_exit;
という具合にループをcontinueするには、loop_continueをdisalbe,抜けるには、loop_exitをdisable します。

 これは、C言語のcontinue,breakみたいになっていないのでは何故だ?と思われるかもしれません。シミュレータの内部的実装の話になってしまいますが以下の通りです。

c言語ならば、break、continueはラベルとジャンプだけを使って実装できますが、VerilogHDLは暗黙並列プロセスなのでそういう風に単純には行きません。実はラベルがあるとその時点でスレッドを一つ生成しています。(Veritak独自という訳ではなく、多分どのシミュレータも同じです。)#10;などがあってdisableされてもよいようにです。つまりdisableは、そのスレッドの消滅指示という訳です。これでは書きにくいというわけでSVでは、構文にbreak,continueが追加されていますが、これはC言語と同じ動きをします。

 とりあえずは、上の例の通り使うと覚えてください。

Q13 ビットを逆順にしたい

 verilog-2001では、次のようにgenerate構文を使うことができます。この例の場合 トップ階層のビット幅WIDTHがインスタンスDUTにコンパイル時に渡されビット幅に不感な記述とすることができます。論理合成的なコストはかかりません。(接続の仕方だけなので、論理セルの使用はないでしょう)

module top;
parameter integer WIDTH=16;
 wire[WIDTH-1:0] OUT;
 reg [WIDTH-1:0] IN;
sub  #(.WIDTH(WIDTH)) dut(IN,OUT);
endmodule

module sub #(parameter integer WIDTH=16)  (input [WIDTH-1:0] IN ,output  [WIDTH-1:0] OUT);

        generate
                genvar g;
                for (g=0;g< WIDTH;g=g+1)begin:label//verilog 2001では、ラベルが必要です。
                        assign OUT[g] = IN[WIDTH-1-g] ;
                end
        endgenerate
endmodule

//systemverilog では、これをさらに簡潔に1行で書けます
module sub #(parameter integer WIDTH=16)  (input [WIDTH-1:0] IN ,output  [WIDTH-1:0] OUT);

        
         for (genvar g=0;g< WIDTH;g++)   assign OUT[g] = IN[WIDTH-1-g] ;//SVでは、ラベルはなくても可です。genvar変数もインラインで書けます。
         
        
endmodule