Verilogユーザー定義プリミティブのマスター:高度なロジックモデリング
標準的なVerilogプリミティブ(nand、notなど)では、複雑なロジックを表現するのが常に簡単または十分とは限りません。UDPまたはユーザー定義プリミティブと呼ばれる新しいプリミティブ要素を定義して、組み合わせロジックや順序ロジックをモデル化できます。
すべてのUDPは正確に1つの出力を持ち、0、1、またはXのいずれかになり、Zにはなりません(サポートされていません)。値Zを持つ入力はXとして扱われます。
Verilog UDP記号
Verilogユーザー定義プリミティブは、module定義と同じレベルで記述できますが、moduleとendmoduleの間には決して記述できません。多くの入力ポートを持てますが、常に1つの出力ポートのみで、双方向ポートは無効です。すべてのポート信号はスカラでなければならず、1ビット幅である必要があります。
ハードウェア動作は、tableとendtable内で、入力のさまざまな組み合わせとそれに対応する出力をリストしたプリミティブ状態テーブルとして記述されます。入力と出力信号の値は以下の記号で示されます。
| 記号 | 説明 |
|---|---|
| 0 | 論理0 |
| 1 | 論理1 |
| x | 不明、論理0または1のいずれか。入力/出力または順序UDPの現在状態として使用可能 |
| ? | 論理0、1、またはx。UDPの出力には使用不可 |
| - | 変化なし、UDPの出力でのみ許可 |
| ab | aからbへの値の変化(aまたはbは0、1、またはx) |
| * | ??と同じ、入力値の任意の変化を示す |
| r | 01と同じ -> 入力の立ち上がりエッジ |
| f | 10と同じ -> 入力の立ち下がりエッジ |
| p | 入力の潜在的な正エッジ:0->1、0->x、またはx->1 |
| n | 入力の潜在的な負エッジ:1->0、x->0、1->x |
組み合わせUDPの例
// 出力はポートリストの最初の信号でなければならない
primitive mux (out, sel, a, b);
output out;
input sel, a, b;
table
// sel a b out
0 1 ? : 1;
0 0 ? : 0;
1 ? 0 : 0;
1 ? 1 : 1;
x 0 0 : 0;
x 1 1 : 1;
endtable
endprimitive
?は、その信号が0、1、またはxのいずれでもよく、最終出力の決定に影響しないことを示します。
以下はUDPをインスタンス化し、入力刺激を与えるテストベンチモジュールです。
module tb;
reg sel, a, b;
reg [2:0] dly;
wire out;
integer i;
// UDPをインスタンス化 - UDPはポート名接続で
// インスタンス化できないことに注意
mux u_mux (out, sel, a, b);
initial begin
a <= 0; b <= 0;
$monitor("[T=%0t] a=%0b b=%0b sel=%0b out=%0b", $time, a, b, sel, out);
// 異なるランダム遅延後にa, b, selを駆動
for (i = 0; i < 10; i = i + 1) begin
dly = $random;
#(dly) a <= $random;
dly = $random;
#(dly) b <= $random;
dly = $random;
#(dly) sel <= $random;
end
end
endmodule
xcelium> run [T=0] a=0 b=0 sel=x out=0 [T=4] a=1 b=0 sel=x out=x [T=5] a=1 b=1 sel=x out=1 [T=10] a=1 b=1 sel=1 out=1 [T=15] a=0 b=1 sel=1 out=1 [T=28] a=0 b=0 sel=1 out=0 [T=33] a=0 b=0 sel=0 out=0 [T=38] a=1 b=0 sel=0 out=1 [T=40] a=1 b=1 sel=0 out=1 [T=51] a=1 b=1 sel=1 out=1 [T=54] a=0 b=0 sel=1 out=0 [T=62] a=1 b=0 sel=1 out=0 [T=67] a=1 b=1 sel=1 out=1 [T=72] a=0 b=1 sel=1 out=1 [T=80] a=0 b=1 sel=0 out=0 [T=84] a=0 b=0 sel=0 out=0 [T=85] a=1 b=0 sel=0 out=1 xmsim: *W,RNQUIE: Simulation is complete.
順序UDPの例
順序ロジックはレベルセンシティブまたはエッジセンシティブのいずれかであり、したがって順序UDPには2種類あります。出力ポートはUDP定義内でreg型として宣言する必要があり、initial文内でオプションで初期化できます。
順序UDPには、入力と出力フィールドの間に:で区切られた追加フィールドがあり、これが現在の状態を表します。
レベルセンシティブUDP
primitive d_latch (q, clk, d);
output q;
input clk, d;
reg q;
table
// clk d q q+
1 1 : ? : 1;
1 0 : ? : 0;
0 ? : ? : -;
endtable
endprimitive
上記テーブルの最後の行のハイフン-は、q+の値に変化がないことを示します。
module tb;
reg clk, d;
reg [1:0] dly;
wire q;
integer i;
d_latch u_latch (q, clk, d);
always #10 clk = ~clk;
initial begin
clk = 0;
$monitor ("[T=%0t] clk=%0b d=%0b q=%0b", $time, clk, d, q);
#10;
// Xの効果を見るため
for (i = 0; i < 50; i = i+1) begin
dly = $random;
#(dly) d <= $random;
end
#20 $finish;
end
endmodule
xcelium> run [T=0] clk=0 d=x q=x [T=10] clk=1 d=1 q=1 [T=13] clk=1 d=0 q=0 [T=14] clk=1 d=1 q=1 [T=17] clk=1 d=0 q=0 [T=20] clk=0 d=1 q=0 [T=28] clk=0 d=0 q=0 [T=30] clk=1 d=1 q=1 [T=38] clk=1 d=0 q=0 [T=39] clk=1 d=1 q=1 [T=40] clk=0 d=1 q=1 [T=42] clk=0 d=0 q=1 [T=47] clk=0 d=1 q=1 [T=50] clk=1 d=0 q=0 [T=55] clk=1 d=1 q=1 [T=59] clk=1 d=0 q=0 [T=60] clk=0 d=0 q=0 [T=61] clk=0 d=1 q=0 [T=64] clk=0 d=0 q=0 [T=67] clk=0 d=1 q=0 [T=70] clk=1 d=0 q=0 [T=73] clk=1 d=1 q=1 [T=74] clk=1 d=0 q=0 [T=77] clk=1 d=1 q=1 [T=79] clk=1 d=0 q=0 [T=80] clk=0 d=0 q=0 [T=84] clk=0 d=1 q=0 [T=86] clk=0 d=0 q=0 [T=87] clk=0 d=1 q=0 [T=90] clk=1 d=1 q=1 [T=91] clk=1 d=0 q=0 [T=100] clk=0 d=0 q=0 [T=110] clk=1 d=0 q=0 Simulation complete via $finish(1) at time 111 NS + 0
エッジセンシティブUDP
以下の例では、DフリップフロップをVerilogユーザー定義プリミティブとしてモデル化しています。クロックの立ち上がりエッジは01または0?で指定されることに注意してください。
primitive d_flop (q, clk, d);
output q;
input clk, d;
reg q;
table
// clk d q q+
// クロックの立ち上がりエッジ(01)で出力を取得
0 : ? : 0;
(01) 1 : ? : 1;
(0?) 1 : 1 : 1;
(0?) 0 : 0 : 0;
// クロックの立ち下がりエッジを無視
(?0) ? : ? : -;
// 安定したクロックでのデータ変化を無視
? (??): ? : -;
endtable
endprimitive
テストベンチでは、UDPをインスタンス化し、ランダムなクロック数後にランダムなd入力値で駆動します。
module tb;
reg clk, d;
reg [1:0] dly;
wire q;
integer i;
d_flop u_flop (q, clk, d);
always #10 clk = ~clk;
initial begin
clk = 0;
$monitor ("[T=%0t] clk=%0b d=%0b q=%0b", $time, clk, d, q);
#10;
for (i = 0; i < 20; i = i+1) begin
dly = $random;
repeat(dly) @(posedge clk);
d <= $random;
end
#20 $finish;
end
endmodule
画像から、出力qが入力dに1クロック遅れで追従していることがわかり、これはDフリップフロップの望ましい動作です。
xcelium> run [T=0] clk=0 d=x q=x [T=10] clk=1 d=1 q=x [T=20] clk=0 d=1 q=x [T=30] clk=1 d=1 q=1 [T=40] clk=0 d=1 q=1 [T=50] clk=1 d=1 q=1 [T=60] clk=0 d=1 q=1 [T=70] clk=1 d=0 q=1 [T=80] clk=0 d=0 q=1 [T=90] clk=1 d=1 q=0 [T=100] clk=0 d=1 q=0 [T=110] clk=1 d=1 q=1 [T=120] clk=0 d=1 q=1 [T=130] clk=1 d=1 q=1 [T=140] clk=0 d=1 q=1 [T=150] clk=1 d=0 q=1 [T=160] clk=0 d=0 q=1 [T=170] clk=1 d=0 q=0 [T=180] clk=0 d=0 q=0 [T=190] clk=1 d=0 q=0 [T=200] clk=0 d=0 q=0 [T=210] clk=1 d=1 q=0 [T=220] clk=0 d=1 q=0 [T=230] clk=1 d=1 q=1 [T=240] clk=0 d=1 q=1 [T=250] clk=1 d=1 q=1 [T=260] clk=0 d=1 q=1 [T=270] clk=1 d=1 q=1 [T=280] clk=0 d=1 q=1 [T=290] clk=1 d=1 q=1 [T=300] clk=0 d=1 q=1 [T=310] clk=1 d=1 q=1 [T=320] clk=0 d=1 q=1 [T=330] clk=1 d=1 q=1 [T=340] clk=0 d=1 q=1 [T=350] clk=1 d=1 q=1 [T=360] clk=0 d=1 q=1 [T=370] clk=1 d=0 q=1 [T=380] clk=0 d=0 q=1 [T=390] clk=1 d=0 q=0 [T=400] clk=0 d=0 q=0 [T=410] clk=1 d=1 q=0 [T=420] clk=0 d=1 q=0 [T=430] clk=1 d=1 q=1 [T=440] clk=0 d=1 q=1 [T=450] clk=1 d=1 q=1 [T=460] clk=0 d=1 q=1 [T=470] clk=1 d=1 q=1 [T=480] clk=0 d=1 q=1 Simulation complete via $finish(1) at time 490 NS + 0
Verilog
- Verilogマスター:デジタル回路設計のための完全チュートリアル
- Verilog連結マスター:実践ガイド
- Verilog代入文の理解:種類、構文、ベストプラクティス
- Verilogブロッキング代入とノンブロッキング代入:違いを徹底解説
- Verilog関数のマスター:効率的なRTL設計のために
- Verilogタスクの理解:関数との違い、構文、使用法
- Verilogクロックジェネレータ:設計と実装ガイド
- Verilog数学関数のマスター:効率的なハードウェア設計のために
- Verilog $timeformatのマスター:精度と表示制御
- 任意のスコープでのVerilog timescale理解と表示
- VerilogファイルI/Oの完全ガイド:オープン、読み取り、書き込み、クローズ