出木杉君演算子 inside
石谷 @PEZY Computing です。SystemVerilog Advent Calendar 2020 と言うことで、inside
演算子の紹介をしたいと思います。
inside
演算子とは
inside
演算子は SystemVerilog で導入された演算子です。LRM 上は Set membership operator
として説明されています。
出木杉君と言っても過言ではないぐ便利な演算子で、以下の3つの機能を有しています。
- 複数個の比較値との比較
- 範囲の比較
- ワイルドカード比較
では、サンプルコードとともに、これらの機能を紹介していきます。
なお、サンプルコードは GitHub 上のリポジトリに置いてあります。
複数個の比較値との比較
「比較値が複数あり、どれかに一致するか」を調べる場合、従来の Verilog では、
is_matched = (v == 0) || (v == 1) || (v == 2);
のように書く必要があります。inside
演算子を使うと、これらを纏めることができて、
is_matched = v inside {0, 1, 2};
のように書くことができます。また、{}
の中に配列を指定することもできて、
int valid_values[3] = '{0, 1, 2};
is_matched = v inside {valid_values};
と書くこともできます。
- 注意
- Synopsys 社の Design Compiler は、上記の比較要素を配列に詰める方法をサポートしていないようです。
以下のサンプルコードを Vivado 合成すると、
module sample_1 (
input var [1:0] i_d,
input var [1:0] i_a,
input var [1:0] i_b,
input var [1:0] i_c,
output var o_matched
);
always_comb begin
o_matched = i_d inside {i_a, i_b, i_c};
end
endmodule
以下のようになります。想像通り、3個の ==
の出力の OR
になりました。
範囲の比較
inside
演算子は出木杉君なので、範囲の比較もすることができます。
min <= v <= max
のような比較を行う場合、Verilog では、
in_range = (v >= 4) && (v <= 7);
のように書く必要があります。inside
演算子では {}
の中に [min:max]
のように値域を指定することができて、
in_range = v inside {[4:7]};
のように書くことができます。[min:max]
の min
/max
は定数だけではなく、変数 (信号) を指定することもできて、
int min = 4;
int max = 7;
in_range = v inside {[min:max]};
と書くこともできます。
また、$
を :
の左側に置くと inside
の左辺値の最小値、右側に置くと最大値を表します。以下のような場合は、
bit [2:0] a;
range_0_to_3 = a inside {[$:3]};
range_4_to_7 = a inside {[4:$]};
a inside {[0:3]}
および a inside {[4:7]}
と等価になります。
範囲比較の場合も合成してみましょう。以下のサンプルコードを合成してみると、
module sample_2 (
input var [1:0] i_d,
input var [1:0] i_min,
input var [1:0] i_max,
output var [3:0] o_in_range
);
always_comb begin
o_in_range[0] = i_d inside {[i_min:i_max]};
o_in_range[1] = i_d inside {[ $:i_max]};
o_in_range[2] = i_d inside {[i_min: $]};
o_in_range[3] = i_d inside {[ $: $]};
end
endmodule
案の定な結果になります。
ワイルドカード比較
{}
の中の値の ?
/x
/z
は don't care として扱われ、ワイルドカードとして振舞います。有能ですね。
例えば、以下のような場合、
bit [3:0] v;
bit is_matched;
is_matched = v inside {4'b1?0?};
v
が 4'b1000
/4'b1001
/4'b1100
/4'b1101
の場合に、is_matched
が 1
になります。それ以外では、0
です。
では、さっそく Vivado に食わせてみましょう。
module samaple_3 (
input var [3:0] i_d,
output var o_matched
);
always_comb begin
o_matched = i_d inside {4'b1?0?};
end
endmodule
ワルドカードを使った場合も合成可能で、以下のような結果になりました。
なるほど、マスクをかけてから、比較するのですか。
補足
SystemVerilog では、右辺値にワイルドカードを取れる演算子として ==?
と !=?
(Wildcard equality operators
) が追加されています。inside
演算子における整数値の比較は ==?
を使って行われています。
合わせ技
上述の3つを、1つの {}
の中に入れることもできます。超絶便利ですね。
合わせ技の場合も合成可能です。
module sample_4 (
input var [3:0] i_d,
input var [3:0] i_a,
input var [3:0] i_b,
input var [3:0] i_min,
input var [3:0] i_max,
output var o_result
);
always_comb begin
o_result = i_d inside {i_a, i_b, [i_min:i_max], 4'b11??};
end
endmodule
それぞれの結果の OR
が最終結果になります。予想通りですね。
注意事項
inside
演算の結果を否定 (!
) する場合は注意が必要ですつ。
inside
演算子は、論理否定演算子より結合度が低いので、
result = !v inside {0, 1};
のように書くと、
result = (!v) inside {0, 1};
のようになり、v
に !
を適用した結果に inside
演算子が適用されてしまいます。
なので、このような場合は、()
で囲ってあげましょう。
result = !(v inside {0, 1});
最後に
inside
演算子がいかに出木杉君かをご理解いただけたかと思います。論理合成可能な演算子なので、どんどん使っていきましょう。
(ただし Quartus ユーザーは除く。)
宣伝
YAML とか Excel で記述したレジスタマップから、RTL 等を自動生成するツール (RgGen) を作っています。
面倒な CSR のコーディングをせずに済むので、手前味噌ですが、便利なツールです。
生成された RTL は、ダメっ子 Quartus でも合成できることを確認しています。
よかったら、使ってみてください。
Qiita にも紹介記事を載せてあります。
- https://qiita.com/taichi-ishitani/items/5155b2928b7d85370ae6
- https://qiita.com/taichi-ishitani/items/d89738b5376503c813d8
UVM で記述した AMBA AXI/APB の VIP (モドキ) も公開しています。
こちらも、よければ、見てみてください。
SystenVerilog で記述した Network On Chip (NoC) も公開しています。
Design Compiler および Vivado でエラボレーションできるところまでは確認しています。
RTL 中で SystemVerilog をどこまで使えるのかの参考になると思います。
(ただし Quartus は知らない。)
UVM を使った検証環境も同梱しているので、UVM を使った検証環境の参考にもなると思います。