Fix root global, arrays, no inline, escaped, fail on missing

This commit is contained in:
wsxarcher 2026-03-23 23:45:43 +01:00
parent b28fde7e57
commit ad50b31077
54 changed files with 1326 additions and 114 deletions

View File

@ -2929,11 +2929,8 @@ std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDu
void VerilatedContext::dumpvarsAdd(int level,
const std::string& hier) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
const VerilatedLockGuard lock{m_timeDumpMutex};
if (level == 0 && hier.empty()) {
m_dumpvars.clear();
} else {
m_dumpvars.emplace_back(level, hier);
}
if (level == 0 && hier.empty()) m_dumpvars.clear();
m_dumpvars.emplace_back(level, hier);
}
std::vector<VerilatedTraceDumpvarsEntry>
VerilatedContext::dumpvars() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {

View File

@ -110,8 +110,11 @@ class VerilatedVcd;
class VerilatedVcdC;
class VerilatedVcdSc;
// Internal: One $dumpvars call.
struct VerilatedTraceDumpvarsEntry final {
/// Maximum hierarchy depth to dump modules.
int m_level;
/// Hierarchy root to dump.
std::string m_hier;
VerilatedTraceDumpvarsEntry(int level, const std::string& hier)

View File

@ -300,6 +300,9 @@ public:
void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
m_sptrace.dumpvars(level, hier);
}
void dumpvars(const std::vector<VerilatedTraceDumpvarsEntry>& entries) VL_MT_SAFE {
m_sptrace.dumpvars(entries);
}
// Internal class access
VerilatedFst* spTrace() { return &m_sptrace; }

View File

@ -318,6 +318,9 @@ public:
void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
m_sptrace.dumpvars(level, hier);
}
void dumpvars(const std::vector<VerilatedTraceDumpvarsEntry>& entries) VL_MT_SAFE {
m_sptrace.dumpvars(entries);
}
// Internal class access
VerilatedSaif* spTrace() { return &m_sptrace; }

View File

@ -509,6 +509,11 @@ public:
// Set variables to dump, using $dumpvars format
// If level = 0, dump everything and hier is then ignored
void dumpvars(int level, const std::string& hier) VL_MT_SAFE;
void dumpvars(const std::vector<VerilatedTraceDumpvarsEntry>& entries) VL_MT_SAFE {
for (const VerilatedTraceDumpvarsEntry& entry : entries) {
dumpvars(entry.m_level, entry.m_hier);
}
}
// Call
void dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);

View File

@ -422,11 +422,8 @@ void VerilatedTrace<VL_SUB_T, VL_BUF_T>::set_time_resolution(const std::string&
}
template <>
void VerilatedTrace<VL_SUB_T, VL_BUF_T>::dumpvars(int level, const std::string& hier) VL_MT_SAFE {
if (level == 0 && hier.empty()) {
m_dumpvars.clear(); // empty = everything on
} else {
m_dumpvars.emplace_back(level, hier);
}
if (level == 0 && hier.empty()) m_dumpvars.clear();
m_dumpvars.emplace_back(level, hier);
}
template <>

View File

@ -351,6 +351,9 @@ public:
void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
m_sptrace.dumpvars(level, hier);
}
void dumpvars(const std::vector<VerilatedTraceDumpvarsEntry>& entries) VL_MT_SAFE {
m_sptrace.dumpvars(entries);
}
// Internal class access
VerilatedVcd* spTrace() { return &m_sptrace; }

View File

