[SystemVerilog] Static functionとDynamic functionの違い

functionとtaskはautomatic function/taskとstatic function/taskに分けることができます。ここでは両者の違いについて説明します。function/task両方に共通する話ですが、説明にはfunctionのみを使用します。

functionの定義時にstatic functionとすればそれはstatic functionとなり、automatic functionと定義すればそれはautomatic functionになります。

```verilog
// This is static function
function static void my_static_function;
  return 1'b1;
endfunction

// This is automatic function
function automatic void my_automatic_function;
  return 1'b1;
endfunction
```

functionにstatic/automaticを付けなかった場合はどこ定義したかによって変わり、module, interface, program, packge内のfunctionはstatic function、class内ではautomatic functionになります(LRM 13.4.2 Static and automatic functions)。

# Static function
static functionは昔のverilogの流れを汲んだモードで、デフォルトではその中の変数もstatic変数として扱われます。static変数の初期化は時間0で一度だけ行われます(LRM 6.21 Scope and lifetime)。
以下のサンプルプログラムでは、sumがシミュレーション開始時には0となり(int型は初期化しないとデフォルトでは0)、その後はファンクションが呼ばれる度に積算されていきます。


```verilog
module topmdl(); 
  function void display_sum(input int i); 
    int sum; 
    sum += i; 
    $display("%d", sum); 
  endfunction
 
  initial begin 
    display_sum(1); 
    display_sum(2); 
    display_sum(3); 
  end 
endmodule
```

```
Compiler version Q-2020.03-SP1-1; Runtime version Q-2020.03-SP1-1;  Nov 10 04:35 2020 
          1 
          3 
          6 
           V C S   S i m u l a t i o n   R e p o r t
Time: 0 ns
```

このようなレジスタがstaticな動作をすることを期待する記述はデザインには適用できないことに注意してください。論理合成ツールはfunctionの中に記憶素子が入ることを想定していません。組み合わせ回路のみで表現できる論理を想定しています。つまりfunctionの中の変数は全てautomaticで宣言しても同じ動作になるように記述しないと論理合成できません。

# Automatic function
automatic functionは後から拡張されたもので、中の変数はデフォルトではautomaticとして扱われ、初期化は、そのfunctionを呼び出す度に行われます(LRM 6.21 Scope and lifetime)。上記コードのdisplay_sumをautomaticに宣言してみます。sumが呼び出しの度に0に初期化されていることがわかります。

```verilog
module topmdl(); 
  function automatic void display_sum(input int i); 
    int sum; 
    sum += i; 
    $display("%d", sum); 
  endfunction 

  initial begin 
    display_sum(1); 
    display_sum(2); 
    display_sum(3); 
  end 
endmodule
```

```
Compiler version Q-2020.03-SP1-1; Runtime version Q-2020.03-SP1-1;  Nov 10 07:09 2020 
          1 
          2 
          3 
           V C S   S i m u l a t i o n   R e p o r t  
Time: 0 ns
```

これは明示的に宣言した変数だけではなく、引数にも当てはまります。従ってstatic functionでは再帰が意図通り動作しません。

```verilog
module topmdl();
  function automatic int factorial(input int i);
    if (i == 0) begin
      return 1;
    end else begin
      return i * factorial(i - 1);
    end
  endfunction

  function static int wrong_factorial(input int i);
    if (i == 0) begin
      return 1;
    end else begin
      return i * wrong_factorial(i - 1);
    end
  endfunction

  initial begin
    $display("%d", factorial(4));
    $display("%d", wrong_factorial(4));
  end
endmodule
```

```
Compiler version Q-2020.03-SP1-1; Runtime version Q-2020.03-SP1-1;  Nov 14 04:07 2020
         24
          0
           V C S   S i m u l a t i o n   R e p o r t 
Time: 0 ns
```

#たまにやってしまうミス
繰り返しになりますがstaticな変数はシミュレーションの開始時に一回しか初期化されないことに注意してください。
以下のサンプルプログラムでは、xを宣言と同時に初期化しています。display_double_val_1ではxの値がシミュレーション開始時に0に初期化され、それ以降更新されないので思った通りの値が出力されません[^1]。

```verilog
module topmdl(); 
  function void display_double_val_1(input int i); 
    int x = i * 2; 
    $display("%d", x); 
  endfunction 

  function void display_double_val_2(input int i); 
    int x; 
    x = i * 2; 
    $display("%d", x); 
  endfunction 

  function automatic void display_double_val_3(input int i); 
    int x = i * 2; 
    $display("%d", x); 
  endfunction 

  initial begin 
    display_double_val_1(3);
    display_double_val_2(3);
    display_double_val_3(3);
  end 
endmodule
```

```
Compiler version Q-2020.03-SP1-1; Runtime version Q-2020.03-SP1-1;  Nov 10 07:15 2020 
          0 
          6 
          6 
           V C S   S i m u l a t i o n   R e p o r t  
Time: 0 ns
```

static functionの動きは昔のverilogとの互換性を保つためにある機能で、通常はプログラマはautomatic functionの動作を期待しているはずです。特に理由がない場合検証用のfunctionには常にautomatic \<function名\>;のように常にautomaticを付けるのが安全かなと思います。よく忘れるんですけどね。module内とinterface内もそうですが、特にpackage内のfunctionがデフォルトstatic functionなのを(筆者は)忘れがちです。
実は先日筆者がこの問題で少しはまったのが今回の記事を書くきっかけでした。


[^1]: ちなみにLRMではstatic 変数の宣言を初期値付きで行う場合明示的にその変数にstaticを付けなければならない(本例だとstatic int x = i * 2;と記述しなければならないというルールがあるのですが(LRM 6.21 Scope and lifetime)、シミュレータの対応はまちまちでした。EDA playgroundで試した結果、XcelilumがWarning、QuestaがErrorで他のツールでは普通に動作しました。