【SystemVerilog】Constrained Randomについて考える

Systemverilog
田中太郎
田中太郎

Constrained Randomの考えを使った検証環境のサンプルを作成しました。

本当はclassにrandの変数を定義してconstraintで範囲を決定→randomize()するのですが、

constraintとrandomize()が対応していなかったので、組み込みコマンドの$urandom()と$urandom_range()を使って作成しています。

Constrained Random(制約付きランダム)とは

Constrained Random(制約付きランダム)は、複雑化したRTLのテストを早期に完了させ、人手では見逃すようなケースをテストします。

近年のハードウェアの高機能化によりRTLが複雑になってきています。

それにより、人手ではテストアイテムを上げきることが困難になってきました。工数がかかり、見逃しも出てきます。

そこで、ランダム手法が注目されました。

その名の通り、ランダムなデータを入力とすることで人手では見逃すようテストを作成します。

しかし、完全なランダムだと本来は起こりえない組み合わせの値が入力されることもあります。

そこで、Constrained Randomが主流となりました。

Constrained Randomのメリット・デメリット

メリット
  • テストを1つ1つ作成しなくて良い
  • 人手では見落とすバグを見つけられる
デメリット
  • カバレッジが埋まらない
    ピンポイントな条件でしか発火しないカバレッジがあると埋まるまでに時間がかかる
  • デバッグが困難
    テストがFailしたときにテストパラメータから回路にどのような挙動をさせているか理解が困難

サンプル検証環境

Constrained Randomを使った検証環境のサンプルを作成しました。

構成
dut.sv

検証対象の回路です。

in_validが1のときのdinに1を加算したdoutを出力します。

out_validが1のときのdoutが有効データです。

module dut(
    input clk,
    input rst_n,
    input in_valid,
    input [5:0] din,
    output logic out_valid,
    output logic [5:0] dout
);
    always_ff @(posedge clk, negedge rst_n) begin
        if (!rst_n) begin
            dout <= '0;
        end
        else begin
            dout <= din + 1'b1;
        end
    end

    always_ff @(posedge clk, negedge rst_n) begin
        if (!rst_n) begin
            out_valid <= '0;
        end
        else begin
            out_valid <= in_valid;
        end
    end
endmodule
monitor.sv

DUTの出力をモニタします。

期待値と異なると、[ERROR]が出力されます。

interface mon_if_t;
    logic clk;
    logic rst_n;
    logic valid;
    logic [5:0] din;
    logic [5:0] dout;

    logic [5:0] exp;
    always_ff @(posedge clk, negedge rst_n) begin
        if (!rst_n) begin
            exp <= '0;
        end
        else begin
            exp <= din + 1'b1;
        end
    end
endinterface

class monitor #(parameter type mon_if_t = virtual mon_if_t);
    mon_if_t mon_if;
    int exp;

    function new(mon_if_t _mon_if);
        mon_if = _mon_if;
    endfunction

    task start;
        forever begin
            @(posedge mon_if.clk iff mon_if.valid);
            if (mon_if.dout != mon_if.exp) begin
                $display("[ERROR]: [act] %d, [exp] %d\n", mon_if.dout, mon_if.exp);
            end
        end
    endtask
endclass
test.sv

テストファイルです。

「SEED」の値を変えることで乱数生成のシードを変更します。

別なテストを作る場合は、「SEED」に異なる値を入れます。異なる結果が得られます。

`define SEED 100
tb.sv
interface dut_if_t;
    logic clk;
    logic rst_n;
    logic in_valid;
    logic [5:0] din;
    logic out_valid;
    logic [5:0] dout;
endinterface

`include "monitor.sv"
`include "test.sv"
module tb;
    initial begin
        $dumpfile("wave.vcd");
        $dumpvars(0,tb);
    end

    bit clk = 0;
    bit rst_n = 0;
    always #5 clk = ~clk;

    monitor #(virtual mon_if_t) mon;

    dut_if_t dut_if();
    mon_if_t mon_if();

    assign dut_if.clk = clk;
    assign dut_if.rst_n = rst_n;

    assign mon_if.clk = dut_if.clk;
    assign mon_if.rst_n = dut_if.rst_n;
    assign mon_if.valid = dut_if.out_valid;
    assign mon_if.din = dut_if.din;
    assign mon_if.dout = dut_if.dout;

    dut i_dut(
        .clk(dut_if.clk),
        .rst_n(dut_if.rst_n),
        .in_valid(dut_if.in_valid),
        .din(dut_if.din),
        .out_valid(dut_if.out_valid),
        .dout(dut_if.dout)
    );

    int cnt;
    initial begin
        $urandom(`SEED);
        mon = new(mon_if);

        @(posedge clk);
        rst_n = 1;

        fork
            mon.start();
        join_none

        cnt = $urandom_range(1, 100);
        for (int i = 1; i <= cnt; i++) begin
            dut_if.din = $urandom_range(0, 64);
            dut_if.in_valid = $urandom_range(0, 1);

            @(posedge clk);
            $display("Data count [%d/%d]", i, cnt);
        end
        $finish;
    end
endmodule
出力結果

標準出力に表示される出力です。

期待値と異なると[ERROR]が表示されます。

もちろん、テストファイルの「SEED」の値を変えると出力が異なります。

Data count [          1/         36]
Data count [          2/         36]
Data count [          3/         36]
Data count [          4/         36]
***************割愛***************
Data count [         34/         36]
Data count [         35/         36]
Data count [         36/         36]
波形

まとめ

Constrained Randomの考え方で検証環境のサンプルを作成しました。

本当はclassにrandの変数を定義してconstraintで範囲を決定→randomize()するのですが、

対応していないシミュレータもあるので使用しませんでした。

コメント

タイトルとURLをコピーしました