18. ゲートレベルとスイッチドレベルのモデリング

本章では、実際の半導体(MOS回路)の動作により近い記述を扱います。(専門的な内容になります。)特に、双方向のゲートやtriregについては、VHDLや、使い慣れた継続代入文ではモデリングすることはできません。VerilogHDLの組み込みプリミティブでのみ可能です

n_input gates n_output gates three-state gates pull gates MOS switches bidirectional
and buf bufif0 pulldown cmos tran
nand not biuif1 pullup rcmos rtanif0
nor notif0 rtanif1
or notif1 rtran
xnor tranif0
xor tranif1

このうち信号強度(DriveStrength)を指定可能なのは、上で青太字色の組み込みゲートだけです。信号強度とは、インピーダンスの概念を簡易的にモデリングしたものだと言ってよいでしょう。

18.1 信号強度とは

0/1について各々8段階のレベルがあり名前がついています。下の名前で信号強度を指定することができます。ただし、large/medium/small については、電荷強度としてしか指定することができないので、後述するtriregと常に組でしか用いることができません。また、(highz0,highz1)または、(highz1,highz0)という組み合わせの信号強度は指定することができません。supply1は、電源、supply0は、GNDのモデリング用、highz0/highz1は、ハイインピーダンスのモデリング用と思ってよいでしょう。何も指定していないとdefaultである strongのレベルにあります。reg等のvariable信号は、信号強度を指定することはできませんがstrongのレベルを持っています。Verilog HDLでは、0/1/X/Zの4値であり、信号強度は陽にでてきませんが、実は信号強度の演算の結果が値として現れます。RTLやテストベンチを書いていると、使うのはpullup 位で通常は意識することはありませんが、厳密には信号強度の演算結果で値が決まってきます。シミュレータ内部で行っている演算方法については、後述します。

レベル
7 supply0 supply1
6 strong0 strong1
5 pull0 pull1
4 large charge strength
3 weak0 weak1
2 medium charge strength
1 small charge strength
0 highz0 highz1

18.2 プリミティブをまとめて使うには?
 
次のようにインスタンス名をつけて宣言することもできます。

module driver (in, out, en);
input [3:0] in;
output [3:0] out;
input en;
  bufif0 ar[3:0] (out, in, en); // array of three-state buffers
endmodule


これは、インスタンス名を除いて次の記述と等価です。

module driver_equiv (in, out, en);
input [3:0] in;
output [3:0] out;
input en;
  bufif0 ar3 (out[3], in[3], en); // each buffer declared separately
  bufif0 ar2 (out[2], in[2], en);
  bufif0 ar1 (out[1], in[1], en);
  bufif0 ar0 (out[0], in[0], en);
endmodule

18.3 and,nand,nor,or,xor, and,xnor ゲート

これらは、多入力にすることができます。出力は、一番最初のポートになります。これらは、Reduction演算と同じ働きをしますので次のようなベンチを書く事もできます。



18.4 buf,not ゲート

これらは、多出力にすることができます。入力は、一番最後ポートになります。それ以外は出力です。次のようなベンチを書く事もできます。



18.5 bufif0,bufif1,notif1, notif0

これらの特徴は、Hizを出力できることです。なので制御の端子が必要になります。

bufif0 CONTROL
D 0 1 x z
A 0 0 z L L
T 1 1 z H H
A x x z x x
M z x z x x

notif0 CONTROL
D 0 1 x z
A 0 1 z H H
T 1 0 z L L
A x x z x x
M z x z x x

bufif1 CONTROL
D 0 1 x z
A 0 z 0 L L
T 1 z 1 H H
A x z x x x
M z z x x x


notif1 CONTROL
D 0 1 x z
A 0 z 1 H H
T 1 z 0 L L
A x z x x x
M z z x x x

ところで、上表で、0/1/x/z以外にLとHというのが出てきました。これは、Lなら0または、z、Hなら1またはzを現しています。
もう少し厳密には、不確かな分布をしていることを現わしています。これはstrengthを%vで表示させてみると明らかになります。

結果は次のようになります。%vでstrengh表示、%bで値表示しています。ここで、StXは、strong X の意味です。信号強度strong1とstrong0が混在していることを示しています。StLは、Strong0からHiz0まで不確定であることを示しています。しかし1の成分(例えば、strong1、highz1)は入っていません。同様にStHは、Strong1からHiz1まで不確定であることを示しています。しかし、0の成分は、入っていません。値表示をするとStL、StHともxになりますが、その中身は、strength表示をして知る事ができます。(等幅フォントを使用して見易くしています。)





