Track AstMemberSel writes through virtual interface refs and connect them to matching interface-member VarScopes, so class-driven interface clocks get proper VCD activity updates. Fixes #5044.
This commit is contained in:
parent
496665800d
commit
5cddbd7fda
|
|
@ -47,6 +47,7 @@
|
|||
#include "V3UniqueNames.h"
|
||||
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
|
||||
|
|
@ -216,6 +217,9 @@ class TraceVisitor final : public VNVisitor {
|
|||
using ActCodeSet = std::set<uint32_t>;
|
||||
// For activity set, what traces apply
|
||||
using TraceVec = std::multimap<ActCodeSet, TraceTraceVertex*>;
|
||||
// Candidate interface-member VarScopes keyed by (interface type, member name)
|
||||
std::map<std::pair<const AstIface*, std::string>, std::vector<AstVarScope*>>
|
||||
m_ifaceMemberVscps;
|
||||
|
||||
// METHODS
|
||||
|
||||
|
|
@ -1125,6 +1129,13 @@ class TraceVisitor final : public VNVisitor {
|
|||
if (nodep->isTop()) m_topModp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstVarScope* nodep) override {
|
||||
if (!m_finding) {
|
||||
if (const AstIface* const ifacep = nodep->varp()->sensIfacep()) {
|
||||
m_ifaceMemberVscps[{ifacep, nodep->varp()->name()}].push_back(nodep);
|
||||
}
|
||||
}
|
||||
}
|
||||
void visit(AstStmtExpr* nodep) override {
|
||||
if (!m_finding && !nodep->user2()) {
|
||||
if (AstCCall* const callp = VN_CAST(nodep->exprp(), CCall)) {
|
||||
|
|
@ -1214,6 +1225,27 @@ class TraceVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
if (m_cfuncp && m_finding && nodep->access().isWriteOrRW()) {
|
||||
AstIfaceRefDType* const dtypep
|
||||
= VN_CAST(nodep->fromp()->dtypep()->skipRefp(), IfaceRefDType);
|
||||
if (dtypep && dtypep->isVirtual()) {
|
||||
const auto it = m_ifaceMemberVscps.find({dtypep->ifacep(), nodep->varp()->name()});
|
||||
if (it != m_ifaceMemberVscps.end()) {
|
||||
V3GraphVertex* const funcVtxp = getCFuncVertexp(m_cfuncp);
|
||||
for (AstVarScope* const vscp : it->second) {
|
||||
V3GraphVertex* varVtxp = vscp->user1u().toGraphVertex();
|
||||
if (!varVtxp) {
|
||||
varVtxp = new TraceVarVertex{&m_graph, vscp};
|
||||
vscp->user1p(varVtxp);
|
||||
}
|
||||
new V3GraphEdge{&m_graph, funcVtxp, varVtxp, 1};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
//--------------------
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
#!/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('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['--binary --trace-vcd --timing'])
|
||||
|
||||
test.execute()
|
||||
|
||||
# Expect 5 posedges and 5 low samples in VCD for the class-driven interface clock.
|
||||
test.file_grep_count(test.trace_filename, r'(?m)^1[!-~]$', 5)
|
||||
test.file_grep_count(test.trace_filename, r'(?m)^0[!-~]$', 5)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`timescale 1ns/1ns
|
||||
`define STRINGIFY(x) `"x`"
|
||||
|
||||
interface clk_iface;
|
||||
bit clk;
|
||||
endinterface
|
||||
|
||||
class clk_driver;
|
||||
virtual clk_iface vif;
|
||||
function new(virtual clk_iface vif);
|
||||
this.vif = vif;
|
||||
endfunction
|
||||
|
||||
task run();
|
||||
vif.clk = 1'b0;
|
||||
forever #5 vif.clk = ~vif.clk;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
module t;
|
||||
clk_iface ci();
|
||||
clk_driver drv;
|
||||
|
||||
int x = 0;
|
||||
always @(posedge ci.clk) x = x + 1;
|
||||
|
||||
initial begin
|
||||
drv = new(ci);
|
||||
drv.run();
|
||||
end
|
||||
|
||||
initial begin
|
||||
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
|
||||
$dumpvars(0, t);
|
||||
repeat (5) @(posedge ci.clk);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#!/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('simulator')
|
||||
|
||||
test.compile(verilator_flags2=['--binary --trace-vcd --timing'])
|
||||
|
||||
test.execute()
|
||||
|
||||
# Two class-driven clocks toggle in lockstep: 10 highs and 10 lows total.
|
||||
test.file_grep_count(test.trace_filename, r'(?m)^1[!-~]$', 10)
|
||||
test.file_grep_count(test.trace_filename, r'(?m)^0[!-~]$', 10)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`timescale 1ns/1ns
|
||||
`define STRINGIFY(x) `"x`"
|
||||
|
||||
interface clk_iface;
|
||||
bit clk;
|
||||
endinterface
|
||||
|
||||
class clk_driver;
|
||||
virtual clk_iface vif;
|
||||
function new(virtual clk_iface vif);
|
||||
this.vif = vif;
|
||||
endfunction
|
||||
|
||||
task run();
|
||||
vif.clk = 1'b0;
|
||||
forever #5 vif.clk = ~vif.clk;
|
||||
endtask
|
||||
endclass
|
||||
|
||||
module t;
|
||||
clk_iface ci0();
|
||||
clk_iface ci1();
|
||||
clk_driver drv0;
|
||||
clk_driver drv1;
|
||||
|
||||
int x0 = 0;
|
||||
int x1 = 0;
|
||||
always @(posedge ci0.clk) x0 = x0 + 1;
|
||||
always @(posedge ci1.clk) x1 = x1 + 1;
|
||||
|
||||
initial begin
|
||||
drv0 = new(ci0);
|
||||
drv1 = new(ci1);
|
||||
fork
|
||||
drv0.run();
|
||||
drv1.run();
|
||||
join_none
|
||||
end
|
||||
|
||||
initial begin
|
||||
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
|
||||
$dumpvars(0, t);
|
||||
repeat (5) @(posedge ci0.clk);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue