関連記事
Verilog の基礎: 式のビット幅の決定 (ビット幅拡張)
Verilog の基本: case、casex、casez ステートメント
Verilog の基本: 識別子の上向きおよび下向きの階層名参照
目次
3.6 full_case を使用した後もラッチは合成されます
3.8Parallel_case ではない case ステートメント
1. casex の悪用
casex は、x が case 式または case 項目に出現するかどうかに関係なく、x を「ドントケア」として扱うため (z についても同様です)、casex は設計上の問題を引き起こします。case 式に x が出現すると、この時点で問題が発生します。これは、caseex ステートメントは、事前シミュレーション中に case 式内の x を考慮しないためです。つまり、x を含むビットは、x とのマッチングに関与しません。 case 項目であり、この時点では非 x ビットのみが照合されます。シミュレーション中、ゲート レベル モデルには不確実な値 x は存在せず、結果は 1 または 0 のいずれかになる必要があり、予期しない分岐が発生する可能性があります。
以下の code6 モジュールは、イネーブル信号を備えたアドレス デコーダです。事前シミュレーション中に、初期化が有効な状態に入っていない場合があり、外部インターフェイスの設計エラーによりイネーブル信号が x 値に変化します。Enable が x の場合、casex は addr の値に従って case 項目と誤って一致します。イネーブルの値を注意深く観察しないと、回路は正常であり、この時点でイネーブルは有効であり、出力も正しいと考える可能性があります。ポストシミュレーションが行われるまで、enable はこの時点で値 x を取り、その後 x がゲートレベル回路に伝播し、出力が変化するため、フロントおよびリアのシミュレーションの不確実性が生じます。
例 1 casex によりシミュレーションの前後で不整合が発生した
module code6 (memce0, memce1, cs, enable, addr);
output reg memce0, memce1, cs;
input enable;
input [31:30]addr;
always@(*)begin
{memce0, memce1, cs} = 3'b0;
casex({addr, enable})
3'b101: memce0 = 1'b1;
3'b111: memce1 = 1'b1;
3'b0?1: cs = 1'b1;
endcase
end
endmodule
たとえば、事前シミュレーション中に何らかの理由でenableが1でaddrが2'bx0だった場合、この時点でmemce0が設定されますが、よく見ていないと、出力に基づいてaddrを当然のものとして認識してしまいます。今回は 2'b10 であるか、この時点で入力アドレスも 2'b10 である必要があり、ポストシミュレーションで問題が発生します。シミュレーション後の場合、x はゲート レベル モデルを通じて伝播し、場合によっては出力がすべて x になることもあります。
2. casez の誤用
Casez も casex と同様の問題を引き起こします。つまり、case 式に z が出現する場合、事前シミュレーションでは z の値が casez ステートメントで「ドントケア」として計算されるため、この種の問題は一般に無視されません。検証中。効率的なロジックを設計する場合、casez を使用してプライオリティ エンコーダやアドレス デコーダなどのより簡潔なコードを作成できるため、エンジニアが有用なコードを設計する場合は、casez をキャンセルしないでください。
次の例は、ここで casez が使用されていることを除いて、code6 と同じです。case 式内の一部の信号が他の入力に従って z に変化すると、誤った一致が発生する可能性があります。ただし、casex (z、x は気にしない) と比較して、casez (z は気にしない) は誤一致を引き起こす可能性が低くなります。したがって、z と一致する間違いを避けるために casez には注意してください。
casez ステートメントを使用した例 2
module code7 (memce0, memce1, cs, enable, addr);
output reg memce0, memce1, cs;
input enable;
input [31:30]addr;
always@(*)begin
{memce0, memce1, cs} = 3'b0;
casez({addr, enable})
3'b101: memce0 = 1'b1;
3'b111: memce1 = 1'b1;
3'b0?1: cs = 1'b1;
endcase
end
endmodule
3.フルケースとパラレルケース
Verilog には、頻繁に使用され批判されている 2 つの合成命令があります: //synopsys full_case と //synopsysParallel_case です。これら 2 つの命令により、ラッチを生成せずにデザインがより小型かつ高速になるという誤解が彼らにはあります。これはまったく真実ではありません。実際、これらはデザインに影響を及ぼさない可能性があり、デザインを大きくして遅くしたり、デザインを不明瞭にしたり、ラッチを合成したりする可能性さえあります。これら 2 つの命令は、フロント シミュレーションとバック シミュレーションの間で不一致を引き起こします。これがゲート レベルのシミュレーション中に見つからなかった場合、問題のある ASIC ダイが発生する可能性があります。
したがって、これら 2 つの命令を使用することは危険であり、使用しないでください。full_case とParallel_case の定義と、それらが合成コードに与える影響については、以下で詳しく説明します。
3.1 フルケース
full_case は、case 式のすべての可能な値に、それに一致する case 項目またはデフォルトがあることを意味します。case ステートメントにデフォルトが含まれていない場合でも、各 case 式が一致する case 項目を見つけることができれば、それは依然として full_case です。
3.2 完全ではない Case ステートメント
次の 3 つの選択肢のデータ セレクターの場合、sel=2'b11 の場合、対応する y 出力割り当てがないため、ここでの case ステートメントは完全ではありません。シミュレーション中、sel=2'b11 の場合、y はデータをラッチし、y に割り当てられた最後の値を保持し、合成ツールがラッチを合成します。
例 3は完全な case ステートメントではありません
module mux3a(y, a, b, c, sel);
output reg y;
input [1:0]sel;
input a, b, c;
always@(*)
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
endcase
endmodule
注: 不完全な case ステートメントは必ずしもラッチを合成するわけではありません。case_expression 信号に制限がある場合、不完全な case ステートメントがすべての可能な値をカバーしている場合、ラッチは生成されません。
このラッチを取り除くにはどうすればよいでしょうか? 初期値を割り当てるか、デフォルトのステートメントを使用できます。詳細については、「デジタル IC フロントエンド学習ノート: ラッチの合成」を参照してください。
3.3 は完全な case ステートメントです
Verilog では、合成またはシミュレーション中に case ステートメントが完全である必要はありませんが、default を追加することで case ステートメントを完全にすることができます。次の 3 つの選択肢のデータ セレクターでは、case のデフォルトが使用されるため、case ステートメントがいっぱいになります。シミュレーションでは、sel が 2'b11 の場合、y は x に駆動されますが、合成では、代入 x はドントケアを意味します (合成結果は 0 または 1、または特定の信号 (合成ツール) に接続される場合もあります)。保存されたロジックを使用します)。これにより、シミュレーション前とシミュレーション後との間で不一致が生じます。一貫性を確保するために、デフォルトの場合は y に定数値を割り当てることができます。
しかし、FSM を設計するときは、デフォルトの場合に next_state を x に割り当てます。これは、偽の状態遷移のデバッグに役立ちます。そのため、間違った遷移がある場合、next_state は x のままで、状態は x になります。これは、波形で非常に便利です。写真の中の。
例 4は完全な case ステートメントですが、前後のシミュレーションの間に不一致が生じます。
module mux3b(y, a, b, c, sel);
output reg y;
input [1:0]sel;
input a,b,c;
always@(*)
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
default: y = 1'bx;
//2'b11: y = 1'bx; 另一种方法,同样的效果
//default: y = 1'b0; 这样前后仿真一致
endcase
endmodule
case ステートメントの前に初期値が出力に割り当てられている場合、case ステートメントが不完全または完全ではない場合でも、ラッチは合成されません。
例 5では、ケースの前に初期値が代入されており、前後のシミュレーションに矛盾はありません。
module mux3c(y, a, b, c, sel);
output reg y;
input [1:0]sel;
input a,b,c;
always@(*)
y = 1'b0;
case (sel)
2'b00: y = a;
2'b01: y = b;
2'b10: y = c;
endcase
endmodule
3.4 full_case合成命令の使用
case ステートメントの先頭に「//synopsys full_case」を追加しても、「//synopsys full_case」はコメントとしてのみみなされるため、Verilog シミュレーションには影響しません。ただし、Synopsys の Design Complier は、「//synopsys」で始まるすべてのコメントを合成命令として解釈します。full_case の機能は、case ステートメントが完全ではない場合、表示されないすべての case 項目について、出力が「ドントケア」として扱われることです。case ステートメントにデフォルト項目が含まれている場合、full_case 命令は無視されます。
次の 3 つのセレクターでは、内部の case ステートメントはフルではありませんが、case ステートメントの先頭に「full_case」命令が追加されると、合成ツールはそれをフルとみなし、ラッチを合成しません。 default:y=1'bx の文も同じ効果があります。Verilog より前のシミュレーションでは、sel=2'b11 のとき、出力 y はラッチとして表されますが、合成の際、合成ツールは Sel=2'b11 のときの出力を「ドントケア」とみなし、このときの出力は時間は 6 つのツールで決定されます (ロジックの保存方法を参照)。これにより、シミュレーションの前後で不一致が生じる可能性があります。
例 6 full_case 合成命令の使用
module mux3d(y, a, b, c, sel);
output reg y;
input [1:0]sel;
input a, b, c;
always@(*)
case(sel) //synopsys full_case
2'b00 : y = a;
2'b01 : y = b;
2'b10 : y = c;
endcase
endmodule
次の 2 つの図は、full_case 合成命令を使用しない場合と使用する場合の合成結果です。
full_case 合成ディレクティブがありません
full_case 合成ディレクティブがあります
3.5 full_case 合成命令の欠点
合成命令「//synopsys full_case」は合成ツールにのみ使用され、シミュレーション ツールには使用されません。この特別なディレクティブは、case ステートメントが完了し、無駄な case 出力の割り当てを気にしないことを合成ツールに伝えるために使用されます。この命令を使用した場合、合成前後で機能が異なる場合があります。さらに、この命令はシンセサイザーにこれらの無駄な状態を気にしないように指示しますが、この命令により、full_case 命令がない場合よりもデザインが大きくなり、速度が遅くなる場合があります。
code4aではcase文は合成命令を一切使用しておらず、最終出力ロジックは3入力ANDゲートとインバータで構成されるデコーダとなっており、前後のシミュレーションが一貫しています。code4b では、case ステートメントで full_case 命令が使用されるため、en 入力は合成中に最適化され、ダングリング (ハング) 入力になります。code4a と code4b のプリエミュレーションには一貫性がありますが、code4b のプリエミュレーションとポストエミュレーションには一貫性がありません。未定義の入力ではケース項目式に現れるすべての信号が考慮されない場合、y[a] などのベクトルの変数インデックスが現れる場合、y のすべての信号は考慮されないことに注意してください。定数インデックスのみが存在する場合、これらのビットは無視されます。
例 7 では full_case を使用していません。シミュレーションの前後で一貫性があります。
module code4a(y, a, en);
output reg [3:0]y;
input [1:0]a;
input en;
always@(*)begin
y=4'h0;
case({en, a})
3'b100: y[a] = 1'b1;
3'b101: y[a] = 1'b1;
3'b110: y[a] = 1'b1;
3'b111: y[a] = 1'b1;
endcase
end
endmodule
例 8 では full_case を使用していますが、前後のシミュレーションが矛盾しています。
module code4b(y, a, en);
output reg [3:0]y;
input [1:0]a;
input en;
always@(*)begin
y=4'h0;
case({en, a}) //synopsys full_case
3'b100: y[a] = 1'b1;
3'b101: y[a] = 1'b1;
3'b110: y[a] = 1'b1;
3'b111: y[a] = 1'b1;
endcase
end
endmodule
3.6 full_case を使用した後もラッチは合成されます
''//synopsys full_case" を使用すると case ステートメント内のすべてのラッチを削除できるという迷信があります。case ステートメントで full_case を追加しない場合、合成によってラッチが生成され、full_case を使用した後でラッチを削除できます。
実際、今回は間違っていたか、不完全でした。case ステートメントに複数の出力への割り当てがあり、一部の case 項目の後で割り当てが不完全な場合、case ステートメントが完全であるか、現時点でデフォルト ステートメントがある場合でも、full_case が追加されていてもラッチは合成されます。手順。
たとえば、以下の単純なアドレス デコーダは、mce0_n、mce1_n、および rce_n のラッチを生成します。このステートメントでは full_case が使用されていますが、すべての出力が各 case_item に割り当てられているわけではないため、ラッチはすべての出力に対して合成されます。このラッチを削除する方法も非常に簡単で、すべての出力の割り当てを完了するか、always ブロックの先頭ですべての出力に初期値を割り当てます。
例 9 full_case がある場合でも、ラッチは合成されます。
module addrDecode1a(mce0_n, mce1_n, rce_n, addr);
output reg mce0_n, mce1_n, rce_n;
input [31:30] addr;
always@(*)
//{mce1_n, mce0_n,rce_n} = 3'b0;
casez(addr) //synopsys full_case
2'b10: {mce1_n, mce0_n} = 2'b10;
2'b11: {mce1_n, mce0_n} = 2'b01;
2'b0?: rce_n = 1'b0;
endcase
endmodule
総合的な結果
Always ブロックの後に初期値を代入した総合的な結果
3.7 パラレルケース
Parallel_case ステートメントは、case 式が 1 つの case 項目にのみ一致するステートメントを指します。case 式が複数の case 項目に一致する可能性があることが判明した場合、これらの一致する case 項目は重複 case 項目と呼ばれ、この case ステートメントは並列ではありません。
3.8Parallel_case ではない case ステートメント
casez を使用した次の例は、並列 case ステートメントではありません。irq=3'b111、3'b101、3'b110、または 3'b111 の場合、irq に一致する case 項目が複数存在するためです。これはシミュレーションにおける優先順位エンコーダーのように機能し、irq[2] の優先順位が最も高く、irq[0] よりも大きい irq[1] よりも大きくなります。この例では、合成時にプライオリティ エンコーダも導出します。
例 10 並列ではない Case ステートメント
module intctl1a(int2, int1, int0, irq);
output reg int2, int1, int0;
input [2:0] irq;
always@(*)begin
{int2, int1, int0} = 3'b0;
casez(irq)
3'b1??: int2=1'b1;
3'b?1?: int1=1'b1;
3'b??1: int0=1'b1;
endcase
end
endmodule
総合的な結果は次のようになります。フラット化されているため優先順位構造は見えませんが、int1 を 1 にしたい場合は irq[2] を 0 にし、int0 を 1 にしたい場合は irq[2] と irq の両方を指定する必要があります。 [1] は 0 でなければなりません。
3.9 は並列 case ステートメントです
上記の例を変更すると、次のコードが得られます。各ケース項目は独立しているため、並列になります。
例 11 は、並列の case ステートメントです。
module intctl2a(int2, int1, int0, irq);
output reg int2, int1, int0;
input [2:0] irq;
always@(*)begin
{int2, int1, int0} = 3'b0;
casez(irq)
3'b1??: int2 = 1'b1;
3'b01?: int1 = 1'b1;
3'b001: int0 = 1'b1;
endcase
end
endmodule
全体的な結果は前の例と同じです。
3.9 は並列 case ステートメントです
以下の例では、case 文の先頭に「synopsysParallel_case」命令を追加しています。この例は、シミュレーション中にプライオリティ エンコーダとしてシミュレートされますが、合成中には非プライオリティ エンコーダが導出されます。Parallel_case 命令は合成中に効果がありますが、この前後のシミュレーションは一貫性がありません。
例 12 では、Parallel_case 合成命令を使用しているため、フロント シミュレーションとバック シミュレーションの間に不一致が生じます。
module intctl1b(int2, int1, int0, irq);
output reg int2, int1, int0;
input [2:0] irq;
always@(*)begin
{int2, int1, int0} = 3'b0;
casez(irq) //synopsys parallel_case
3'b1??: int2 = 1'b1;
3'b?1?: int1 = 1'b1;
3'b??1: int0 = 1'b1;
endcase
end
endmodule
総合的な結果は、次の図の Verilog コードに示すように、この時点で入力と出力が直接接続されていることを示しています。
この結果、case ステートメントは case 項目を上から下までチェックする機能を失いますが、すべての case 項目を並行してチェックし、それらが一致する限り、これらの case 項目の背後にあるステートメントは並行して実行されます。
3.10Parallel_case合成命令のデメリット
合成命令「//synopsysParallel_case」は合成ツールでのみ使用され、シミュレーション ツールには影響しません。この特別な命令は、たとえ導出可能なプライオリティ エンコーダがあり、重複がある場合でも、すべてのケース項目が並行してチェックされることを合成ツールに指示するために使用されます。このディレクティブにより、デザインが大きくなり、速度が遅くなる場合があります。
code5a および code5b モジュールの事前シミュレーション、code5a の事前シミュレーションと事後シミュレーションは一貫しており、それらはすべてプライオリティ エンコーダーに従って機能します。つまり、a と b が同時に 1 でない場合にのみ、y は実行できます。 1になります。しかし、code5b の総合的な結果は 2 つの AND ゲートです。つまり、a と b が同時に 1 である限り、z は 1、c と d が同時に 1 である限り、y は 1 になります。この並列構造により、シミュレーションの前後で一貫性のないシミュレーションが発生します。
例 13Parallel_case コマンドを使用しない場合、前後のシミュレーションは一貫しています。
module code5a(y, z, a, b, c, d);
output reg y, z;
input a, b, c, d;
always@(*)begin
{y, z} = 2'b0;
casez ({a, b, c, d})
4'b11??: z = 1;
4'b??11: y = 1;
endcase
end
endmodule
総合的な結果
例 14Parallel_case コマンドを使用すると、フロント シミュレーションとバック シミュレーションの間で不一致が発生する
module code5b(y, z, a, b, c, d);
output reg y, z;
input a, b, c, d;
always@(*)begin
{y, z} = 2'b0;
casez ({a, b, c, d}) //synopsys parallel_case
4'b11??: z = 1;
4'b??11: y = 1;
endcase
end
endmodule
総合的な結果
15
3.11 パラレルケースは必要ありません
次の casez の例は、もともと並列ですが、Parallel_case を使用して合成されたロジックは、並列なしで合成されたロジックと同じであるため、case ステートメントの先頭にParallel_case 命令を追加することは、実際にはあまり意味がありません。
例 15 不要なParallel_case ディレクティブ
module intctl2b(int2, int1, int0, irq);
output reg int2, int1, int0;
input [2:0] irq;
always@(*)begin
{int2, int1, int0} = 3'b0;
casez(irq) //synopsys parallel_case
3'b1??: int2 = 1'b1;
3'b01?: int1 = 1'b1;
3'b001: int0 = 1'b1;
endcase
end
endmodule
したがって、Parallel_case が機能する場合、Parallel_case は危険です。Parallel_case が機能しない場合、それは case ステートメントのヘッダーに余分な文字が含まれているだけです。
4. case文のコーディング原則
以下に、case ステートメント、full_case ディレクティブ、およびParallel_case ディレクティブを使用するためのガイドラインをいくつか示します。
1. 式を記述する並列設計には、case ステートメントが適しており、コードがより簡潔で明確になります。
2. 合成可能なコードを設計するときは、casex ステートメントの代わりに casez ステートメントを使用するように注意してください。
3. reverse case ステートメントの使用には注意してください。並列 case ステートメントにのみ使用することをお勧めします。
4. 優先順位構造の設計には casez ステートメントを使用するように注意してください。意図がより明確になるように、if else if ステートメントを使用して優先順位構造を実装することもできます。
5. casez ステートメントを使用する場合は、関係のないビットを示すために「?」を使用します。「z」は使用しないことをお勧めします。
6. case ステートメントにデフォルトを追加し、出力を "x" に割り当てないことをお勧めします。前後のシミュレーション間で不一致を引き起こすロジックを少し保存する必要はありません。もちろん、always ブロックの先頭ですべての出力に初期値を割り当てることも可能です。
7. 通常、「//synopsys full_case およびParallel_case」は使用しないでください。これら 2 つの命令は合成ツールに特定の情報のみを提供し、シミュレーション ツールには提供しないため、フロント シミュレーションとバック シミュレーションの間で不一致が発生する可能性があります。
8. これら 2 つの命令の動作メカニズムをよく理解し、意図を理解していれば、これら 2 つの命令を使用できます。
9. 1 つのホット FSM に対してのみ「//synopsys full_case」を使用することが最善です。
10. 合成ツールが出力した case 文に関するレポートを確認します。例外が見つかった場合は、対応する case ステートメントを変更する必要があります。
最終的な結論は、合成ディレクティブが機能する場合、実際にはそれらが最も危険であるということです。最善の方法は、これら 2 つの命令を使用せず、フルおよびパラレルの case ステートメントを直接記述することです。casez ステートメントと casex ステートメントの使用にも注意してください。
上記の内容は、「Verilog Programming Art」からのもので、削除および変更が加えられています。