@ -86,6 +86,7 @@ set(HEADERS
V3DfgPeepholePatterns.h
V3DfgVertices.h
V3DiagSarif.h
V3Dumpvars.h
V3DupFinder.h
V3EmitC.h
V3EmitCBase.h

47
src/V3Dumpvars.h Normal file
View File

@ -0,0 +1,47 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Dumpvars helpers
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// 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: 2003-2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3DUMPVARS_H_
#define VERILATOR_V3DUMPVARS_H_
#include "config_build.h"
#include "verilatedos.h"
// Tagged $dumpvars target string. During compile-time resolution in V3LinkDot
// each target is tagged with a prefix that tells EmitC how to emit the
// corresponding runtime code. The three tag types are:
// Resolved fully resolved to a compile-time hierarchy path
// RuntimeRoot first component must match the C++ wrapper root name at runtime
// Missing proven invalid at compile time; emit VL_FATAL_MT at runtime
struct DumpvarsTag final {
const char* const prefix;
const size_t prefixLen;
template <size_t N>
constexpr DumpvarsTag(const char (&s)[N])
: prefix{s}
, prefixLen{N - 1} {}
bool matches(const string& target) const { return target.compare(0, prefixLen, prefix) == 0; }
string make(const string& target) const { return string{prefix, prefixLen} + target; }
string strip(const string& target) const {
return matches(target) ? target.substr(prefixLen) : target;
}
};
constexpr DumpvarsTag kDumpvarsResolved{"@dumpvars:"};
constexpr DumpvarsTag kDumpvarsRuntimeRoot{"@dumpvars_root:"};
constexpr DumpvarsTag kDumpvarsMissing{"@dumpvars_missing:"};
#endif // Guard

View File

@ -21,6 +21,7 @@
#include "verilatedos.h"
#include "V3EmitCConstInit.h"
#include "V3Dumpvars.h"
#include "V3Global.h"
#include "V3MemberMap.h"
@ -903,8 +904,9 @@ public:
static string dumpvarsHierPath(const string& scope, const string& target) {
if (target.empty()) return scope;
if (kDumpvarsResolved.matches(target)) return kDumpvarsResolved.strip(target);
if (scope.empty() || dumpvarsHasScopePrefix(target, scope)) return target;
return scope + "." + target;
return VString::dot(scope, ".", target);
}
// Emit C++ code that registers a $dumpvars filter at runtime.
@ -923,6 +925,41 @@ public:
puts(levelExpr);
puts(", __vlDvHier); }\n");
}
void emitDumpVarsAddRuntimeRoot(const AstDumpCtl* nodep, const string& target,
const string& levelExpr) {
const string::size_type dotPos = target.find('.');
const string rootName = dotPos == string::npos ? target : target.substr(0, dotPos);
const string suffix = dotPos == string::npos ? "" : target.substr(dotPos + 1);
putns(nodep, "{ const std::string __vlDvRoot{vlSymsp->name()};\n");
puts("if (__vlDvRoot != \"");
puts(V3OutFormatter::quoteNameControls(rootName));
puts("\") VL_FATAL_MT(\"");
puts(V3OutFormatter::quoteNameControls(protect(nodep->fileline()->filename())));
puts("\", ");
puts(cvtToStr(nodep->fileline()->lineno()));
puts(", \"\", \"$dumpvars target not found: ");
puts(V3OutFormatter::quoteNameControls(target));
puts("\");\n");
puts("std::string __vlDvHier{__vlDvRoot};\n");
if (!suffix.empty()) {
puts("__vlDvHier += '.';\n");
puts("__vlDvHier += \"");
puts(V3OutFormatter::quoteNameControls(suffix));
puts("\";\n");
}
puts("vlSymsp->_vm_contextp__->dumpvarsAdd(");
puts(levelExpr);
puts(", __vlDvHier); }\n");
}
void emitDumpVarsTargetMissing(const AstDumpCtl* nodep, const string& target) {
putns(nodep, "VL_FATAL_MT(\"");
puts(V3OutFormatter::quoteNameControls(protect(nodep->fileline()->filename())));
puts("\", ");
puts(cvtToStr(nodep->fileline()->lineno()));
puts(", \"\", \"$dumpvars target not found: ");
puts(V3OutFormatter::quoteNameControls(target));
puts("\");\n");
}
// Emit $dumpvars filter logic when scope info is available.
void emitDumpVarsWithScope(AstDumpCtl* nodep) {
UASSERT_OBJ(nodep->scopeNamep(), nodep, "$dumpvars missing AstScopeName");
@ -938,14 +975,29 @@ public:
puts(";\n");
levelExpr = "__vlDvLevel";
}
// Emit one dumpvarsAdd call per target, or one for the scope itself
// Emit one dumpvarsAdd call per target, or one for the scope itself.
// The no-target $dumpvars(0) form is design-global and should not be
// narrowed to the lexical scope where it appears.
if (nodep->targetsp()) {
for (AstNode* tp = nodep->targetsp(); tp; tp = tp->nextp()) {
const string target = VN_AS(tp, Text)->text();
emitDumpVarsAdd(nodep, dumpvarsHierPath(scope, target), levelExpr);
if (kDumpvarsMissing.matches(target)) {
emitDumpVarsTargetMissing(nodep, kDumpvarsMissing.strip(target));
} else if (kDumpvarsRuntimeRoot.matches(target)) {
emitDumpVarsAddRuntimeRoot(nodep, kDumpvarsRuntimeRoot.strip(target),
levelExpr);
} else {
emitDumpVarsAdd(nodep, dumpvarsHierPath(scope, target), levelExpr);
}
}
} else {
emitDumpVarsAdd(nodep, scope, levelExpr);
if (levelp && levelp->toUInt() == 0 && scope.find('.') == string::npos) {
putns(nodep, "vlSymsp->_vm_contextp__->dumpvarsAdd(");
puts(levelExpr);
puts(", \"\"s);\n");
} else {
emitDumpVarsAdd(nodep, scope, levelExpr);
}
}
if (!levelp) puts("}\n");
putns(nodep, "vlSymsp->_traceDumpClose();\n");

View File

