MacOSでのverilogHDL開発備忘録(icarus verilog編)

# はじめに
FPGAを用いたアクセラレータを作成する研究室に所属しているにも関わらず、卒論以来verilogHDLを触らずに半年経過し、思い出しがてらに、はじめからハードウェアプログラミングとやらを復習しようと思い立ち、メイン機であるMacbook Airでのプログラム作成の備忘録を残そうと思った。いつもは、研究室のマシンを使うが、夏のタイミングにエアコンが壊れ研究室が地獄となっている中、わざわざ行こうとも思わないので、ラップトップでの開発環境を[わざわざ]整えた。<- エアコンを早く修理してください。泣
# Icarus Verilog
当初、大学で使っている、QuartusのModelSimをMacにインストールして環境を一発構築！！なんて思ったが、なんとwindowsとlinuxにしか対応していない。渋々、フリーで使えるコンパイラを探していると、Icarus Verilogを発見。
導入も簡単なようで。。brewをインストールしているなら以下の方法で一発！

```
$brew install icarus-verilog 
```
インストールできていれば、以下のコマンドで確認可能!

```
$iverilog
iverilog: no source files.

Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile]
                [-g1995|-g2001|-g2005|-g2005-sv|-g2009|-g2012] [-g<feature>]
                [-D macro[=defn]] [-I includedir]
                [-M [mode=]depfile] [-m module]
                [-N file] [-o filename] [-p flag=value]
                [-s topmodule] [-t target] [-T min|typ|max]
                [-W class] [-y dir] [-Y suf] [-l file] source_file(s)

See the man page for details.
```

# GtkWave
次にデバッグを容易にするための波形観測ツールを導入すべく、GtkWaveを発見。
これのインストールはまたまたbrewで可能です。ただ、途中で不足パッケージのインストールを求められた記憶が。。。でも簡単！

```
$brew install caskroom/cask/gtkwave
```
インストールされたら/Applicationに入っているのでopenコマンドで開くことができる。

```
$open /Applications/gtkwave.app
```

# サンプル
実際に使い物になるのか確認するために、同期FIFOをverilogHDLを用いてちょっちょっと書いてみた。
所々間違いが見つかりそうだが、今後使い回す予定もないので今回は目をつぶる。

```verilog:FIFO.v
//---------------------------------------
//module FIFO 
//                    made by Ryohei.K
//---------------------------------------

`default_nettype none
`define FIFO_D 4
module FIFO(
  input wire        clk,
  input wire        n_rst,
  input wire  [DATA_WIDTH - 1:0] din,
  input wire        rd_en,
  input wire        wr_en,
  output reg        empty,
  output reg        full,
  output reg  [DATA_WIDTH - 1:0] dout
);
  parameter DATA_WIDTH   = 8;
  parameter FIFO_DEPTH   = 4;
  
  reg [0:2**FIFO_DEPTH]  rd_addr;
  reg [0:2**FIFO_DEPTH]  wr_addr;
  reg [DATA_WIDTH - 1:0]  data[0:2**FIFO_DEPTH - 1];

//FIFO
  always @(posedge clk)begin
    if(wr_en & !full)data[wr_addr] <= din;
    if(rd_en & !empty)dout <= data[rd_addr];
  end

//Write address
  always @(posedge clk or posedge n_rst)begin
    if(n_rst)begin
      wr_addr <= 0;
    end else begin
      if(wr_en == 1'b1 & full == 1'b0)begin
        if(wr_addr == `FIFO_D'b1111)begin
          wr_addr <= 0;
        end else begin
          wr_addr <= wr_addr + 1'b1;
        end
      end
    end
  end

