From d2b7b567dfa55d5bbf35d7514a530df404f598ec Mon Sep 17 00:00:00 2001 From: Todd Strader Date: Wed, 19 Mar 2025 19:01:31 -0400 Subject: [PATCH] Fix expression coverage on additional nodes (#5849 partial) (#5867) --- src/V3Ast.h | 2 + src/V3AstNodeExpr.h | 1 + src/V3Coverage.cpp | 61 +++++++++++++++++++++------ test_regress/t/t_cover_expr.out | 6 +++ test_regress/t/t_cover_expr.v | 6 +++ test_regress/t/t_cover_expr_max.out | 6 +++ test_regress/t/t_cover_expr_trace.out | 6 +++ 7 files changed, 74 insertions(+), 14 deletions(-) diff --git a/src/V3Ast.h b/src/V3Ast.h index 6838be5ae..58d61d085 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2486,6 +2486,8 @@ public: virtual bool isGateOptimizable() const { return !isTimingControl(); } // GateDedupable is a slightly larger superset of GateOptimzable (eg, AstNodeIf) virtual bool isGateDedupable() const { return isGateOptimizable(); } + // Whether the node can be used in expression coverage + virtual bool isExprCoverageEligible() const { return isGateDedupable(); } // Else creates output or exits, etc, not unconsumed virtual bool isOutputter() { return false; } // Else a AstTime etc which output can't be predicted from input diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 4bf4f2731..25b99a86c 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1600,6 +1600,7 @@ public: string name() const override VL_MT_STABLE { return m_name; } // * = Var name void name(const string& name) override { m_name = name; } bool index() const { return m_index; } + bool isExprCoverageEligible() const override { return false; } }; class AstMemberSel final : public AstNodeExpr { // @astgen op1 := fromp : AstNodeExpr diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 4477a92c0..d46807be0 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -35,6 +35,23 @@ VL_DEFINE_DEBUG_FUNCTIONS; +class ExprCoverageEligibleVisitor final : public VNVisitor { + // STATE + bool m_eligible = true; + + void visit(AstNode* nodep) override { + if (!nodep->isExprCoverageEligible()) { m_eligible = false; } + iterateChildren(nodep); + } + +public: + // CONSTRUCTORS + explicit ExprCoverageEligibleVisitor(AstNode* nodep) { iterateChildren(nodep); } + ~ExprCoverageEligibleVisitor() override = default; + + bool eligible() { return m_eligible; } +}; + //###################################################################### // Coverage state, as a visitor of each AstNode @@ -669,6 +686,7 @@ class CoverageVisitor final : public VNVisitor { m_objective = true; iterate(nodep); if (checkMaxExprs(falseExprs.size())) return; + if (m_seeking == ABORTED) return; addExprCoverInc(nodep); const int start = m_exprs.size(); @@ -872,26 +890,30 @@ class CoverageVisitor final : public VNVisitor { lineTrack(nodep); } - // Lambdas not supported for expression coverage - void visit(AstWith* nodep) override { - VL_RESTORER(m_seeking); - if (m_seeking == SEEKING) abortExprCoverage(); - m_seeking = ABORTED; - iterateChildren(nodep); - lineTrack(nodep); + void visit(AstFuncRef* nodep) override { + if (nodep->taskp()->lifetime().isAutomatic()) { + visit(static_cast(nodep)); + } else { + exprUnsupported(nodep, "non-automatic function"); + } } void visit(AstNodeExpr* nodep) override { if (m_seeking != SEEKING) { iterateChildren(nodep); } else { - std::stringstream emitV; - V3EmitV::verilogForTree(nodep, emitV); - // Add new expression with a single term - CoverExpr expr; - expr.emplace_back(nodep, m_objective, emitV.str()); - m_exprs.push_back(std::move(expr)); - checkMaxExprs(); + ExprCoverageEligibleVisitor elgibleVisitor(nodep); + if (elgibleVisitor.eligible()) { + std::stringstream emitV; + V3EmitV::verilogForTree(nodep, emitV); + // Add new expression with a single term + CoverExpr expr; + expr.emplace_back(nodep, m_objective, emitV.str()); + m_exprs.push_back(std::move(expr)); + checkMaxExprs(); + } else { + exprUnsupported(nodep, "not coverage eligible"); + } } lineTrack(nodep); } @@ -902,6 +924,17 @@ class CoverageVisitor final : public VNVisitor { lineTrack(nodep); } + void exprUnsupported(AstNode* nodep, const string& why) { + UINFO(9, "unsupported: " << why << " " << nodep << endl); + bool wasSeeking = m_seeking == SEEKING; + Objective oldSeeking = m_seeking; + if (wasSeeking) { abortExprCoverage(); } + m_seeking = ABORTED; + iterateChildren(nodep); + lineTrack(nodep); + if (!wasSeeking) { m_seeking = oldSeeking; } + } + public: // CONSTRUCTORS explicit CoverageVisitor(AstNetlist* rootp) { iterateChildren(rootp); } diff --git a/test_regress/t/t_cover_expr.out b/test_regress/t/t_cover_expr.out index 0a0f3ff76..4afc39500 100644 --- a/test_regress/t/t_cover_expr.out +++ b/test_regress/t/t_cover_expr.out @@ -5,6 +5,10 @@ // any use, without warranty, 2024 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 + class cls; + rand int x; + endclass + module t (/*AUTOARG*/ // Inputs clk @@ -284,6 +288,7 @@ logic ta, tb, tc; initial begin + cls obj = new; int q[5]; int qv[$]; @@ -304,6 +309,7 @@ tb = ta; ta = '0; end + if (!bit'(obj.randomize() with {x < 100;})) $write(""); end sub the_sub_1 (.p(t1), .q(t2)); diff --git a/test_regress/t/t_cover_expr.v b/test_regress/t/t_cover_expr.v index 95cbe881e..59521677a 100644 --- a/test_regress/t/t_cover_expr.v +++ b/test_regress/t/t_cover_expr.v @@ -4,6 +4,10 @@ // any use, without warranty, 2024 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 +class cls; + rand int x; +endclass + module t (/*AUTOARG*/ // Inputs clk @@ -124,6 +128,7 @@ module t (/*AUTOARG*/ logic ta, tb, tc; initial begin + cls obj = new; int q[5]; int qv[$]; @@ -140,6 +145,7 @@ module t (/*AUTOARG*/ tb = ta; ta = '0; end + if (!bit'(obj.randomize() with {x < 100;})) $write(""); end sub the_sub_1 (.p(t1), .q(t2)); diff --git a/test_regress/t/t_cover_expr_max.out b/test_regress/t/t_cover_expr_max.out index d01d1a412..5d71fc251 100644 --- a/test_regress/t/t_cover_expr_max.out +++ b/test_regress/t/t_cover_expr_max.out @@ -5,6 +5,10 @@ // any use, without warranty, 2024 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 + class cls; + rand int x; + endclass + module t (/*AUTOARG*/ // Inputs clk @@ -412,6 +416,7 @@ logic ta, tb, tc; initial begin + cls obj = new; int q[5]; int qv[$]; @@ -432,6 +437,7 @@ tb = ta; ta = '0; end + if (!bit'(obj.randomize() with {x < 100;})) $write(""); end sub the_sub_1 (.p(t1), .q(t2)); diff --git a/test_regress/t/t_cover_expr_trace.out b/test_regress/t/t_cover_expr_trace.out index 0a0f3ff76..4afc39500 100644 --- a/test_regress/t/t_cover_expr_trace.out +++ b/test_regress/t/t_cover_expr_trace.out @@ -5,6 +5,10 @@ // any use, without warranty, 2024 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 + class cls; + rand int x; + endclass + module t (/*AUTOARG*/ // Inputs clk @@ -284,6 +288,7 @@ logic ta, tb, tc; initial begin + cls obj = new; int q[5]; int qv[$]; @@ -304,6 +309,7 @@ tb = ta; ta = '0; end + if (!bit'(obj.randomize() with {x < 100;})) $write(""); end sub the_sub_1 (.p(t1), .q(t2));