@ -1215,11 +1215,8 @@ void EmitCSyms::emitSymImp(const AstNetlist* netlistp) {
puts("const VerilatedLockGuard lock{__Vm_dumperMutex};\n");
puts("if (VL_UNLIKELY(!__Vm_dumperp)) {\n");
puts("__Vm_dumperp = new " + v3Global.opt.traceClassLang() + "();\n");
puts("const auto dvars = _vm_contextp__->dumpvars();\n");
puts("for (const auto& dv : dvars) {\n");
puts("__Vm_dumperp->dumpvars(dv.m_level, dv.m_hier);\n");
puts("}\n");
puts("__Vm_modelp->trace(__Vm_dumperp, 0, 0);\n");
puts("__Vm_dumperp->dumpvars(_vm_contextp__->dumpvars());\n");
puts("_vm_contextp__->trace(__Vm_dumperp, 0, 0);\n");
puts("const std::string dumpfile = _vm_contextp__->dumpfileCheck();\n");
puts("__Vm_dumperp->open(dumpfile.c_str());\n");
puts("__Vm_dumping = true;\n");

View File

@ -84,19 +84,29 @@ static string dumpvarsTargetText(const AstNode* nodep) {
if (!nodep) return "";
if (const AstText* const textp = VN_CAST(nodep, Text)) return textp->text();
if (const AstCellRef* const refp = VN_CAST(nodep, CellRef)) return refp->name();
if (const AstCellArrayRef* const refp = VN_CAST(nodep, CellArrayRef)) return refp->name();
if (const AstSelBit* const selp = VN_CAST(nodep, SelBit)) {
const string from = dumpvarsTargetText(selp->fromp());
const string bit = dumpvarsTargetText(selp->bitp());
if (from.empty()) return "";
return from + "[" + bit + "]";
}
if (const AstCellArrayRef* const refp = VN_CAST(nodep, CellArrayRef)) {
string out = refp->name();
for (const AstNodeExpr* selp = refp->selp(); selp; selp = VN_CAST(selp->nextp(), NodeExpr)) {
out += "[" + dumpvarsTargetText(selp) + "]";
}
return out;
}
if (const AstConst* const constp = VN_CAST(nodep, Const)) return cvtToStr(constp->toSInt());
if (const AstParseRef* const refp = VN_CAST(nodep, ParseRef)) return refp->prettyName();
if (const AstVarRef* const refp = VN_CAST(nodep, VarRef)) return refp->name();
if (const AstVarXRef* const refp = VN_CAST(nodep, VarXRef)) {
if (refp->dotted().empty()) return refp->name();
return refp->dotted() + "." + refp->name();
return VString::dot(refp->dotted(), ".", refp->name());
}
if (const AstDot* const dotp = VN_CAST(nodep, Dot)) {
const string lhs = dumpvarsTargetText(dotp->lhsp());
const string rhs = dumpvarsTargetText(dotp->rhsp());
if (lhs.empty()) return rhs;
if (rhs.empty()) return lhs;
return lhs + "." + rhs;
return VString::dot(lhs, ".", rhs);
}
return nodep->prettyName();
}
@ -3163,6 +3173,127 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
} m_ds; // State to preserve across recursions
static string dumpvarsSymPathPiece(const AstNode* nodep) {
if (!nodep) return "";
if (const AstNodeModule* const modp = VN_CAST(nodep, NodeModule)) return modp->origName();
if (const AstCell* const cellp = VN_CAST(nodep, Cell)) return cellp->origName();
if (const AstCellInline* const inlinep = VN_CAST(nodep, CellInline)) return inlinep->name();
if (const AstVarScope* const vscp = VN_CAST(nodep, VarScope)) return vscp->varp()->name();
return nodep->name();
}
static bool dumpvarsMatchesLocalModule(const VSymEnt* symp, const string& ident) {
if (!symp) return false;
if (const AstCell* const cellp = VN_CAST(symp->nodep(), Cell)) {
return cellp->modp() && cellp->modp()->origName() == ident;
}
if (const AstCellInline* const inlinep = VN_CAST(symp->nodep(), CellInline)) {
return inlinep->origModName() == ident;
}
if (const AstNodeModule* const modp = VN_CAST(symp->nodep(), NodeModule)) {
return modp->origName() == ident;
}
return false;
}
static string dumpvarsResolvedPath(VSymEnt* symp) {
std::vector<string> pieces;
for (VSymEnt* walkp = symp; walkp && walkp->parentp(); walkp = walkp->parentp()) {
const string piece = dumpvarsSymPathPiece(walkp->nodep());
if (!piece.empty()) pieces.push_back(piece);
}
std::reverse(pieces.begin(), pieces.end());
string path;
for (const string& piece : pieces) {
if (!path.empty()) path += '.';
path += piece;
}
return path;
}
static bool dumpvarsHasBareTarget(AstNode* targetsp, const string& name) {
for (AstNode* tp = targetsp; tp; tp = tp->nextp()) {
if (dumpvarsTargetText(tp) == name) return true;
}
return false;
}
VSymEnt* findDumpvarsLocal(FileLine* refLocationp, const string& dotname, string& baddot,
VSymEnt*& okSymp) {
if (!m_curSymp) return nullptr;
string leftname = dotname;
string::size_type pos;
string ident;
if ((pos = leftname.find('.')) != string::npos) {
ident = leftname.substr(0, pos);
leftname = leftname.substr(pos + 1);
} else {
ident = leftname;
leftname = "";
}
baddot = ident;
okSymp = m_curSymp;
string altIdent;
if (m_statep->forPrearray()) {
if ((pos = ident.rfind("__BRA__")) != string::npos) altIdent = ident.substr(0, pos);
}
VSymEnt* symp = nullptr;
if (dumpvarsMatchesLocalModule(m_curSymp, ident)) {
symp = m_curSymp;
} else {
symp = m_curSymp->findIdFallback(ident);
if (!symp && !altIdent.empty()) symp = m_curSymp->findIdFallback(altIdent);
}
if (!symp) return nullptr;
if (leftname.empty()) {
okSymp = symp;
return symp;
}
return m_statep->findDotted(refLocationp, symp, leftname, baddot, okSymp, false);
}
// Resolve a single $dumpvars target string against the symbol table.
// Returns a tagged string that tells EmitC how to generate the runtime code.
string resolveDumpvarsTarget(FileLine* fl, const string& target, AstNode* targetsp) {
if (target.empty() || !m_curSymp) return target;
string baddot;
VSymEnt* matchSymp = nullptr;
// Step 1: Try local scope lookup.
if (findDumpvarsLocal(fl, target, baddot, matchSymp)) return target;
// Step 2: Try global lookup from $root.
if (VSymEnt* const rootSymp = m_statep->findDotted(
fl, m_statep->rootEntp(), target, baddot, matchSymp, true)) {
const string resolved = dumpvarsResolvedPath(rootSymp);
if (!resolved.empty()) return kDumpvarsResolved.make(resolved);
}
// Step 3: Single-component name — defer to runtime root matching.
const string::size_type dotPos = target.find('.');
const string firstComp = (dotPos != string::npos) ? target.substr(0, dotPos) : target;
if (dotPos == string::npos) return kDumpvarsRuntimeRoot.make(target);
// Step 4: Multi-component "X.y.z" where X might be the runtime root.
const string remaining = target.substr(dotPos + 1);
if (dumpvarsHasBareTarget(targetsp, firstComp)) {
string runtimeBaddot;
VSymEnt* runtimeMatchSymp = nullptr;
if (m_statep->findDotted(fl, m_statep->rootEntp(), remaining, runtimeBaddot,
runtimeMatchSymp, true)) {
return kDumpvarsRuntimeRoot.make(target);
}
return kDumpvarsMissing.make(target);
}
UINFO(5, "$dumpvars target '" << target << "' not found in hierarchy" << endl);
return target;
}
// METHODS - Variables
AstVar* createImplicitVar(VSymEnt* /*lookupSymp*/, AstParseRef* nodep, AstNodeModule* modp,
VSymEnt* moduleSymp, bool noWarn) {
@ -6064,19 +6195,17 @@ class LinkDotResolveVisitor final : public VNVisitor {
for (AstNode* targetp = targetsp; targetp;) {
AstNode* const nextp = targetp->nextp();
if (nextp) nextp->unlinkFrBackWithNext();
const string target = dumpvarsTargetText(targetp);
if (!target.empty() && m_curSymp) {
string baddot;
VSymEnt* matchSymp = nullptr;
if (!m_statep->findDotted(nodep->fileline(), m_curSymp, target,
baddot, matchSymp, true)) {
UINFO(5, "$dumpvars target '" << target
<< "' not found in hierarchy" << endl);
}
// Skip if already resolved to text.
if (VN_IS(targetp, Text)) {
newTargetsp = AstNode::addNextNull(newTargetsp, targetp);
targetp = nextp;
continue;
}
const string target = dumpvarsTargetText(targetp);
const string linkedTarget = resolveDumpvarsTarget(nodep->fileline(), target, targetsp);
VL_DO_DANGLING(pushDeletep(targetp), targetp);
newTargetsp
= AstNode::addNextNull(newTargetsp, new AstText{nodep->fileline(), target});
= AstNode::addNextNull(newTargetsp, new AstText{nodep->fileline(), linkedTarget});
targetp = nextp;
}
relinker.relink(newTargetsp);

View File

@ -21,6 +21,7 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Dumpvars.h"
#include "V3Error.h"
//============================================================================

View File

@ -96,13 +96,6 @@ AstArg* V3ParseGrammar::argWrapList(AstNodeExpr* nodep) {
return outp;
}
AstDumpCtl* V3ParseGrammar::createDumpVarsScoped(FileLine* fl, AstNodeExpr* levelp,
AstNode* exprListp) {
AstDumpCtl* const resultp = new AstDumpCtl{fl, VDumpCtlType::VARS, levelp};
resultp->addTargetsp(exprListp);
return resultp;
}
AstAssignW* V3ParseGrammar::createSupplyExpr(FileLine* fileline, const string& name, int value) {
AstAssignW* assignp = new AstAssignW{fileline, new AstParseRef{fileline, name},
value ? new AstConst{fileline, AstConst::All1{}}

View File

@ -67,8 +67,6 @@ public:
// METHODS
AstArg* argWrapList(AstNodeExpr* nodep) VL_MT_DISABLED;
AstDumpCtl* createDumpVarsScoped(FileLine* fl, AstNodeExpr* levelp,
AstNode* exprListp) VL_MT_DISABLED;
bool allTracingOn(const FileLine* fl) const {
return v3Global.opt.trace() && m_tracingParse && fl->tracingOn();
}

View File

@ -1598,28 +1598,33 @@ class TaskVisitor final : public VNVisitor {
beginp = createInlinedFTask(nodep, namePrefix, outvscp);
++m_statInlines;
}
// When a function call has inlinedDots (from V3Inline's cell hierarchy),
// propagate that info to:
// Propagate the caller scope into the cloned task body so scope-sensitive
// operations such as $dumpvars reflect the call site even when the task
// was defined at $unit. When V3Inline added extra hierarchy, include it.
// This applies to:
// 1. Any AstDumpCtl/AstScopeName in the inlined body (direct $dumpvars)
// 2. Any nested AstNodeFTaskRef in the inlined body (indirect $dumpvars)
if (!nodep->inlinedDots().empty()) {
{
const string& callerDots = nodep->inlinedDots();
string dots = callerDots;
string::size_type pos;
while ((pos = dots.find('.')) != string::npos) dots.replace(pos, 1, "__DOT__");
const string scopePath = "__DOT__"s + m_scopep->name() + "__DOT__" + dots;
string scopePath = "__DOT__"s + m_scopep->name();
if (!callerDots.empty()) {
string dots = callerDots;
string::size_type pos;
while ((pos = dots.find('.')) != string::npos) dots.replace(pos, 1, "__DOT__");
scopePath += "__DOT__" + dots;
}
beginp->foreachAndNext([&](AstDumpCtl* dcp) {
if (AstScopeName* const snp = dcp->scopeNamep()) {
snp->scopeAttr(scopePath);
snp->scopeEntr(scopePath);
}
});
// Propagate inlinedDots to nested task references
beginp->foreachAndNext([&](AstNodeFTaskRef* refp) {
if (refp->inlinedDots().empty()) {
refp->inlinedDots(callerDots);
}
});
if (!callerDots.empty()) {
// Propagate inlinedDots to nested task references.
beginp->foreachAndNext([&](AstNodeFTaskRef* refp) {
if (refp->inlinedDots().empty()) refp->inlinedDots(callerDots);
});
}
}
if (VN_IS(nodep, New)) { // New not legal as while() condition

View File

@ -4215,15 +4215,17 @@ system_t_stmt_call<nodeStmtp>: // IEEE: part of system_tf_call (as task returni
//
| yD_DUMPPORTS '(' idDottedSel ',' expr ')' { $$ = new AstDumpCtl{$<fl>1, VDumpCtlType::FILE, $5}; DEL($3);
$$->addNext(new AstDumpCtl{$<fl>1, VDumpCtlType::VARS,
new AstConst{$<fl>1, 1}}); }
new AstConst{$<fl>1, 0}}); }
| yD_DUMPPORTS '(' ',' expr ')' { $$ = new AstDumpCtl{$<fl>1, VDumpCtlType::FILE, $4};
$$->addNext(new AstDumpCtl{$<fl>1, VDumpCtlType::VARS,
new AstConst{$<fl>1, 1}}); }
new AstConst{$<fl>1, 0}}); }
| yD_DUMPFILE '(' expr ')' { $$ = new AstDumpCtl{$<fl>1, VDumpCtlType::FILE, $3}; }
| yD_DUMPVARS parenE { $$ = new AstDumpCtl{$<fl>1, VDumpCtlType::VARS,
new AstConst{$<fl>1, 0}}; }
| yD_DUMPVARS '(' expr ')' { $$ = new AstDumpCtl{$<fl>1, VDumpCtlType::VARS, $3}; }
| yD_DUMPVARS '(' expr ',' exprList ')' { $$ = GRAMMARP->createDumpVarsScoped($<fl>1, $3, $5); }
| yD_DUMPVARS '(' expr ',' exprList ')' { AstDumpCtl* const dumpctlp = new AstDumpCtl{$<fl>1, VDumpCtlType::VARS, $3};
dumpctlp->addTargetsp($5);
$$ = dumpctlp; }
| yD_DUMPALL parenE { $$ = new AstDumpCtl{$<fl>1, VDumpCtlType::ALL}; }
| yD_DUMPALL '(' expr ')' { $$ = new AstDumpCtl{$<fl>1, VDumpCtlType::ALL}; DEL($3); }
| yD_DUMPFLUSH parenE { $$ = new AstDumpCtl{$<fl>1, VDumpCtlType::FLUSH}; }

View File

@ -1,6 +1,7 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module $rootio $end
$var wire 1 % clk $end
$upscope $end
$scope module t $end
$var wire 1 % clk $end

View File

@ -0,0 +1,31 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module $rootio $end
$upscope $end
$scope module t $end
$var wire 32 ( top [31:0] $end
$scope module sub_a $end
$scope module deep_i $end
$var wire 32 * ADD [31:0] $end
$var wire 32 " cyc [31:0] $end
$var wire 32 $ inner [31:0] $end
$upscope $end
$upscope $end
$scope module sub_b $end
$scope module deep_i $end
$var wire 32 , ADD [31:0] $end
$var wire 32 " cyc [31:0] $end
$var wire 32 & inner [31:0] $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
b00000000000000000000000000000000 "
b00000000000000000000000000001011 $
b00000000000000000000000000010101 &
b00000000000000000000000000000000 (
b00000000000000000000000000001011 *
b00000000000000000000000000010101 ,

View File

@ -0,0 +1,20 @@
#!/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 by Marco Bartoli.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--binary --trace-vcd'])
test.execute()
test.vcd_identical(test.trace_filename, test.golden_filename)
test.passes()

View File

@ -0,0 +1,57 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
module t(
input clk
);
int cyc;
int top;
sub #(10) sub_a(.*);
sub #(20) sub_b(.*);
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
initial begin
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
// First restrict to sub_a.deep_i only, then let deep_i issue $dumpvars(0)
// to confirm the no-target override still expands tracing globally.
$dumpvars(1, t.sub_a.deep_i);
$dumpvars(0, top);
end
endmodule
module sub #(
parameter int ADD
)(
input int cyc
);
int value;
always_comb value = cyc + ADD;
deep #(ADD + 1) deep_i(.*);
endmodule
module deep #(
parameter int ADD
)(
input int cyc
);
int inner;
always_comb inner = cyc + ADD;
initial begin
$dumpvars(0);
end
endmodule

View File

@ -0,0 +1,36 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
#include <verilated.h>
#include <memory>
#include VM_PREFIX_INCLUDE
unsigned long long main_time = 0;
double sc_time_stamp() { return (double)main_time; }
int main(int argc, char** argv) {
Verilated::debug(0);
Verilated::traceEverOn(true);
Verilated::commandArgs(argc, argv);
// Name the top module "cpptop" instead of default "TOP"
std::unique_ptr<VM_PREFIX> top{new VM_PREFIX{"cpptop"}};
top->clk = 0;
while (!Verilated::gotFinish()) {
top->eval();
++main_time;
top->clk = !top->clk;
}
top->final();
top.reset();
printf("*-* All Finished *-*\n");
return 0;
}

View File

@ -0,0 +1,69 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module cpptop $end
$var wire 1 % clk $end
$scope module t $end
$var wire 1 % clk $end
$var wire 32 " cyc [31:0] $end
$scope module sub_a $end
$var wire 32 & ADD [31:0] $end
$var wire 32 " cyc [31:0] $end
$var wire 32 # value [31:0] $end
$upscope $end
$scope module sub_b $end
$var wire 32 ' ADD [31:0] $end
$var wire 32 " cyc [31:0] $end
$var wire 32 $ value [31:0] $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
b00000000000000000000000000000000 "
b00000000000000000000000000001010 #
b00000000000000000000000000010100 $
0%
b00000000000000000000000000001010 &
b00000000000000000000000000010100 '
#1
b00000000000000000000000000000001 "
b00000000000000000000000000001011 #
b00000000000000000000000000010101 $
1%
#2
0%
#3
b00000000000000000000000000000010 "
b00000000000000000000000000001100 #
b00000000000000000000000000010110 $
1%
#4
0%
#5
b00000000000000000000000000000011 "
b00000000000000000000000000001101 #
b00000000000000000000000000010111 $
1%
#6
0%
#7
b00000000000000000000000000000100 "
b00000000000000000000000000001110 #
b00000000000000000000000000011000 $
1%
#8
0%
#9
b00000000000000000000000000000101 "
b00000000000000000000000000001111 #
b00000000000000000000000000011001 $
1%
#10
0%
#11
b00000000000000000000000000000110 "
b00000000000000000000000000010000 #
b00000000000000000000000000011010 $
1%

View File

@ -0,0 +1,27 @@
#!/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 by Marco Bartoli.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(
make_main=False,
verilator_flags2=[
'--cc',
'--exe',
'--trace-vcd',
't/t_trace_dumpvars_cpptop.cpp',
])
test.execute()
test.vcd_identical(test.trace_filename, test.golden_filename)
test.passes()

