Fix interface exposure with `--public-depth` or `--trace-depth` (#5758).
This commit is contained in:
parent
d972b7465a
commit
fd12ab3413
1
Changes
1
Changes
|
|
@ -35,6 +35,7 @@ Verilator 5.041 devel
|
|||
* Optimize constant folding in wide expression expansion (#6381). [Geza Lore]
|
||||
* Fix missing BLKSEQ when connecting module port to array (#2973).
|
||||
* Fix false CONSTVAR error on initializers (#4992).
|
||||
* Fix interface exposure with `--public-depth` or `--trace-depth` (#5758).
|
||||
* Fix cell scoping performance (#6059). [Jerry Tianchen]
|
||||
* Fix hierarchical `--prof-pgo` (#6213). [Bartłomiej Chmiel, Antmicro Ltd.]
|
||||
* Fix while loop hang on timing-delayed assignment (#6343) (#6354). [Krzysztof Bieganski, Antmicro Ltd.]
|
||||
|
|
|
|||
|
|
@ -293,7 +293,8 @@ class AstNodeModule VL_NOT_FINAL : public AstNode {
|
|||
string m_someInstanceName; // Hierarchical name of some arbitrary instance of this module.
|
||||
// Used for user messages only.
|
||||
string m_libname; // Work library
|
||||
int m_level = 0; // 1=top module, 2=cell off top module, ...
|
||||
int m_depth = 0; // 1=top module, 2=cell off top, shared things low, for -depth options
|
||||
int m_level = 0; // 1=top module, 2=cell off top, shared things have high number
|
||||
VLifetime m_lifetime; // Lifetime
|
||||
VTimescale m_timeunit; // Global time unit
|
||||
VOptionBool m_unconnectedDrive; // State of `unconnected_drive
|
||||
|
|
@ -341,7 +342,9 @@ public:
|
|||
void someInstanceName(const string& name) { m_someInstanceName = name; }
|
||||
bool inLibrary() const { return m_inLibrary; }
|
||||
void inLibrary(bool flag) { m_inLibrary = flag; }
|
||||
void level(int level) { m_level = level; }
|
||||
void depth(int value) { m_depth = value; }
|
||||
int depth() const VL_MT_SAFE { return m_depth; }
|
||||
void level(int value) { m_level = value; }
|
||||
int level() const VL_MT_SAFE { return m_level; }
|
||||
string libname() const { return m_libname; }
|
||||
string prettyLibnameQ() const { return "'" + prettyName(libname()) + "'"; }
|
||||
|
|
|
|||
|
|
@ -2419,6 +2419,7 @@ void AstNetlist::createTopScope(AstScope* scopep) {
|
|||
void AstNodeModule::dump(std::ostream& str) const {
|
||||
this->AstNode::dump(str);
|
||||
str << " L" << level();
|
||||
str << " D" << depth();
|
||||
if (modPublic()) str << " [P]";
|
||||
if (inLibrary()) str << " [LIB]";
|
||||
if (dead()) str << " [DEAD]";
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@ class V3GraphVertex VL_NOT_FINAL {
|
|||
friend class V3GraphEdge;
|
||||
friend class GraphAcyc;
|
||||
friend class GraphAlgRank;
|
||||
friend class GraphAlgRankDepth;
|
||||
V3ListLinks<V3GraphVertex> m_links; // List links to store instances of this class
|
||||
V3GraphEdge::OList m_outs; // List of outbound edges
|
||||
V3GraphEdge::IList m_ins; // List of inbound edges
|
||||
|
|
@ -382,11 +383,19 @@ public:
|
|||
void stronglyConnected(V3EdgeFuncP edgeFuncp) VL_MT_DISABLED;
|
||||
|
||||
/// Assign an ordering number to all vertexes in a tree.
|
||||
/// For multiple usages of a vertex, get the maximum of all inbound ranks + 1
|
||||
/// All nodes with no inputs will get rank 1
|
||||
/// Side-effect: changes user()
|
||||
void rank(V3EdgeFuncP edgeFuncp) VL_MT_DISABLED;
|
||||
void rank() VL_MT_DISABLED;
|
||||
|
||||
/// Assign an ordering number to all vertexes in a tree.
|
||||
/// For multiple usages of a vertex, get the minimu of all inbound ranks + 1
|
||||
/// All nodes with no inputs will get rank 1
|
||||
/// Side-effect: changes user()
|
||||
void rankMin(V3EdgeFuncP edgeFuncp) VL_MT_DISABLED;
|
||||
void rankMin() VL_MT_DISABLED;
|
||||
|
||||
/// Sort all vertices and edges using the V3GraphVertex::sortCmp() function
|
||||
void sortVertices() VL_MT_DISABLED;
|
||||
/// Sort all edges and edges using the V3GraphEdge::sortCmp() function
|
||||
|
|
|
|||
|
|
@ -271,12 +271,9 @@ class GraphAlgRank final : GraphAlg<> {
|
|||
vertex.user(0);
|
||||
}
|
||||
for (V3GraphVertex& vertex : m_graphp->vertices()) {
|
||||
if (!vertex.user()) { //
|
||||
vertexIterate(&vertex, 1);
|
||||
}
|
||||
if (!vertex.user()) vertexIterate(&vertex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void vertexIterate(V3GraphVertex* vertexp, uint32_t currentRank) {
|
||||
// Assign rank to each unvisited node
|
||||
// If larger rank is found, assign it and loop back through
|
||||
|
|
@ -302,10 +299,63 @@ public:
|
|||
~GraphAlgRank() = default;
|
||||
};
|
||||
|
||||
void V3Graph::rank() { GraphAlgRank{this, &V3GraphEdge::followAlwaysTrue}; }
|
||||
|
||||
void V3Graph::rank() { rank(&V3GraphEdge::followAlwaysTrue); }
|
||||
void V3Graph::rank(V3EdgeFuncP edgeFuncp) { GraphAlgRank{this, edgeFuncp}; }
|
||||
|
||||
//######################################################################
|
||||
//######################################################################
|
||||
// Algorithms - ranking min
|
||||
// Changes user() and rank()
|
||||
|
||||
class GraphAlgRankMin final : GraphAlg<> {
|
||||
void main() {
|
||||
// Rank each vertex, ignoring cutable edges
|
||||
// Vertex::m_user begin: 1 indicates processing, 2 indicates completed
|
||||
// Clear existing ranks
|
||||
for (V3GraphVertex& vertex : m_graphp->vertices()) {
|
||||
vertex.rank(0);
|
||||
vertex.user(0);
|
||||
}
|
||||
for (V3GraphVertex& vertex : m_graphp->vertices()) {
|
||||
if (!vertex.user()) vertexIterate(&vertex);
|
||||
}
|
||||
}
|
||||
uint32_t vertexIterate(V3GraphVertex* vertexp) {
|
||||
// Assign rank to each unvisited node
|
||||
// If we hit a back node make a list of all loops
|
||||
if (vertexp->user() == 1) {
|
||||
m_graphp->loopsMessageCb(vertexp, m_edgeFuncp);
|
||||
return vertexp->rank();
|
||||
}
|
||||
if (vertexp->user()) return vertexp->rank(); // Done earlier
|
||||
vertexp->user(1);
|
||||
vertexp->rank(1); // In case loop
|
||||
// If no input edges, then rank 1 (+ adder)
|
||||
// Otherwise, get minimum from following all inputs.
|
||||
uint32_t minrank = ~0U;
|
||||
for (V3GraphEdge& edge : vertexp->inEdges()) {
|
||||
if (followEdge(&edge)) {
|
||||
const uint32_t nrank = vertexIterate(edge.fromp());
|
||||
minrank = std::min(minrank, nrank);
|
||||
}
|
||||
}
|
||||
if (minrank == ~0U) minrank = 0;
|
||||
vertexp->rank(minrank + vertexp->rankAdder());
|
||||
vertexp->user(2);
|
||||
return vertexp->rank();
|
||||
}
|
||||
|
||||
public:
|
||||
GraphAlgRankMin(V3Graph* graphp, V3EdgeFuncP edgeFuncp)
|
||||
: GraphAlg<>{graphp, edgeFuncp} {
|
||||
main();
|
||||
}
|
||||
~GraphAlgRankMin() = default;
|
||||
};
|
||||
|
||||
void V3Graph::rankMin() { rankMin(&V3GraphEdge::followAlwaysTrue); }
|
||||
void V3Graph::rankMin(V3EdgeFuncP edgeFuncp) { GraphAlgRankMin{this, edgeFuncp}; }
|
||||
|
||||
//######################################################################
|
||||
//######################################################################
|
||||
// Algorithms - report loops
|
||||
|
|
|
|||
|
|
@ -205,6 +205,14 @@ class LinkCellsVisitor final : public VNVisitor {
|
|||
modp->level(vvertexp->rank() + 1);
|
||||
}
|
||||
}
|
||||
m_graph.rankMin();
|
||||
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
||||
if (const LinkCellsVertex* const vvertexp = vtx.cast<LinkCellsVertex>()) {
|
||||
// +1 so we leave level 1 for the new wrapper we'll make in a moment
|
||||
AstNodeModule* const modp = vvertexp->modp();
|
||||
modp->depth(vvertexp->rank() + 1);
|
||||
}
|
||||
}
|
||||
if (v3Global.opt.topModule() != "" && !m_topVertexp) {
|
||||
v3error("Specified --top-module '" << v3Global.opt.topModule()
|
||||
<< "' was not found in design.");
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ void V3LinkLevel::wrapTop(AstNetlist* rootp) {
|
|||
// Make the new module first in the list
|
||||
oldmodp->unlinkFrBackWithNext();
|
||||
newmodp->addNext(oldmodp);
|
||||
newmodp->depth(1);
|
||||
newmodp->level(1);
|
||||
newmodp->modPublic(true);
|
||||
newmodp->protect(false);
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
} else if (v3Global.opt.publicParams() && nodep->isParam()) {
|
||||
nodep->sigUserRWPublic(true);
|
||||
} else if (m_modp && v3Global.opt.publicDepth()) {
|
||||
if ((m_modp->level() - 1) <= v3Global.opt.publicDepth()) {
|
||||
if ((m_modp->depth() - 1) <= v3Global.opt.publicDepth()) {
|
||||
nodep->sigUserRWPublic(true);
|
||||
} else if (VN_IS(m_modp, Package) && nodep->isParam()) {
|
||||
nodep->sigUserRWPublic(true);
|
||||
|
|
@ -365,7 +365,7 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
// We used modTrace before leveling, and we may now
|
||||
// want to turn it off now that we know the levelizations
|
||||
if (v3Global.opt.traceDepth() && m_modp
|
||||
&& (m_modp->level() - 1) > v3Global.opt.traceDepth()) {
|
||||
&& (m_modp->depth() - 1) > v3Global.opt.traceDepth()) {
|
||||
m_modp->modTrace(false);
|
||||
nodep->trace(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1761,6 +1761,9 @@ class ParamTop final : VNDeleter {
|
|||
maxParentLevel = std::max(maxParentLevel, parentp->level());
|
||||
}
|
||||
if (modp->level() <= maxParentLevel) modp->level(maxParentLevel + 1);
|
||||
// Not correct fixup of depth(), as it should be mininum. But depth() is unused
|
||||
// past V3LinkParse, so just do something sane in case this code doesn't get updated
|
||||
modp->depth(modp->level());
|
||||
}
|
||||
|
||||
void resortNetlistModules(AstNetlist* netlistp) {
|
||||
|
|
|
|||
|
|
@ -2285,7 +2285,7 @@ class VlTest:
|
|||
#######################################################################
|
||||
# File utilities
|
||||
|
||||
def files_identical(self, fn1: str, fn2: str, is_logfile=False) -> None:
|
||||
def files_identical(self, fn1: str, fn2: str, is_logfile=False, strip_hex=False) -> None:
|
||||
"""Test if two files have identical contents"""
|
||||
delay = 0.25
|
||||
for tryn in range(Args.log_retries, -1, -1):
|
||||
|
|
@ -2294,10 +2294,11 @@ class VlTest:
|
|||
delay = min(1, delay * 2)
|
||||
moretry = tryn != 0
|
||||
if not self._files_identical_try(
|
||||
fn1=fn1, fn2=fn2, is_logfile=is_logfile, moretry=moretry):
|
||||
fn1=fn1, fn2=fn2, is_logfile=is_logfile, strip_hex=strip_hex, moretry=moretry):
|
||||
break
|
||||
|
||||
def _files_identical_try(self, fn1: str, fn2: str, is_logfile: bool, moretry: bool) -> bool:
|
||||
def _files_identical_try(self, fn1: str, fn2: str, is_logfile: bool, strip_hex: bool,
|
||||
moretry: bool) -> bool:
|
||||
# If moretry, then return true to try again
|
||||
try:
|
||||
f1 = open( # pylint: disable=consider-using-with
|
||||
|
|
@ -2321,6 +2322,7 @@ class VlTest:
|
|||
fn1=fn1,
|
||||
fn2=fn2,
|
||||
is_logfile=is_logfile,
|
||||
strip_hex=strip_hex,
|
||||
moretry=moretry)
|
||||
if f1:
|
||||
f1.close()
|
||||
|
|
@ -2329,7 +2331,7 @@ class VlTest:
|
|||
return again
|
||||
|
||||
def _files_identical_reader(self, f1, f2, fn1: str, fn2: str, is_logfile: bool,
|
||||
moretry: bool) -> None:
|
||||
strip_hex: bool, moretry: bool) -> None:
|
||||
# If moretry, then return true to try again
|
||||
l1s = f1.readlines()
|
||||
l2s = f2.readlines() if f2 else []
|
||||
|
|
@ -2377,6 +2379,13 @@ class VlTest:
|
|||
#
|
||||
l1s = l1o
|
||||
|
||||
if strip_hex:
|
||||
l1o = []
|
||||
for line in l1s:
|
||||
line = re.sub(r'\b0x[0-9a-f]+', '0x#', line)
|
||||
l1o.append(line)
|
||||
l1s = l1o
|
||||
|
||||
for lineno_m1 in range(0, max(len(l1s), len(l2s))):
|
||||
l1 = l1s[lineno_m1] if lineno_m1 < len(l1s) else "*EOF*\n"
|
||||
l2 = l2s[lineno_m1] if lineno_m1 < len(l2s) else "*EOF*\n"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface AXIS_IF (
|
||||
input logic aclk
|
||||
);
|
||||
logic [127:0] tdata;
|
||||
logic [ 31:0] tuser;
|
||||
logic tvalid, tready;
|
||||
modport master(input aclk, output tdata, tuser, tvalid, input tready);
|
||||
modport slave(input aclk, input tdata, tuser, tvalid, output tready);
|
||||
endinterface : AXIS_IF
|
||||
|
||||
module sub (
|
||||
input clk,
|
||||
AXIS_IF.slave s_axis_if
|
||||
);
|
||||
assign s_axis_if.tready = s_axis_if.tdata[0];
|
||||
endmodule
|
||||
|
||||
module dut (
|
||||
input clk,
|
||||
AXIS_IF.slave s_axis_if
|
||||
);
|
||||
sub u_sub(.*);
|
||||
endmodule
|
||||
|
||||
module t(/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
AXIS_IF s_axis_if (.aclk(clk));
|
||||
dut u_dut (.clk, .s_axis_if(s_axis_if));
|
||||
initial begin
|
||||
$c("Verilated::scopesDump();");
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
scopesDump:
|
||||
SCOPE 0x#: top.TOP
|
||||
SCOPE 0x#: top.t
|
||||
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.top_filename = "t/t_vpi_public_depthn.v"
|
||||
|
||||
test.compile(verilator_flags2=['--public-depth 1'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
scopesDump:
|
||||
SCOPE 0x#: top.TOP
|
||||
SCOPE 0x#: top.t
|
||||
SCOPE 0x#: top.t.s_axis_if
|
||||
SCOPE 0x#: top.t.u_dut
|
||||
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.top_filename = "t/t_vpi_public_depthn.v"
|
||||
|
||||
test.compile(verilator_flags2=['--public-depth 2'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
scopesDump:
|
||||
SCOPE 0x#: top.TOP
|
||||
SCOPE 0x#: top.t
|
||||
SCOPE 0x#: top.t.s_axis_if
|
||||
SCOPE 0x#: top.t.u_dut
|
||||
SCOPE 0x#: top.t.u_dut.u_sub
|
||||
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
test.top_filename = "t/t_vpi_public_depthn.v"
|
||||
|
||||
test.compile(verilator_flags2=['--public-depth 3'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.files_identical(test.run_log_filename, test.golden_filename, is_logfile=True, strip_hex=True)
|
||||
|
||||
test.passes()
|
||||
Loading…
Reference in New Issue