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 位で通常は意識することはありませんが、厳密には信号強度の演算結果で値が決まってきます。シミュレータ内部で行っている演算方法については、後述します。
| レベル | 0 | 1 | |
| 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は、rnmosとrpmosが合体した動きをします。
ところで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への遷移時間が定義されます。電荷がリークしてなくなったというイメージではないかと思います。


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