View File

@ -0,0 +1,39 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
module t(
input clk
);
int cyc;
sub #(10) sub_a(.*);
sub #(20) sub_b(.*);
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
initial begin
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
// cpptop is defined in the C++ testbench as the root of the trace hierarchy, so $dumpvars(0, cpptop) should dump everything.
$dumpvars(0, cpptop);
end
endmodule
module sub #(
parameter int ADD
)(
input int cyc
);
int value;
always_comb value = cyc + ADD;
endmodule

View File

@ -0,0 +1,36 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
#include <verilated.h>
#include <memory>
#include VM_PREFIX_INCLUDE
unsigned long long main_time = 0;
double sc_time_stamp() { return (double)main_time; }
int main(int argc, char** argv) {
Verilated::debug(0);
Verilated::traceEverOn(true);
Verilated::commandArgs(argc, argv);
// Name the top module "cpptop" instead of default "TOP"
std::unique_ptr<VM_PREFIX> top{new VM_PREFIX{"cpptop"}};
top->clk = 0;
while (!Verilated::gotFinish()) {
top->eval();
++main_time;
top->clk = !top->clk;
}
top->final();
top.reset();
printf("*-* All Finished *-*\n");
return 0;
}

View File

@ -0,0 +1,2 @@
%Error: t/t_trace_dumpvars_cpptop2.v:27: $dumpvars target not found: cpptop.notfound
Aborting...

