diff --git a/bin/verilator b/bin/verilator index e2c3c343a..0285e2df7 100755 --- a/bin/verilator +++ b/bin/verilator @@ -328,6 +328,8 @@ detailed descriptions of these arguments. --compiler-include Include additional header in the precompiled one --converge-limit Tune convergence settle time --coverage Enable all coverage + --coverage-expr Enable expression coverage + --coverage-expr-max Maximum permutations allowed for an expression --coverage-line Enable line coverage --coverage-max-width Maximum array depth for coverage --coverage-toggle Enable toggle coverage diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 61a99b23e..7f031b4cc 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -237,7 +237,17 @@ Summary: .. option:: --coverage Enables all forms of coverage, an alias for :vlopt:`--coverage-line` - :vlopt:`--coverage-toggle` :vlopt:`--coverage-user`. + :vlopt:`--coverage-toggle :vlopt:`--coverage-expr`` :vlopt:`--coverage-user`. + +.. option:: --coverage-expr + + Enables expression coverage analysis. See :ref:`Expression Coverage`. + +.. option:: --coverage-expr-max + + Rarely needed. Specifies the maximum number of permutations able to be + covered for a given expression. Defaults to 32. Increasing may slow + coverage simulations and make analyzing the results unwieldy. .. option:: --coverage-line diff --git a/docs/guide/exe_verilator_coverage.rst b/docs/guide/exe_verilator_coverage.rst index c29f9d329..23caac867 100644 --- a/docs/guide/exe_verilator_coverage.rst +++ b/docs/guide/exe_verilator_coverage.rst @@ -58,7 +58,7 @@ to read multiple inputs. If no data file is specified, by default, Specifies the directory name to which source files with annotated coverage data should be written. -Points are children of each line coverage- branches or toggle points. +Points are children of each line coverage- branches, expressions or toggle points. When point counts are aggregated into a line, the minimum and maximum counts are used to determine the status of the line (complete, partial, failing) The count is equal to the maximum of the points. diff --git a/docs/guide/simulating.rst b/docs/guide/simulating.rst index 1048199b4..bee16e38a 100644 --- a/docs/guide/simulating.rst +++ b/docs/guide/simulating.rst @@ -270,6 +270,41 @@ A :option:`/*verilator&32;coverage_off*/` signals that do not need toggle analysis, such as RAMs and register files. +.. _Expression Coverage: + +Expression Coverage +------------- + +With :vlopt:`--coverage` or :vlopt:`--coverage-expr`, Verilator will +automatically add coverage analysis at each expression, indicating with +a truth table how every Boolean truth-table possiblity in the expression occurred. + +Multi-bit expressions are ignored, but sub-expressions with are entirely +Boolean are analyzed. Expression coverage does not fully explore the truth +table of an expression, rather is looks at each term's contribution. E.g. +an AND operation will check coverage for TT, XF and FX. + +Some expressions may produce too many cover points. Verilator limits the +maximum number of cover poitns per expression to 32, but this may be +controlled with :vlopt:`--coverage-expr-max`. + +Below is an example showing expression coverage produced from `verilator_coverage` +as applied to the condition expression inside an if statement. Each line +shows the minimum number of terms and their values (e.g. `(t1==0 && t2==1)`) needed +to reach a result for the overall expression (e.g. `=> 1`). Each line also +shows the number of times this combination was hit. Note that individual lines +are not mutually exclusive. + +.. code-block:: + + %000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); + -000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t + -000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t + -000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t + -000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t + -000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t + -000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t + .. _Suppressing Coverage: Suppressing Coverage diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index afb674551..2f81cdcf9 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -28,6 +28,9 @@ #include "V3Coverage.h" +#include "V3EmitV.h" + +#include #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -39,6 +42,18 @@ class CoverageVisitor final : public VNVisitor { // TYPES using LinenoSet = std::set; + struct CoverTerm final { + AstNodeExpr* m_exprp; // Expression branch term + bool m_objective; // Term objective + std::string m_emitV; // V3EmitV string for cover point comment + CoverTerm(AstNodeExpr* exprp, bool objective, const string& emitV) + : m_exprp{exprp} + , m_objective{objective} + , m_emitV(emitV) {} + }; + using CoverExpr = std::deque; + using CoverExprs = std::list; + struct ToggleEnt final { const string m_comment; // Comment for coverage dump AstNodeExpr* m_varRefp; // How to get to this element @@ -64,12 +79,20 @@ class CoverageVisitor final : public VNVisitor { return m_on && !m_inModOff && nodep->fileline()->coverageOn() && v3Global.opt.coverageLine(); } + bool exprCoverageOn(const AstNode* nodep) const { + return m_on && !m_inModOff && nodep->fileline()->coverageOn() + && v3Global.opt.coverageExpr(); + } }; + enum Objective : uint8_t { NONE, SEEKING, ABORTED }; + // NODE STATE // Entire netlist: // AstIf::user1() -> bool. True indicates ifelse processed + // AstIf::user2() -> bool. True indicates coverage-generated const VNUser1InUse m_inuser1; + const VNUser2InUse m_inuser2; // STATE - across all visitors int m_nextHandle = 0; @@ -77,6 +100,13 @@ class CoverageVisitor final : public VNVisitor { // STATE - for current visit position (use VL_RESTORER) CheckState m_state; // State save-restored on each new coverage scope/block AstNodeModule* m_modp = nullptr; // Current module to add statement to + AstNode* m_exprStmtsp = nullptr; // Node to add expr coverage to + bool m_then = false; // Whether we're iterating the then or else branch + // when m_exprStmtps is an AstIf + CoverExprs m_exprs; // List of expressions that can reach objective + Objective m_seeking = NONE; // Seeking objective for expression coverage + bool m_objective = false; // Expression objective + bool m_ifCond = false; // Visiting if condition bool m_inToggleOff = false; // In function/task etc string m_beginHier; // AstBegin hier name for user coverage points @@ -160,7 +190,7 @@ class CoverageVisitor final : public VNVisitor { UINFO(9, "line create h" << m_state.m_handle << " " << nodep << endl); } void lineTrack(const AstNode* nodep) { - if (m_state.lineCoverageOn(nodep) + if (m_state.lineCoverageOn(nodep) && !m_ifCond && m_state.m_nodep->fileline()->filenameno() == nodep->fileline()->filenameno()) { for (int lineno = nodep->fileline()->firstLineno(); lineno <= nodep->fileline()->lastLineno(); ++lineno) { @@ -222,13 +252,36 @@ class CoverageVisitor final : public VNVisitor { } void visit(AstNodeProcedure* nodep) override { iterateProcedure(nodep); } - void visit(AstWhile* nodep) override { iterateProcedure(nodep); } + // we can cover expressions in while loops, but the counting goes outside + // the while, see: "minimally-intelligent decision about ... clock domain" + // in the Toggle Coverage docs + void visit(AstWhile* nodep) override { iterateProcedure(nodep, false); } void visit(AstNodeFTask* nodep) override { if (!nodep->dpiImport()) iterateProcedure(nodep); } - void iterateProcedure(AstNode* nodep) { + + void insertProcStatement(AstNode* nodep, AstNode* stmtp) { + if (AstNodeProcedure* const itemp = VN_CAST(nodep, NodeProcedure)) { + itemp->addStmtsp(stmtp); + } else if (AstNodeFTask* const itemp = VN_CAST(nodep, NodeFTask)) { + itemp->addStmtsp(stmtp); + } else if (AstWhile* const itemp = VN_CAST(nodep, While)) { + itemp->addStmtsp(stmtp); + } else if (AstIf* const itemp = VN_CAST(nodep, If)) { + if (m_then) { + itemp->addThensp(stmtp); + } else { + itemp->addElsesp(stmtp); + } + } else { + nodep->v3fatalSrc("Bad node type"); + } + } + void iterateProcedure(AstNode* nodep, bool exprProc = true) { VL_RESTORER(m_state); + VL_RESTORER(m_exprStmtsp); VL_RESTORER(m_inToggleOff); + if (exprProc) m_exprStmtsp = nodep; m_inToggleOff = true; createHandle(nodep); iterateChildren(nodep); @@ -237,15 +290,7 @@ class CoverageVisitor final : public VNVisitor { AstNode* const newp = newCoverInc(nodep->fileline(), "", "v_line", "block", linesCov(m_state, nodep), 0, traceNameForLine(nodep, "block")); - if (AstNodeProcedure* const itemp = VN_CAST(nodep, NodeProcedure)) { - itemp->addStmtsp(newp); - } else if (AstNodeFTask* const itemp = VN_CAST(nodep, NodeFTask)) { - itemp->addStmtsp(newp); - } else if (AstWhile* const itemp = VN_CAST(nodep, While)) { - itemp->addStmtsp(newp); - } else { - nodep->v3fatalSrc("Bad node type"); - } + insertProcStatement(nodep, newp); } } @@ -404,6 +449,8 @@ class CoverageVisitor final : public VNVisitor { // VISITORS - LINE COVERAGE // Note not AstNodeIf; other types don't get covered void visit(AstIf* nodep) override { + if (nodep->user2()) return; + UINFO(4, " IF: " << nodep << endl); if (m_state.m_on) { // An else-if. When we iterate the if, use "elsif" marking @@ -422,6 +469,10 @@ class CoverageVisitor final : public VNVisitor { CheckState ifState; CheckState elseState; { + VL_RESTORER(m_exprStmtsp); + VL_RESTORER(m_then); + m_exprStmtsp = nodep; + m_then = true; createHandle(nodep); iterateAndNextNull(nodep->thensp()); lineTrack(nodep); @@ -429,6 +480,10 @@ class CoverageVisitor final : public VNVisitor { } m_state = lastState; { + VL_RESTORER(m_exprStmtsp); + VL_RESTORER(m_then); + m_exprStmtsp = nodep; + m_then = false; createHandle(nodep); iterateAndNextNull(nodep->elsesp()); elseState = m_state; @@ -478,6 +533,9 @@ class CoverageVisitor final : public VNVisitor { } m_state = lastState; } + VL_RESTORER(m_ifCond); + m_ifCond = true; + iterateAndNextNull(nodep->condp()); UINFO(9, " done HANDLE " << m_state.m_handle << " for " << nodep << endl); } void visit(AstCaseItem* nodep) override { @@ -542,6 +600,301 @@ class CoverageVisitor final : public VNVisitor { lineTrack(nodep); } + void abortExprCoverage() { + // is possible to hit max while in NONE, see: exprReduce() + // if that happens we don't want to set ABORTED if it isn't already + // since that will bleed into other expressions + if (m_seeking != NONE) m_seeking = ABORTED; + m_exprs.clear(); + } + + bool checkMaxExprs(size_t additional = 0) { + if (m_seeking != ABORTED + && static_cast(m_exprs.size() + additional) <= v3Global.opt.coverageExprMax()) + return false; + abortExprCoverage(); + return true; + } + + void addExprCoverInc(AstNodeExpr* nodep, int start = 0) { + FileLine* const fl = nodep->fileline(); + int count = start; + for (CoverExpr& expr : m_exprs) { + const string name = "expr_" + std::to_string(count); + string comment = "("; + bool first = true; + AstNodeExpr* condp = nullptr; + for (CoverTerm& term : expr) { + comment += (first ? "" : " && ") + term.m_emitV + + "==" + (term.m_objective ? "1" : "0"); + AstNodeExpr* const clonep = term.m_exprp->cloneTree(true); + AstNodeExpr* const termp = term.m_objective ? clonep : new AstLogNot{fl, clonep}; + if (condp) { + condp = new AstLogAnd{fl, condp, termp}; + } else { + condp = termp; + } + first = false; + } + comment += ") => "; + comment += (m_objective ? '1' : '0'); + AstNode* const newp + = newCoverInc(fl, "", "v_expr", comment, "", 0, traceNameForLine(nodep, name)); + UASSERT_OBJ(condp, nodep, "No terms in expression coverage branch"); + AstIf* const ifp = new AstIf{fl, condp, newp, nullptr}; + ifp->user2(true); + insertProcStatement(m_exprStmtsp, ifp); + ++count; + } + } + + void coverExprs(AstNodeExpr* nodep) { + if (!m_state.exprCoverageOn(nodep) || nodep->dtypep()->width() != 1 || !m_exprStmtsp) { + return; + } + + UASSERT_OBJ(m_seeking == NONE, nodep, "recursively covering expressions is not expected"); + UASSERT_OBJ(m_exprs.empty(), nodep, "unexpected expression coverage garbage"); + VL_RESTORER(m_seeking); + VL_RESTORER(m_objective); + VL_RESTORER(m_exprs); + + m_seeking = SEEKING; + m_objective = false; + iterate(nodep); + CoverExprs falseExprs; + m_exprs.swap(falseExprs); + + m_objective = true; + iterate(nodep); + if (checkMaxExprs(falseExprs.size())) return; + + addExprCoverInc(nodep); + const int start = m_exprs.size(); + m_objective = false; + m_exprs.swap(falseExprs); + addExprCoverInc(nodep, start); + } + + void exprEither(AstNodeBiop* nodep, bool overrideObjective = false, bool lObjective = false, + bool rObjective = false) { + VL_RESTORER(m_objective); + AstNodeExpr* const lhsp = nodep->lhsp(); + AstNodeExpr* const rhsp = nodep->rhsp(); + + if (overrideObjective) m_objective = lObjective; + iterate(lhsp); + if (checkMaxExprs()) return; + CoverExprs lhsExprs; + m_exprs.swap(lhsExprs); + if (overrideObjective) m_objective = rObjective; + iterate(rhsp); + m_exprs.splice(m_exprs.end(), lhsExprs); + checkMaxExprs(); + } + + void exprBoth(AstNodeBiop* nodep, bool overrideObjective = false, bool lObjective = false, + bool rObjective = false) { + VL_RESTORER(m_objective); + AstNodeExpr* const lhsp = nodep->lhsp(); + AstNodeExpr* const rhsp = nodep->rhsp(); + + if (overrideObjective) m_objective = lObjective; + iterate(lhsp); + if (checkMaxExprs()) return; + CoverExprs lhsExprs; + m_exprs.swap(lhsExprs); + if (overrideObjective) m_objective = rObjective; + iterate(rhsp); + if (checkMaxExprs()) return; + CoverExprs rhsExprs; + m_exprs.swap(rhsExprs); + + for (CoverExpr& l : lhsExprs) { + for (CoverExpr& r : rhsExprs) { + // array size 2 -> (false, true) + std::array, 2> varps; + std::array, 2> strs; + + UASSERT_OBJ(!l.empty() && !r.empty(), nodep, "Empty coverage expression branch"); + CoverExpr expr; + + // Compare Vars for simple VarRefs otherwise compare stringified terms + // remove redundant terms and remove entire expression branches when + // terms conflict + // Equivalent terms which don't match on either of these criteria will + // not be flagged as redundant or impossible, however the results will + // still be valid, albeit messier + for (CoverTerm& term : l) { + if (AstNodeVarRef* const refp = VN_CAST(term.m_exprp, NodeVarRef)) { + varps[term.m_objective].insert(refp->varp()); + } else { + strs[term.m_objective].insert(term.m_emitV); + } + expr.push_back(term); + } + bool impossible = false; + for (CoverTerm& term : r) { + bool redundant = false; + if (AstNodeVarRef* const refp = VN_CAST(term.m_exprp, NodeVarRef)) { + if (varps[term.m_objective].find(refp->varp()) + != varps[term.m_objective].end()) + redundant = true; + if (varps[!term.m_objective].find(refp->varp()) + != varps[!term.m_objective].end()) + impossible = true; + } else { + if (strs[term.m_objective].find(term.m_emitV) + != strs[term.m_objective].end()) + redundant = true; + if (strs[!term.m_objective].find(term.m_emitV) + != strs[!term.m_objective].end()) + impossible = true; + } + + if (!redundant) expr.push_back(term); + } + if (!impossible) m_exprs.push_back(std::move(expr)); + if (checkMaxExprs()) return; + } + } + } + + void orExpr(AstNodeBiop* nodep) { + if (m_seeking == NONE) { + coverExprs(nodep); + } else if (m_objective) { + exprEither(nodep); + } else { + exprBoth(nodep); + } + lineTrack(nodep); + } + void visit(AstLogOr* nodep) override { orExpr(nodep); } + void visit(AstOr* nodep) override { orExpr(nodep); } + + void andExpr(AstNodeBiop* nodep) { + if (m_seeking == NONE) { + coverExprs(nodep); + } else if (m_objective) { + exprBoth(nodep); + } else { + exprEither(nodep); + } + lineTrack(nodep); + } + void visit(AstLogAnd* nodep) override { andExpr(nodep); } + void visit(AstAnd* nodep) override { andExpr(nodep); } + + void xorExpr(AstNodeBiop* nodep) { + if (m_seeking == NONE) { + coverExprs(nodep); + } else { + for (const bool lObjective : {false, true}) { + CoverExprs prevExprs; + m_exprs.swap(prevExprs); + const bool rObjective = lObjective ^ m_objective; + exprBoth(nodep, true, lObjective, rObjective); + m_exprs.splice(m_exprs.end(), prevExprs); + if (checkMaxExprs()) break; + } + } + lineTrack(nodep); + } + void visit(AstXor* nodep) override { xorExpr(nodep); } + + void exprNot(AstNodeExpr* nodep) { + VL_RESTORER(m_objective); + if (m_seeking == NONE) { + coverExprs(nodep); + } else { + m_objective = !m_objective; + iterateChildren(nodep); + lineTrack(nodep); + } + } + void visit(AstNot* nodep) override { exprNot(nodep); } + void visit(AstLogNot* nodep) override { exprNot(nodep); } + + template + void exprReduce(AstNodeUniop* nodep) { + if (m_seeking != ABORTED) { + FileLine* const fl = nodep->fileline(); + AstNodeExpr* const lhsp = nodep->lhsp(); + const int width = lhsp->dtypep()->width(); + const size_t expected = std::is_same::value ? 0x1 << width : width + 1; + if (checkMaxExprs(expected)) return; + AstNodeExpr* unrolledp = new AstSel{fl, lhsp->cloneTree(true), + new AstConst{fl, static_cast(width - 1)}, + new AstConst{fl, 1}}; + for (int bit = width - 2; bit >= 0; bit--) { + AstSel* const selp = new AstSel{fl, lhsp->cloneTree(true), + new AstConst{fl, static_cast(bit)}, + new AstConst{fl, 1}}; + unrolledp = new T_Oper{fl, selp, unrolledp}; + } + iterate(unrolledp); + pushDeletep(unrolledp); + } else { + iterateChildren(nodep); + lineTrack(nodep); + } + } + void visit(AstRedOr* nodep) override { exprReduce(nodep); } + void visit(AstRedAnd* nodep) override { exprReduce(nodep); } + void visit(AstRedXor* nodep) override { exprReduce(nodep); } + + void visit(AstLogIf* nodep) override { + if (m_seeking == NONE) { + coverExprs(nodep); + } else if (m_objective) { + exprEither(nodep, true, false, true); + } else { + exprBoth(nodep, true, true, false); + } + lineTrack(nodep); + } + + void visit(AstLogEq* nodep) override { + VL_RESTORER(m_objective); + if (m_seeking == NONE) { + coverExprs(nodep); + } else { + m_objective = !m_objective; + xorExpr(nodep); + lineTrack(nodep); + } + } + + void visit(AstCond* nodep) override { + if (m_seeking == NONE) coverExprs(nodep->condp()); + 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(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(); + } + lineTrack(nodep); + } + // VISITORS - BOTH void visit(AstNode* nodep) override { iterateChildren(nodep); diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 1e2024d37..dd0176c84 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1238,6 +1238,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-compiler-include", CbVal, callStrSetter(&V3Options::addCompilerIncludes)); DECL_OPTION("-converge-limit", Set, &m_convergeLimit); DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); }); + DECL_OPTION("-coverage-expr", OnOff, &m_coverageExpr); + DECL_OPTION("-coverage-expr-max", Set, &m_coverageExprMax); DECL_OPTION("-coverage-line", OnOff, &m_coverageLine); DECL_OPTION("-coverage-max-width", Set, &m_coverageMaxWidth); DECL_OPTION("-coverage-toggle", OnOff, &m_coverageToggle); diff --git a/src/V3Options.h b/src/V3Options.h index 9ec3918de..2e3c37da8 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -230,6 +230,7 @@ private: bool m_build = false; // main switch: --build bool m_cmake = false; // main switch: --make cmake bool m_context = true; // main switch: --Wcontext + bool m_coverageExpr = false; // main switch: --coverage-expr bool m_coverageLine = false; // main switch: --coverage-block bool m_coverageToggle = false; // main switch: --coverage-toggle bool m_coverageUnderscore = false; // main switch: --coverage-underscore @@ -305,6 +306,7 @@ private: bool m_xmlOnly = false; // main switch: --xml-only int m_buildJobs = -1; // main switch: --build-jobs, -j + int m_coverageExprMax = 32; // main switch: --coverage-expr-max int m_convergeLimit = 100; // main switch: --converge-limit int m_coverageMaxWidth = 256; // main switch: --coverage-max-width int m_expandLimit = 64; // main switch: --expand-limit @@ -426,7 +428,9 @@ private: void addLibExtV(const string& libext); void optimize(int level); void showVersion(bool verbose); - void coverage(bool flag) { m_coverageLine = m_coverageToggle = m_coverageUser = flag; } + void coverage(bool flag) { + m_coverageLine = m_coverageToggle = m_coverageExpr = m_coverageUser = flag; + } static bool suffixed(const string& sw, const char* arg); static string parseFileArg(const string& optdir, const string& relfilename); string filePathCheckOneDir(const string& modname, const string& dirname); @@ -488,8 +492,9 @@ public: bool cmake() const { return m_cmake; } bool context() const VL_MT_SAFE { return m_context; } bool coverage() const VL_MT_SAFE { - return m_coverageLine || m_coverageToggle || m_coverageUser; + return m_coverageLine || m_coverageToggle || m_coverageExpr || m_coverageUser; } + bool coverageExpr() const { return m_coverageExpr; } bool coverageLine() const { return m_coverageLine; } bool coverageToggle() const { return m_coverageToggle; } bool coverageUnderscore() const { return m_coverageUnderscore; } @@ -565,6 +570,7 @@ public: int buildJobs() const VL_MT_SAFE { return m_buildJobs; } int convergeLimit() const { return m_convergeLimit; } + int coverageExprMax() const { return m_coverageExprMax; } int coverageMaxWidth() const { return m_coverageMaxWidth; } bool dumpTreeAddrids() const VL_MT_SAFE; int expandLimit() const { return m_expandLimit; } diff --git a/test_regress/t/t_cover_expr.out b/test_regress/t/t_cover_expr.out new file mode 100644 index 000000000..18a70c05d --- /dev/null +++ b/test_regress/t/t_cover_expr.out @@ -0,0 +1,317 @@ +// // verilator_coverage annotation + // DESCRIPTION: Verilator: Verilog Test module + // + // This file ONLY is placed under the Creative Commons Public Domain, for + // any use, without warranty, 2024 by Wilson Snyder. + // SPDX-License-Identifier: CC0-1.0 + + module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + 000015 return ~x; ++000012 point: comment=(x==0) => 1 hier=top.t ++000015 point: comment=(x==1) => 0 hier=top.t + endfunction + + function automatic bit and_oper(bit a, bit b); +%000005 return a & b; +-000004 point: comment=(a==0) => 0 hier=top.t +-000002 point: comment=(a==1 && b==1) => 1 hier=top.t +-000005 point: comment=(b==0) => 0 hier=top.t + endfunction + + always @ (posedge clk) begin + cyc <= cyc + 1; +%000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); +-000002 point: comment=(cyc[0]==0 && cyc[1]==1) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000004 point: comment=(cyc[0]==1 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000005 if (t3 && (t1 == t2)) $write(""); +-000005 point: comment=((t1 == t2)==0) => 0 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +-000002 point: comment=(t3==1 && (t1 == t2)==1) => 1 hier=top.t +%000005 if (123 == (124 - 32'(t1 || t2))) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000004 some_int <= (t2 || t3) ? 345 : 567; +-000003 point: comment=(t2==0 && t3==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +-000004 point: comment=(t3==1) => 1 hier=top.t +%000005 some_bool <= t1 && t2; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2) $write(""); +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000004 if ((!t1 && t2) | (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000003 if (t1 ^ t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +%000005 if (~(t1 & t2)) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 1 hier=top.t +%000004 if (t1 -> t2) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000003 if (t1 <-> t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 0 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +%000005 if (&cyc[2:0]) $write(""); +-000004 point: comment=(cyc[2:0][0]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +-000005 point: comment=(cyc[2:0][1]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][2]==0) => 0 hier=top.t +%000007 if (&cyc[3:2]) $write(""); +-000005 point: comment=(cyc[3:2][0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[3:2][0]==1 && cyc[3:2][1]==1) => 1 hier=top.t +-000007 point: comment=(cyc[3:2][1]==0) => 0 hier=top.t +%000005 if (|cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +%000002 if (^cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 0 hier=top.t +-000002 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +%000005 if (|cyc[2:0] || cyc[3]) $write(""); +-000000 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0 && cyc[3]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +-000002 point: comment=(cyc[3]==1) => 1 hier=top.t +%000005 if (t1 & t2 & 1'b1) $write(""); +-000000 point: comment=(1'h1==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && 1'h1==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & 1'b0) $write(""); +-000009 point: comment=(1'h0==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && 1'h0==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2 & ONE) $write(""); +-000000 point: comment=(ONE==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && ONE==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & ZERO) $write(""); +-000009 point: comment=(ZERO==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && ZERO==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 && t2) begin +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + $write(""); +%000003 end else if (t1 || t2) begin +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000003 point: comment=(t1==1) => 1 hier=top.t +-000002 point: comment=(t2==1) => 1 hier=top.t + $write(""); + end +%000005 if (invert(t1) && t2) $write(""); +-000005 point: comment=(invert(t1)==0) => 0 hier=top.t +-000002 point: comment=(invert(t1)==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + if (and_oper(t1, t2)) $write(""); +%000005 if (t2 && t3) begin +-000005 point: comment=(t2==0) => 0 hier=top.t +-000002 point: comment=(t2==1 && t3==1) => 1 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +%000001 if (t1 && t2) $write(""); +-000001 point: comment=(t1==0) => 0 hier=top.t +-000001 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin +%000000 if (cyc[loop_var] && t2) $write(""); +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + end + // stop at the first layer even if there's more to find +%000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==1) => 1 hier=top.t +-000000 point: comment=(cyc[31]==1) => 1 hier=top.t + // impossible branches and redundant terms +%000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); +-000003 point: comment=(t1==0 && t4==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && t3==0 && t4==1) => 1 hier=top.t +-000001 point: comment=(t1==1 && t2==1 && t3==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); +-000003 point: comment=(cyc[0]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + // demonstrate current limitations of term matching scheme +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) +%000004 || +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 +%000005 ] & +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[1]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation +%000005 other_int <= t1 ? t2 ? 1 : 2 : 3; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large + if (^cyc[6:0]) $write(""); + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin +%000005 if (t1 && t2) $write(""); +-000005 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; +%000001 while (ta || tb || tc) begin +-000001 point: comment=(ta==0 && tb==0 && tc==0) => 0 hier=top.t +-000000 point: comment=(ta==1) => 1 hier=top.t +-000000 point: comment=(tb==1) => 1 hier=top.t +-000000 point: comment=(tc==1) => 1 hier=top.t + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + + endmodule + + module sub ( + input p, + input q + ); + + always_comb begin +~000019 if (p && q) $write(""); ++000017 point: comment=(p==0) => 0 hier=top.t.the_sub_* +-000002 point: comment=(p==1 && q==1) => 1 hier=top.t.the_sub_* ++000019 point: comment=(q==0) => 0 hier=top.t.the_sub_* + end + + endmodule + diff --git a/test_regress/t/t_cover_expr.py b/test_regress/t/t_cover_expr.py new file mode 100755 index 000000000..0f9b3aca8 --- /dev/null +++ b/test_regress/t/t_cover_expr.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap +from pathlib import Path + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--cc', '--coverage-expr']) + +test.execute() + +# Read the input .v file and do any CHECK_COVER requests +test.inline_checks() + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate-points", + "--annotate", + test.obj_dir + "/annotated", + test.obj_dir + "/coverage.dat", +], + verilator_run=True) + +top = Path(test.top_filename) +test.files_identical(test.obj_dir + f"/annotated/{top.name}", test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_cover_expr.v b/test_regress/t/t_cover_expr.v new file mode 100644 index 000000000..df6a93113 --- /dev/null +++ b/test_regress/t/t_cover_expr.v @@ -0,0 +1,161 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + return ~x; + endfunction + + function automatic bit and_oper(bit a, bit b); + return a & b; + endfunction + + always @ (posedge clk) begin + cyc <= cyc + 1; + if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); + if ((~t1 && t2) || (~t3 && t4)) $write(""); + if (t3 && (t1 == t2)) $write(""); + if (123 == (124 - 32'(t1 || t2))) $write(""); + some_int <= (t2 || t3) ? 345 : 567; + some_bool <= t1 && t2; + if (t1 & t2) $write(""); + if ((!t1 && t2) | (~t3 && t4)) $write(""); + if (t1 ^ t2) $write(""); + if (~(t1 & t2)) $write(""); + if (t1 -> t2) $write(""); + if (t1 <-> t2) $write(""); + if (&cyc[2:0]) $write(""); + if (&cyc[3:2]) $write(""); + if (|cyc[2:0]) $write(""); + if (^cyc[2:0]) $write(""); + if (|cyc[2:0] || cyc[3]) $write(""); + if (t1 & t2 & 1'b1) $write(""); + if (t1 & t2 & 1'b0) $write(""); + if (t1 & t2 & ONE) $write(""); + if (t1 & t2 & ZERO) $write(""); + if (t1 && t2) begin + $write(""); + end else if (t1 || t2) begin + $write(""); + end + if (invert(t1) && t2) $write(""); + if (and_oper(t1, t2)) $write(""); + if (t2 && t3) begin + if (t1 && t2) $write(""); + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin + if (cyc[loop_var] && t2) $write(""); + end + end + // stop at the first layer even if there's more to find + if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); + // impossible branches and redundant terms + if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); + if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); + // demonstrate current limitations of term matching scheme + if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) + || + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 + ] & + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation + other_int <= t1 ? t2 ? 1 : 2 : 3; + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large + if (^cyc[6:0]) $write(""); + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin + if (t1 && t2) $write(""); + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; + while (ta || tb || tc) begin + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + +endmodule + +module sub ( + input p, + input q +); + + always_comb begin + if (p && q) $write(""); + end + +endmodule diff --git a/test_regress/t/t_cover_expr_max.out b/test_regress/t/t_cover_expr_max.out new file mode 100644 index 000000000..b755a4ad3 --- /dev/null +++ b/test_regress/t/t_cover_expr_max.out @@ -0,0 +1,445 @@ +// // verilator_coverage annotation + // DESCRIPTION: Verilator: Verilog Test module + // + // This file ONLY is placed under the Creative Commons Public Domain, for + // any use, without warranty, 2024 by Wilson Snyder. + // SPDX-License-Identifier: CC0-1.0 + + module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + 000015 return ~x; ++000012 point: comment=(x==0) => 1 hier=top.t ++000015 point: comment=(x==1) => 0 hier=top.t + endfunction + + function automatic bit and_oper(bit a, bit b); +%000005 return a & b; +-000004 point: comment=(a==0) => 0 hier=top.t +-000002 point: comment=(a==1 && b==1) => 1 hier=top.t +-000005 point: comment=(b==0) => 0 hier=top.t + endfunction + + always @ (posedge clk) begin + cyc <= cyc + 1; +%000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); +-000002 point: comment=(cyc[0]==0 && cyc[1]==1) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000004 point: comment=(cyc[0]==1 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000005 if (t3 && (t1 == t2)) $write(""); +-000005 point: comment=((t1 == t2)==0) => 0 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +-000002 point: comment=(t3==1 && (t1 == t2)==1) => 1 hier=top.t +%000005 if (123 == (124 - 32'(t1 || t2))) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000004 some_int <= (t2 || t3) ? 345 : 567; +-000003 point: comment=(t2==0 && t3==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +-000004 point: comment=(t3==1) => 1 hier=top.t +%000005 some_bool <= t1 && t2; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2) $write(""); +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000004 if ((!t1 && t2) | (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000003 if (t1 ^ t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +%000005 if (~(t1 & t2)) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 1 hier=top.t +%000004 if (t1 -> t2) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000003 if (t1 <-> t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 0 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +%000005 if (&cyc[2:0]) $write(""); +-000004 point: comment=(cyc[2:0][0]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +-000005 point: comment=(cyc[2:0][1]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][2]==0) => 0 hier=top.t +%000007 if (&cyc[3:2]) $write(""); +-000005 point: comment=(cyc[3:2][0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[3:2][0]==1 && cyc[3:2][1]==1) => 1 hier=top.t +-000007 point: comment=(cyc[3:2][1]==0) => 0 hier=top.t +%000005 if (|cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +%000002 if (^cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 0 hier=top.t +-000002 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +%000005 if (|cyc[2:0] || cyc[3]) $write(""); +-000000 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0 && cyc[3]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +-000002 point: comment=(cyc[3]==1) => 1 hier=top.t +%000005 if (t1 & t2 & 1'b1) $write(""); +-000000 point: comment=(1'h1==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && 1'h1==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & 1'b0) $write(""); +-000009 point: comment=(1'h0==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && 1'h0==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2 & ONE) $write(""); +-000000 point: comment=(ONE==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && ONE==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & ZERO) $write(""); +-000009 point: comment=(ZERO==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && ZERO==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 && t2) begin +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + $write(""); +%000003 end else if (t1 || t2) begin +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000003 point: comment=(t1==1) => 1 hier=top.t +-000002 point: comment=(t2==1) => 1 hier=top.t + $write(""); + end +%000005 if (invert(t1) && t2) $write(""); +-000005 point: comment=(invert(t1)==0) => 0 hier=top.t +-000002 point: comment=(invert(t1)==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + if (and_oper(t1, t2)) $write(""); +%000005 if (t2 && t3) begin +-000005 point: comment=(t2==0) => 0 hier=top.t +-000002 point: comment=(t2==1 && t3==1) => 1 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +%000001 if (t1 && t2) $write(""); +-000001 point: comment=(t1==0) => 0 hier=top.t +-000001 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin +%000000 if (cyc[loop_var] && t2) $write(""); +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + end + // stop at the first layer even if there's more to find +%000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==1) => 1 hier=top.t +-000000 point: comment=(cyc[31]==1) => 1 hier=top.t + // impossible branches and redundant terms +%000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); +-000003 point: comment=(t1==0 && t4==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && t3==0 && t4==1) => 1 hier=top.t +-000001 point: comment=(t1==1 && t2==1 && t3==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); +-000003 point: comment=(cyc[0]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + // demonstrate current limitations of term matching scheme +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) +%000004 || +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 +%000005 ] & +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[1]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation +%000005 other_int <= t1 ? t2 ? 1 : 2 : 3; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large +%000001 if (^cyc[6:0]) $write(""); +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin +%000005 if (t1 && t2) $write(""); +-000005 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; +%000001 while (ta || tb || tc) begin +-000001 point: comment=(ta==0 && tb==0 && tc==0) => 0 hier=top.t +-000000 point: comment=(ta==1) => 1 hier=top.t +-000000 point: comment=(tb==1) => 1 hier=top.t +-000000 point: comment=(tc==1) => 1 hier=top.t + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + + endmodule + + module sub ( + input p, + input q + ); + + always_comb begin +~000019 if (p && q) $write(""); ++000017 point: comment=(p==0) => 0 hier=top.t.the_sub_* +-000002 point: comment=(p==1 && q==1) => 1 hier=top.t.the_sub_* ++000019 point: comment=(q==0) => 0 hier=top.t.the_sub_* + end + + endmodule + diff --git a/test_regress/t/t_cover_expr_max.py b/test_regress/t/t_cover_expr_max.py new file mode 100755 index 000000000..bc3049c66 --- /dev/null +++ b/test_regress/t/t_cover_expr_max.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap +from pathlib import Path + +test.scenarios('simulator') +test.top_filename = "t/t_cover_expr.v" + +test.compile(verilator_flags2=['--cc', '--coverage-expr', '--coverage-expr-max', '128']) + +test.execute() + +# Read the input .v file and do any CHECK_COVER requests +test.inline_checks() + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate-points", + "--annotate", + test.obj_dir + "/annotated", + test.obj_dir + "/coverage.dat", +], + verilator_run=True) + +top = Path(test.top_filename) +test.files_identical(test.obj_dir + f"/annotated/{top.name}", test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_cover_expr_trace.out b/test_regress/t/t_cover_expr_trace.out new file mode 100644 index 000000000..18a70c05d --- /dev/null +++ b/test_regress/t/t_cover_expr_trace.out @@ -0,0 +1,317 @@ +// // verilator_coverage annotation + // DESCRIPTION: Verilator: Verilog Test module + // + // This file ONLY is placed under the Creative Commons Public Domain, for + // any use, without warranty, 2024 by Wilson Snyder. + // SPDX-License-Identifier: CC0-1.0 + + module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + 000015 return ~x; ++000012 point: comment=(x==0) => 1 hier=top.t ++000015 point: comment=(x==1) => 0 hier=top.t + endfunction + + function automatic bit and_oper(bit a, bit b); +%000005 return a & b; +-000004 point: comment=(a==0) => 0 hier=top.t +-000002 point: comment=(a==1 && b==1) => 1 hier=top.t +-000005 point: comment=(b==0) => 0 hier=top.t + endfunction + + always @ (posedge clk) begin + cyc <= cyc + 1; +%000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); +-000002 point: comment=(cyc[0]==0 && cyc[1]==1) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000004 point: comment=(cyc[0]==1 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000005 if (t3 && (t1 == t2)) $write(""); +-000005 point: comment=((t1 == t2)==0) => 0 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +-000002 point: comment=(t3==1 && (t1 == t2)==1) => 1 hier=top.t +%000005 if (123 == (124 - 32'(t1 || t2))) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000004 some_int <= (t2 || t3) ? 345 : 567; +-000003 point: comment=(t2==0 && t3==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +-000004 point: comment=(t3==1) => 1 hier=top.t +%000005 some_bool <= t1 && t2; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2) $write(""); +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000004 if ((!t1 && t2) | (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000003 if (t1 ^ t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +%000005 if (~(t1 & t2)) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 1 hier=top.t +%000004 if (t1 -> t2) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000003 if (t1 <-> t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 0 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +%000005 if (&cyc[2:0]) $write(""); +-000004 point: comment=(cyc[2:0][0]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +-000005 point: comment=(cyc[2:0][1]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][2]==0) => 0 hier=top.t +%000007 if (&cyc[3:2]) $write(""); +-000005 point: comment=(cyc[3:2][0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[3:2][0]==1 && cyc[3:2][1]==1) => 1 hier=top.t +-000007 point: comment=(cyc[3:2][1]==0) => 0 hier=top.t +%000005 if (|cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +%000002 if (^cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 0 hier=top.t +-000002 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +%000005 if (|cyc[2:0] || cyc[3]) $write(""); +-000000 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0 && cyc[3]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +-000002 point: comment=(cyc[3]==1) => 1 hier=top.t +%000005 if (t1 & t2 & 1'b1) $write(""); +-000000 point: comment=(1'h1==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && 1'h1==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & 1'b0) $write(""); +-000009 point: comment=(1'h0==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && 1'h0==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2 & ONE) $write(""); +-000000 point: comment=(ONE==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && ONE==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & ZERO) $write(""); +-000009 point: comment=(ZERO==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && ZERO==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 && t2) begin +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + $write(""); +%000003 end else if (t1 || t2) begin +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000003 point: comment=(t1==1) => 1 hier=top.t +-000002 point: comment=(t2==1) => 1 hier=top.t + $write(""); + end +%000005 if (invert(t1) && t2) $write(""); +-000005 point: comment=(invert(t1)==0) => 0 hier=top.t +-000002 point: comment=(invert(t1)==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + if (and_oper(t1, t2)) $write(""); +%000005 if (t2 && t3) begin +-000005 point: comment=(t2==0) => 0 hier=top.t +-000002 point: comment=(t2==1 && t3==1) => 1 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +%000001 if (t1 && t2) $write(""); +-000001 point: comment=(t1==0) => 0 hier=top.t +-000001 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin +%000000 if (cyc[loop_var] && t2) $write(""); +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + end + // stop at the first layer even if there's more to find +%000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==1) => 1 hier=top.t +-000000 point: comment=(cyc[31]==1) => 1 hier=top.t + // impossible branches and redundant terms +%000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); +-000003 point: comment=(t1==0 && t4==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && t3==0 && t4==1) => 1 hier=top.t +-000001 point: comment=(t1==1 && t2==1 && t3==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); +-000003 point: comment=(cyc[0]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + // demonstrate current limitations of term matching scheme +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) +%000004 || +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 +%000005 ] & +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[1]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation +%000005 other_int <= t1 ? t2 ? 1 : 2 : 3; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large + if (^cyc[6:0]) $write(""); + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin +%000005 if (t1 && t2) $write(""); +-000005 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; +%000001 while (ta || tb || tc) begin +-000001 point: comment=(ta==0 && tb==0 && tc==0) => 0 hier=top.t +-000000 point: comment=(ta==1) => 1 hier=top.t +-000000 point: comment=(tb==1) => 1 hier=top.t +-000000 point: comment=(tc==1) => 1 hier=top.t + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + + endmodule + + module sub ( + input p, + input q + ); + + always_comb begin +~000019 if (p && q) $write(""); ++000017 point: comment=(p==0) => 0 hier=top.t.the_sub_* +-000002 point: comment=(p==1 && q==1) => 1 hier=top.t.the_sub_* ++000019 point: comment=(q==0) => 0 hier=top.t.the_sub_* + end + + endmodule + diff --git a/test_regress/t/t_cover_expr_trace.py b/test_regress/t/t_cover_expr_trace.py new file mode 100755 index 000000000..c1fd9df23 --- /dev/null +++ b/test_regress/t/t_cover_expr_trace.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap +from pathlib import Path + +test.scenarios('simulator') +test.top_filename = "t/t_cover_expr.v" + +test.compile(verilator_flags2=['--cc', '--coverage-expr', '--trace']) + +test.execute() + +# Read the input .v file and do any CHECK_COVER requests +test.inline_checks() + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate-points", + "--annotate", + test.obj_dir + "/annotated", + test.obj_dir + "/coverage.dat", +], + verilator_run=True) + +top = Path(test.top_filename) +test.files_identical(test.obj_dir + f"/annotated/{top.name}", test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_opt_const_cov.py b/test_regress/t/t_opt_const_cov.py index 1af2400b7..147adc0df 100755 --- a/test_regress/t/t_opt_const_cov.py +++ b/test_regress/t/t_opt_const_cov.py @@ -16,6 +16,6 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", "--coverage", "-- test.execute() if test.vlt: - test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 144) + test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 620) test.passes() diff --git a/test_regress/t/t_wrapper_context_top0.out b/test_regress/t/t_wrapper_context_top0.out index d028fb387..74af3f696 100644 --- a/test_regress/t/t_wrapper_context_top0.out +++ b/test_regress/t/t_wrapper_context_top0.out @@ -75,5 +75,8 @@ C 'ft/t_wrapper_context.vl38n4pagev_line/topoblockS38-39htop0.top' C 'ft/t_wrapper_context.vl40n7pagev_branch/topoifS40htop0.top' 0 C 'ft/t_wrapper_context.vl40n8pagev_branch/topoelseS46htop0.top' 34 C 'ft/t_wrapper_context.vl41n11pagev_line/topoelsehtop0.top' 0 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==0) => 0htop0.top' 0 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==1 && stop==1) => 1htop0.top' 0 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo(stop==0) => 0htop0.top' 0 C 'ft/t_wrapper_context.vl47n10pagev_branch/topoifS47-49htop0.top' 1 C 'ft/t_wrapper_context.vl47n11pagev_branch/topoelsehtop0.top' 33 diff --git a/test_regress/t/t_wrapper_context_top1.out b/test_regress/t/t_wrapper_context_top1.out index 391e5cb35..f7fab4025 100644 --- a/test_regress/t/t_wrapper_context_top1.out +++ b/test_regress/t/t_wrapper_context_top1.out @@ -75,5 +75,8 @@ C 'ft/t_wrapper_context.vl38n4pagev_line/topoblockS38-39htop1.top' C 'ft/t_wrapper_context.vl40n7pagev_branch/topoifS40htop1.top' 19 C 'ft/t_wrapper_context.vl40n8pagev_branch/topoelseS46htop1.top' 0 C 'ft/t_wrapper_context.vl41n11pagev_line/topoelsehtop1.top' 18 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==0) => 0htop1.top' 18 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==1 && stop==1) => 1htop1.top' 1 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo(stop==0) => 0htop1.top' 0 C 'ft/t_wrapper_context.vl47n10pagev_branch/topoifS47-49htop1.top' 0 C 'ft/t_wrapper_context.vl47n11pagev_branch/topoelsehtop1.top' 0