diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 646b042d6..e9809e8a1 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -186,6 +186,7 @@ Maxim Fonarev Michael Bedford Taylor Michael Bikovitsky Michael Killough +Michael Rogenmoser Michal Czyz Michaƫl Lefebvre Miguel Perez Andrade diff --git a/test_regress/t/t_scheduling_virt_iface_array.py b/test_regress/t/t_scheduling_virt_iface_array.py new file mode 100755 index 000000000..c63aaf108 --- /dev/null +++ b/test_regress/t/t_scheduling_virt_iface_array.py @@ -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() diff --git a/test_regress/t/t_scheduling_virt_iface_array.v b/test_regress/t/t_scheduling_virt_iface_array.v new file mode 100644 index 000000000..576c8b757 --- /dev/null +++ b/test_regress/t/t_scheduling_virt_iface_array.v @@ -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 diff --git a/test_regress/t/t_scheduling_virt_iface_class.py b/test_regress/t/t_scheduling_virt_iface_class.py new file mode 100755 index 000000000..c63aaf108 --- /dev/null +++ b/test_regress/t/t_scheduling_virt_iface_class.py @@ -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() diff --git a/test_regress/t/t_scheduling_virt_iface_class.v b/test_regress/t/t_scheduling_virt_iface_class.v new file mode 100644 index 000000000..0994cb95d --- /dev/null +++ b/test_regress/t/t_scheduling_virt_iface_class.v @@ -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 diff --git a/test_regress/t/t_scheduling_virt_iface_converge_simple.py b/test_regress/t/t_scheduling_virt_iface_converge_simple.py new file mode 100755 index 000000000..c63aaf108 --- /dev/null +++ b/test_regress/t/t_scheduling_virt_iface_converge_simple.py @@ -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() diff --git a/test_regress/t/t_scheduling_virt_iface_converge_simple.v b/test_regress/t/t_scheduling_virt_iface_converge_simple.v new file mode 100644 index 000000000..f3643275e --- /dev/null +++ b/test_regress/t/t_scheduling_virt_iface_converge_simple.v @@ -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 diff --git a/test_regress/t/t_scheduling_virt_iface_dual_vif.py b/test_regress/t/t_scheduling_virt_iface_dual_vif.py new file mode 100755 index 000000000..c63aaf108 --- /dev/null +++ b/test_regress/t/t_scheduling_virt_iface_dual_vif.py @@ -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() diff --git a/test_regress/t/t_scheduling_virt_iface_dual_vif.v b/test_regress/t/t_scheduling_virt_iface_dual_vif.v new file mode 100644 index 000000000..1d9668d44 --- /dev/null +++ b/test_regress/t/t_scheduling_virt_iface_dual_vif.v @@ -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 diff --git a/test_regress/t/t_scheduling_virt_iface_nonvirt_write.py b/test_regress/t/t_scheduling_virt_iface_nonvirt_write.py new file mode 100755 index 000000000..c63aaf108 --- /dev/null +++ b/test_regress/t/t_scheduling_virt_iface_nonvirt_write.py @@ -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() diff --git a/test_regress/t/t_scheduling_virt_iface_nonvirt_write.v b/test_regress/t/t_scheduling_virt_iface_nonvirt_write.v new file mode 100644 index 000000000..b20b1b527 --- /dev/null +++ b/test_regress/t/t_scheduling_virt_iface_nonvirt_write.v @@ -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