Tests: Add virtual interface scheduling convergence tests (#7323 test) (#7536)

This commit is contained in:
Michael Rogenmoser 2026-05-05 16:17:53 -07:00 committed by GitHub
parent 0e423a4b39
commit a2874b324a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 493 additions and 0 deletions

View File

@ -186,6 +186,7 @@ Maxim Fonarev
Michael Bedford Taylor
Michael Bikovitsky
Michael Killough
Michael Rogenmoser
Michal Czyz
Michaël Lefebvre
Miguel Perez Andrade

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios("vlt")
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,71 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Regression test: Class member is an array of virtual interfaces.
// The conditional trigger must handle VIF accesses through array
// elements correctly.
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
interface SimpleIf;
logic [7:0] data;
endinterface
class VifHolder;
virtual SimpleIf vifs[2];
function new(virtual SimpleIf a, virtual SimpleIf b);
vifs[0] = a;
vifs[1] = b;
endfunction
endclass
module t;
logic clk = 0;
int cyc = 0;
SimpleIf intf0();
SimpleIf intf1();
virtual SimpleIf vi0 = intf0;
virtual SimpleIf vi1 = intf1;
VifHolder holder = new(intf0, intf1);
// Write through virtual interface handles
always @(posedge clk) begin
if (cyc == 1) vi0.data <= 8'hAA;
if (cyc == 2) vi1.data <= 8'hBB;
if (cyc == 3) vi0.data <= 8'hCC;
if (cyc == 4) vi1.data <= 8'hDD;
end
// Read through array-of-VIF class member
logic [7:0] obs0, obs1;
assign obs0 = holder.vifs[0].data;
assign obs1 = holder.vifs[1].data;
always @(posedge clk) begin
cyc <= cyc + 1;
case (cyc)
2: `checkh(obs0, 8'hAA);
3: `checkh(obs1, 8'hBB);
4: `checkh(obs0, 8'hCC);
5: `checkh(obs1, 8'hDD);
6: begin
$write("*-* All Finished *-*\n");
$finish;
end
endcase
end
initial begin
repeat (20) #5 clk = ~clk;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios("vlt")
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,120 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Regression test for virtual interface trigger convergence.
// When combinational logic continuously writes the same value to a virtual
// interface signal (via continuous assign), the VIF trigger must only fire
// on actual value changes. Otherwise, the ICO/NBA scheduling loop never
// converges.
//
// This reproduces the pattern from the AXI bus_compare testbench:
// 1. A DV (driver) interface instance is accessed via virtual interface
// in a class (drv_if)
// 2. A plain interface instance (dut_if) is connected to drv_if via
// continuous assigns in both directions (request & response)
// 3. Combinational logic (always_comb) reads from dut_if members (extracted
// into struct-like variables) and writes response variables back
// 4. Response variables are assigned back to dut_if, which flow to drv_if
// 5. Writing to drv_if fires VIF triggers unconditionally, causing
// re-evaluation of all VIF-dependent logic in the ICO loop
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Simple bus interface with request and response signals
interface BusIf #(
parameter int DW = 8
) (
input logic clk
);
logic req_valid;
logic req_ready;
logic [DW-1:0] req_data;
endinterface
// Driver class that holds a virtual interface reference.
// This is what makes drv_if a "virtual interface" in Verilator's eyes.
class Driver #(
parameter int DW = 8
);
virtual BusIf #(.DW(DW)) vif;
function new(virtual BusIf #(.DW(DW)) vif);
this.vif = vif;
endfunction
task reset();
vif.req_valid <= 1'b0;
vif.req_data <= '0;
endtask
task send(input logic [DW-1:0] data);
vif.req_valid <= 1'b1;
vif.req_data <= data;
@(posedge vif.clk);
while (!vif.req_ready) @(posedge vif.clk);
vif.req_valid <= 1'b0;
endtask
endclass
module t;
logic clk = 0;
int cyc = 0;
// Driver interface (like AXI_BUS_DV) -- used via virtual interface in class
BusIf #(.DW(8)) drv_if (.clk(clk));
// DUT interface (like AXI_BUS) -- plain interface
BusIf #(.DW(8)) dut_if (.clk(clk));
// Instantiate driver class with virtual interface handle
Driver #(.DW(8)) drv = new(drv_if);
// --- Bidirectional continuous assigns (like `AXI_ASSIGN(dut_if, drv_if)) ---
// Request direction: drv_if -> dut_if
assign dut_if.req_valid = drv_if.req_valid;
assign dut_if.req_data = drv_if.req_data;
// Response direction: dut_if -> drv_if (WRITES TO VIF!)
assign drv_if.req_ready = dut_if.req_ready;
// --- Extract signals from dut_if (like AXI_ASSIGN_TO_REQ) ---
logic ext_valid;
logic [7:0] ext_data;
assign ext_valid = dut_if.req_valid;
assign ext_data = dut_if.req_data;
// --- Combinational response logic (like always_comb in tb_axi_bus_compare) ---
logic rsp_ready;
always_comb begin
rsp_ready = 1'b1; // Always accept
end
// --- Write response back to dut_if (like AXI_ASSIGN_FROM_RESP) ---
assign dut_if.req_ready = rsp_ready;
// --- Testbench stimulus using the driver class ---
initial begin
drv.reset();
@(posedge clk);
@(posedge clk);
// These writes to drv_if (VIF) trigger VIF triggers. The response
// path writes back to drv_if.req_ready with the same value (1'b1),
// which should NOT re-trigger infinitely.
drv.send(8'hAB);
@(posedge clk);
// Send same value again
drv.send(8'hAB);
@(posedge clk);
drv.send(8'h00);
@(posedge clk);
@(posedge clk);
$write("*-* All Finished *-*\n");
$finish;
end
initial begin
repeat (30) #5ns clk = ~clk;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios("vlt")
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,89 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Regression test for virtual interface trigger convergence.
// When combinational logic continuously writes the same value to a virtual
// interface signal (via continuous assign), the VIF trigger must only fire
// on actual value changes. Otherwise, the ICO/NBA scheduling loop never
// converges.
//
// This reproduces the pattern:
// 1. A initial block drived the interface via virtual interface (drv_if)
// 2. A plain interface instance (dut_if) is connected to drv_if via
// continuous assigns in both directions (request & response)
// 3. Combinational logic (assign) reads from dut_if members and writes
// response variables back
// 4. Response variables are assigned back to dut_if, which flow to drv_if
// 5. Writing to drv_if fires VIF triggers unconditionally, causing
// re-evaluation of all VIF-dependent logic in the ICO loop
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Simple bus interface with request and response signals
interface BusIf ();
logic req_valid;
logic req_ready;
endinterface
module t;
logic clk = 0;
int cyc = 0;
// Driver interface -- used via virtual interface in class
BusIf drv_if();
// DUT interface -- plain interface
BusIf dut_if();
// Instantiate virtual interface handle
virtual BusIf vif = drv_if;
// --- Bidirectional continuous assigns ---
// Request direction: drv_if -> dut_if
assign dut_if.req_valid = drv_if.req_valid;
// Response direction: dut_if -> drv_if (WRITES TO VIF!)
assign drv_if.req_ready = 1'b1; // dut_if.req_ready;
// --- Extract signals from dut_if ---
logic ext_valid;
assign ext_valid = dut_if.req_valid;
// --- Write response back to dut_if (like AXI_ASSIGN_FROM_RESP) ---
assign dut_if.req_ready = 1'b1;
// --- Testbench stimulus using the vif ---
initial begin
vif.req_valid = 1'b0;
@(posedge clk);
@(posedge clk);
// These writes to drv_if (VIF) trigger VIF triggers. The response
// path writes back to drv_if.req_ready with the same value (1'b1),
// which should NOT re-trigger infinitely.
vif.req_valid = 1'b1;
@(posedge clk);
while (!vif.req_ready) @(posedge clk);
vif.req_valid = 1'b0;
@(posedge clk);
// Send same value again
vif.req_valid = 1'b1;
@(posedge clk);
while (!vif.req_ready) @(posedge clk);
vif.req_valid = 1'b0;
@(posedge clk);
vif.req_valid = 1'b1;
@(posedge clk);
while (!vif.req_ready) @(posedge clk);
vif.req_valid = 1'b0;
@(posedge clk);
@(posedge clk);
$write("*-* All Finished *-*\n");
$finish;
end
initial begin
repeat (30) #5ns clk = ~clk;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios("vlt")
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,61 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Regression test: VIF member written through two different virtual
// interface handles pointing to the same underlying interface instance.
// The conditional trigger must detect value changes correctly regardless
// of which virtual handle performs the write.
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
interface SimpleIf;
logic [7:0] data;
endinterface
module t;
logic clk = 0;
int cyc = 0;
SimpleIf intf();
// Two different virtual interface handles to the same interface
virtual SimpleIf vi1 = intf;
virtual SimpleIf vi2 = intf;
virtual SimpleIf vi_rd = intf;
// Write through first virtual handle on odd cycles, second on even
always @(posedge clk) begin
if (cyc == 1) vi1.data <= 8'hAA;
if (cyc == 2) vi2.data <= 8'hBB;
if (cyc == 3) vi1.data <= 8'hCC;
if (cyc == 4) vi2.data <= 8'hDD;
end
// Combinational logic reading through yet another virtual handle
logic [7:0] observed;
assign observed = vi_rd.data;
always @(posedge clk) begin
cyc <= cyc + 1;
case (cyc)
2: `checkh(observed, 8'hAA);
3: `checkh(observed, 8'hBB);
4: `checkh(observed, 8'hCC);
5: `checkh(observed, 8'hDD);
6: begin
$write("*-* All Finished *-*\n");
$finish;
end
endcase
end
initial begin
repeat (20) #5 clk = ~clk;
end
endmodule

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of either the GNU Lesser General Public License Version 3
# or the Perl Artistic License Version 2.0.
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios("vlt")
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,61 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// Regression test: VIF member written through both a virtual interface and
// a non-virtual (plain) interface reference. The conditional trigger must
// detect value changes correctly even when the member is updated through
// the non-virtual path.
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
// verilog_format: on
interface SimpleIf;
logic [7:0] data;
endinterface
module t;
logic clk = 0;
int cyc = 0;
SimpleIf intf();
virtual SimpleIf vi = intf;
// Write through virtual interface
always @(posedge clk) begin
if (cyc == 1) vi.data <= 8'hAA;
if (cyc == 3) vi.data <= 8'hBB;
end
// Write through non-virtual (plain) interface
always @(posedge clk) begin
if (cyc == 2) intf.data <= 8'hCC;
if (cyc == 4) intf.data <= 8'hDD;
end
// Combinational logic reading through virtual interface
logic [7:0] observed;
assign observed = vi.data;
always @(posedge clk) begin
cyc <= cyc + 1;
case (cyc)
2: `checkh(observed, 8'hAA);
3: `checkh(observed, 8'hCC);
4: `checkh(observed, 8'hBB);
5: `checkh(observed, 8'hDD);
6: begin
$write("*-* All Finished *-*\n");
$finish;
end
endcase
end
initial begin
repeat (20) #5 clk = ~clk;
end
endmodule