verilogテスト記述思い出し[2](テストモジュール作成)

テストモジュールを作成してみる。
※ 割りと内容に自信がない…テストモジュール作成がやはりいちばんむずかしい…

# とりあえずテスト対象のモジュールを作成
テスト対象がないと確認もできないので、足し算をするだけの簡単なモジュールを作成。

```verilog
module adder(
    input clk,
    input xrst,
    input [3:0] add1,
    input [3:0] add2,
    output reg [4:0] result
);

    // result
    always @(posedge clk or negedge xrst) begin
        if(!xrst) begin
            result <= 5'b0;
        end
        else begin
            result <= add1 + add2;
        end
    end

endmodule

```
これで、入力を与えた次のクロックサイクルで、加算結果がレジスタに格納され、出力となる。

# テストモジュールを作成
テストに必要なものが揃ったので、ここからテストモジュールの作成を開始する。

## 内部で呼び出すモジュールを用意
テストモジュール内で以下の2点を呼び出す。

* adder.v (上記)
* gen_clk.v
(前回記事: http://qiita.com/fumi-san/items/7983d0956b9f0887d10e)

上記二点を呼び出す記述をテストモジュール(test_adder.v)に記述する。

```verilog
module test_adder;

wire clk,xrst;
reg [3:0] add1, add2;
wire [4:0] result;

    gen_clk gen_clk(
        .clk(clk),
        .xrst(xrst)
    );

    adder adder(
        .clk(clk),
        .xrst(xrst),
        .add1(add1),
        .add2(add2),
        .result(result)
    );

endmodule
```
## ファイルからの読み込み準備
ここからがむずい。全然わからん。

### とりあえずファイル名を決める
テストの入力・出力の模範解答をファイルから取ってくる。
ファイル名を先にparameterで書いておくとあとが楽。

```verilog
parameter FILE_R1="test_input1.txt";
parameter FILE_R2="test_input2.txt";
parameter FILE_R3="test_output.txt";
```

ファイルから数値を読み込むのに、具体的に以下の3ステップが必要みたい。

1. ファイルをOpenする
2. Openしたファイルから一行ずつ読み込む
3. 読み込んだ文字列を数値に変換

以下では一つづつ確認していく。

### ファイルをOpenする

fileをopenするのは以下のようにする。

```verilog
// file pointer
integer fpr1,fpr2,fpr3;

initial begin

    /* step1: open files */

    fpr1 = $fopen(FILE_R1,"r")
    // exception
    if (fpr1 == 0) begin
        $display("Error: file couldn't open: %s",FILE_R1);
    end
    fpr2 = $fopen(FILE_R2,"r")
    // exception
    if (fpr2 == 0) begin
        $display("Error: file couldn't open: %s",FILE_R1);
    end
    fpr3 = $fopen(FILE_R1,"r")
    // exception
    if (fpr3 == 0) begin
        $display("Error: file couldn't open: %s",FILE_R1);
    end
end
```

### Openしたファイルから1行ずつ読み込む
まず、読み込んだ文字列を格納するための入れ物と、結果を格納する変数を作る。

```verilog
integer fread1, fread2, fread3;
reg [2*128-1:0] str1,str2,str3;
```

文字列を入れる入れ物のサイズは、128(4byte) x 文字数でよかった(はず)。

読み出す際は以下のように。

```verilog
fread1 = $fgets(str1,fpr1);
// EOF
if(fread1 == 0) begin
    $fclose(fpr1);
    $stop;
end
```

### 文字列を数値に変換

読み出した文字列がstr1,str2,str3に格納されているので、ここからデータを取り出す。

```verilog
integer scan1,scan2,scan3;
scan1 <= $sscanf(str1, "%d", add1);
```

## あとは適当に組み合わせて頑張るだけ…！！
あとは要素としては普通のモジュール作成と変わらないはずなので、頑張って作る。
(ここからがむずいんだよなぁ・・・)

3分クッキングよろしく、作ってみたのがあるので以下に記載。

```verilog
`timescale 1ns/1ps

module test_adder;
    // filename list
        parameter FILE_R1 = "test_input1.txt";
        parameter FILE_R2 = "test_input2.txt";
        parameter FILE_R3 = "test_output.txt";

    // wires/regs for modules
        wire clk,xrst;
        reg [3:0] add1, add2;
        wire [4:0] result;
    reg [4:0] compare;

    // start flag for check
        reg test_start_r;
        reg [1:0] test_start2_r;

    // vars for opening files
        integer fpr1,fpr2,fpr3;
        integer fread1, fread2, fread3;
        reg [2*128-1:0] str1,str2,str3;
        integer scan1, scan2, scan3;

        initial begin
                test_start_r <= 1'b0;
                test_start2_r <= 2'b0;
                /* step1: open files */
                fpr1 = $fopen(FILE_R1,"r");
                // exception
                if (fpr1 == 0) begin
                        $display("Error: file couldn't open: %s",FILE_R1);
                end
                fpr2 = $fopen(FILE_R2,"r");
                // exception
                if (fpr2 == 0) begin
                        $display("Error: file couldn't open: %s",FILE_R1);
                end
                fpr3 = $fopen(FILE_R3,"r");
                // exception
                if (fpr3 == 0) begin
                        $display("Error: file couldn't open: %s",FILE_R1);
                end

                // wait for a moment(xrst)
                # 1000
                @(posedge clk)
                test_start_r <= 1'b1;
        end

        // test_start2_r
        always @(posedge clk) begin
                test_start2_r[1] <= test_start2_r[0];
                test_start2_r[0] <= test_start_r;
        end

        // add1
        always @(posedge clk) begin
                if(test_start_r) begin
                        fread1 = $fgets(str1,fpr1);
                        if(fread1 == 0) begin
                                $fclose(fpr1);
                                test_start_r <= 1'b0;
                        end
                        scan2 <= $sscanf(str1, "%d", add1);
                end
        end

        // add2
        always @(posedge clk) begin
                if(test_start_r) begin
                        fread2 = $fgets(str2,fpr2);
                        if(fread2 == 0) begin
                                $fclose(fpr2);
                                test_start_r <= 1'b0;
                        end
                        scan2 <= $sscanf(str2, "%d", add2);
                end
        end
        // compare
        always @(posedge clk) begin
                if(test_start2_r[1]) begin
                        fread3 = $fgets(str3,fpr3);
                        if(fread3 == 0) begin
                                $fclose(fpr3);
                $display("finished comparing output");
                                $finish;
                        end
                        scan3 = $sscanf(str3, "%d", compare);
                        $display("output: %d, ideal: %d", result, compare);
            if (result !== compare) begin
                $display("err: value isn't match");
                $stop;
            end
                end
        end


        gen_clk gen_clk(
            .clk(clk),
            .xrst(xrst)
        );

        adder adder(
            .clk(clk),
            .xrst(xrst),
            .add1(add1),
            .add2(add2),
            .result(result)
        );

endmodule

```

# 実行してみる
実際にテストを実行してみる。

## テスト用のファイルを準備しておく(python)。

```py
f=open("test_input1.txt","w")
g=open("test_input2.txt","w")
h=open("test_output.txt","w")
for i in range(7):
    for j in range(7):
        f.write(str(i) + "\n")
        g.write(str(j) + "\n")
        h.write(str(i+j) + "\n")
f.close()
g.close()
h.close()
```

## いざ実行

以下のような感じでやってやれば動くはず

```bash
$ vlib work
$ vlog *.v
$ vsim test_adder -c << EOF
  run -all
  EOF
```

# 最後に

3分クッキングの箇所をよく読むとわかるが、ファイルからの入力値を比較するところでタイミング調整に非常に苦労している。
どうやら、clkのposedgeを条件にfileからoutputを読み出しているが、ここが良くないらしい
(posedge時点ではまだresultに値が入っておらず、結果全部のテストが1つずれてしまう)。

こういうミスを防ぐため？に、賢いモジュールなどはvalidフラグを用いていたりする。
今回だと、"if valid begin"みたいな書き方ができて大変便利。

非常に苦戦したが、こんかいの記述内容は色んな所に使いまわせるので、自分自身でもまた読み返して参考にしたい。