View File

@ -0,0 +1,25 @@
#!/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 by Marco Bartoli.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(
make_main=False,
verilator_flags2=[
'--cc',
'--exe',
'--trace-vcd',
't/t_trace_dumpvars_cpptop.cpp',
])
test.execute(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,38 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
module t(
input clk
);
int cyc;
sub #(10) sub_a(.*);
sub #(20) sub_b(.*);
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
initial begin
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
$dumpvars(0, cpptop, cpptop.notfound);
end
endmodule
module sub #(
parameter int ADD
)(
input int cyc
);
int value;
always_comb value = cyc + ADD;
endmodule

View File

@ -0,0 +1,24 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module $rootio $end
$upscope $end
$scope module t $end
$scope module arr[0] $end
$scope module deep $end
$upscope $end
$upscope $end
$scope module arr[1] $end
$scope module deep $end
$var wire 32 ' ADD [31:0] $end
$var wire 32 " cyc [31:0] $end
$var wire 32 $ inner [31:0] $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
b00000000000000000000000000000000 "
b00000000000000000000000000001011 $
b00000000000000000000000000001011 '

View File

@ -0,0 +1,20 @@
#!/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 by Marco Bartoli.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--binary --trace-vcd'])
test.execute()
test.vcd_identical(test.trace_filename, test.golden_filename)
test.passes()

