diff --git a/Changes b/Changes index 1f1f57347..7676fdf5d 100644 --- a/Changes +++ b/Changes @@ -27,6 +27,7 @@ Verilator 5.007 devel * Support class extending its parameter (#3904). [Ryszard Rozak, Antmicro Ltd] * Support static function variables (#3830). [Ryszard Rozak, Antmicro Ltd] * Support vpiDefName (#3906) (#3931). [Andrew Nolte] +* Removed deprecated --cdc option. * Fix real parameters of infinity and NaN. * Fix pattern assignment to unpacked structs (#3510). [Mostafa Garnal] * Fix single-element replication to dynarray/unpacked/queue (#3548). [Gustav Svensk] diff --git a/docs/guide/deprecations.rst b/docs/guide/deprecations.rst index eb3f05f2c..1fd8252fd 100644 --- a/docs/guide/deprecations.rst +++ b/docs/guide/deprecations.rst @@ -22,10 +22,6 @@ Verilated_heavy.h "verilated.h". Verilated_heavy.h is planned for removal no sooner than July 2022. -Option `--cdc` - The experimental `--cdc` option is believed to be generally unused and is - planned for removal no sooner than January 2023. - Option `-O` The debug `-O` options have been replaced with `-fno-` debug options to match GCC. The old options are diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index c6e86a7fc..7027fc1f0 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -155,18 +155,6 @@ Summary: Specify C++ without SystemC output mode; see also the :vlopt:`--sc` option. -.. option:: --cdc - - Permanently experimental. Perform some clock domain crossing checks and - issue related warnings (CDCRSTLOGIC) and then exit; if warnings other - than CDC warnings are needed, make a second run with - :vlopt:`--lint-only`. Additional warning information is also written to - the file :file:`__cdc.txt`. - - Currently only checks some items that other CDC tools missed; if you are - interested in adding more traditional CDC checks, please contact the - authors. - .. option:: -CFLAGS Add specified C compiler argument to the generated makefiles. For diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index c39d4475f..b0fee4695 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -318,9 +318,11 @@ List Of Warnings .. option:: CDCRSTLOGIC - With :vlopt:`--cdc` only, it warns that asynchronous flop reset terms come - from other than primary inputs or flopped outputs, creating the - potential for reset glitches. + Historical, never issued since version 5.008. + + Warned with a no longer supported clock domain crossing option that + asynchronous flop reset terms came from other than primary inputs or + flopped outputs, creating the potential for reset glitches. .. option:: CLKDATA diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b25c900b5..dc8dcf182 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,7 +47,6 @@ set(HEADERS V3Case.h V3Cast.h V3CCtors.h - V3Cdc.h V3Class.h V3Clean.h V3Clock.h @@ -187,7 +186,6 @@ set(COMMON_SOURCES V3CUse.cpp V3Case.cpp V3Cast.cpp - V3Cdc.cpp V3Class.cpp V3Clean.cpp V3Clock.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 35a21f847..1db782e52 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -164,7 +164,6 @@ RAW_OBJS = \ V3CUse.o \ V3Case.o \ V3Cast.o \ - V3Cdc.o \ V3Class.o \ V3Clean.o \ V3Clock.o \ diff --git a/src/V3Cdc.cpp b/src/V3Cdc.cpp deleted file mode 100644 index 0875bde87..000000000 --- a/src/V3Cdc.cpp +++ /dev/null @@ -1,765 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Clock Domain Crossing Lint -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// Copyright 2003-2023 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 -// -//************************************************************************* -// V3Cdc's Transformations: -// -// Create V3Graph-ish graph -// Find all negedge reset flops -// Trace back to previous flop -// -//************************************************************************* - -#include "config_build.h" -#include "verilatedos.h" - -#include "V3Cdc.h" - -#include "V3Ast.h" -#include "V3Const.h" -#include "V3EmitV.h" -#include "V3File.h" -#include "V3Global.h" -#include "V3Graph.h" - -#include -#include -#include -#include - -VL_DEFINE_DEBUG_FUNCTIONS; - -constexpr int CDC_WEIGHT_ASYNC = 0x1000; // Weight for edges that feed async logic - -//###################################################################### - -class CdcBaseVisitor VL_NOT_FINAL : public VNVisitor { -public: -}; - -//###################################################################### -// Graph support classes - -class CdcEitherVertex VL_NOT_FINAL : public V3GraphVertex { - AstScope* const m_scopep; - AstNode* const m_nodep; - AstSenTree* m_srcDomainp = nullptr; - AstSenTree* m_dstDomainp = nullptr; - bool m_srcDomainSet : 1; - bool m_dstDomainSet : 1; - bool m_asyncPath : 1; - -public: - CdcEitherVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep) - : V3GraphVertex{graphp} - , m_scopep{scopep} - , m_nodep{nodep} - , m_srcDomainSet{false} - , m_dstDomainSet{false} - , m_asyncPath{false} {} - ~CdcEitherVertex() override = default; - // ACCESSORS - FileLine* fileline() const override { return nodep()->fileline(); } - AstScope* scopep() const { return m_scopep; } - AstNode* nodep() const { return m_nodep; } - AstSenTree* srcDomainp() const { return m_srcDomainp; } - void srcDomainp(AstSenTree* nodep) { m_srcDomainp = nodep; } - bool srcDomainSet() const { return m_srcDomainSet; } - void srcDomainSet(bool flag) { m_srcDomainSet = flag; } - AstSenTree* dstDomainp() const { return m_dstDomainp; } - void dstDomainp(AstSenTree* nodep) { m_dstDomainp = nodep; } - bool dstDomainSet() const { return m_dstDomainSet; } - void dstDomainSet(bool flag) { m_dstDomainSet = flag; } - bool asyncPath() const { return m_asyncPath; } - void asyncPath(bool flag) { m_asyncPath = flag; } -}; - -class CdcVarVertex final : public CdcEitherVertex { - AstVarScope* const m_varScp; - int m_cntAsyncRst = 0; - bool m_fromFlop = false; - -public: - CdcVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp) - : CdcEitherVertex{graphp, scopep, varScp} - , m_varScp{varScp} {} - ~CdcVarVertex() override = default; - // ACCESSORS - AstVarScope* varScp() const { return m_varScp; } - string name() const override { return (cvtToHex(m_varScp) + " " + varScp()->name()); } - string dotColor() const override { - return fromFlop() ? "green" : cntAsyncRst() ? "red" : "blue"; - } - int cntAsyncRst() const { return m_cntAsyncRst; } - void cntAsyncRst(int flag) { m_cntAsyncRst = flag; } - bool fromFlop() const { return m_fromFlop; } - void fromFlop(bool flag) { m_fromFlop = flag; } -}; - -class CdcLogicVertex final : public CdcEitherVertex { - bool m_hazard : 1; - bool m_isFlop : 1; - -public: - CdcLogicVertex(V3Graph* graphp, AstScope* scopep, AstNode* nodep, AstSenTree* sensenodep) - : CdcEitherVertex{graphp, scopep, nodep} - , m_hazard{false} - , m_isFlop{false} { - srcDomainp(sensenodep); - dstDomainp(sensenodep); - } - ~CdcLogicVertex() override = default; - // ACCESSORS - string name() const override { return (cvtToHex(nodep()) + "@" + scopep()->prettyName()); } - string dotColor() const override { return hazard() ? "black" : "yellow"; } - bool hazard() const { return m_hazard; } - void setHazard(AstNode* nodep) { - m_hazard = true; - nodep->user3(true); - } - void clearHazard() { m_hazard = false; } - bool isFlop() const { return m_isFlop; } - void isFlop(bool flag) { m_isFlop = flag; } -}; - -//###################################################################### - -class CdcDumpVisitor final : public CdcBaseVisitor { -private: - // NODE STATE - // Entire netlist: - // {statement}Node::user3 -> bool, indicating not hazard - std::ofstream* const m_ofp = nullptr; // Output file - string m_prefix; - - void visit(AstNode* nodep) override { - *m_ofp << m_prefix; - if (nodep->user3()) { - *m_ofp << " %%"; - } else { - *m_ofp << " "; - } - *m_ofp << nodep->prettyTypeName() << "\n"; - const string lastPrefix = m_prefix; - m_prefix = lastPrefix + "1:"; - iterateAndNextNull(nodep->op1p()); - m_prefix = lastPrefix + "2:"; - iterateAndNextNull(nodep->op2p()); - m_prefix = lastPrefix + "3:"; - iterateAndNextNull(nodep->op3p()); - m_prefix = lastPrefix + "4:"; - iterateAndNextNull(nodep->op4p()); - m_prefix = lastPrefix; - } - -public: - // CONSTRUCTORS - CdcDumpVisitor(AstNode* nodep, std::ofstream* ofp, const string& prefix) - : m_ofp{ofp} - , m_prefix{prefix} { - iterate(nodep); - } - ~CdcDumpVisitor() override = default; -}; - -//###################################################################### - -class CdcWidthVisitor final : public CdcBaseVisitor { -private: - size_t m_maxFilenameLen = 0; - int m_maxLineno = 0; - - void visit(AstNode* nodep) override { - iterateChildren(nodep); - // Keeping line+filename lengths separate is much faster than calling ascii().length() - if (nodep->fileline()->lineno() >= m_maxLineno) { - m_maxLineno = nodep->fileline()->lineno() + 1; - } - if (nodep->fileline()->filename().length() >= m_maxFilenameLen) { - m_maxFilenameLen = nodep->fileline()->filename().length() + 1; - } - } - -public: - // CONSTRUCTORS - explicit CdcWidthVisitor(AstNode* nodep) { iterate(nodep); } - ~CdcWidthVisitor() override = default; - // ACCESSORS - int maxWidth() const { - size_t width = 1; - width += m_maxFilenameLen; - width += 1; // The : - width += cvtToStr(m_maxLineno).length(); - width += 1; // Final : - return static_cast(width); - } -}; - -//###################################################################### -// Cdc class functions - -class CdcVisitor final : public CdcBaseVisitor { -private: - // NODE STATE - // Entire netlist: - // AstVarScope::user1p -> CdcVarVertex* for usage var, 0=not set yet - // AstVarScope::user2 -> bool Used in sensitivity list - // {statement}Node::user1p -> CdcLogicVertex* for this statement - // AstNode::user3 -> bool True indicates to print %% (via V3EmitV) - const VNUser1InUse m_inuser1; - const VNUser2InUse m_inuser2; - const VNUser3InUse m_inuser3; - - // STATE - V3Graph m_graph; // Scoreboard of var usages/dependencies - CdcLogicVertex* m_logicVertexp = nullptr; // Current statement being tracked, nullptr=ignored - AstScope* m_scopep = nullptr; // Current scope being processed - const AstNodeModule* m_modp = nullptr; // Current module - AstSenTree* m_domainp = nullptr; // Current sentree - bool m_inDly = false; // In delayed assign - int m_inSenItem = 0; // Number of senitems - string m_ofFilename; // Output filename - std::ofstream* m_ofp; // Output file - uint32_t m_userGeneration = 0; // Generation count to avoid slow userClearVertices - int m_filelineWidth = 0; // Characters in longest fileline - - // METHODS - void iterateNewStmt(AstNode* nodep) { - if (m_scopep) { - VL_RESTORER(m_logicVertexp); - UINFO(4, " STMT " << nodep << endl); - m_logicVertexp = new CdcLogicVertex{&m_graph, m_scopep, nodep, m_domainp}; - if (m_domainp && m_domainp->hasClocked()) { // To/from a flop - m_logicVertexp->isFlop(true); - m_logicVertexp->srcDomainp(m_domainp); - m_logicVertexp->srcDomainSet(true); - m_logicVertexp->dstDomainp(m_domainp); - m_logicVertexp->dstDomainSet(true); - } - iterateChildren(nodep); - - if (false && debug() >= 9) { - UINFO(9, "Trace Logic:\n"); - nodep->dumpTree("- log1: "); - } - } - } - - CdcVarVertex* makeVarVertex(AstVarScope* varscp) { - CdcVarVertex* vertexp = reinterpret_cast(varscp->user1p()); - if (!vertexp) { - UINFO(6, "New vertex " << varscp << endl); - vertexp = new CdcVarVertex{&m_graph, m_scopep, varscp}; - varscp->user1p(vertexp); - if (varscp->varp()->isUsedClock()) {} - if (varscp->varp()->isPrimaryIO()) { - // Create IO vertex - note it's relative to the pointed to var, not where we are - // now This allows reporting to easily print the input statement - CdcLogicVertex* const ioVertexp - = new CdcLogicVertex{&m_graph, varscp->scopep(), varscp->varp(), nullptr}; - if (varscp->varp()->isWritable()) { - new V3GraphEdge{&m_graph, vertexp, ioVertexp, 1}; - } else { - new V3GraphEdge{&m_graph, ioVertexp, vertexp, 1}; - } - } - } - if (m_inSenItem) { - varscp->user2(true); // It's like a clock... - // TODO: In the future mark it here and do normal clock tree glitch checks also - } else if (varscp->user2()) { // It was detected in a sensitivity list earlier - // And now it's used as logic. So must be a reset. - vertexp->cntAsyncRst(vertexp->cntAsyncRst() + 1); - } - return vertexp; - } - - void warnAndFile(AstNode* nodep, V3ErrorCode code, const string& msg) { - static bool told_file = false; - nodep->v3warnCode(code, msg); - if (!told_file) { - told_file = true; - std::cerr << V3Error::msgPrefix() << " See details in " << m_ofFilename << endl; - } - *m_ofp << "%Warning-" << code.ascii() << ": " << nodep->fileline() << " " << msg << '\n'; - } - - void setNodeHazard(AstNode* nodep) { - // Need to not clear if warnings are off (rather than when report it) - // as bypassing this warning may turn up another path that isn't warning off'ed. - // We can't modifyWarnOff here, as one instantiation might not be - // an issue until we find a hitting flop. - // Furthermore, a module like a "Or" module would only get flagged - // once, even though the signals feeding it are radically different. - if (!m_domainp || m_domainp->hasCombo()) { - // Source flop logic in a posedge block is OK for reset (not async though) - if (m_logicVertexp && !nodep->fileline()->warnIsOff(V3ErrorCode::CDCRSTLOGIC)) { - UINFO(8, "Set hazard " << nodep << endl); - m_logicVertexp->setHazard(nodep); - } - } - } - - static string spaces(int level) { - string out; - while (level--) out += " "; - return out; - } // LCOV_EXCL_LINE - static string pad(unsigned column, const string& in) { - string out = in; - while (out.length() < column) out += ' '; - return out; - } - - void analyze() { - UINFO(3, __FUNCTION__ << ": " << endl); - if (dumpGraph() > 6) m_graph.dumpDotFilePrefixed("cdc_pre"); - // This will MAX across edge weights - m_graph.removeRedundantEdges(&V3GraphEdge::followAlwaysTrue); - // - if (dumpGraph() >= 3) m_graph.dumpDotFilePrefixed("cdc_simp"); - // - analyzeReset(); - } - - int filelineWidth() { - if (!m_filelineWidth) { - const CdcWidthVisitor visitor{v3Global.rootp()}; - m_filelineWidth = visitor.maxWidth(); - } - return m_filelineWidth; - } - - //---------------------------------------- - // RESET REPORT - - void analyzeReset() { - // Find all async reset wires, and trace backwards - // userClearVertices is very slow, so we use a generation count instead - m_graph.userClearVertices(); // user1: uint32_t - was analyzed generation - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (CdcVarVertex* const vvertexp = dynamic_cast(itp)) { - if (vvertexp->cntAsyncRst()) { - m_userGeneration++; // Effectively a userClearVertices() - UINFO(8, " Trace One async: " << vvertexp << endl); - // Twice, as we need to detect, then propagate - CdcEitherVertex* const markp = traceAsyncRecurse(vvertexp, false); - if (markp) { // Mark is non-nullptr if something bad on this path - UINFO(9, " Trace One bad! " << vvertexp << endl); - m_userGeneration++; // Effectively a userClearVertices() - traceAsyncRecurse(vvertexp, true); - m_userGeneration++; // Effectively a userClearVertices() - dumpAsync(vvertexp, markp); - } - } - } - } - } - - CdcEitherVertex* traceAsyncRecurse(CdcEitherVertex* vertexp, bool mark) { - // First pass: Return vertex of any hazardous stuff attached, or nullptr if OK - // If first pass returns true, second pass calls asyncPath() on appropriate nodes - if (vertexp->user() >= m_userGeneration) return nullptr; // Processed - prevent loop - vertexp->user(m_userGeneration); - - CdcEitherVertex* mark_outp = nullptr; - UINFO(9, " Trace: " << vertexp << endl); - - // Clear out in prep for marking next path - if (!mark) vertexp->asyncPath(false); - - if (CdcLogicVertex* const vvertexp = dynamic_cast(vertexp)) { - // Any logic considered bad, at the moment, anyhow - if (vvertexp->hazard() && !mark_outp) mark_outp = vvertexp; - // And keep tracing back so the user can understand what's up - } else if (CdcVarVertex* const vvertexp = dynamic_cast(vertexp)) { - if (mark) vvertexp->asyncPath(true); - // If primary I/O, it's ok here back - if (vvertexp->varScp()->varp()->isPrimaryInish()) { - // Show the source "input" statement if it exists - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - CdcEitherVertex* const eFromVertexp - = static_cast(edgep->fromp()); - eFromVertexp->asyncPath(true); - } - return nullptr; - } - // Also ok if from flop, but partially trace the flop so more obvious to users - if (vvertexp->fromFlop()) { - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - CdcEitherVertex* const eFromVertexp - = static_cast(edgep->fromp()); - eFromVertexp->asyncPath(true); - } - return nullptr; - } - } - - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - CdcEitherVertex* const eFromVertexp = static_cast(edgep->fromp()); - CdcEitherVertex* const submarkp = traceAsyncRecurse(eFromVertexp, mark); - if (submarkp && !mark_outp) mark_outp = submarkp; - } - - if (mark) vertexp->asyncPath(true); - return mark_outp; - } - - void dumpAsync(CdcVarVertex* vertexp, CdcEitherVertex* markp) { - const AstNode* const nodep = vertexp->varScp(); - *m_ofp << "\n"; - *m_ofp << "\n"; - CdcEitherVertex* targetp = vertexp; // One example destination flop (of possibly many) - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - CdcEitherVertex* const eToVertexp = static_cast(edgep->top()); - if (!eToVertexp) targetp = eToVertexp; - if (const CdcLogicVertex* const vvertexp = dynamic_cast(eToVertexp)) { - if (vvertexp->isFlop() // IE the target flop that is upsetting us - && edgep->weight() >= CDC_WEIGHT_ASYNC) { // And var feeds an async reset line - targetp = eToVertexp; - // UINFO(9," targetasync "<name()<<" "<<" from - // "<name()<name()<<" "<nodep()->fileline()<nodep(), V3ErrorCode::CDCRSTLOGIC, - "Logic in path that feeds async reset, via signal: " + nodep->prettyNameQ()); - dumpAsyncRecurse(targetp, "", " ", 0); - } - bool dumpAsyncRecurse(CdcEitherVertex* vertexp, const string& prefix, const string& sep, - int level) { - // level=0 is special, indicates to dump destination flop - // Return true if printed anything - // If mark, also mark the output even if nothing hazardous below - if (vertexp->user() >= m_userGeneration) return false; // Processed - prevent loop - vertexp->user(m_userGeneration); - if (!vertexp->asyncPath() && level != 0) return false; // Not part of path - - // Other logic in the path - const string cont = prefix + sep; - string nextsep = " "; - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - CdcEitherVertex* const eFromVertexp = static_cast(edgep->fromp()); - if (dumpAsyncRecurse(eFromVertexp, cont, nextsep, level + 1)) nextsep = " | "; - } - - // Dump single variable/logic block - // See also OrderGraph::loopsVertexCb(V3GraphVertex* vertexp) - AstNode* const nodep = vertexp->nodep(); - const string front - = pad(filelineWidth(), nodep->fileline()->ascii() + ":") + " " + prefix + " +- "; - if (VN_IS(nodep, VarScope)) { - *m_ofp << front << "Variable: " << nodep->prettyName() << '\n'; - } else { - V3EmitV::verilogPrefixedTree(nodep, *m_ofp, prefix + " +- ", filelineWidth(), - vertexp->srcDomainp(), true); - if (debug()) { CdcDumpVisitor{nodep, m_ofp, front + "DBG: "}; } - } - - nextsep = " | "; - if (level) - *m_ofp << V3OutFile::indentSpaces(filelineWidth()) << " " << prefix << nextsep << "\n"; - - if (CdcLogicVertex* const vvertexp = dynamic_cast(vertexp)) { - // Now that we've printed a path with this hazard, don't bother to print any more - // Otherwise, we'd get a path for almost every destination flop - vvertexp->clearHazard(); - } - return true; - } - - //---------------------------------------- - // EDGE REPORTS - - void edgeReport() { - // Make report of all signal names and what clock edges they have - // - // Due to flattening, many interesting direct-connect signals are - // lost, so we can't make a report showing I/Os for a low level - // module. Disabling flattening though makes us consider each - // signal in it's own unique clock domain. - - UINFO(3, __FUNCTION__ << ": " << endl); - - // Trace all sources and sinks - for (const bool& traceDests : {false, true}) { - UINFO(9, " Trace Direction " << (traceDests ? "dst" : "src") << endl); - m_graph.userClearVertices(); // user1: bool - was analyzed - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (CdcVarVertex* const vvertexp = dynamic_cast(itp)) { - UINFO(9, " Trace One edge: " << vvertexp << endl); - edgeDomainRecurse(vvertexp, traceDests, 0); - } - } - } - - const string filename - = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc_edges.txt"; - const std::unique_ptr ofp{V3File::new_ofstream(filename)}; - if (ofp->fail()) v3fatal("Can't write " << filename); - *ofp << "Edge Report for " << v3Global.opt.prefix() << '\n'; - - std::deque report; // Sort output by name - for (V3GraphVertex* itp = m_graph.verticesBeginp(); itp; itp = itp->verticesNextp()) { - if (const CdcVarVertex* const vvertexp = dynamic_cast(itp)) { - const AstVar* const varp = vvertexp->varScp()->varp(); - { - string what = "wire"; - if (varp->isPrimaryIO()) what = varp->direction().prettyName(); - - std::ostringstream os; - os.setf(std::ios::left); - // Module name - doesn't work due to flattening having lost the original - // so we assume the modulename matches the filebasename - const string fname = vvertexp->varScp()->fileline()->filebasename() + ":"; - os << " " << std::setw(20) << fname; - os << " " << std::setw(8) << what; - os << " " << std::setw(40) << vvertexp->varScp()->prettyName(); - os << " SRC="; - if (vvertexp->srcDomainp()) { - V3EmitV::verilogForTree(vvertexp->srcDomainp(), os); - } - os << " DST="; - if (vvertexp->dstDomainp()) { - V3EmitV::verilogForTree(vvertexp->dstDomainp(), os); - } - os << std::setw(0); - os << '\n'; - report.push_back(os.str()); - } - } - } - stable_sort(report.begin(), report.end()); - for (const auto& line : report) *ofp << line; - } - - void edgeDomainRecurse(CdcEitherVertex* vertexp, bool traceDests, int level) { - // Scan back to inputs/outputs, flops, and compute clock domain information - UINFO(8, spaces(level) << " Tracein " << vertexp << endl); - if (vertexp->user() >= m_userGeneration) return; // Mid-Processed - prevent loop - vertexp->user(m_userGeneration); - - // Variables from flops already are domained - if (traceDests ? vertexp->dstDomainSet() : vertexp->srcDomainSet()) { - return; - } // Fully computed - - std::set senouts; // List of all sensitivities for new signal - if (dynamic_cast(vertexp)) { - } else if (const CdcVarVertex* const vvertexp = dynamic_cast(vertexp)) { - // If primary I/O, give it domain of the input - const AstVar* const varp = vvertexp->varScp()->varp(); - if (varp->isPrimaryIO() && varp->isNonOutput() && !traceDests) { - senouts.insert(new AstSenTree{ - varp->fileline(), new AstSenItem{varp->fileline(), AstSenItem::Combo{}}}); - } - } - - // Now combine domains of sources/dests - if (traceDests) { - for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep = edgep->outNextp()) { - CdcEitherVertex* const eToVertexp = static_cast(edgep->top()); - edgeDomainRecurse(eToVertexp, traceDests, level + 1); - if (eToVertexp->dstDomainp()) senouts.insert(eToVertexp->dstDomainp()); - } - } else { - for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep = edgep->inNextp()) { - CdcEitherVertex* const eFromVertexp - = static_cast(edgep->fromp()); - edgeDomainRecurse(eFromVertexp, traceDests, level + 1); - if (eFromVertexp->srcDomainp()) senouts.insert(eFromVertexp->srcDomainp()); - } - } - - // Convert list of senses into one sense node - AstSenTree* senoutp = nullptr; - bool senedited = false; - for (const auto& itr : senouts) { - if (!senoutp) { - senoutp = itr; - } else { - if (!senedited) { - senedited = true; - senoutp = senoutp->cloneTree(true); - } - senoutp->addSensesp(itr->sensesp()->cloneTree(true)); - } - } - // If multiple domains need to do complicated optimizations - if (senedited) senoutp = VN_AS(V3Const::constifyExpensiveEdit(senoutp), SenTree); - if (traceDests) { - vertexp->dstDomainSet(true); // Note it's set - domainp may be null, so can't use that - vertexp->dstDomainp(senoutp); - if (debug() >= 9) { - UINFO(9, spaces(level) + " Tracedst " << vertexp); - if (senoutp) { - V3EmitV::verilogForTree(senoutp, cout); - cout << endl; - } - } - } else { - vertexp->srcDomainSet(true); // Note it's set - domainp may be null, so can't use that - vertexp->srcDomainp(senoutp); - if (debug() >= 9) { - UINFO(9, spaces(level) + " Tracesrc " << vertexp); - if (senoutp) { - V3EmitV::verilogForTree(senoutp, cout); - cout << endl; - } - } - } - } - - // VISITORS - void visit(AstNodeModule* nodep) override { - VL_RESTORER(m_modp); - { - m_modp = nodep; - iterateChildren(nodep); - } - } - void visit(AstScope* nodep) override { - UINFO(4, " SCOPE " << nodep << endl); - m_scopep = nodep; - m_logicVertexp = nullptr; - iterateChildren(nodep); - m_scopep = nullptr; - } - void visit(AstActive* nodep) override { - // Create required blocks and add to module - UINFO(4, " BLOCK " << nodep << endl); - AstNode::user2ClearTree(); - m_domainp = nodep->sensesp(); - // Ignore static initializers, initial and final blocks - if (!m_domainp || m_domainp->hasCombo() || m_domainp->hasClocked()) { - iterateNewStmt(nodep); - } - m_domainp = nullptr; - AstNode::user2ClearTree(); - } - void visit(AstNodeVarRef* nodep) override { - if (m_scopep) { - UASSERT_OBJ(m_logicVertexp, nodep, "Var ref not under a logic block"); - AstVarScope* const varscp = nodep->varScopep(); - UASSERT_OBJ(varscp, nodep, "Var didn't get varscoped in V3Scope.cpp"); - CdcVarVertex* const varvertexp = makeVarVertex(varscp); - UINFO(5, " VARREF to " << varscp << endl); - // We use weight of one for normal edges, - // Weight of CDC_WEIGHT_ASYNC to indicate feeds async (for reporting) - // When simplify we'll take the MAX weight - if (nodep->access().isWriteOrRW()) { - new V3GraphEdge{&m_graph, m_logicVertexp, varvertexp, 1}; - if (m_inDly) { - varvertexp->fromFlop(true); - varvertexp->srcDomainp(m_domainp); - varvertexp->srcDomainSet(true); - } - } else { - if (varvertexp->cntAsyncRst()) { - // UINFO(9," edgeasync "<name()<<" to "<name()<<" to "<lsbp(), Const)) setNodeHazard(nodep); - iterateChildren(nodep); - } - void visit(AstNodeSel* nodep) override { - if (!VN_IS(nodep->bitp(), Const)) setNodeHazard(nodep); - iterateChildren(nodep); - } - - // Ignores - void visit(AstInitial*) override {} - void visit(AstInitialAutomatic*) override {} - void visit(AstInitialStatic*) override {} - void visit(AstTraceDecl*) override {} - void visit(AstCoverToggle*) override {} - void visit(AstNodeDType*) override {} - - //-------------------- - // Default - void visit(AstNodeExpr* nodep) override { - setNodeHazard(nodep); - iterateChildren(nodep); - } - void visit(AstNode* nodep) override { iterateChildren(nodep); } - -public: - // CONSTRUCTORS - explicit CdcVisitor(AstNode* nodep) { - // Make report of all signal names and what clock edges they have - const string filename = v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "__cdc.txt"; - m_ofp = V3File::new_ofstream(filename); - if (m_ofp->fail()) v3fatal("Can't write " << filename); - m_ofFilename = filename; - *m_ofp << "CDC Report for " << v3Global.opt.prefix() << '\n'; - *m_ofp - << "Each dump below traces logic from inputs/source flops to destination flop(s).\n"; - *m_ofp << "First source logic is listed, then a variable that logic generates,\n"; - *m_ofp << "repeating recursively forwards to the destination flop(s).\n"; - *m_ofp << "%% Indicates the operator considered potentially hazardous.\n"; - - iterate(nodep); - analyze(); - if (debug() >= 1) edgeReport(); // Not useful to users at the moment - if (false) { - *m_ofp << "\nDBG-test-dumper\n"; - V3EmitV::verilogPrefixedTree(nodep, *m_ofp, "DBG ", 40, nullptr, true); - *m_ofp << '\n'; - } - } - ~CdcVisitor() override { - if (m_ofp) VL_DO_CLEAR(delete m_ofp, m_ofp = nullptr); - } -}; - -//###################################################################### -// Cdc class functions - -void V3Cdc::cdcAll(AstNetlist* nodep) { - UINFO(2, __FUNCTION__ << ": " << endl); - { CdcVisitor{nodep}; } -} diff --git a/src/V3Cdc.h b/src/V3Cdc.h deleted file mode 100644 index d065cbd25..000000000 --- a/src/V3Cdc.h +++ /dev/null @@ -1,32 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: Break always into sensitivity block domains -// -// Code available from: https://verilator.org -// -//************************************************************************* -// -// Copyright 2003-2023 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 -// -//************************************************************************* - -#ifndef VERILATOR_V3CDC_H_ -#define VERILATOR_V3CDC_H_ - -#include "config_build.h" -#include "verilatedos.h" - -class AstNetlist; - -//============================================================================ - -class V3Cdc final { -public: - static void cdcAll(AstNetlist* nodep); -}; - -#endif // Guard diff --git a/src/V3Error.h b/src/V3Error.h index c5aecadee..05f865f13 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -82,7 +82,7 @@ public: CASEWITHX, // Case with X values CASEX, // Casex CASTCONST, // Cast is constant - CDCRSTLOGIC, // Logic in async reset path + CDCRSTLOGIC, // Logic in async reset path. Historical, never issued. CLKDATA, // Clock used as data. Historical, never issued. CMPCONST, // Comparison is constant due to limited range COLONPLUS, // :+ instead of +: diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 25dcdb317..7fd603ac9 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -776,15 +776,11 @@ void V3Options::notify() { FileLine* const cmdfl = new FileLine{FileLine::commandLineFilename()}; if (!outFormatOk() && v3Global.opt.main()) ccSet(); // --main implies --cc if not provided - if (!outFormatOk() && !cdc() && !dpiHdrOnly() && !lintOnly() && !preprocOnly() && !xmlOnly()) { - v3fatal("verilator: Need --binary, --cc, --sc, --cdc, --dpi-hdr-only, --lint-only, " + if (!outFormatOk() && !dpiHdrOnly() && !lintOnly() && !preprocOnly() && !xmlOnly()) { + v3fatal("verilator: Need --binary, --cc, --sc, --dpi-hdr-only, --lint-only, " "--xml-only or --E option"); } - if (cdc()) { - cmdfl->v3warn(DEPRECATED, "Option --cdc is deprecated and is planned for removal"); - } - if (m_build && (m_gmake || m_cmake)) { cmdfl->v3error("--make cannot be used together with --build. Suggest see manual"); } @@ -826,16 +822,14 @@ void V3Options::notify() { // Default some options if not turned on or off if (v3Global.opt.skipIdentical().isDefault()) { v3Global.opt.m_skipIdentical.setTrueOrFalse( // - !v3Global.opt.cdc() // - && !v3Global.opt.dpiHdrOnly() // + !v3Global.opt.dpiHdrOnly() // && !v3Global.opt.lintOnly() // && !v3Global.opt.preprocOnly() // && !v3Global.opt.xmlOnly()); } if (v3Global.opt.makeDepend().isDefault()) { v3Global.opt.m_makeDepend.setTrueOrFalse( // - !v3Global.opt.cdc() // - && !v3Global.opt.dpiHdrOnly() // + !v3Global.opt.dpiHdrOnly() // && !v3Global.opt.lintOnly() // && !v3Global.opt.preprocOnly() // && !v3Global.opt.xmlOnly()); @@ -1085,7 +1079,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char DECL_OPTION("-CFLAGS", CbVal, callStrSetter(&V3Options::addCFlags)); DECL_OPTION("-cc", CbCall, [this]() { ccSet(); }); - DECL_OPTION("-cdc", OnOff, &m_cdc); DECL_OPTION("-clk", CbVal, callStrSetter(&V3Options::addClocker)); DECL_OPTION("-no-clk", CbVal, callStrSetter(&V3Options::addNoClocker)); DECL_OPTION("-comp-limit-blocks", Set, &m_compLimitBlocks).undocumented(); diff --git a/src/V3Options.h b/src/V3Options.h index 55cdac36a..aa681b3ea 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -224,7 +224,6 @@ private: bool m_bboxSys = false; // main switch: --bbox-sys bool m_bboxUnsup = false; // main switch: --bbox-unsup bool m_build = false; // main switch: --build - bool m_cdc = false; // main switch: --cdc bool m_cmake = false; // main switch: --make cmake bool m_context = true; // main switch: --Wcontext bool m_coverageLine = false; // main switch: --coverage-block @@ -441,7 +440,6 @@ public: bool build() const { return m_build; } string buildDepBin() const { return m_buildDepBin; } void buildDepBin(const string& flag) { m_buildDepBin = flag; } - bool cdc() const { return m_cdc; } bool cmake() const { return m_cmake; } bool context() const VL_MT_SAFE { return m_context; } bool coverage() const VL_MT_SAFE { diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 73c542ac9..7ee1fb9e1 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -26,7 +26,6 @@ #include "V3CUse.h" #include "V3Case.h" #include "V3Cast.h" -#include "V3Cdc.h" #include "V3Class.h" #include "V3Clean.h" #include "V3Clock.h" @@ -372,13 +371,6 @@ static void process() { V3Const::constifyAll(v3Global.rootp()); V3Dead::deadifyAllScoped(v3Global.rootp()); - // Clock domain crossing analysis - if (v3Global.opt.cdc()) { - V3Cdc::cdcAll(v3Global.rootp()); - V3Error::abortIfErrors(); - return; - } - // Reorder assignments in pipelined blocks if (v3Global.opt.fReorder()) V3Split::splitReorderAll(v3Global.rootp()); @@ -579,8 +571,6 @@ static void process() { if (v3Global.opt.cmake()) V3EmitCMake::emit(); if (v3Global.opt.gmake()) V3EmitMk::emitmk(); } - - // Note early return above when opt.cdc() } static void verilate(const string& argString) { diff --git a/test_regress/t/t_cdc_async_bad.out b/test_regress/t/t_cdc_async_bad.out deleted file mode 100644 index 3d767fbe4..000000000 --- a/test_regress/t/t_cdc_async_bad.out +++ /dev/null @@ -1,14 +0,0 @@ -%Warning-DEPRECATED: Option --cdc is deprecated and is planned for removal - ... For warning description see https://verilator.org/warn/DEPRECATED?v=latest - ... Use "/* verilator lint_off DEPRECATED */" and lint_on around source to disable this message. -%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:53:21: Logic in path that feeds async reset, via signal: 't.rst6a_bad_n' - 53 | wire rst6a_bad_n = rst6_bad_n ^ $c1("0"); - | ^ -%Warning-CDCRSTLOGIC: See details in obj_vlt/t_cdc_async_bad/Vt_cdc_async_bad__cdc.txt -%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:54:21: Logic in path that feeds async reset, via signal: 't.rst6b_bad_n' - 54 | wire rst6b_bad_n = rst6_bad_n ^ $c1("1"); - | ^ -%Warning-CDCRSTLOGIC: t/t_cdc_async_bad.v:28:21: Logic in path that feeds async reset, via signal: 't.rst2_bad_n' - 28 | wire rst2_bad_n = rst0_n | rst1_n; - | ^ -%Error: Exiting due to diff --git a/test_regress/t/t_cdc_async_bad.pl b/test_regress/t/t_cdc_async_bad.pl deleted file mode 100755 index d1b47ec05..000000000 --- a/test_regress/t/t_cdc_async_bad.pl +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env perl -if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } -# DESCRIPTION: Verilator: Verilog Test driver/expect definition -# -# Copyright 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 => 1); - -compile( - v_flags => ['--cdc'], - verilator_make_gmake => 0, - make_top_shell => 0, - make_main => 0, - fails => 1, - expect_filename => $Self->{golden_filename}, - ); - -file_grep("$Self->{obj_dir}/V$Self->{name}__cdc.txt", qr/CDC Report/); - -ok(1); -1; diff --git a/test_regress/t/t_cdc_async_bad.v b/test_regress/t/t_cdc_async_bad.v deleted file mode 100644 index 5a9dd3c9a..000000000 --- a/test_regress/t/t_cdc_async_bad.v +++ /dev/null @@ -1,82 +0,0 @@ -// DESCRIPTION: Verilator: Verilog Test module -// -// This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2009 by Wilson Snyder. -// SPDX-License-Identifier: CC0-1.0 - -module t (/*AUTOARG*/ - // Outputs - q0, q1, q2, q3, q4, q5, q6a, q6b, - // Inputs - clk, d, rst0_n - ); - input clk; - input d; - - // OK -- from primary - input rst0_n; - output wire q0; - Flop flop0 (.q(q0), .rst_n(rst0_n), .clk(clk), .d(d)); - - // OK -- from flop - reg rst1_n; - always @ (posedge clk) rst1_n <= rst0_n; - output wire q1; - Flop flop1 (.q(q1), .rst_n(rst1_n), .clk(clk), .d(d)); - - // Bad - logic - wire rst2_bad_n = rst0_n | rst1_n; - output wire q2; - Flop flop2 (.q(q2), .rst_n(rst2_bad_n), .clk(clk), .d(d)); - - // Bad - logic in submodule - wire rst3_bad_n; - Sub sub (.z(rst3_bad_n), .a(rst0_n), .b(rst1_n)); - output wire q3; - Flop flop3 (.q(q3), .rst_n(rst3_bad_n), .clk(clk), .d(d)); - - // OK - bit selection - reg [3:0] rst4_n; - always @ (posedge clk) rst4_n <= {4{rst0_n}}; - output wire q4; - Flop flop4 (.q(q4), .rst_n(rst4_n[1]), .clk(clk), .d(d)); - - // Bad - logic, but waived - // verilator lint_off CDCRSTLOGIC - wire rst5_waive_n = rst0_n & rst1_n; - // verilator lint_on CDCRSTLOGIC - output wire q5; - Flop flop5 (.q(q5), .rst_n(rst5_waive_n), .clk(clk), .d(d)); - - // Bad - for graph test - logic feeds two signals, three destinations - wire rst6_bad_n = rst0_n ^ rst1_n; - wire rst6a_bad_n = rst6_bad_n ^ $c1("0"); // $c prevents optimization - wire rst6b_bad_n = rst6_bad_n ^ $c1("1"); - output wire q6a; - output wire q6b; - Flop flop6a (.q(q6a), .rst_n(rst6a_bad_n), .clk(clk), .d(d)); - Flop flop6v (.q(q6b), .rst_n(rst6b_bad_n), .clk(clk), .d(d)); - - initial begin - $display("%%Error: Not a runnable test"); - $stop; - end - -endmodule - -module Flop ( - input clk, - input d, - input rst_n, - output logic q); - - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) q <= 1'b0; - else q <= d; - end -endmodule - -module Sub (input a, b, - output z); - assign z = a|b; -endmodule diff --git a/test_regress/t/t_cdc_async_debug_bad.out b/test_regress/t/t_cdc_async_debug_bad.out deleted file mode 100644 index 2a6956531..000000000 --- a/test_regress/t/t_cdc_async_debug_bad.out +++ /dev/null @@ -1,20 +0,0 @@ -Edge Report for Vt_cdc_async_debug_bad - t_cdc_async_bad.v: input clk SRC=@(*) DST=@(posedge clk or negedge rst0_n or negedge t.__Vcellinp__flop4__rst_n or negedge t.rst1_n or negedge t.rst2_bad_n or negedge t.rst5_waive_n or negedge t.rst6a_bad_n or negedge t.rst6b_bad_n) - t_cdc_async_bad.v: input d SRC=@(*) DST=@(posedge clk or negedge rst0_n or negedge t.__Vcellinp__flop4__rst_n or negedge t.rst1_n or negedge t.rst2_bad_n or negedge t.rst5_waive_n or negedge t.rst6a_bad_n or negedge t.rst6b_bad_n) - t_cdc_async_bad.v: input rst0_n SRC=@(*) DST=@(posedge clk or negedge rst0_n or negedge t.rst2_bad_n or negedge t.rst5_waive_n or negedge t.rst6a_bad_n or negedge t.rst6b_bad_n) - t_cdc_async_bad.v: output q0 SRC=@(posedge clk or negedge rst0_n) DST= - t_cdc_async_bad.v: output q1 SRC=@(posedge clk or negedge t.rst1_n) DST= - t_cdc_async_bad.v: output q2 SRC=@(posedge clk or negedge t.rst2_bad_n) DST= - t_cdc_async_bad.v: output q3 SRC=@(posedge clk or negedge t.rst2_bad_n) DST= - t_cdc_async_bad.v: output q4 SRC=@(posedge clk or negedge t.__Vcellinp__flop4__rst_n) DST= - t_cdc_async_bad.v: output q5 SRC=@(posedge clk or negedge t.rst5_waive_n) DST= - t_cdc_async_bad.v: output q6a SRC=@(posedge clk or negedge t.rst6a_bad_n) DST= - t_cdc_async_bad.v: output q6b SRC=@(posedge clk or negedge t.rst6b_bad_n) DST= - t_cdc_async_bad.v: wire t.__Vcellinp__flop4__rst_n SRC=@(posedge clk) DST=@(posedge clk or negedge t.__Vcellinp__flop4__rst_n) - t_cdc_async_bad.v: wire t.rst1_n SRC=@(posedge clk) DST=@(posedge clk or negedge t.rst1_n or negedge t.rst2_bad_n or negedge t.rst5_waive_n or negedge t.rst6a_bad_n or negedge t.rst6b_bad_n) - t_cdc_async_bad.v: wire t.rst2_bad_n SRC=@(* or posedge clk) DST=@(posedge clk or negedge t.rst2_bad_n) - t_cdc_async_bad.v: wire t.rst4_n SRC=@(posedge clk) DST=@(posedge clk or negedge t.__Vcellinp__flop4__rst_n) - t_cdc_async_bad.v: wire t.rst5_waive_n SRC=@(* or posedge clk) DST=@(posedge clk or negedge t.rst5_waive_n) - t_cdc_async_bad.v: wire t.rst6_bad_n SRC=@(* or posedge clk) DST=@(posedge clk or negedge t.rst6a_bad_n or negedge t.rst6b_bad_n) - t_cdc_async_bad.v: wire t.rst6a_bad_n SRC=@(* or posedge clk) DST=@(posedge clk or negedge t.rst6a_bad_n) - t_cdc_async_bad.v: wire t.rst6b_bad_n SRC=@(* or posedge clk) DST=@(posedge clk or negedge t.rst6b_bad_n) diff --git a/test_regress/t/t_cdc_async_debug_bad.pl b/test_regress/t/t_cdc_async_debug_bad.pl deleted file mode 100755 index 5634177a8..000000000 --- a/test_regress/t/t_cdc_async_debug_bad.pl +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env perl -if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } -# DESCRIPTION: Verilator: Verilog Test driver/expect definition -# -# Copyright 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 => 1); - -top_filename("t/t_cdc_async_bad.v"); - -compile( - # --debug so we get code coverage of Cdc - v_flags => ['--cdc --debug'], - verilator_make_gmake => 0, - make_top_shell => 0, - make_main => 0, - fails => 1, - ); - -files_identical("$Self->{obj_dir}/V$Self->{name}__cdc_edges.txt", $Self->{golden_filename}); - -ok(1); -1; diff --git a/test_regress/t/t_flag_noop_bad.out b/test_regress/t/t_flag_noop_bad.out index d7bd1b71c..74b28bf7a 100644 --- a/test_regress/t/t_flag_noop_bad.out +++ b/test_regress/t/t_flag_noop_bad.out @@ -1 +1 @@ -%Error: verilator: Need --binary, --cc, --sc, --cdc, --dpi-hdr-only, --lint-only, --xml-only or --E option +%Error: verilator: Need --binary, --cc, --sc, --dpi-hdr-only, --lint-only, --xml-only or --E option