前章で解析したmul_1.vの実装に対して、 tb_mul を作成して検証することにした。
リファレンスモデルの実装
function automatic logic [63:0] model_mul( bit s1, bit s2, logic [31:0] a, logic [31:0] b ); logic [63:0] A_u, B_u; logic signed [63:0] A_s, B_s; logic signed [63:0] P_s; // Sign-extend or zero-extend to 64 bits A_u = {32'b0, a}; B_u = {32'b0, b}; A_s = s1 ? $signed({{32{a[31]}}, a}) : $signed(A_u); B_s = s2 ? $signed({{32{b[31]}}, b}) : $signed(B_u); P_s = A_s * B_s; // 64-bit multiply return P_s; endfunction
スコアボードによる検証
typedef struct packed { int due_cycle; logic [63:0] exp; } exp_t; exp_t exp_q[$]; // Queue of expected results always_ff @(posedge clk) begin if (reset) begin cycle <= 0; exp_q.delete(); end else begin cycle <= cycle + 1; // Compare when due_cycle is reached if (exp_q.size() != 0 && (exp_q[0].due_cycle == cycle)) begin if (resp_result !== exp_q[0].exp) begin $display("[TIME %0t] MISMATCH at cycle %0d:", $time, cycle); $fatal(1, "Result mismatch."); end exp_q.pop_front(); end end end
ドライバーの設計
task automatic drive_and_expect( bit s1, bit s2, logic [31:0] a, logic [31:0] b ); logic [63:0] expected = model_mul(s1, s2, a, b); @(posedge clk); req_in_1_signed = s1; req_in_2_signed = s2; req_in_1 = a; req_in_2 = b; req_valid = 1'b1; // Push expected result into queue exp_q.push_back({cycle+RESP_LATENCY+1, expected}); @(posedge clk); req_valid = 1'b0; endtask
ダイレクト・テスト
task automatic run_directed(); // Corner cases drive_and_expect(0,0, 32'd0, 32'd0); // ゼロ乗算 drive_and_expect(1,1, 32'h8000_0000, 32'd2); // INT_MIN * 2 drive_and_expect(1,1, 32'h8000_0000, 32'hFFFF_FFFF); // INT_MIN * -1 drive_and_expect(0,0, 32'hFFFF_FFFF, 32'hFFFF_FFFF); // 最大値同士 // ... その他の境界条件 endtask
ランダムテスト
task automatic run_random(int n); for (int i = 0; i < n; i++) begin bit s1 = $urandom_range(0,1); bit s2 = $urandom_range(0,1); logic [31:0] a = $urandom(); logic [31:0] b = $urandom(); drive_and_expect(s1, s2, a, b); end endtask