もう少し、strengthについて見てみましょう。上記ソースにstrengthを追加してみます。


これに対するシミュレーション結果です。
strength表示した部分を見ていきましょう。最初の35Xは、weak0の3とpull1の間で不確定であることを示しています。これは、
Enableがxなので、出力もxになりますが、bufif0のstrengthが、(pull1,weak0)で指定されているためにこのような結果になります。
同様に65Xは、bufif1のstrengthが(pull1,strong0)と指定されているためです。
また、値が0/1表示されているところでも、そのstrengthは、指定値になっていることが分かります。



18.6 MOS 回路(cmos nmos pmos rcmos rnmos rpmos)

NMOSとPMOSの論理です。


CMOS Inverterは、


次のように表現できます。

module my_inv(out,in);
        output out;
        input in;
        supply1 pwr;
        supply0 gnd;
        pmos p1(out,pwr,in);
        nmos n1(out,gnd,in);
endmodule

テストベンチです。


NAND 回路です。




これも次のようになります。

module nand2(out,ina,inb);
  input ina,inb;
  output out;
  wire wn;      //connect the series nmos switches
  supply1 vdd;
  supply0 gnd;
  pmos p1(out,vdd,ina);
  pmos p2(out,vdd,inb);
  nmos n1(wn,gnd,ina);
  nmos n2(out,wn,inb);
endmodule

InverterとNANDのシミュレーション結果です。




CMOSのゲート回路図です。



cmos (w, datain, ncontrol, pcontrol);

は、次に等価です。

nmos (w, datain, ncontrol);
pmos (w, datain, pcontrol);

同様にrcmosは、rnmosrpmosが合体した動きをします。
ところでrxxとついたデバイスとそうでないデバイスの違いはstrengthの伝播の仕方です。rがついていないほうは、ゲートがsupplyレベルのときだけ、strongにReductionしますが、それ以外は、そのまま伝播します。rがついている方は、supplyだけではなく、一段強度が下がって(たとえば、strongがpullのように)伝播します。rは、抵抗を持ったデバイスのモデリング用途という趣旨だと思います。

以下は、pmos/nmos rpmos/rnmos(置き換えての)シミュレーションテストベンチとソースです。


上の段がpmos/nmos で下の段がrpmos/rcmosに置き換えたシミュレーション結果です。pmos/nmos は、ゲートのstrength strongが減衰せずに伝播しますが、rpmos/rcmosの方は、pullレベルに減衰していることが分かります。



