[SystemVerilog]連想配列の話など

# はじめに
こんにちは。@imatatsuです。
ネタとして新しいものではないですが、SystemVerilogの連想配列についてちょっとだけご紹介します。
SystemVerilogよく使ってるつもりですが、厳密な部分はよくわからないのでテキトウに書きます。


# 連想配列ってなに？
Wikipediaより
http://ja.wikipedia.org/wiki/%E9%80%A3%E6%83%B3%E9%85%8D%E5%88%97

配列の添字に色々なデータ型が使えるよ！ってやつですね。
まぁ、何物なのかってことより、
何に使えて、何がうれしいのか
というのがポイントだと思います。

# 使い方
様々な使い道はあると思いますが一番簡単な使い道としては、
テストベンチ上にメモリを作成する場合に使用します。

32bitアドレスのメモリをテストベンチ上に作成する場合、
静的配列で下記のように宣言しているのをたまに見かけます。

```v
  reg [31:0] memory[0:1073741823];
```

配列のサイズが小さいうちはこれでもいいのですが、
64bitアドレスなど、大きいサイズになるとシミュレータがギブアップします。
ちなみに某社のシミュレータでは2147483648が最大でした。

どうせ全部は使わないから使う部分だけを宣言するとか、
何個かに分けるとか方法が無くはないのですが、なんにせよ面倒です。
32bitアドレスでテストシナリオが既に作ってあって、
別のプロジェクトで64bitアドレスにする、とかなった場合はそれなりの作業が発生する場合があります。

とゆうことで、静的配列を使うのはやめて、連想配列を使いましょう。
下記のコードのlogic [7:0] mem[longint unsigned]の部分が連想配列の宣言になります。

```v:test1.sv
`timescale 1ns/1ps
module test1();
  logic [7:0] mem[longint unsigned];

  initial begin
    mem[1] = 8'h01;
    mem[3] = 8'h03;
    mem[5] = 8'h05;
    mem[7] = 8'h07;
    mem[9] = 8'h09;
    
    for(int i=0; i<10; i++) begin
      logic [7:0] data;

      data = mem.exists(i) ? mem[i] : 8'h00;

      $display("mem[%0d] = 0x%02h", i, data);
    end
  end
  
endmodule : test1
```

mem[1] = 8'h01・・・と書いてある部分が連想配列への代入です。
代入した所だけ領域が確保されるのでサイズを気にしないですみます（たぶん）。

領域が確保されているかどうかはexistsというメソッドで調べる事ができます。
Readした時に、1度もWriteした事が無い番地だったら、existsで調べて、
0なり不定なりを返すようにすれば静的配列と同じ動きをさせることができます。

実行結果はこちら

```
mem[0] = 0x00
mem[1] = 0x01
mem[2] = 0x00
mem[3] = 0x03
mem[4] = 0x00
mem[5] = 0x05
mem[6] = 0x00
mem[7] = 0x07
mem[8] = 0x00
mem[9] = 0x09
```

# 応用編
たいした応用ではないですが、
連想配列はforeachと組み合わせて使用するとより強力になります。
foreachでまわすと、確保されている部分だけ実行されます。

ということで下記のようなuvm_report_infoもどきに
連想配列+foreachを組み込んでみました。

final begin～endの部分がforeachで回してる部分です。
my_dispに渡されたkindの内容が何であろうが、
INFOやERRORの個数を分別できるという仕組みです。

```v:test2.sv
`timescale 1ns/1ps
module test2();
  
// ----------------------------------------------------------------------------
// variables
// ----------------------------------------------------------------------------
  longint unsigned num[string][string];
  
// ----------------------------------------------------------------------------
// function
// ----------------------------------------------------------------------------
  // my_disp
  function my_disp(
      string msg      = ""
    , string kind     = ""
    , string severity = "INFO"
  );
    $write("%0tps [%-10s][%-5s] %s", $time, kind, severity, msg);
    num[kind][severity]++;
  endfunction : my_disp

  // my_checker
  function my_checker(logic [7:0] data, logic [7:0] exp);
    if(data === exp) begin
      my_disp($psprintf("Data = 0x%02h, EXP = 0x%02h\n", data, exp), "DATA_CHK", "OKAY");
    end
    else begin
      my_disp($psprintf("Data = 0x%02h, EXP = 0x%02h\n", data, exp), "DATA_CHK", "ERROR");
    end
  endfunction : my_checker
  
// ----------------------------------------------------------------------------
// scenario
// ----------------------------------------------------------------------------
  initial begin
    logic [7:0] data = 8'hA5;
    logic [7:0] exp  = 8'hA5;
    
    #10; my_disp("Test Message: INFO\n" , "TEST", "INFO" );
    #10; my_disp("Test Message: OKAY\n" , "TEST", "OKAY" );
    #10; my_disp("Test Message: WARN\n" , "TEST", "WARN" );
    #10; my_disp("Test Message: ERROR\n", "TEST", "ERROR");

    $display("");
    
    my_checker(data, exp);
    data = 8'h5A;
    my_checker(data, exp);
    data = 8'h00;
    my_checker(data, exp);

    $finish(2);
  end

  final begin
    $display("--------------------");
    $display(" Simulation Resullt ");
    $display("--------------------");

    foreach(num[k]) begin
      $display("%s", k);
      foreach(num[k][s]) begin
        $display("    %-5s: %0d", s, num[k][s]);
      end
      $display("");
    end
  end

endmodule : test2
```

実行結果

```
10000ps [TEST      ][INFO ] Test Message: INFO
20000ps [TEST      ][OKAY ] Test Message: OKAY
30000ps [TEST      ][WARN ] Test Message: WARN
40000ps [TEST      ][ERROR] Test Message: ERROR

40000ps [DATA_CHK  ][OKAY ] Data = 0xa5, EXP = 0xa5
40000ps [DATA_CHK  ][ERROR] Data = 0x5a, EXP = 0xa5
40000ps [DATA_CHK  ][ERROR] Data = 0x00, EXP = 0xa5

--------------------
 Simulation Resullt
--------------------
DATA_CHK
    ERROR: 2
    OKAY : 1

TEST
    ERROR: 1
    INFO : 1
    OKAY : 1
    WARN : 1
```