出木杉君演算子 inside

# 出木杉君演算子 inside

石谷 @[PEZY Computing](https://www.pezy.co.jp/) です。[SystemVerilog Advent Calendar 2020](https://qiita.com/advent-calendar/2020/systemverilog) と言うことで、`inside` 演算子の紹介をしたいと思います。

## `inside` 演算子とは

`inside` 演算子は SystemVerilog で導入された演算子です。LRM 上は `Set membership operator` として説明されています。
出木杉君と言っても過言ではないぐ便利な演算子で、以下の３つの機能を有しています。

* 複数個の比較値との比較
* 範囲の比較
* ワイルドカード比較

では、サンプルコードとともに、これらの機能を紹介していきます。
なお、サンプルコードは GitHub 上のリポジトリに置いてあります。

https://github.com/taichi-ishitani/introduction_of_inside_operator

## 複数個の比較値との比較

「比較値が複数あり、どれかに一致するか」を調べる場合、従来の Verilog では、

```verilog
is_matched = (v == 0) || (v == 1) || (v == 2);
```

のように書く必要があります。`inside` 演算子を使うと、これらを纏めることができて、

```verilog
is_matched = v inside {0, 1, 2};
```

のように書くことができます。また、`{}` の中に配列を指定することもできて、

```verilog
int valid_values[3] = '{0, 1, 2};
is_matched = v inside {valid_values};
```

と書くこともできます。

* 注意
    * Synopsys 社の Design Compiler は、上記の比較要素を配列に詰める方法をサポートしていないようです。

以下のサンプルコードを Vivado 合成すると、

```verilog
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` になりました。

![sample_1](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144465/af26b3ed-90b1-a9de-a45a-5da01c7ffdc9.png)

## 範囲の比較

`inside` 演算子は出木杉君なので、範囲の比較もすることができます。
`min <= v <= max` のような比較を行う場合、Verilog では、

```verilog
in_range = (v >= 4) && (v <= 7);
```

のように書く必要があります。`inside` 演算子では `{}` の中に `[min:max]` のように値域を指定することができて、

```verilog
in_range = v inside {[4:7]};
```

のように書くことができます。`[min:max]` の `min`/`max` は定数だけではなく、変数 (信号) を指定することもできて、

```verilog
int min = 4;
int max = 7;
in_range = v inside {[min:max]};
```

と書くこともできます。
また、`$` を `:` の左側に置くと `inside` の左辺値の最小値、右側に置くと最大値を表します。以下のような場合は、

```verilog
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]}` と等価になります。

範囲比較の場合も合成してみましょう。以下のサンプルコードを合成してみると、

```verilog
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
```

案の定な結果になります。

![sample_2](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144465/7605a934-ce0a-1911-82e1-7367c6af9337.png)


## ワイルドカード比較

`{}` の中の値の `?`/`x`/`z` は don't care として扱われ、ワイルドカードとして振舞います。有能ですね。
例えば、以下のような場合、

```verilog
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 に食わせてみましょう。

```verilog
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
```

ワルドカードを使った場合も合成可能で、以下のような結果になりました。
なるほど、マスクをかけてから、比較するのですか。

![sample_3](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144465/d3725297-3fff-d74f-6026-eeab835d2a0f.png)

### 補足

SystemVerilog では、右辺値にワイルドカードを取れる演算子として `==?` と `!=?` (`Wildcard equality operators`) が追加されています。`inside` 演算子における整数値の比較は `==?` を使って行われています。

## 合わせ技

上述の３つを、１つの `{}` の中に入れることもできます。超絶便利ですね。
合わせ技の場合も合成可能です。

```verilog
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` が最終結果になります。予想通りですね。

![sample_4](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/144465/4d82d3fc-f5e9-bb5c-cab0-c4ebf20545f0.png)

## 注意事項

`inside` 演算の結果を否定 (`!`) する場合は注意が必要ですつ。
`inside` 演算子は、論理否定演算子より結合度が低いので、

```verilog
result = !v inside {0, 1};
```

のように書くと、

```verilog
result = (!v) inside {0, 1};
```

のようになり、`v` に `!` を適用した結果に `inside` 演算子が適用されてしまいます。
なので、このような場合は、`()` で囲ってあげましょう。

```verilog
result = !(v inside {0, 1});
```

## 最後に

`inside` 演算子がいかに出木杉君かをご理解いただけたかと思います。論理合成可能な演算子なので、どんどん使っていきましょう。
(ただし [Quartus ユーザーは除く](https://www.intel.com/content/www/us/en/programmable/quartushelp/17.0/mapIdTopics/jka1465580570693.htm)。)

## 宣伝

YAML とか Excel で記述したレジスタマップから、RTL 等を自動生成するツール (RgGen) を作っています。
面倒な CSR のコーディングをせずに済むので、手前味噌ですが、便利なツールです。
生成された RTL は、~~ダメっ子~~ Quartus でも合成できることを確認しています。
よかったら、使ってみてください。

* https://github.com/rggen/rggen

Qiita にも紹介記事を載せてあります。

* https://qiita.com/taichi-ishitani/items/5155b2928b7d85370ae6
* https://qiita.com/taichi-ishitani/items/d89738b5376503c813d8

UVM で記述した AMBA AXI/APB の VIP (モドキ) も公開しています。
こちらも、よければ、見てみてください。

* https://github.com/taichi-ishitani/tvip-axi
* https://github.com/taichi-ishitani/tvip-apb

SystenVerilog で記述した Network On Chip (NoC) も公開しています。
Design Compiler および Vivado でエラボレーションできるところまでは確認しています。
RTL 中で SystemVerilog をどこまで使えるのかの参考になると思います。
(ただし [Quartus は知らない](https://www.intel.com/content/www/us/en/programmable/quartushelp/17.0/mapIdTopics/jka1465580570693.htm)。)
UVM を使った検証環境も同梱しているので、UVM を使った検証環境の参考にもなると思います。

* https://github.com/taichi-ishitani/tnoc