//Read address
    always @(posedge clk or posedge n_rst)begin
      if(n_rst)begin
        rd_addr <= 0;
      end else begin
        if(rd_en)begin
          if(rd_addr == `FIFO_D'b1111)begin
            rd_addr <= 0;
          end else begin
            rd_addr <= rd_addr + 1'b1;
          end
        end
      end
    end

//Full flag
  always @(posedge clk or posedge n_rst)begin
    if(n_rst)begin
      full <= 0;
    end else begin
      if( wr_addr == `FIFO_D'b1111 & wr_en == 1 & rd_en == 0)begin
        full <= 1;
      end else if(full == 1 &rd_en == 1)begin
        full <= 0;
      end
    end
  end

  //Empty flag
  always @(posedge clk or posedge n_rst)begin
    if(n_rst)begin
      empty <= 1;
    end else begin
      if(wr_en)begin
        empty <= 0;
      end
    end
  end
endmodule
```

```verilog:FIFO_bench.v
`timescale 1ps/1ps
module FIFO_bench();

  parameter DATA_WIDTH   = 8;
  parameter FIFO_DEPTH   = 4;
  reg clk,n_rst,rd_en,wr_en;
  reg  [DATA_WIDTH - 1:0]  din;
  wire [DATA_WIDTH - 1:0]  dout;

  FIFO #(.FIFO_DEPTH(FIFO_DEPTH))u0(
    .clk(clk),
    .n_rst(n_rst),
    .rd_en(rd_en),
    .wr_en(wr_en),
    .din(din),
    .dout(dout)
  );

  initial begin
    n_rst = 1;
    #10 n_rst = ~n_rst;
  end

  initial begin
    clk = 0;
    rd_en = 0;
    forever #10 clk = ~clk;
  end

  initial begin 
    #10 wr_en = 1;
        din = 1;
    #10 wr_en = ~wr_en;
    #10 wr_en = 1;
        din = 2;
    #10 wr_en = ~wr_en;
    #10 wr_en = 1;
        din = 3;
    #10 wr_en = ~wr_en;
    #10 wr_en = 1;
        din = 4;
    #10 wr_en = ~wr_en;
    #10 wr_en = 1;
        din = 5;
    #10 wr_en = ~wr_en;
    #10 wr_en = 1;
        din = 6;
    #10 wr_en = ~wr_en;
    #10 wr_en = 1;
        din = 7;
    #10 wr_en = ~wr_en;
    #10 wr_en = 1;
        din = 8;
    #10 wr_en = ~wr_en;
    #10 wr_en = 1;
        din = 9;
    #10 wr_en = ~wr_en;
    #10 wr_en = 1;
        din = 10;
    #10 wr_en = ~wr_en;
    #10 rd_en = 1'b1;
    #10 rd_en = ~rd_en;
    #10 rd_en = 1'b1;
    #10 rd_en = ~rd_en;
    #10 rd_en = 1'b1;
    #10 rd_en = ~rd_en;
    #10 rd_en = 1'b1;
    #10 rd_en = ~rd_en;
    #10 rd_en = 1'b1;
    #10 rd_en = ~rd_en;
  end
  initial begin
    $dumpfile("FIFO.vcd");
    $dumpvars(0, FIFO_bench);
    $monitor ("%t: clk = %b r_data = %b", $time, clk,dout);
  end

  initial begin
    #500 $finish();
  end

endmodule

```
\$dumpfile("ファイル名")で波形データを生成することができる。
\$dumpvars(階層,ターゲット)でターゲットのデータを観測できるらしい。

<img width="1012" alt="Screen Shot 2019-07-17 at 0.27.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328277/a5be723b-347f-98ab-941b-0a2e92e06404.png">

# 問題点
ここで問題が発生した、いつも大学ではシミュレーションをするときにncverilogを用いるがそのときには下位モジュールの配列メモリのデータを確認することができたが、今回のIcarus verilogを用いたところ、どうやら一筋縄ではできないようだ。ncverilogであれば

```
$shm_probe("",M)
```
こんな表記を入れれば全ての信号をダンプすることができるがIcarusverilogはそううまくいかなかった。
# 解決策
どうやら調べた結果、直接メモリの値にパスを通さす必要があるようだ。

```Verilog:FIFO_bench.v
integer i;       /add
 initial begin
    $dumpfile("FIFO.vcd");
    $dumpvars(0, FIFO_bench);    
    for(i = 0;i < 2**FIFO_DEPTH;i++)  /add
      $dumpvars(1,u0.data[i]);        /add
    $monitor ("%t: clk = %b r_data = %b", $time, clk,dout);
  end
```
テストベンチの最後に上記の記述を足すことで波形を見ることができた。
<img width="1012" alt="Screen Shot 2019-07-17 at 0.31.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328277/4200832d-2c49-70a8-58af-9f0a630d9721.png">

# おわりに
あんまり慣れない備忘録系の記述に戸惑いつつもどうにか書き終わってホッとしている。
これからもっと勉強する予定なので、順次学習の記録を残していこうと思う。