diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 30dd5322c..78e29bdbe 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1171,6 +1171,31 @@ void AstNode::checkTreeIter(const AstNode* prevBackp) const VL_MT_STABLE { default: this->v3fatalSrc("Bad case"); break; } } + if (v3Global.opt.debugWidth() && v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH) { + if (const AstNodeExpr* const exprp = VN_CAST(this, NodeExpr)) { + const char* const whyp = exprp->widthMismatch(); + if (whyp) { + auto dtypeStr = [](const AstNodeExpr* exprp) VL_MT_STABLE { + std::ostringstream ss; + exprp->dtypep()->dumpSmall(ss); + return ss.str(); + }; + if (const AstNodeUniop* const uniopp = VN_CAST(exprp, NodeUniop)) { + UASSERT_OBJ(!whyp, uniopp, + "widthMismatch detected " << whyp << "OUT:" << dtypeStr(uniopp) + << " LHS:" << dtypeStr(uniopp->lhsp())); + } else if (const AstNodeBiop* const biopp = VN_CAST(exprp, NodeBiop)) { + UASSERT_OBJ(!whyp, biopp, + "widthMismatch detected " << whyp << "OUT:" << dtypeStr(biopp) + << " LHS:" << dtypeStr(biopp->lhsp()) + << " RHS:" << dtypeStr(biopp->rhsp())); + } else { + UASSERT_OBJ(false, exprp, + "widthMismatch detected " << whyp << " in an unexpected type"); + } + } + } + } } // cppcheck-suppress unusedFunction // Debug only diff --git a/src/V3Ast.h b/src/V3Ast.h index 0c8227236..86b1c266f 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2002,7 +2002,7 @@ public: // TODO stomp these width functions out, and call via dtypep() instead inline int width() const VL_MT_STABLE; - inline int widthMin() const; + inline int widthMin() const VL_MT_STABLE; int widthMinV() const { return v3Global.widthMinUsage() == VWidthMinUsage::VERILOG_WIDTH ? widthMin() : width(); } diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 9e61a7740..8a1e1f96f 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -26,7 +26,7 @@ // Inline METHODS int AstNode::width() const VL_MT_STABLE { return dtypep() ? dtypep()->width() : 0; } -int AstNode::widthMin() const { return dtypep() ? dtypep()->widthMin() : 0; } +int AstNode::widthMin() const VL_MT_STABLE { return dtypep() ? dtypep()->widthMin() : 0; } bool AstNode::width1() const { // V3Const uses to know it can optimize return dtypep() && dtypep()->width() == 1; } diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 56e2c014f..b6457f9f3 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -53,7 +53,7 @@ public: // ACCESSORS void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; - virtual void dumpSmall(std::ostream& str) const; + virtual void dumpSmall(std::ostream& str) const VL_MT_STABLE; bool hasDType() const override { return true; } /// Require VlUnpacked, instead of [] for POD elements. /// A non-POD object is always compound, but some POD elements diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 5c23b9fac..dcebf3f3d 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -65,6 +65,9 @@ public: // Wrap This expression into an AstStmtExpr to denote it occurs in statement position inline AstStmtExpr* makeStmt(); + // Returns an error message if widthMin() is not correct otherwise returns nullptr like + // broken() + virtual const char* widthMismatch() const VL_MT_STABLE { return nullptr; } }; class AstNodeBiop VL_NOT_FINAL : public AstNodeExpr { // Binary expression @@ -3830,6 +3833,7 @@ public: bool cleanRhs() const override { return false; } bool sizeMattersLhs() const override { return false; } bool sizeMattersRhs() const override { return false; } + const char* widthMismatch() const override VL_MT_STABLE; }; class AstMul final : public AstNodeBiComAsv { public: @@ -3924,6 +3928,7 @@ public: bool cleanRhs() const override { return false; } bool sizeMattersLhs() const override { return false; } bool sizeMattersRhs() const override { return false; } + const char* widthMismatch() const override VL_MT_STABLE; }; class AstXor final : public AstNodeBiComAsv { public: @@ -3946,6 +3951,7 @@ public: bool cleanRhs() const override { return false; } bool sizeMattersLhs() const override { return false; } bool sizeMattersRhs() const override { return false; } + const char* widthMismatch() const override VL_MT_STABLE; }; // === AstNodeDistBiop === @@ -5082,6 +5088,7 @@ public: bool cleanOut() const override { return false; } bool cleanLhs() const override { return false; } bool sizeMattersLhs() const override { return true; } + const char* widthMismatch() const override VL_MT_STABLE; }; class AstNullCheck final : public AstNodeUniop { // Return LHS after checking that LHS is non-null diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index b6c3aa1bf..5c269915f 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1916,7 +1916,7 @@ void AstNodeDType::dumpJson(std::ostream& str) const { dumpJsonBoolFunc(str, generic); dumpJsonGen(str); } -void AstNodeDType::dumpSmall(std::ostream& str) const { +void AstNodeDType::dumpSmall(std::ostream& str) const VL_MT_STABLE { str << "(" << (generic() ? "G/" : "") << ((isSigned() && !isDouble()) ? "s" : "") << (isNosign() ? "n" : "") << (isDouble() ? "d" : "") << (isString() ? "str" : ""); if (!isDouble() && !isString()) str << "w" << (widthSized() ? "" : "u") << width(); @@ -2757,3 +2757,22 @@ void AstDelay::dumpJson(std::ostream& str) const { dumpJsonBoolFunc(str, isCycleDelay); dumpJsonGen(str); } +const char* AstAnd::widthMismatch() const VL_MT_STABLE { + BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin()); + BROKEN_RTN(lhsp()->widthMin() != widthMin()); + return nullptr; +} +const char* AstOr::widthMismatch() const VL_MT_STABLE { + BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin()); + BROKEN_RTN(lhsp()->widthMin() != widthMin()); + return nullptr; +} +const char* AstXor::widthMismatch() const VL_MT_STABLE { + BROKEN_RTN(lhsp()->widthMin() != rhsp()->widthMin()); + BROKEN_RTN(lhsp()->widthMin() != widthMin()); + return nullptr; +} +const char* AstNot::widthMismatch() const VL_MT_STABLE { + BROKEN_RTN(lhsp()->widthMin() != widthMin()); + return nullptr; +} diff --git a/src/V3Global.h b/src/V3Global.h index 1c387ab60..8fcb31582 100644 --- a/src/V3Global.h +++ b/src/V3Global.h @@ -75,16 +75,17 @@ public: VWidthMinUsage() : m_e{LINT_WIDTH} {} // cppcheck-suppress noExplicitConstructor - constexpr VWidthMinUsage(en _e) - : m_e{_e} {} + constexpr VWidthMinUsage(en _e) VL_PURE : m_e{_e} {} + constexpr VWidthMinUsage(const VWidthMinUsage& _e) VL_PURE = default; explicit VWidthMinUsage(int _e) : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning constexpr operator en() const { return m_e; } + constexpr VWidthMinUsage& operator=(const VWidthMinUsage& _e) VL_PURE = default; }; constexpr bool operator==(const VWidthMinUsage& lhs, const VWidthMinUsage& rhs) { return lhs.m_e == rhs.m_e; } -constexpr bool operator==(const VWidthMinUsage& lhs, VWidthMinUsage::en rhs) { +constexpr bool operator==(const VWidthMinUsage& lhs, VWidthMinUsage::en rhs) VL_PURE { return lhs.m_e == rhs; } constexpr bool operator==(VWidthMinUsage::en lhs, const VWidthMinUsage& rhs) { @@ -140,7 +141,7 @@ public: // ACCESSORS (general) AstNetlist* rootp() const VL_MT_SAFE { return m_rootp; } - VWidthMinUsage widthMinUsage() const { return m_widthMinUsage; } + VWidthMinUsage widthMinUsage() const VL_PURE { return m_widthMinUsage; } bool assertDTypesResolved() const { return m_assertDTypesResolved; } bool assertScoped() const { return m_assertScoped; } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index fc47d3cbd..f0daaef6c 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1197,6 +1197,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-debug-self-test", OnOff, &m_debugSelfTest).undocumented(); DECL_OPTION("-debug-sigsegv", CbCall, throwSigsegv).undocumented(); // See also --debug-abort DECL_OPTION("-debug-stack-check", OnOff, &m_debugStackCheck).undocumented(); + DECL_OPTION("-debug-width", OnOff, &m_debugWidth).undocumented(); DECL_OPTION("-decoration", CbCall, [this, fl]() { decorations(fl, "medium"); }); DECL_OPTION("-decorations", CbVal, [this, fl](const char* optp) { decorations(fl, optp); }); DECL_OPTION("-no-decoration", CbCall, [this, fl]() { decorations(fl, "none"); }); diff --git a/src/V3Options.h b/src/V3Options.h index 99ba731d1..ef0a7b627 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -245,6 +245,7 @@ private: bool m_debugProtect = false; // main switch: --debug-protect bool m_debugSelfTest = false; // main switch: --debug-self-test bool m_debugStackCheck = false; // main switch: --debug-stack-check + bool m_debugWidth = false; // main switch: --debug-width bool m_decoration = true; // main switch: --decoration bool m_decorationNodes = false; // main switch: --decoration=nodes bool m_dpiHdrOnly = false; // main switch: --dpi-hdr-only @@ -483,6 +484,7 @@ public: bool debugProtect() const VL_MT_SAFE { return m_debugProtect; } bool debugSelfTest() const { return m_debugSelfTest; } bool debugStackCheck() const { return m_debugStackCheck; } + bool debugWidth() const VL_PURE { return m_debugWidth; } bool decoration() const VL_MT_SAFE { return m_decoration; } bool decorationNodes() const VL_MT_SAFE { return m_decorationNodes; } bool dpiHdrOnly() const { return m_dpiHdrOnly; } diff --git a/test_regress/t/t_debug_width.out b/test_regress/t/t_debug_width.out new file mode 100644 index 000000000..0a1f7b907 --- /dev/null +++ b/test_regress/t/t_debug_width.out @@ -0,0 +1,4 @@ +%Error: Internal Error: t/t_const_opt.v:531:34: ../V3Ast.cpp:#: widthMismatch detected 'lhsp()->widthMin() != rhsp()->widthMin()' @ ../V3AstNodes.cpp:#OUT:(G/wu32/1) LHS:(G/w32) RHS:(G/wu32/1) + 531 | always_ff @(posedge clkin_data[0], posedge myfirst, posedge mysecond) + | ^ + ... See the manual at https://verilator.org/verilator_doc.html for more assistance. diff --git a/test_regress/t/t_debug_width.pl b/test_regress/t/t_debug_width.pl new file mode 100755 index 000000000..b4ae3a10c --- /dev/null +++ b/test_regress/t/t_debug_width.pl @@ -0,0 +1,21 @@ +#!/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 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 + +top_filename("t/t_const_opt.v"); +scenarios(simulator => 1); + +lint( + verilator_flags2 => ["--lint-only", "--debug-width"], + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1;