View File

@ -0,0 +1,49 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
module t(
input clk
);
int cyc;
sub #(10) arr[2](.*);
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
initial begin
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
// Test $dumpvars with an arrayed hierarchical scope path.
$dumpvars(1, t.arr[1].deep);
end
endmodule
module sub #(
parameter int ADD
)(
input int cyc
);
int value;
always_comb value = cyc + ADD;
deep #(ADD + 1) deep(.*);
endmodule
module deep #(
parameter int ADD
)(
input int cyc
);
int inner;
always_comb inner = cyc + ADD;
endmodule

View File

@ -0,0 +1,26 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module $rootio $end
$upscope $end
$scope module t $end
$var wire 1 ' clk $end
$var wire 32 " cyc [31:0] $end
$scope module sub_a $end
$scope module deep_i $end
$var wire 32 * t [31:0] $end
$upscope $end
$upscope $end
$scope module sub_b $end
$scope module deep_i $end
$var wire 32 - t [31:0] $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
b00000000000000000000000000000000 "
0'
b00000000000000000000000000000000 *
b00000000000000000000000000000000 -

View File

@ -0,0 +1,20 @@
#!/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 by Marco Bartoli.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--binary --trace-vcd'])
test.execute()
test.vcd_identical(test.trace_filename, test.golden_filename)
test.passes()

View File

