VHDLユーザがSystemVerilogでハマったところの記録

# はじめに
Verilogを使ったことがないVHDLユーザが
SystemVerilogを使いはじめてハマったところを随時まとめていきます。

LRMを読んだら参考ページを追記します。

# 開発環境
* Windows 10 64bit
* Python 3.7.0
* vunit-hdl  4.0.8
* ModelSim-Intel FPGA Edition

# ハマったところ

## case文内に複数のif文を書く

VHDLでcase文内に2つのif文を書く場合はそのまま書けば問題ありません。

```vhdl:VHDL
case state is
    when S0 =>
        if cond0 = '1' then
            a := b;
        end if;
        if cond1 = '1' then
            c := d;
        end if;
    when S1 =>
        -- ...
end case;
```

これをそのままSystemVerilogの文法に置き換えると2つ目のif文がエラーになります。

```verilog:SystemVerilog
case (state)
    S0 :
        if (cond0) begin
            a = b;
        end
        if (cond1) begin // illegal
            c = d;
        end
    S1 :
       // ...
endcase
```

2つのif文を書くためには`S0 : begin ... end`とする必要があります。

```verilog:SystemVerilog
case (state)
    S0 : begin
        if (cond0) begin
            a = b;
        end
        if (cond1) begin
            c = d;
        end
    end
    S1 :
       // ...
endcase
```

根本的にstatementが2つ以上ある場合は`begin ... end`で囲う必要があるようです。

## テストベンチのクロックでalwaysを使わずに書く

Verilogのテストベンチでは以下のように`always`でクロックを生成できます。

```verilog:SystemVerilog
always begin
    #CLK_PERIOD;
    clk = ~clk;
end
```

上記で問題はないですが、将来的にSystemVerilogのLinterで
`always_comb`, `always_ff`, `always_latch`のみ許可し`always`を禁止にしたいと考えていたため
`always`を使用しない書き方を探していました。

以下のように`initial`と`forever`でクロックを生成することができます。

```verilog:SystemVerilog
initial begin
    clk = 1'b0;
    forever #(CLK_PERIOD / 2) clk <= ~clk;
end
```

## VHDLのaggregate assignmentをどう書くか

例えば何かの処理で符号付固定小数点の信号を正の最大値にしたい場合、
VHDLではaggregate assignmentを使って以下のように書けます。

```vhdl:VHDL
signal a : signed(DATA_BITS - 1 downto 0);

a <= (DATA_BITS - 1 => '0', others => '1');
```

SystemVerilogでは`$bits`を使って以下のように書けます。

```verilog:SystemVerilog
logic signed[DATA_BITS - 1 : 0] a;

assign a = {1'b0, {($bits(a) - 1){1'b1}}};
```

正の最大値という意味では以下の記述のほうがわかりやすいですが
aが32bitより大きくなったときにエラーになりそうなので避けています。(動作未確認)

```verilog:SystemVerilog
assign a = (2 ** ($bits(a) - 1)) - 1;
```

## stringを含むstructの配列を作る

SystemVerilogではstructを使うとテストパターンを見やすく書けます。

```verilog:SystemVerilog
    localparam DATA_BITS = 16;

    typedef struct{
        string name;
        logic signed[0 : 1][DATA_BITS - 1 : 0] test_data;
        logic signed[DATA_BITS - 1 : 0] ans_data;
    } test_pattern_type;

    localparam test_pattern_type TEST_PATTERN = '{
        name : "0 -> + 1",
        test_data : '{16'h0000, 16'h0001},
        ans_data : 16'h0001
    };
```

ただし、以下のように変数の左側で配列の要素数を宣言すると
`TEST_PATTERN`が`packed array`になるので
`Cannot create a packed array from unpacked elements.`というエラーが出ます。

```verilog:SystemVerilog
    ...
    localparam TEST_PATTERN_NUM = 2;
    localparam test_pattern_type[0 : TEST_PATTERN_NUM - 1] TEST_PATTERN = {
        {
            name : "0 -> + 1",
            test_data : {16'h0000, 16'h0001},
            ans_data : 16'h0001
        },
        {
            name : "0 -> - 1",
            test_data : {16'h0000, 16'hFFFF},
            ans_data : 16'hFFFF
        }
    };
```

`unpacked array`にするには変数の右側で配列の要素数を宣言します。

```verilog:SystemVerilog
    ...
    localparam test_pattern_type TEST_PATTERN[0 : TEST_PATTERN_NUM - 1] = '{
    ...
```

ただし`unpacked array`は合成ツール上がエラーを出すことが多いので
合成用コードには使わない、テストコードでも最低限の使用にとどめておくのか無難です。