18.7双方向スイッチ(tran tranif1 tranif0 rtran rtranif1 rtranif0

ところで、これはどういうときに使うのでしょうか?以下ユーザ様から教えていただきました。

            ...デジアナ混在の設計をしていますとやはり
tranif、
rtranif、
trireg、
の正式対応がうれしいですね。
...
このtriregはデジアナ混在を強引にVerilogSimしようとすれば必須ですね。
例えばSRAMの場合、
リードの場合を考えると、CELLを開く前にセンスアンプ両脇D,DB配線(bit線)のプリチャージ動作が必要で、
そのプリチャージされた電荷をCELL内nchでD,DB側のどちらかの電荷を引き抜き、その微少な電位の違いをセンスアンプで振り切り上げるイメージなるのですが、センスアンプON寸前にはそのD,DB両配線はHiZ状態(CELLとセンスアンプがぶつからないため)にします。
これをVerilogSimで実現しようとすると、動作的にHiZになった瞬間にD,DB配線も論理的にHiZになり、センスアンプ両脇がHiZなので論理が固まらなくなりセンスアンプON後に不定Xが回ってしまいます。
そこでそのD,DB配線にtriregを指定しておくことで、CELLに引き抜かれた側は0ですが、引き抜かれない側は、D,DBが切り離されてHiZ状態になっても論理的には1が保ち続けられます。
そのため、センスアンプ両脇は、0,1となっており、出力は正常にされると言うことになります。
簡単に言うと、状態としてはHiZになるが、電位(電荷)を持つHiZ部分としてGATE入力に使用する部分に必要と言うことです。
ここでのSRAMの検証はあくまで、アナログ動作を見る物では無く、NETの配線接続と論理です。

rtranifはCELLやレベルシフタの様に入出力がぶつかっている構成では当然ぶつかってXになるのですが、
そこを通過するをrtranifで弱める事で信号がぶつかっても強いレベルに決定される正常動作すると言うことで使っています。


実際、次のトランスミッション系での記述でやってみますとtranで一度ZになってしまうとINVERTER等の論理ゲートでXになってしまいXが回ってしまう現象が起こりました。時間0のフィードバックループに起因しシミュレータの実装依存の現象に陥りやすいようです。そこで,tran系を含むファイルの場合は、

`default_nettype trireg

にしてしまい、WIREを暗黙NETにしてしまい上記宣言でtriregにしてしまうとよいでしょう。また、triregは、シミュレータのリソース使用量が大きいので、使い終わったら元に戻しておきます。

`default_nettype trireg
module compare5_test;
        reg [4:0] A,B;
        reg Cin;
        wire same_s1;
        integer i;
        initial begin
                Cin=0;
                for (i=0;i<20;i=i+1) begin
                        A=$random;
                        B=$random;
                        #10;
                        $display("A=%h B=%h same=%b",A,B,same_s1);

                end

        end



compare5 c5(A, B, Cin, same_s1);


endmodule

module compare1 (A, B, In, Out);
        input           A;
        input           B;
        input           In;
        output          Out;
 
//      wire            net_2;
//      wire            net_1;
//      trireg net_1,net2;

        std_xor std_xor(.In0(A), .In1(B), .Out(net_1));
        nor2 nor2(.In0(net_1), .In1(In), .Out_b(net_2));
        inv84 inv84(.in(net_2), .out(Out));

endmodule               // compare1
module std_xor (In0, In1, Out);
        input           In0;
        input           In1;
        output          Out;
 
        wire            net_6;
        wire            net_2;
        wire            net_3;
        wire            net_4;
        wire            net_5;
        wire            net_1;
 
 supply1 Vss;
 supply0 Vdd;

        tranif1 n(Out,net_1,In1);
        tranif0 p(net_2,Vss,net_3);
        tranif0 p_1(net_4,Vss,In0);
        tranif0 p_2(Out,net_2,In1);
        tranif0 p_3(Out,net_4,net_5);
        tranif1 n_1(net_1,Vdd,In0);
        tranif1 n_2(Out,net_6,net_5);
        tranif1 n_3(net_6,Vdd,net_3);
        tranif0 p_4(net_3,Vss,In0);
        tranif1 n_4(net_3,Vdd,In0);
        tranif0 p_5(net_5,Vss,In1);
        tranif1 n_5(net_5,Vdd,In1);

endmodule               // std_xor


module nor2 (In0, In1, Out_b);
        input           In0;
        input           In1;
        output          Out_b;
 
        //wire          net_1;
 
 supply1 Vss;
 supply0 Vdd;

        tranif1 n(Out_b,Vdd,In1);
        tranif1 n_1(Out_b,Vdd,In0);
        tranif0 p(net_1,Vss,In0);
        tranif0 p_1(Out_b,net_1,In1);

endmodule               // nor2

module inv84 (in, out);
        input           in;
        output          out;
 supply1 Vss;
 supply0 Vdd;
 
 
        tranif1 n(out,Vdd,in);
        tranif0 p(out,Vss,in);

endmodule
module compare5 (A, B, Cin, same_s1);
        input           Cin;
        input   [4:0]   A;
        input   [4:0]   B;
        output          same_s1;
 
//      wire            net_2;
//      wire            net_3;
//      wire            net_4;
//      wire            net_5;
//      wire            net_1;
//      trireg net_1,net_2,net_3,net_4,net_5; 

        compare1 compare1(.A(A[4]), .B(B[4]), .Out(net_1), .In(net_2));
        compare1 compare1_1(.A(A[3]), .B(B[3]), .Out(net_2), .In(net_3));
        compare1 compare1_2(.A(A[2]), .B(B[2]), .Out(net_3), .In(net_4));
        compare1 compare1_3(.A(A[1]), .B(B[1]), .Out(net_4), .In(net_5));
        compare1 compare1_4(.A(A[0]), .B(B[0]), .Out(net_5), .In(Cin));
        inv84 inv84(.in(net_1), .out(same_s1));

endmodule

`default_nettype wire
        


シミュレーション結果です。




18.8 pullup と pulldown

たとえば、次のように記述できます。

pullup (strong1)p1 (neta), p2 (netb);

プルアップでありながら、strong1にneta とnetbを ドライブする記述です。なにも指定しないとpull レベルになります。
インスタンス名(ここではp1,p2)は省略可能ですが、括弧は省けません。

pullup  (neta), (netb);

テストベンチです。strong1でプルアップしています。





シミュレーション結果です。CMOSの方は、ゲートのStrongがそのまま伝播するので、PullUpソースと衝突してXになります。
しかし、RCMOSの方は、ゲートのStrongはPullに減少するためPullUpソースのドライブレベルStrongが全てで勝ちます。よってすべて1になります。


 一般化したstrength演算のルールは次の通りです。
1) ambiguous(不明確な) level とunambiguous(明確な)の合成は unambiguous level 以上になる
 ambiguous とは、StLみたいにHiz0からStrong0までレベルの可能性があるものです。これに対してunambiguousは、St1みたいに明確な一つのレベルをもつものです。
 
  例)   

StH + We1 => 361
Su1 Su1
St1 St1
Pu1 Pu1
La1 La1
We1 We1 We1
Me1
Sm1
Hiz1


2)ambiguous(不明確な) level とunambiguous(明確な)の合成で ambiguous level 以下は消える。ただしルール3優先

例)

36X + La1 => 461
Su1 Su1
St1 St1
Pu1 Pu1
La1 La1 La1
We1
Me1
Sm1
Hiz1
Hiz0
Sm0
Me0
We0
La0
Pu0
St0
Su0

3)ambiguous(不明確な) level とunambiguous(明確な)の合成で 値が異なるときは、ルール1を適用

361 + La0 => 46X
Su1 Su1
St1 St1
Pu1 Pu1
La1 La1
We1 We1
Me1 Me1
Sm1 Sm1
Hiz1 Hiz1
Hiz0 Hiz0
Sm0 Sm0
Me0 Me0
We0 We0
La0 La0 La0
Pu0 Pu0
St0 St0
Su0 Su0



このようにNET系(wire 系)は、このような面倒なstrengthの演算がシミュレータ内で計算されます。シミュレータ内では複数のドライバがあるNETに限ってこのような演算を行って値を決定しています。これは、ソフト的な式の演算イメージ(reg 等のvariable変数のブロッキング代入)と大きく異なります。ソフトウェアでは最後に代入した値が残るのに対して、このような演算はまさに回路素子挙動に近いものであることがお分かりと思います。ソフトウェア的な面とハードウェア的な面の両面を持つのがHDLの難しさでもあります。

18.9  Gate と net delays
 トランスポートDelayとイナーシャDelay

 サンプルソースで見ていきます。ラッチをゲートレベルで書いています。色々なところに#が入っていますが、それぞれDelayを表現しています。
最初の wire #5 datawは、Net Delayと呼ばれるものです。宣言のところで#をつけるとDelayLineを模擬したみたいなDelayになります。#5ですから、5ns遅延になります。ここで短いパルスが入ってきたとしてもそのまま伝播します。これをトランスポートDelayと呼んでいます。一方wire #5 dataw2=dataw; はさらに5ns遅延になりますが、これは、assign dataw2=dataw;のcontinous assign となりイナーシャDelayとなります。イナーシャDelayでは、プロパゲーションDelay値よりも小さなDelayは次のアルゴリズムで無視されます。

1)変化したらEventQueueに次のスジェジュール時刻を入れてスケジュールします。
2)次の変化が入ってきて1)のスケジュールが未だ実行されていなかったら1)のイベントをキャンセルします(Deschedule)

さて、ラッチ記述内のnotゲートにも#5がついていますが、ゲートのプロパゲーションモードはイナーシャになります。従って短いパルスは無視されます。


シミュレーションの様子を以下に示します。


さて #(3,5)は、それぞれ立ち上がりに3ns、立下りに5nsという意味です。#(3,7,13)のように3値の3番目のパラーメータは、Hizへの遅延時間(DecayTime)です。ですので、HizがないNOTゲートやNANDゲートでは指定することはできません。
さらに、つぎのような記述もできます。
これは、min,typ,max の指定もできますが、コンパイル時にシミュレータにどの値を使うか(min,typ,max)を指示しておく必要があります。
(Veritakの場合はプロジェクト設定のSDFで指定します。)


18.10 trireg
trireg とはNETの一種で、wireに容量がついたNETです。

ですので、MOSがターンオフしたときに(Hizになったときに)は、Zにならずに今まで溜め込んだ電荷で電圧を維持します。従って原則的にtriregでは、zになることはありません。Delay値も指定できますが、Decay値は存在せず、代わりにその電荷のXへの遷移時間が定義されます。電荷がリークしてなくなったというイメージではないかと思います。



また、別な使い方としては、このような使い方もあるようです。