[SystemVerilog] SystemVerilogで拡張されたビット操作機能(論理合成用)

SystemVerilogではVerilogに対してビット操作の機能が拡張されています。ここでは論理合成にも使える機能を見ていきましょう。
筆者は頻繁に使うわけではないですが、うまくはまるとコードが非常に簡潔に書けることがあります。

# Streaming Operator
最初にStreaming Operatorです。LRMによるとbit streamはUnpack/Pack変換を主用途としているようなのですが、これはシミュレーション用の機能です。おまけ的な機能として(おまけにしては便利すぎる機能なのですが)ビットの順番を変換する機能があり、こちらは論理合成でサポートされています。
Stream Operatorには<<と>>の2種類があるのですが、RTL記述に役立つのは<<のみです。
以下の例のように使用し、<<nのnビット毎に位置を逆に並び替えます。

```verilog
program top();
  initial begin
    logic [7:0] v0 = 8'b0011_1101; // 8'h3d
    logic [7:0] v1, v2;

    v1 = {<<1{v0}}; // Bit reverse (8'b1011_1100)
    v2 = {<<4{v0}}; // 4 Bit chunk reverse (8'b1101_0011 or 8'hd3)

    $display("%08b", v1);
    $display("%08b", v2);
  end
endprogram
```

# ベクタのビット選択

Verilog/Systemverilogではベクタから複数ビットを選択するのに変数が使用できないため、以下のようなコードはコンパイルエラーとなります。


```verilog
module u1(output logic [3:0] vout,
          input logic [7:0] vin,
          input logic [2:0] sel // Assuming ranges 0 to 4
         );
  assign vout = vin[sel + 3 : sel]; // Verilog/SystemVerilog do not support!!
endmodule
```

同等の機能を実現するには以下のようにコードを書き直す必要があります。ちょっと面倒ですね。

```verilog
module u1(output logic [3:0] vout,
         input logic [7:0] vin,
         input logic [2:0] sel // Assuming ranging 0 to 4
         );
  always_comb begin
    for (int i = 0; i < 4; i++) begin
      vout[i] = vin[sel + i];
    end
  end
endmodule
```

SystemVerilogでは以下のように簡潔に書くことができます。n+:mで、ベクタのnビット目からmビット幅を表します。同様にn-:mでnビット目からマイナス方向にmビット幅を表します。
nには変数を使えますがmは定数でなければなりません。

```verilog
module u1(output logic [3:0] vout,
          input logic [7:0] vin,
          input logic [2:0] sel // Assuming ranging 0 to 4
         );
  assign vout = vin[sel +: 4]; // i.e. vout[3:0] = vin[sel + 3 : sel]
endmodule
```

上記は代入式の右辺に使用していますが、左辺にも使用できます。

```verilog
module u1(output logic [7:0] vout,
          input logic [3:0] vin,
          input logic [2:0] sel // Assuming ranging 0 to 4
         );
  always_comb begin
    vout = 0;
    vout[sel +: 4] = vin; // i.e. vout[sel + 3 : sel] = vin[3:0]
  end
endmodule
```


# 複合技
上記の2つを利用して、さらに複雑なビット操作ができます。以下は32bit x 4set=128bit長のデータを受け取り、
32bit毎にエンディアンを変換して出力するモジュールの例です。

```verilog
module EndianConverter(output logic[127:0] vout,
                       input logic[127:0] vin);
  localparam BIT_CHUNK = 32;
  always_comb begin
    for (int i = 0; i < 127; i += BIT_CHUNK) begin
      vout[i +: BIT_CHUNK] = {<<8{vin[i +: BIT_CHUNK]}};
    end
  end
endmodule
```
上記モジュールのテストプログラムです。

```verilog
module top;
  logic [127:0] vin;
  logic [127:0] vout;

  EndianConverter u1(.vout(vout), .vin(vin));

  initial begin
    vin = 128'h01234567_89abcdef_01020304_05060708;
    #1ns;
    $display("VIN:  %032h", vin);
    $display("VOUT: %032h", vout); // 67452301_efcdab89_04030201_08070605
    $display("");
    vin = 128'h090a0b0c_0d0e0f00_11121314_15161718;
    #1ns;
    $display("VIN:  %032h", vin);
    $display("VOUT: %032h", vout); // 0c0b0a09_000f0e0d_14131211_18171615
  end
endmodule
```

# 参考文献
"11.4.14 Streaming operators (pack/unpack)". [1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language](https://doi.org/10.1109/IEEESTD.2018.8299595). pp. 274-278.
"11.5.1 Vector bit-select and part-select addressing". [1800-2017 - IEEE Standard for SystemVerilog--Unified Hardware Design, Specification, and Verification Language](https://doi.org/10.1109/IEEESTD.2018.8299595). pp. 279-281.