また`unpacked array`の場合は配列の大きさだけを指定することができます。([参考コメント](https://qiita.com/ryo_i6/items/2e175edfb0759b86f82f#comment-500c30a0a2d64ae44dfb))

```verilog:SystemVerilog
    ...
    localparam test_pattern_type TEST_PATTERN[TEST_PATTERN_NUM] = '{
    ...
```

この場合は`[0 : TEST_PATTERN_NUM - 1]`で宣言した場合と同じ順になりました。

### 参考
[電子回路日和 - Associated Array](https://sites.google.com/site/dejitarudezainfan/systemverilog/associatedarray)

## Interface arrayに対するfor

SystemVerilogではInterfaceをarrayとして使用できます。
例えば、複数個のADCが独立で並んでいるようなmoduleを作成する場合には以下のようにgenerateで書けます。

SystemVerilog では generate/endgenerate は文法上は省略可能です。([参考コメント](https://qiita.com/ryo_i6/items/2e175edfb0759b86f82f#comment-500c30a0a2d64ae44dfb))
しかし使用しているQuartusではエラーになったので、ここでは省略せずに書いています。

```verilog:SystemVerilog
module adc_receiver_array #(
    parameter IC_NUM = 2
) (
    input logic clk,
    input logic rst,
    adc_ic_interface.receiver ic_if[IC_NUM - 1 : 0],
    adc_data_interface.receiver data_if[IC_NUM - 1 : 0]
);

    generate
        for(genvar i = 0; i <= IC_NUM - 1; i++) begin : generate_ic_array
            adc_receiver adc_receiver_inst (
                .clk,
                .rst,
                .adc_ic_interface(ic_if[i]),
                .adc_data_interface(data_if[i])
            );
        end
    endgenerate

end module
```

一方で、互いの要素が独立でないときにalways内のforで使おうとするとエラーになります。
interface arrayは変数ではなくモジュールインスタンス扱いです。
このためインデックスはエラボレーション時に決定される必要があります。
しかし、always内のforのインデックスは実行時に評価されるのでエラーになります。

あまり良い例ではないですが、
すべての入力データがvalidの時に出力を更新するようなモジュールを以下のように書くとエラーになります。

```verilog:SystemVerilog
interface data_interface;
    logic valid;
    logic signed[7 : 0] data;
    modport master(output valid, data);
    modport slave(input valid, data);
endinterface

module sample #(
    localparam NUM = 2
) (
    input logic clk,
    data_interface.slave din_if[NUM - 1 : 0],
    data_interface.master dout_if[NUM - 1 : 0]
);

    logic valid;

    always_comb begin
        valid = 1'b1;
        for(int i = 0; i <= INPUT_NUM - 1; i++) begin // illegal
            valid = valid & din_if[i].valid;
        end
    end

    generate
        for(genvar i = 0; i <= NUM - 1; i++) begin : output_ff_array
            always_ff @(posedge clk) begin
                dout_if[i].valid <= valid;
                if (valid) begin
                    dout_if[i].data <= din[i].data;
                end
            end
        end
    endgenerate

end module
```

このような場合には一度別の変数にassignする必要があります。

```verilog:SystemVerilog
    // ...
    logic [NUM - 1 : 0] valid_array;
    logic valid;

    generate
        for(genvar i = 0; i <= NUM - 1; i++) begin : generate_valid_array
            assign valid_array[i] = din_if[i].valid;
        end
    endgenerate

    always_comb begin
        valid = 1'b1;
        for(int i = 0; i <= INPUT_NUM - 1; i++) begin // OK
            valid = valid & valid_array[i];
        end
    end
    // ...
```

<details><summary>本質的でない話(折りたたみ)</summary>
上記の`always_comb`はただのリダクション演算なので
`for`を使わずに`assign valid = &valid_array;`と書けます。

</details>

### 参考
[PRHYZMICA – BLOG - System Verilog Array of Interfaces](http://blog.prhyzmica.com/?p=197)
Vivadoのバグではないかとの記載がありますが言語仕様として正しいようです。

## inoutの接続

SystemVerilogでも`inout`のポートを作ることができますが、
以下のように`logic`型で`inout`のポート同士を接続すると合成時にエラーになります。

```verilog:SystemVerilog
module module_A (
    inout logic inout_port // illegal
);
    module_B inst(
        .inout_port(inout_port)
    );
endmodule
```

`inout`は複数のドライバを持つ可能性があるため、`wire`を指定する必要があります。

```verilog:SystemVerilog
module module_A (
    inout wire inout_port
);
    module_B inst(
        .inout_port(inout_port)
    );
endmodule
```

## case文で複数条件で共通処理をする

stateがS1またはS2のときに同じ処理をするようにcase文を書く場合、
VHDLでは以下のように`|`でorの条件を書くことができます。

```vhdl:VHDL
case state is
    when S0 =>
        a <= b;
    when S1 | S2 =>
        a <= c;
    -- ...
end case;
```

SystemVerilogの場合は`,`で複数の条件を書くことができます。

```verilog:SystemVerilog
case (state)
    S0 :
        a = b;
    S1, S2 :
        a = c;
    // ...
endcase
```

### 参考
LRM 12.5 Case Statement Syntax 12-3に以下の記述があります。

```verilog:SystemVerilog
    case_item_expression {, case_item_expression} : statement
```

## 配列への一括代入

例えば、配列の全てのビットに初期値を一括代入する場合、
VHDLでは`others`を使って以下のように書けます。

```vhdl:VHDL
    constant INIT : std_logic := '1';

    type data_array_type is array (HEIGHT - 1 downto 0) of std_logic_vector(WIDTH - 1 downto 0)；
    signal data_array : data_array_type := (others => (others => INIT));
```

SystemVerilogではVHDLと同じイメージで書くと以下のようになりますが
`{}`の位置が複雑で読みにくくなります。

```verilog:SystemVerilog
    parameter logic INIT = 1'b1;

    logic[HEIGHT - 1 : 0][WIDTH - 1 : 0] data_array = {HEIGHT{{WIDTH{INIT}}}};
```

`default`を使うと以下のように読みやすくなります。

```verilog:SystemVerilog
    logic[HEIGHT - 1 : 0][WIDTH - 1 : 0] data_array = '{default : INIT};
```

また、`default`の代わりに三項演算子を使って以下のようにも書けます。

```verilog:SystemVerilog
    logic[HEIGHT - 1 : 0][WIDTH - 1 : 0] data_array = INIT ? '1 : '0;
```