関連記事
Verilog の基礎: 式のビット幅の決定 (ビット幅拡張)
Verilog の基本: casex と full_case、Parallel_case の使用
Verilog の基本: 識別子の上向きおよび下向きの階層名参照
目次
1 はじめに
Verilog の case ステートメントは多方向決定ステートメントであり、式の値が他の式の値と等しいかどうかを確認するために使用されます。一致が見つかった場合は、分岐ジャンプが実行され、対応するステートメントが実行されます。 。C 言語の switch ステートメントと同様ですが、Verilog の case ステートメントにも次の特徴があります。
1. case に加えて、casez および casex のバリアントもサポートします。
2. case_expression および case_item は、さまざまなデータ型 (変数/定数、定数/変数、変数/変数) の組み合わせにすることができます。
3. 並列構成と優先構成の両方を実現できます。
4. 逆の case ステートメントをサポートします。つまり、case_expression と case_item の組み合わせが定数/変数になります。
5. 使用時に注意しないと引っ掛かりが生じる可能性があります。
6. エミュレータとシンセサイザでは x と z の理解が異なるため、前後のシミュレーション間で不一致が発生する可能性があります。
7. Synopsys の full_case および並列合成命令によっても、シミュレーションの前後で一貫性がなくなる可能性があります。
2. case文の定義
case のさまざまな特性を完全に理解するために、最初に次の case ステートメントのさまざまな部分について説明します。
case ステートメント全体
Verilog では、case ステートメントは case と endcase (casex と casez を含む) の間に含まれるすべてのコードであり、次に示すように、論理的には if-elseif-else ステートメントと同等です。
case(case_expression)
case_item1 : case_item_statement1;
case_item2 : case_item_statement2;
case_item3 : case_item_statement3;
case_item4 : case_item_statement4;
default : case_item_statement5;
endcase
次の if ステートメントと同等です。
if (case_expression === case_item1) case_item_statement1;
else if(case_expression === case_item2) case_item_statement2;
else if(case_expression === case_item3) case_item_statement3;
else if(case_expression === case_item4) case_item_statement4;
else case_item_statement5;
case の先頭 (case ステートメントの先頭)
case ステートメントのヘッダーには、キーワード case/casex/casez が含まれており、その後に括弧内の case 式が続きます。full_case およびParallel_case の包括的な命令を case ステートメントに追加する場合、これらの命令は case ステートメントのヘッダーの case 式の後に追加されます。
case 式 (case 式)
case 式は、case キーワードの直後にあり、かっこで囲まれた式です。Verilog では、case 式を使用して case 項目と比較します。case 項目は定数または変数のいずれかになります。
ケースアイテム(ケース分岐アイテム)
case 項目は、case 式との比較に使用されます。VHDL とは異なり、Verilog のケース項目は変数にすることもできます。C 言語とは異なり、Verilog の case ステートメントは Break ステートメントを意味します。case ステートメントによる一致チェックは毎回最初の case 項目から開始され、一致に成功した場合は対応する case 項目ステートメントが実行されて case ステートメントが飛び出し、それ以降の case 項目のチェックは行われません。
case item ステートメント (case 分岐式)
case item ステートメントは、case item が case 式と一致した場合に実行される 1 つ以上のステートメントです。複数のステートメントを実行する必要がある場合は、begin end または fork jion を使用してこれらのステートメントを囲み、実行順序の参照を提供します。
casedefault (ケースデフォルト分岐項目)
case default はオプションです。定義された case 項目が一致しない場合は、default オプションが一致します。Verilog はデフォルト ステートメントの位置を強制しませんが、デフォルト ステートメントをすべてのケース項目の最後に記述するのは良い習慣です。
詰め込む
casez ステートメントは、case ステートメントの変形です。casez を使用すると、比較中に「z」と「?」の値を無関係な値として扱うことができます。「z」と「?」の値が case 式または case 項目に出現する場合、対応するビットが関係ありません。つまり、対応するビットと一致しません。「z」と「?」は同等ですが、「?」を使用するともう少し明確になります。合成可能なコードを記述するときは、casez の使用に注意してください。casez を使用する場合は、「z」の代わりに「?」を使用することをお勧めします。
ケースックス
casex ステートメントは、case ステートメントの変形です。casex を使用すると、比較時に「x」、「z」、「?」の値を無関係な値として扱うことができます。「x」、「z」、「?」の値が case 式または case 項目に出現する場合、その後、対応するビットは気にしない、つまり、対応するビットと一致しません。合成可能なコードを作成するときは、casex を使用しないでください。
3. caseステートメントの実行
case ステートメントの実行プロセスは次のとおりです。
1. case ステートメントが実行されるたびに、括弧内の case 式が 1 回だけ計算され、各 case 項目と上から順に比較されます。
2. このプロセスでケースのデフォルトが発生した場合は、この上から下のプロセスでは無視します。
3. 比較の結果、case 式と一致する case 項目があった場合は、case 項目に対応する文を実行し、case 文を終了します。
4. デフォルト項目を除くすべての一致が失敗し、ケースデフォルトがある場合は、ケースデフォルトに対応するステートメントを実行し、その後ケースステートメントを終了します。
5. すべての比較が失敗した場合、case ステートメントは終了します。
実際、C 言語の switch ステートメントはジャンプ テーブルです。つまり、ケース項目項目は相互に排他的であり、ケース項目を 1 つずつ比較するのではなく、異なるアドレスにジャンプして、異なる項目に従って対応する式を実行します。ただし、Verilog では、ケース項目の項目は上から下に比較され、前の項目の優先順位が高くなります (もちろん、ケース項目の項目が相互に排他的である場合、この優先順位は効果がありません)。
ケース項目の比較を行う場合、成功した一致とみなされるには、各ビットが正確に (0,1,x,z) 一致する必要があります (これは、上記の同等の if ステートメントからもわかります。等価性の代わりに厳密な等価性= == を使用します) ==) となりますので、case 式と case 項目を指定する際は、ビット幅が一致するように注意してください。ビット幅が一致しない場合、case 式と case 項目のビット幅は最大ビットまで拡張されます。 case 式と case 項目の一方が符号なし数値の場合はゼロ埋め拡張を実行し、case 式と case 項目の両方が符号付き数値の場合は符号拡張を実行します。
case は、x と z を検出する方法を提供するために完全一致を提供しますが、2 つのバリアント casez と casex は、比較時に x と z を考慮しないメカニズムを提供します。また、if 文は厳密等価 === により x と z を検出することもできますが、case 文の case 式および case 項目に x または z が出現すると、case 文を合成できなくなりますので注意してください。
case ステートメント内の case 式は、複数の case 項目と順番に比較する必要があるため、if ステートメントはより一般的であり、1 対多の比較に限定する必要はありません。
4. 事例の適用
例 1 : このコードは z 値と x 値を検出できます (これも合成できません)
case(sig)
1'bz:$display("signal is floating");
1'bx:&display("signal is unknown");
default:$display("signal is %b",sig);
endcase
例 2 : このコードも合成できません
case(select[2:1])
2'b00: result = 0;
2'b01: result = flaga;
2'b0x,2'b0z: result = flaga ? 'bx : 0;
2'b10: result = flagb;
2'bx0,2'bz0: result = flagb ? 'bx : 0;
default: result = 'bx;
endcase
例 3 : これは、reverse case ステートメントとプライオリティ エンコーダです。
reg [2:0] encode;
case(1)
encode[2] : $displsy("Select Line 2");
encode[1] : $displsy("Select Line 1");
encode[0] : $displsy("Select Line 0");
default:$display("Error : one of the bits expected");
endcase
case ステートメントはフルおよびパラレルにすることができます。フルの場合、割り当てが割り当てられたすべての変数をカバーする場合、ラッチは生成されません。パラレルの場合、プライオリティ エンコーダーは生成されません。合成ツールは通常、それ自体が完全であるか並列であるかを推定できます。
例 4 : このコードは完全な並列コードです。合成結果はラッチを生成せず、優先構造も生成しません。
always@(*)begin
case(sel)
2'b00 : outc = a;
2'b01 : outc = b;
2'b10 : outc = c;
2'b11 : outc = d;
endcase
end
例 5 : if ステートメントを使用すると、表面的にはプライオリティ エンコーダが形成されるように見えますが、実際には、合成ツールはこれらのオプションが相互に排他的であると推定し、並列構造を合成します。
always@(*) begin
if(sel == 2'b00) outi = a;
else if(sel == 2'b01) outi = b;
else if(sel == 2'b10) outi = c;
else (sel == 2'b11) outi = d;
end
以下の図に示すように、例 4 と例 5 の総合的な結果は同じです。
例 6:以下は、APB バスが UART レジスタを読み取るコードの一部です。デフォルトは書かれていませんが、case の前に変数に初期値が代入されているためラッチは生成されません。このコードは冗長に見え、保守が難しく、エラーが発生しやすくなります。これは完全な構造ではないように見えますが、最終的に優先構造は生成されません。その理由は例 7 に示されています。
wire rbr_en,dll_en,dlh_en,....;
reg [31:0] iprdata;
always@(*) begin
iprdata=32'b0;
case({rbr_en,dll_en,dlh_en,ier_en,iir_en,lcr_en,mcr_en,lsr_en,msr_en,scr_en})
10'h10_0000_0000 : iprdata[`LEGACY_RW-1 : 0] = rbr[`LEGACY_RW-1 : 0];
10'h01_0000_0000 : iprdata[`LEGACY_RW-1 : 0] = dll[`LEGACY_RW-1 : 0];
10'h00_1000_0000 : iprdata[`LEGACY_RW-1 : 0] = dlh[`LEGACY_RW-1 : 0];
10'h00_0100_0000 : iprdata[`LEGACY_RW-1 : 0] = ier[`LEGACY_RW-1 : 0];
10'h00_0010_0000 : iprdata[`LEGACY_RW-1 : 0] = iir[`LEGACY_RW-1 : 0];
10'h00_0001_0000 : iprdata[`LEGACY_RW-1 : 0] = lcr[`LEGACY_RW-1 : 0];
10'h00_0000_1000 : iprdata[`LEGACY_RW-1 : 0] = mcr[`MCR_RW-1 : 0];
10'h00_0000_0100 : iprdata[`LEGACY_RW-1 : 0] = lsr[`LEGACY_RW-1 : 0];
10'h00_0000_0010 : iprdata[`LEGACY_RW-1 : 0] = msr[`LEGACY_RW-1 : 0];
10'h00_0000_0001 : iprdata[`LEGACY_RW-1 : 0] = scr[`LEGACY_RW-1 : 0];
endcase
end
例 7 : 以下は、変更されたコードです。ここでは、reverse case ステートメントが使用されており、より簡潔で保守性が向上しています。信号定義では同時に有効な en 信号は 1 つだけであるため、シンセサイザーはこれに従って優先順位なしで構造体を合成します (つまり、case ステートメントの合成は信号ソースの制限に関係します)。信号ソースの制限はなく、優先構造は引き続き合成され、rbr_en が最高の優先順位を持ちます。
wire rbr_en,dll_en,dlh_en,....;
reg [31:0] iprdata;
always@(*) begin
iprdata=32'b0;
case(1'b1)
rbr_en : iprdata[`LEGACY_RW-1 : 0] = rbr[`LEGACY_RW-1 : 0];
dll_en : iprdata[`LEGACY_RW-1 : 0] = dll[`LEGACY_RW-1 : 0];
dlh_en : iprdata[`LEGACY_RW-1 : 0] = dlh[`LEGACY_RW-1 : 0];
ier_en : iprdata[`LEGACY_RW-1 : 0] = ier[`LEGACY_RW-1 : 0];
iir_en : iprdata[`LEGACY_RW-1 : 0] = iir[`LEGACY_RW-1 : 0];
lcr_en : iprdata[`LEGACY_RW-1 : 0] = lcr[`LEGACY_RW-1 : 0];
mcr_en : iprdata[`LEGACY_RW-1 : 0] = mcr[`MCR_RW-1 : 0];
lsr_en : iprdata[`LEGACY_RW-1 : 0] = lsr[`LEGACY_RW-1 : 0];
msr_en : iprdata[`LEGACY_RW-1 : 0] = msr[`LEGACY_RW-1 : 0];
scr_en : iprdata[`LEGACY_RW-1 : 0] = scr[`LEGACY_RW-1 : 0];
endcase
end
5. casezの適用
case 項目では、0、1、z、x はすべて比較対象となり、無視されません。ただし、casez を使用すると、特定のビットを無視できます。casez の場合、z と ? に対応するビットは比較中に無視されますが、x は無視されません。casez を使用する場合は、? を使用して、比較時に無視する対応するビットを示すことをお勧めします。
例 8: casez はプライオリティ エンコーダを実装します。
reg [7 : 0] ir;
casez (ir)
8'b1???????:instruction1(ir);
8'b01??????:instruction2(ir);
8'b001?????:instruction3(ir);
8'b0001????:instruction4(ir);
8'b00001???:instruction5(ir);
8'b000001??:instruction6(ir);
8'b0000001?:instruction7(ir);
8'b00000001:instruction8(ir);
endcase
例9:入力制限がないため、優先構造となります。
always(*) begin
case(1'b1)
int_scr[0]: intc_number = 0;
int_scr[1]: intc_number = 1;
int_scr[2]: intc_number = 2;
int_scr[3]: intc_number = 3;
int_scr[4]: intc_number = 4;
int_scr[5]: intc_number = 5;
int_scr[6]: intc_number = 6;
int_scr[7]: intc_number = 7;
default : intc_number = (1'b1 << WIDTH)
endcase
end
上記の内容は「Verilog Programming Art」より引用しています。