diff --git a/Changes b/Changes index 1c9166ce4..acf6dcee5 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,7 @@ Verilator 4.223 devel **Minor:** +* Support compile time trace signal selection with tracing_on/off (#3323). [Shunyao CAD] * Fix hang with large case statement optimization (#3405). [Mike Urbach] diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index d129c4a55..53b66550b 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1676,9 +1676,28 @@ The grammar of configuration commands is as follows: .. option:: tracing_off [-file "" [-lines [ - ]]] - Enable/disable waveform tracing for all future signals declared in the - specified filename (or wildcard with '\*' or '?', or all files if - omitted) and range of line numbers (or all lines if omitted). +.. option:: tracing_on [-scope "" [-levels ]] - For tracing_off, instances below any module in the files/ranges - specified will also not be traced. +.. option:: tracing_off [-scope "" [-levels ]] + + Enable/disable waveform tracing for all future signals declared in + all files. + + With -file, enable/disable waveform tracing in the specified + filename (or wildcard with '\*' or '?'), and -line range of line + numbers (or all lines if omitted). + + For tracing_off with -file, instances below any module in the + files/ranges specified will also not be traced. To overcome this + feature, use tracing_on on the upper module declaration and on any + cells, or use the -scope flavor of the command. + + With -scope enable/disable waveform tracing for the specified scope (or + wildcard with '\*' or '?'), and optional --levels number of levels + below. These controls only take place after other file/line/module + based controls have indicated the signal should be traced. + + With -levels (used with -scope), the number of levels below that + scope which the rule is to match, where 0 means all levels below, 1 + the exact level as the provided scope, and 2 meaning an additional + level of children below the provided scope, etc. diff --git a/src/V3Config.cpp b/src/V3Config.cpp index 8ffaa21d6..d27db65ca 100644 --- a/src/V3Config.cpp +++ b/src/V3Config.cpp @@ -339,12 +339,109 @@ public: using V3ConfigFileResolver = V3ConfigWildcardResolver; +//###################################################################### +// ScopeTrace tracking + +class V3ConfigScopeTraceEntry final { +public: + const string m_scope; // Scope or regexp to match + const bool m_on = false; // True to enable message + int m_levels = 0; // # levels, 0 = all, 1 = only this, ... + // CONSTRUCTORS + V3ConfigScopeTraceEntry(const string& scope, bool on, int levels) + : m_scope{scope} + , m_on{on} + , m_levels{levels} {} + ~V3ConfigScopeTraceEntry() = default; + bool operator<(const V3ConfigScopeTraceEntry& other) const { + if (m_on < other.m_on) return true; + if (m_on > other.m_on) return false; + if (m_levels < other.m_levels) return true; + if (m_levels > other.m_levels) return false; + return m_scope < other.m_scope; + } +}; + +// Tracks what matches are known to hit against V3ConfigScopeTraceEntries +class V3ConfigScopeTraceEntryMatch final { +public: + const V3ConfigScopeTraceEntry* m_entryp; + const string m_scopepart; + V3ConfigScopeTraceEntryMatch(const V3ConfigScopeTraceEntry* entryp, const string& scopepart) + : m_entryp{entryp} + , m_scopepart{scopepart} {} + bool operator<(const V3ConfigScopeTraceEntryMatch& other) const { + if (m_entryp < other.m_entryp) return true; + if (m_entryp > other.m_entryp) return false; + return m_scopepart < other.m_scopepart; + } +}; + +class V3ConfigScopeTraceResolver final { + std::vector m_entries; // User specified on/offs and levels + std::map m_matchCache; // Matching entries for speed + +public: + void addScopeTraceOn(bool on, const string& scope, int levels) { + UINFO(9, "addScopeTraceOn " << on << " '" << scope << "' " + << " levels=" << levels << endl); + m_entries.emplace_back(V3ConfigScopeTraceEntry{scope, on, levels}); + m_matchCache.clear(); + } + + bool getEntryMatch(const V3ConfigScopeTraceEntry* entp, const string& scopepart) { + // Return if a entry matches the scopepart, with memoization + const auto& key = V3ConfigScopeTraceEntryMatch{entp, scopepart}; + const auto& it = m_matchCache.find(key); + if (it != m_matchCache.end()) return it->second; // Cached + const bool matched = VString::wildmatch(scopepart, entp->m_scope); + m_matchCache.emplace(key, matched); + return matched; + } + + bool getScopeTraceOn(const string& scope) { + // Apply in the order the user provided them, so they can choose on/off preferencing + int maxLevel = 1; + for (const auto& ch : scope) { + if (ch == '.') ++maxLevel; + } + UINFO(9, "getScopeTraceOn " << scope << " maxLevel=" << maxLevel << endl); + + bool enabled = true; + for (const auto& ent : m_entries) { + // We apply shortest match first for each rule component + // (Otherwise the levels would be useless as "--scope top* --levels 1" would + // always match at every scopepart, and we wound't know how to count levels) + int partLevel = 1; + for (string::size_type partEnd = 0; true;) { + partEnd = scope.find('.', partEnd + 1); + if (partEnd == string::npos) partEnd = scope.length(); + const std::string scopepart = scope.substr(0, partEnd); + if (getEntryMatch(&ent, scopepart)) { + const bool levelMatch + = !ent.m_levels || (ent.m_levels >= maxLevel - partLevel); + if (levelMatch) enabled = ent.m_on; + UINFO(9, "getScopeTraceOn-part " << scope << " enabled=" << enabled + << " @ lev=" << partLevel + << (levelMatch ? "[match]" : "[miss]") + << " from scopepart=" << scopepart << endl); + break; + } + if (partEnd == scope.length()) break; + ++partLevel; + } + } + return enabled; + } +}; + //###################################################################### // Resolve modules and files in the design class V3ConfigResolver final { V3ConfigModuleResolver m_modules; // Access to module names (with wildcards) V3ConfigFileResolver m_files; // Access to file names (with wildcards) + V3ConfigScopeTraceResolver m_scopeTraces; // Regexp to trace enables std::unordered_map> m_profileData; // Access to profile_data records FileLine* m_profileFileLine = nullptr; @@ -359,6 +456,7 @@ public: } V3ConfigModuleResolver& modules() { return m_modules; } V3ConfigFileResolver& files() { return m_files; } + V3ConfigScopeTraceResolver& scopeTraces() { return m_scopeTraces; } void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost) { if (!m_profileFileLine) m_profileFileLine = fl; @@ -428,6 +526,10 @@ void V3Config::addProfileData(FileLine* fl, const string& model, const string& k V3ConfigResolver::s().addProfileData(fl, model, key, cost); } +void V3Config::addScopeTraceOn(bool on, const string& scope, int levels) { + V3ConfigResolver::s().scopeTraces().addScopeTraceOn(on, scope, levels); +} + void V3Config::addVarAttr(FileLine* fl, const string& module, const string& ftask, const string& var, VAttrType attr, AstSenTree* sensep) { // Semantics: sensep only if public_flat_rw @@ -534,6 +636,9 @@ uint64_t V3Config::getProfileData(const string& model, const string& key) { FileLine* V3Config::getProfileDataFileLine() { return V3ConfigResolver::s().getProfileDataFileLine(); } +bool V3Config::getScopeTraceOn(const string& scope) { + return V3ConfigResolver::s().scopeTraces().getScopeTraceOn(scope); +} bool V3Config::waive(FileLine* filelinep, V3ErrorCode code, const string& message) { V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filelinep->filename()); diff --git a/src/V3Config.h b/src/V3Config.h index add53c11b..80d1fb9f2 100644 --- a/src/V3Config.h +++ b/src/V3Config.h @@ -37,6 +37,7 @@ public: static void addModulePragma(const string& module, VPragmaType pragma); static void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost); + static void addScopeTraceOn(bool on, const string& scope, int levels); static void addVarAttr(FileLine* fl, const string& module, const string& ftask, const string& signal, VAttrType type, AstSenTree* nodep); static void addWaiver(V3ErrorCode code, const string& filename, const string& message); @@ -50,6 +51,7 @@ public: static uint64_t getProfileData(const string& model, const string& key); static FileLine* getProfileDataFileLine(); + static bool getScopeTraceOn(const string& scope); static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message); }; diff --git a/src/V3TraceDecl.cpp b/src/V3TraceDecl.cpp index 15669c0d1..a3f0643e0 100644 --- a/src/V3TraceDecl.cpp +++ b/src/V3TraceDecl.cpp @@ -25,9 +25,10 @@ #include "verilated_trace_defs.h" // For VLT_TRACE_SCOPE_* #include "V3Global.h" -#include "V3TraceDecl.h" +#include "V3Config.h" #include "V3EmitCBase.h" #include "V3Stats.h" +#include "V3TraceDecl.h" #include #include @@ -140,10 +141,13 @@ private: return "Verilator trace_off"; } else if (!nodep->isTrace()) { return "Verilator instance trace_off"; - } else if (!v3Global.opt.traceUnderscore()) { + } else { const string prettyName = varp->prettyName(); - if (!prettyName.empty() && prettyName[0] == '_') return "Leading underscore"; - if (prettyName.find("._") != string::npos) return "Inlined leading underscore"; + if (!v3Global.opt.traceUnderscore()) { + if (!prettyName.empty() && prettyName[0] == '_') return "Leading underscore"; + if (prettyName.find("._") != string::npos) return "Inlined leading underscore"; + } + if (!V3Config::getScopeTraceOn(prettyName)) return "Vlt scope trace_off"; } return nullptr; } diff --git a/src/verilog.l b/src/verilog.l index 588da6073..c3eb40f4c 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -138,12 +138,14 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} -?"-cost" { FL; return yVLT_D_COST; } -?"-file" { FL; return yVLT_D_FILE; } -?"-function" { FL; return yVLT_D_FUNCTION; } + -?"-levels" { FL; return yVLT_D_LEVELS; } -?"-lines" { FL; return yVLT_D_LINES; } -?"-match" { FL; return yVLT_D_MATCH; } -?"-model" { FL; return yVLT_D_MODEL; } -?"-module" { FL; return yVLT_D_MODULE; } -?"-mtask" { FL; return yVLT_D_MTASK; } -?"-rule" { FL; return yVLT_D_RULE; } + -?"-scope" { FL; return yVLT_D_SCOPE; } -?"-task" { FL; return yVLT_D_TASK; } -?"-var" { FL; return yVLT_D_VAR; } diff --git a/src/verilog.y b/src/verilog.y index bca9397e1..84c778b45 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -366,12 +366,14 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yVLT_D_COST "--cost" %token yVLT_D_FILE "--file" %token yVLT_D_FUNCTION "--function" +%token yVLT_D_LEVELS "--levels" %token yVLT_D_LINES "--lines" %token yVLT_D_MATCH "--match" %token yVLT_D_MODEL "--model" %token yVLT_D_MODULE "--module" %token yVLT_D_MTASK "--mtask" %token yVLT_D_RULE "--rule" +%token yVLT_D_SCOPE "--scope" %token yVLT_D_TASK "--task" %token yVLT_D_VAR "--var" @@ -6458,6 +6460,18 @@ vltItem: { V3Config::addIgnore($1, false, *$3, $5->toUInt(), $5->toUInt()+1); } | vltOffFront yVLT_D_FILE yaSTRING yVLT_D_LINES yaINTNUM '-' yaINTNUM { V3Config::addIgnore($1, false, *$3, $5->toUInt(), $7->toUInt()+1); } + | vltOffFront yVLT_D_SCOPE yaSTRING + { if ($1 != V3ErrorCode::I_TRACING) { + $1->v3error("Argument -scope only supported for tracing_on/off"); + } else { + V3Config::addScopeTraceOn(false, *$3, 0); + }} + | vltOffFront yVLT_D_SCOPE yaSTRING yVLT_D_LEVELS yaINTNUM + { if ($1 != V3ErrorCode::I_TRACING) { + $1->v3error("Argument -scope only supported for tracing_on/off_off"); + } else { + V3Config::addScopeTraceOn(false, *$3, $5->toUInt()); + }} | vltOffFront yVLT_D_FILE yaSTRING yVLT_D_MATCH yaSTRING { if (($1 == V3ErrorCode::I_COVERAGE) || ($1 == V3ErrorCode::I_TRACING)) { $1->v3error("Argument -match only supported for lint_off"); @@ -6472,6 +6486,18 @@ vltItem: { V3Config::addIgnore($1, true, *$3, $5->toUInt(), $5->toUInt()+1); } | vltOnFront yVLT_D_FILE yaSTRING yVLT_D_LINES yaINTNUM '-' yaINTNUM { V3Config::addIgnore($1, true, *$3, $5->toUInt(), $7->toUInt()+1); } + | vltOnFront yVLT_D_SCOPE yaSTRING + { if ($1 != V3ErrorCode::I_TRACING) { + $1->v3error("Argument -scope only supported for tracing_on/off"); + } else { + V3Config::addScopeTraceOn(true, *$3, 0); + }} + | vltOnFront yVLT_D_SCOPE yaSTRING yVLT_D_LEVELS yaINTNUM + { if ($1 != V3ErrorCode::I_TRACING) { + $1->v3error("Argument -scope only supported for tracing_on/off_off"); + } else { + V3Config::addScopeTraceOn(true, *$3, $5->toUInt()); + }} | vltVarAttrFront vltDModuleE vltDFTaskE vltVarAttrVarE attr_event_controlE { V3Config::addVarAttr($1, *$2, *$3, *$4, $1, $5); } | vltInlineFront vltDModuleE vltDFTaskE diff --git a/test_regress/t/t_trace_scope_vlt.out b/test_regress/t/t_trace_scope_vlt.out new file mode 100644 index 000000000..94628f611 --- /dev/null +++ b/test_regress/t/t_trace_scope_vlt.out @@ -0,0 +1,157 @@ +$version Generated by VerilatedVcd $end +$date Thu May 12 22:13:21 2022 $end +$timescale 1ps $end + + $scope module top $end + $var wire 1 ) clk $end + $scope module t $end + $var wire 1 ) clk $end + $scope module sub1a $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 sub1b $end + $var wire 32 + ADD [31:0] $end + $var wire 32 # cyc [31:0] $end + $var wire 32 % value [31:0] $end + $scope module sub2a $end + $var wire 32 # cyc [31:0] $end + $var wire 32 & value [31:0] $end + $upscope $end + $scope module sub2b $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 sub2c $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 + $upscope $end +$enddefinitions $end + + +#0 +b00000000000000000000000000000000 # +b00000000000000000000000000001010 $ +b00000000000000000000000000010100 % +b00000000000000000000000000010101 & +b00000000000000000000000000010110 ' +b00000000000000000000000000010111 ( +0) +b00000000000000000000000000001010 * +b00000000000000000000000000010100 + +b00000000000000000000000000010110 , +b00000000000000000000000000010111 - +#10 +b00000000000000000000000000000001 # +b00000000000000000000000000001011 $ +b00000000000000000000000000010101 % +b00000000000000000000000000010110 & +b00000000000000000000000000010111 ' +b00000000000000000000000000011000 ( +1) +#15 +0) +#20 +b00000000000000000000000000000010 # +b00000000000000000000000000001100 $ +b00000000000000000000000000010110 % +b00000000000000000000000000010111 & +b00000000000000000000000000011000 ' +b00000000000000000000000000011001 ( +1) +#25 +0) +#30 +b00000000000000000000000000000011 # +b00000000000000000000000000001101 $ +b00000000000000000000000000010111 % +b00000000000000000000000000011000 & +b00000000000000000000000000011001 ' +b00000000000000000000000000011010 ( +1) +#35 +0) +#40 +b00000000000000000000000000000100 # +b00000000000000000000000000001110 $ +b00000000000000000000000000011000 % +b00000000000000000000000000011001 & +b00000000000000000000000000011010 ' +b00000000000000000000000000011011 ( +1) +#45 +0) +#50 +b00000000000000000000000000000101 # +b00000000000000000000000000001111 $ +b00000000000000000000000000011001 % +b00000000000000000000000000011010 & +b00000000000000000000000000011011 ' +b00000000000000000000000000011100 ( +1) +#55 +0) +#60 +b00000000000000000000000000000110 # +b00000000000000000000000000010000 $ +b00000000000000000000000000011010 % +b00000000000000000000000000011011 & +b00000000000000000000000000011100 ' +b00000000000000000000000000011101 ( +1) +#65 +0) +#70 +b00000000000000000000000000000111 # +b00000000000000000000000000010001 $ +b00000000000000000000000000011011 % +b00000000000000000000000000011100 & +b00000000000000000000000000011101 ' +b00000000000000000000000000011110 ( +1) +#75 +0) +#80 +b00000000000000000000000000001000 # +b00000000000000000000000000010010 $ +b00000000000000000000000000011100 % +b00000000000000000000000000011101 & +b00000000000000000000000000011110 ' +b00000000000000000000000000011111 ( +1) +#85 +0) +#90 +b00000000000000000000000000001001 # +b00000000000000000000000000010011 $ +b00000000000000000000000000011101 % +b00000000000000000000000000011110 & +b00000000000000000000000000011111 ' +b00000000000000000000000000100000 ( +1) +#95 +0) +#100 +b00000000000000000000000000001010 # +b00000000000000000000000000010100 $ +b00000000000000000000000000011110 % +b00000000000000000000000000011111 & +b00000000000000000000000000100000 ' +b00000000000000000000000000100001 ( +1) +#105 +0) +#110 +b00000000000000000000000000001011 # +b00000000000000000000000000010101 $ +b00000000000000000000000000011111 % +b00000000000000000000000000100000 & +b00000000000000000000000000100001 ' +b00000000000000000000000000100010 ( +1) diff --git a/test_regress/t/t_trace_scope_vlt.pl b/test_regress/t/t_trace_scope_vlt.pl new file mode 100755 index 000000000..9847c375d --- /dev/null +++ b/test_regress/t/t_trace_scope_vlt.pl @@ -0,0 +1,26 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2009 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 + +scenarios(vlt_all => 1); + +top_filename("t/t_trace_scope_vlt.v"); + +compile( + v_flags2 => ["--trace t/t_trace_scope_vlt.vlt"], + ); + +execute( + check_finished => 1, + ); + +vcd_identical("$Self->{obj_dir}/simx.vcd", $Self->{golden_filename}); + +ok(1); +1; diff --git a/test_regress/t/t_trace_scope_vlt.v b/test_regress/t/t_trace_scope_vlt.v new file mode 100644 index 000000000..dfeaa16dd --- /dev/null +++ b/test_regress/t/t_trace_scope_vlt.v @@ -0,0 +1,42 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + int cyc; + + sub1 #(10) sub1a (.*); + sub1 #(20) sub1b (.*); + + always @ (posedge clk) begin + cyc <= cyc + 1; + if (cyc == 10) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module sub1 #(parameter int ADD) + (input int cyc); + + wire int value = cyc + ADD; + + sub2 #(ADD + 1) sub2a(.*); + sub2 #(ADD + 2) sub2b(.*); + sub2 #(ADD + 3) sub2c(.*); +endmodule + +module sub2 #(parameter int ADD) + (input int cyc); + + wire int value = cyc + ADD; +endmodule diff --git a/test_regress/t/t_trace_scope_vlt.vlt b/test_regress/t/t_trace_scope_vlt.vlt new file mode 100644 index 000000000..cd7dd85ab --- /dev/null +++ b/test_regress/t/t_trace_scope_vlt.vlt @@ -0,0 +1,13 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2022 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`verilator_config + +tracing_off -scope "t*" -levels 0 +tracing_on -scope "t.clk" +tracing_on -scope "t.sub1a" -levels 1 +tracing_on -scope "t.sub1b" -levels 2 +tracing_off -scope "*.sub2a.ADD" diff --git a/test_regress/t/t_vlt_syntax_bad.out b/test_regress/t/t_vlt_syntax_bad.out index 7db02e54b..ebe31f2ce 100644 --- a/test_regress/t/t_vlt_syntax_bad.out +++ b/test_regress/t/t_vlt_syntax_bad.out @@ -1,7 +1,22 @@ %Error: t/t_vlt_syntax_bad.vlt:9:20: sensitivity not expected for attribute 9 | public -module "t" @(posedge clk) | ^ -%Error: t/t_vlt_syntax_bad.vlt:10:1: isolate_assignments only applies to signals or functions/tasks - 10 | isolate_assignments -module "t" +%Error: t/t_vlt_syntax_bad.vlt:11:1: isolate_assignments only applies to signals or functions/tasks + 11 | isolate_assignments -module "t" | ^~~~~~~~~~~~~~~~~~~ +%Error: t/t_vlt_syntax_bad.vlt:13:1: Argument -match only supported for lint_off + 13 | tracing_off --file "*" -match "nothing" + | ^~~~~~~~~~~ +%Error: t/t_vlt_syntax_bad.vlt:15:1: Argument -scope only supported for tracing_on/off + 15 | lint_off --rule UNOPTFLAT -scope "top*" + | ^~~~~~~~ +%Error: t/t_vlt_syntax_bad.vlt:16:1: Argument -scope only supported for tracing_on/off_off + 16 | lint_off --rule UNOPTFLAT -scope "top*" -levels 0 + | ^~~~~~~~ +%Error: t/t_vlt_syntax_bad.vlt:17:1: Argument -scope only supported for tracing_on/off + 17 | lint_on --rule UNOPTFLAT -scope "top*" + | ^~~~~~~ +%Error: t/t_vlt_syntax_bad.vlt:18:1: Argument -scope only supported for tracing_on/off_off + 18 | lint_on --rule UNOPTFLAT -scope "top*" -levels 0 + | ^~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_vlt_syntax_bad.vlt b/test_regress/t/t_vlt_syntax_bad.vlt index df7bda927..7f1d2d092 100644 --- a/test_regress/t/t_vlt_syntax_bad.vlt +++ b/test_regress/t/t_vlt_syntax_bad.vlt @@ -7,4 +7,12 @@ `verilator_config public -module "t" @(posedge clk) +// only signals/functions/tasks isolate_assignments -module "t" +// -match not supported +tracing_off --file "*" -match "nothing" +// -scope not supported +lint_off --rule UNOPTFLAT -scope "top*" +lint_off --rule UNOPTFLAT -scope "top*" -levels 0 +lint_on --rule UNOPTFLAT -scope "top*" +lint_on --rule UNOPTFLAT -scope "top*" -levels 0