@ -0,0 +1,56 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
module t(
input clk
);
int cyc;
sub #(10) sub_a(.*);
sub #(20) sub_b(.*);
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module sub #(
parameter int ADD
)(
input int cyc
);
int value;
always_comb value = cyc + ADD;
deep #(ADD + 1) deep_i(.*);
initial begin
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
// Test $dumpvars with hierarchical scope: level 1 limits to direct signals of t outside the scope
$dumpvars(1, t);
end
endmodule
module deep #(
parameter int ADD
)(
input int cyc
);
int inner;
int t;
always_comb inner = cyc + ADD;
initial begin
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
$dumpvars(1, t);
end
endmodule

View File

@ -0,0 +1,26 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module $rootio $end
$upscope $end
$scope module t $end
$var wire 1 ' clk $end
$var wire 32 " cyc [31:0] $end
$scope module sub_a $end
$scope module deep_i $end
$var wire 32 * t [31:0] $end
$upscope $end
$upscope $end
$scope module sub_b $end
$scope module deep_i $end
$var wire 32 - t [31:0] $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
b00000000000000000000000000000000 "
0'
b00000000000000000000000000000000 *
b00000000000000000000000000000000 -

View File

@ -0,0 +1,20 @@
#!/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 by Marco Bartoli.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--binary --trace-vcd --fno-inline'])
test.execute()
test.vcd_identical(test.trace_filename, test.golden_filename)
test.passes()

View File

@ -0,0 +1,71 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
module t(
input clk
);
int cyc;
sub #(10) sub_a(.*);
sub #(20) sub_b(.*);
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module sub #(
parameter int ADD
)(
input int cyc
);
int value;
always_comb value = cyc + ADD;
function void dump_from_func;
$dumpvars(1, t);
endfunction
task setup_trace;
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
dump_from_func();
endtask
deep #(ADD + 1) deep_i(.*);
initial begin
setup_trace();
end
endmodule
module deep #(
parameter int ADD
)(
input int cyc
);
int inner;
int t;
always_comb inner = cyc + ADD;
function void dump_from_func;
$dumpvars(1, t);
endfunction
task setup_trace;
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
dump_from_func();
endtask
initial begin
setup_trace();
end
endmodule

View File

@ -0,0 +1,20 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module $rootio $end
$upscope $end
$scope module t $end
$scope module mystruct $end
$scope module deep $end
$var wire 32 ' add [31:0] $end
$var wire 32 # cyc [31:0] $end
$var wire 32 $ inner [31:0] $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
b00000000000000000000000000000000 #
b00000000000000000000000000001011 $
b00000000000000000000000000001011 '

View File

@ -0,0 +1,20 @@
#!/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 by Marco Bartoli.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--binary --trace-vcd --trace-structs'])
test.execute()
test.vcd_identical(test.trace_filename, test.golden_filename)
test.passes()

View File

@ -0,0 +1,46 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
typedef struct packed {
logic [31:0] add;
logic [31:0] cyc;
logic [31:0] inner;
} deep_t;
typedef struct packed {
deep_t deep;
logic [31:0] value;
} top_t;
module t(
input clk
);
int cyc;
top_t mystruct;
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
initial begin
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
// Test $dumpvars with a traced struct sub-scope.
$dumpvars(1, t.mystruct.deep);
end
always_comb begin
mystruct.value = cyc + 32'd10;
mystruct.deep.add = 32'd11;
mystruct.deep.cyc = cyc;
mystruct.deep.inner = cyc + mystruct.deep.add;
end
endmodule

View File

@ -1,15 +1,2 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module $rootio $end
$upscope $end
$scope module t $end
$scope module sub_a $end
$scope module deep_i $end
$upscope $end
$upscope $end
$scope module sub_b $end
$scope module deep_i $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
%Error: t/t_trace_dumpvars_missing_scope.v:28: $dumpvars target not found: missing_module
Aborting...

View File

