FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

VHDL/VerilogのテスティングフレームワークVUnitを試行する

VUnit は VHDL/SystemVerilogのテスティングフレームワークだ。VUnitにはいくつかテストパタンが用意されているが、少し自分でもテストパタンを用意してみよう。

作ってみたのは、32bit×32bit=64bitの符号なし整数の演算器を実装して、VUnitでテストを実行してみよう。この演算器は4サイクルで32bit×32bit=64bitの演算を実行する。

VUnitのテストスイートの実装

VUnitのテストスイートを実装してみる。基本的に、TEST_CASEにテストを定義して、テスト内容を記述している。 今回は、以下のテストを記述した。

  • test_0 :  2\times 3
  • test_1 :  2726929837 \times 13
  • test_2 :  13 \times 2726929837
  • test_3 :  475228202 \times 175707306

テストパタンを以下のように実装した。

  • vunit/examples/verilog/mult_4cycle/src/test/tb_mult_4cycle.sv
`include "vunit_defines.svh"

module tb_mult_4cycle;
   localparam integer clk_period = 20; // ns

  logic [63: 0] out_c;
  logic [31: 0] in_a, in_b;
  logic         clk   = 1'b0;
  logic         reset = 1'b1;
  logic         en    = 1'b0;

  `TEST_SUITE begin
    `TEST_SUITE_SETUP begin
      #1;
      reset = 1'b0;
    end
    `TEST_CASE("test_0") begin
      en   = 1'b1;
      in_a = 2;
      in_b = 3;

      #(clk_period * 4 * 1ns);
      en = 1'b0;
      `CHECK_EQUAL (out_c, {32'h0000_0000, in_a} * {32'h0000_0000, in_b});
    end

    `TEST_CASE("test_1") begin
      en   = 1'b1;
      in_a = 2726929837;
      in_b = 13;

      #(clk_period * 4 * 1ns);
      en = 1'b0;
      `CHECK_EQUAL (out_c, {32'h0000_0000, in_a} * {32'h0000_0000, in_b});
    end

    `TEST_CASE("test_2") begin
      en   = 1'b1;
      in_a = 13;
      in_b = 2726929837;

      #(clk_period * 4 * 1ns);
      en = 1'b0;
      `CHECK_EQUAL (out_c, {32'h0000_0000, in_a} * {32'h0000_0000, in_b});
    end

    `TEST_CASE("test_3") begin
      en   = 1'b1;
      in_a = 475228202;
      in_b = 175707306;

      #(clk_period * 4 * 1ns);
      en = 1'b0;
      `CHECK_EQUAL (out_c, {32'h0000_0000, in_a} * {32'h0000_0000, in_b});
    end
  end

  `WATCHDOG(10ms);

  always begin
    #(clk_period/2 * 1ns);
    clk <= !clk;
  end

  mult_4cycle dut
    (
     .clk   (clk),
     .reset (reset),
     .en    (en),
     .in_a  (in_a),
     .in_b  (in_b),
     .out_c (out_c)
     );

endmodule

テスト結果

f:id:msyksphinz:20171009005946p:plain

実行結果は、正しく4つのテストをPassできることを確認した。結局、Pythonフレームワークを使っているとはいえ、結局はVerilogを記述しているので、まあまあ使い易いかな。

おまけ: 4サイクル32bit符号なし整数の乗算器の実装

  • vunit/examples/verilog/mult_4cycle/src/mult_4cycle.sv
module mult_4cycle
  (
   input wire          clk,
   input wire          reset,
   input wire          en,
   input wire [31: 0]  in_a,
   input wire [31: 0]  in_b,
   output wire [63: 0] out_c
   );

  logic [63: 0]        ab_lo_lo,
                       ab_lo_hi,
                       ab_hi_lo,
                       ab_hi_hi;
  logic [31: 0]        r_a, r_b;
  always_ff @ (posedge clk, posedge reset) begin
    if (reset) begin
      r_a <= 32'h0000_0000;
      r_b <= 32'h0000_0000;
    end else begin
      if (en) begin
        r_a <= in_a;
        r_b <= in_b;
      end
    end
  end // always_ff @

  logic [ 1: 0] mul_state;
  localparam state_0 = 2'b00,
    state_1 = 2'b01,
    state_2 = 2'b10,
    state_3 = 2'b11;

  logic [63: 0] r_total;

  logic [31: 0] r_total_in;
  assign r_total_in = ((mul_state == state_0) && en) ? {16'h0000, in_a[15: 0]} * {16'h0000, in_b[15: 0]} :
                      (mul_state == state_1) ? {16'h0000, r_a[15: 0]} * {16'h0000, r_b[31:16]} :
                      (mul_state == state_2) ? {16'h0000, r_a[31:16]} * {16'h0000, r_b[15: 0]} :
                      (mul_state == state_3) ? {16'h0000, r_a[31:16]} * {16'h0000, r_b[31:16]} :
                      32'h0000_0000;

  always_ff @ (posedge clk, posedge reset) begin
    if (reset) begin
      mul_state <= state_0;
      r_total   <= 64'h0000_0000_0000_0000;
    end else begin
      case (mul_state)
        state_0 : begin
          if (en) begin
            r_total  <= {32'h0000_0000, r_total_in};
            mul_state <= state_1;
          end
        end
        state_1 : begin
          r_total   <= r_total + {16'h0000, r_total_in, 16'h0000};
          mul_state <= state_2;
        end
        state_2 : begin
          r_total   <= r_total + {16'h0000, r_total_in, 16'h0000};
          mul_state <= state_3;
        end
        state_3 : begin
          r_total   <= r_total + {r_total_in, 32'h0000_0000};
          mul_state <= state_0;
        end
      endcase // case (mul_state)
    end // else: !if(reset)
  end // always_ff @

  assign out_c = r_total;

endmodule