@ -13,9 +13,6 @@ test.scenarios('vlt')
test.compile(verilator_flags2=['--binary --trace-vcd'])
test.execute()
test.file_grep(test.trace_filename, r'^\$enddefinitions \$end')
test.file_grep_not(test.trace_filename, r'^\s*\$var\s')
test.execute(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -1,6 +1,7 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module $rootio $end
$var wire 1 ' clk $end
$upscope $end
$scope module t $end
$var wire 1 ' clk $end

View File

@ -3,22 +3,16 @@ $timescale 1ps $end
$scope module $rootio $end
$upscope $end
$scope module t $end
$var wire 1 * clk $end
$var wire 32 " cyc [31:0] $end
$scope module rect $end
$scope module origin $end
$var wire 8 # x [7:0] $end
$var wire 8 $ y [7:0] $end
$upscope $end
$scope module size $end
$var wire 8 % x [7:0] $end
$var wire 8 & y [7:0] $end
$upscope $end
$upscope $end
$scope module pt $end
$var wire 8 ' x [7:0] $end
$var wire 8 ( y [7:0] $end
$upscope $end
$scope module rect $end
$scope module origin $end
$var wire 8 # x [7:0] $end
$upscope $end
$scope module size $end
$upscope $end
$upscope $end
$scope module pt $end
$var wire 8 ( y [7:0] $end
$upscope $end
$scope module sub_a $end
$upscope $end
$upscope $end
@ -26,11 +20,5 @@ $enddefinitions $end
#0
b00000000000000000000000000000000 "
b00000000 #
b00000000 $
b00000000 %
b00000000 &
b00000000 '
b00000000 (
0*

View File

@ -10,7 +10,7 @@ module t(
input clk
);
typedef struct packed {
logic [7:0] x;
logic [7:0] \x ;
logic [7:0] y;
} point_t;
@ -21,17 +21,17 @@ module t(
int cyc;
rect_t rect;
point_t pt;
point_t \pt ;
sub #(10) sub_a(.*);
always @(posedge clk) begin
cyc <= cyc + 1;
pt.x <= pt.x + 1;
pt.y <= pt.y + 2;
rect.origin.x <= rect.origin.x + 1;
\pt .\x <= \pt .\x + 1;
\pt .y <= \pt .y + 2;
rect.origin.\x <= rect.origin.\x + 1;
rect.origin.y <= rect.origin.y + 2;
rect.size.x <= 8'd100;
rect.size.\x <= 8'd100;
rect.size.y <= 8'd200;
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
@ -41,9 +41,9 @@ module t(
initial begin
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
// Level 1 counts only module nesting, so nested struct members under t
// are dumped, but sub_a's signals are still excluded.
$dumpvars(1);
// Target a single escaped struct member in $dumpvars.
$dumpvars(1, rect.origin.\x );
$dumpvars(1, \pt .\y );
end
endmodule

View File

@ -0,0 +1,29 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module $rootio $end
$upscope $end
$scope module t $end
$scope module sub_a $end
$var wire 32 ( ADD [31:0] $end
$var wire 32 " cyc [31:0] $end
$var wire 32 # value [31:0] $end
$scope module deep_i $end
$upscope $end
$upscope $end
$scope module sub_b $end
$var wire 32 * ADD [31:0] $end
$var wire 32 " cyc [31:0] $end
$var wire 32 % value [31:0] $end
$scope module deep_i $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
b00000000000000000000000000000000 "
b00000000000000000000000000001010 #
b00000000000000000000000000010100 %
b00000000000000000000000000001010 (
b00000000000000000000000000010100 *

View File

@ -0,0 +1,20 @@
#!/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 by Marco Bartoli.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--binary --trace-vcd --fno-inline'])
test.execute()
test.vcd_identical(test.trace_filename, test.golden_filename)
test.passes()

View File

@ -0,0 +1,65 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
function int get_trace_level;
return 1;
endfunction
function void varsdump;
$dumpvars(get_trace_level());
endfunction
task setup_trace;
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
varsdump();
endtask
task setup_trace_nested;
setup_trace();
endtask
module t(
input clk
);
int cyc;
sub #(10) sub_a(.*);
sub #(20) sub_b(.*);
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module sub #(
parameter int ADD
)(
input int cyc
);
int value;
always_comb value = cyc + ADD;
initial begin
setup_trace_nested;
end
deep #(ADD + 1) deep_i(.*);
endmodule
module deep #(
parameter int ADD
)(
input int cyc
);
int inner;
always_comb inner = cyc + ADD;
endmodule

View File

@ -0,0 +1,29 @@
$version Generated by VerilatedVcd $end
$timescale 1ps $end
$scope module $rootio $end
$upscope $end
$scope module t $end
$scope module sub_a $end
$var wire 32 ( ADD [31:0] $end
$var wire 32 " cyc [31:0] $end
$var wire 32 # value [31:0] $end
$scope module deep_i $end
$upscope $end
$upscope $end
$scope module sub_b $end
$var wire 32 * ADD [31:0] $end
$var wire 32 " cyc [31:0] $end
$var wire 32 % value [31:0] $end
$scope module deep_i $end
$upscope $end
$upscope $end
$upscope $end
$enddefinitions $end
#0
b00000000000000000000000000000000 "
b00000000000000000000000000001010 #
b00000000000000000000000000010100 %
b00000000000000000000000000001010 (
b00000000000000000000000000010100 *

View File

@ -0,0 +1,20 @@
#!/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 by Marco Bartoli.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=['--binary --trace-vcd --fno-inline'])
test.execute()
test.vcd_identical(test.trace_filename, test.golden_filename)
test.passes()

View File

@ -0,0 +1,61 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 by Marco Bartoli.
// SPDX-License-Identifier: CC0-1.0
`define STRINGIFY(x) `"x`"
function int get_trace_level;
return 1;
endfunction
function void varsdump;
$dumpvars(get_trace_level());
endfunction
task setup_trace;
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
varsdump();
endtask
module t(
input clk
);
int cyc;
sub #(10) sub_a(.*);
sub #(20) sub_b(.*);
always @(posedge clk) begin
cyc <= cyc + 1;
if (cyc == 5) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module sub #(
parameter int ADD
)(
input int cyc
);
int value;
always_comb value = cyc + ADD;
initial begin
setup_trace;
end
deep #(ADD + 1) deep_i(.*);
endmodule
module deep #(
parameter int ADD
)(
input int cyc
);
int inner;
always_comb inner = cyc + ADD;
endmodule

View File

@ -26,7 +26,7 @@ module t;
initial begin
$dumpfile(`STRINGIFY(`TEST_DUMPFILE));
$dumpvars(0, top);
$dumpvars(0);
for (i = 0; i < 10; i++) begin
@(posedge clk);

View File

@ -26,9 +26,9 @@ module t (
`ifdef TEST_DUMP
$dumpfile(filename);
$dumpvars(0); // Intentionally no ", top" for parsing coverage with just (expr)
$dumpvars(1, top); // Intentionally checking parsing coverage
$dumpvars(1, top, top); // Intentionally checking parsing coverage
$dumpvars(0); // Intentionally no ", topa" for parsing coverage with just (expr)
$dumpvars(1, topa); // Intentionally checking parsing coverage
$dumpvars(1, topa, topa); // Intentionally checking parsing coverage
$dumplimit(10 * 1024 * 1024);
`elsif TEST_DUMPPORTS
$dumpports(top, filename);