Skip to content

Commit

Permalink
Support immediate cover statements & refactor coverage internals.
Browse files Browse the repository at this point in the history
  • Loading branch information
wsnyder committed Dec 17, 2019
1 parent 8cdc0c4 commit 83a1bd0
Show file tree
Hide file tree
Showing 14 changed files with 150 additions and 152 deletions.
2 changes: 2 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ The contributors that suggested a given feature are shown in []. Thanks!

*** Support string compare, ato*, etc methods, bug1606. [Yutetsu TAKATSUKASA]

**** Support immediate cover statements.

**** Ignore `uselib to end-of-line, bug1634. [Frederic Antonin]

**** Update FST trace API for better performance.
Expand Down
109 changes: 47 additions & 62 deletions src/V3Assert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ class AssertVisitor : public AstNVisitor {
AstNodeModule* m_modp; // Last module
AstBegin* m_beginp; // Last begin
unsigned m_modPastNum; // Module past numbering
VDouble0 m_statAsCover; // Statistic tracking
VDouble0 m_statAsPsl; // Statistic tracking
VDouble0 m_statCover; // Statistic tracking
VDouble0 m_statAsNotImm; // Statistic tracking
VDouble0 m_statAsImm; // Statistic tracking
VDouble0 m_statAsFull; // Statistic tracking
VDouble0 m_statAsSV; // Statistic tracking

// METHODS
string assertDisplayMessage(AstNode* nodep, const string& prefix, const string& message) {
Expand Down Expand Up @@ -104,17 +104,28 @@ class AssertVisitor : public AstNVisitor {
return bodysp;
}

void newPslAssertion(AstNode* nodep, AstNode* propp, AstSenTree* sentreep,
AstNode* stmtsp, const string& message) {
propp->unlinkFrBack();
sentreep->unlinkFrBack();
if (stmtsp) stmtsp->unlinkFrBack();
void newPslAssertion(AstNodeCoverOrAssert* nodep, AstNode* failsp) {
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());

AstNode* propp = nodep->propp()->unlinkFrBackWithNext();
AstSenTree* sentreep = nodep->sentreep();
const string& message = nodep->name();
AstNode* passsp = nodep->passsp();
if (passsp) passsp->unlinkFrBackWithNext();
if (failsp) failsp->unlinkFrBackWithNext();

if (nodep->immediate()) {
UASSERT_OBJ(!sentreep, nodep, "Immediate assertions don't have sensivity");
} else {
UASSERT_OBJ(sentreep, nodep, "Concurrent assertions must have sensivity");
sentreep->unlinkFrBack();
}
//
AstNode* bodysp = NULL;
bool selfDestruct = false;
AstIf* ifp = NULL;
if (AstPslCover* snodep = VN_CAST(nodep, PslCover)) {
++m_statAsCover;
if (AstCover* snodep = VN_CAST(nodep, Cover)) {
++m_statCover;
if (!v3Global.opt.coverageUser()) {
selfDestruct = true;
} else {
Expand All @@ -126,35 +137,29 @@ class AssertVisitor : public AstNVisitor {
bodysp = covincp;
}

if (bodysp && stmtsp) bodysp = bodysp->addNext(stmtsp);
if (bodysp && passsp) bodysp = bodysp->addNext(passsp);
ifp = new AstIf(nodep->fileline(), propp, bodysp, NULL);
bodysp = ifp;

} else if (VN_IS(nodep, PslAssert)) {
++m_statAsPsl;
// Insert an automatic error message and $stop after
// any user-supplied statements.
AstNode* autoMsgp = newFireAssertUnchecked(nodep, "'assert property' failed.");
if (stmtsp) {
stmtsp->addNext(autoMsgp);
} else {
stmtsp = autoMsgp;
}
ifp = new AstIf(nodep->fileline(), propp, NULL, stmtsp);
} else if (VN_IS(nodep, Assert)) {
if (nodep->immediate()) ++m_statAsImm;
else ++m_statAsNotImm;
if (passsp) passsp = newIfAssertOn(passsp);
if (failsp) failsp = newIfAssertOn(failsp);
if (!failsp) failsp = newFireAssertUnchecked(nodep, "'assert' failed.");
ifp = new AstIf(nodep->fileline(), propp, passsp, failsp);
// It's more LIKELY that we'll take the NULL if clause
// than the sim-killing else clause:
ifp->branchPred(VBranchPred::BP_LIKELY);
bodysp = newIfAssertOn(ifp);
} else if (VN_IS(nodep, PslRestrict)) {
// IEEE says simulator ignores these
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
return;
bodysp = ifp;
} else {
nodep->v3fatalSrc("Unknown node type");
}

AstNode* newp = new AstAlways(nodep->fileline(),
VAlwaysKwd::ALWAYS, sentreep, bodysp);
AstNode* newp;
if (sentreep) {
newp = new AstAlways(nodep->fileline(),
VAlwaysKwd::ALWAYS, sentreep, bodysp);
} else { newp = bodysp; }
// Install it
if (selfDestruct) {
// Delete it after making the tree. This way we can tell the user
Expand All @@ -168,28 +173,6 @@ class AssertVisitor : public AstNVisitor {
pushDeletep(nodep); VL_DANGLING(nodep);
}

void newVAssertion(AstVAssert* nodep, AstNode* propp) {
propp->unlinkFrBackWithNext();
AstNode* passsp = nodep->passsp(); if (passsp) passsp->unlinkFrBackWithNext();
AstNode* failsp = nodep->failsp(); if (failsp) failsp->unlinkFrBackWithNext();
//
if (VN_IS(nodep, VAssert)) {
if (passsp) passsp = newIfAssertOn(passsp);
if (failsp) failsp = newIfAssertOn(failsp);
} else {
nodep->v3fatalSrc("Unknown node type");
}

AstIf* ifp = new AstIf(nodep->fileline(), propp, passsp, failsp);
AstNode* newp = ifp;
if (VN_IS(nodep, VAssert)) ifp->branchPred(VBranchPred::BP_UNLIKELY);
//
// Install it
nodep->replaceWith(newp);
// Bye
pushDeletep(nodep); VL_DANGLING(nodep);
}

// VISITORS
virtual void visit(AstIf* nodep) {
if (nodep->user1SetOnce()) return;
Expand Down Expand Up @@ -358,16 +341,18 @@ class AssertVisitor : public AstNVisitor {
}
}

virtual void visit(AstNodePslCoverOrAssert* nodep) {
virtual void visit(AstAssert* nodep) {
iterateChildren(nodep);
if (m_beginp && nodep->name() == "") nodep->name(m_beginp->name());
newPslAssertion(nodep, nodep->propp(), nodep->sentreep(),
nodep->stmtsp(), nodep->name()); VL_DANGLING(nodep);
newPslAssertion(nodep, nodep->failsp());
}
virtual void visit(AstCover* nodep) {
iterateChildren(nodep);
newPslAssertion(nodep, NULL);
}
virtual void visit(AstVAssert* nodep) {
virtual void visit(AstRestrict* nodep) {
iterateChildren(nodep);
newVAssertion(nodep, nodep->propp()); VL_DANGLING(nodep);
++m_statAsSV;
// IEEE says simulator ignores these
pushDeletep(nodep->unlinkFrBack()); VL_DANGLING(nodep);
}

virtual void visit(AstNodeModule* nodep) {
Expand Down Expand Up @@ -402,9 +387,9 @@ class AssertVisitor : public AstNVisitor {
iterate(nodep);
}
virtual ~AssertVisitor() {
V3Stats::addStat("Assertions, PSL asserts", m_statAsPsl);
V3Stats::addStat("Assertions, SystemVerilog asserts", m_statAsSV);
V3Stats::addStat("Assertions, cover statements", m_statAsCover);
V3Stats::addStat("Assertions, assert non-immediate statements", m_statAsNotImm);
V3Stats::addStat("Assertions, assert immediate statements", m_statAsImm);
V3Stats::addStat("Assertions, cover statements", m_statCover);
V3Stats::addStat("Assertions, full/parallel case", m_statAsFull);
}
};
Expand Down
10 changes: 6 additions & 4 deletions src/V3AssertPre.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,22 @@ class AssertPreVisitor : public AstNVisitor {
m_seniAlwaysp = NULL;
}

virtual void visit(AstNodePslCoverOrAssert* nodep) {
virtual void visit(AstNodeCoverOrAssert* nodep) {
if (nodep->sentreep()) return; // Already processed
clearAssertInfo();
// Find PslClocking's buried under nodep->exprsp
// Find Clocking's buried under nodep->exprsp
iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
if (!nodep->immediate()) {
nodep->sentreep(newSenTree(nodep));
}
clearAssertInfo();
}
virtual void visit(AstPast* nodep) {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
}
virtual void visit(AstPslClocked* nodep) {
virtual void visit(AstPropClocked* nodep) {
// No need to iterate the body, once replace will get iterated
iterateAndNextNull(nodep->sensesp());
if (m_senip) {
Expand Down
4 changes: 4 additions & 0 deletions src/V3AstNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,10 @@ void AstCellInline::dump(std::ostream& str) const {
this->AstNode::dump(str);
str<<" -> "<<origModName();
}
void AstNodeCoverOrAssert::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (immediate()) str<<" [IMMEDIATE]";
}
void AstDisplay::dump(std::ostream& str) const {
this->AstNode::dump(str);
//str<<" "<<displayType().ascii();
Expand Down
77 changes: 33 additions & 44 deletions src/V3AstNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -6167,28 +6167,6 @@ class AstPatMember : public AstNodeMath {
void isDefault(bool flag) { m_default = flag; }
};

//======================================================================
// SysVerilog assertions

class AstVAssert : public AstNodeStmt {
// Verilog Assertion
// Parents: {statement list}
// Children: expression, if pass statements, if fail statements
public:
AstVAssert(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp)
: AstNodeStmt(fl) {
addOp1p(propp);
addNOp2p(passsp);
addNOp3p(failsp);
}
ASTNODE_NODE_FUNCS(VAssert)
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const { return true; }
AstNode* propp() const { return op1p(); } // op1 = property
AstNode* passsp() const { return op2p(); } // op2 = if passes
AstNode* failsp() const { return op3p(); } // op3 = if fails
};

//======================================================================
// Assertions

Expand All @@ -6210,69 +6188,80 @@ class AstClocking : public AstNode {
//======================================================================
// PSL

class AstPslClocked : public AstNode {
class AstPropClocked : public AstNode {
// A clocked property
// Parents: ASSERT|COVER (property)
// Children: SENITEM, Properties
public:
AstPslClocked(FileLine* fl, AstNodeSenItem* sensesp, AstNode* disablep, AstNode* propp)
AstPropClocked(FileLine* fl, AstNodeSenItem* sensesp, AstNode* disablep, AstNode* propp)
: AstNode(fl) {
addNOp1p(sensesp);
addNOp2p(disablep);
addOp3p(propp);
}
ASTNODE_NODE_FUNCS(PslClocked)
virtual bool hasDType() const { return true; } // Used under PslCover, which expects a bool child
ASTNODE_NODE_FUNCS(PropClocked)
virtual bool hasDType() const { return true; } // Used under Cover, which expects a bool child
AstNodeSenItem* sensesp() const { return VN_CAST(op1p(), NodeSenItem); } // op1 = Sensitivity list
AstNode* disablep() const { return op2p(); } // op2 = disable
AstNode* propp() const { return op3p(); } // op3 = property
};

class AstNodePslCoverOrAssert : public AstNodeStmt {
// Psl Cover
class AstNodeCoverOrAssert : public AstNodeStmt {
// Cover or Assert
// Parents: {statement list}
// Children: expression, report string
private:
bool m_immediate; // Immediate assertion/cover
string m_name; // Name to report
public:
AstNodePslCoverOrAssert(FileLine* fl, AstNode* propp, AstNode* stmtsp, const string& name="")
AstNodeCoverOrAssert(FileLine* fl, AstNode* propp, AstNode* passsp,
bool immediate, const string& name="")
: AstNodeStmt(fl)
, m_immediate(immediate)
, m_name(name) {
addOp1p(propp);
addNOp4p(stmtsp);
addNOp4p(passsp);
}
ASTNODE_BASE_FUNCS(NodePslCoverOrAssert)
ASTNODE_BASE_FUNCS(NodeCoverOrAssert)
virtual string name() const { return m_name; } // * = Var name
virtual V3Hash sameHash() const { return V3Hash(name()); }
virtual bool same(const AstNode* samep) const { return samep->name() == name(); }
virtual void name(const string& name) { m_name = name; }
virtual void dump(std::ostream& str=std::cout) const;
AstNode* propp() const { return op1p(); } // op1 = property
AstSenTree* sentreep() const { return VN_CAST(op2p(), SenTree); } // op2 = clock domain
void sentreep(AstSenTree* sentreep) { addOp2p(sentreep); } // op2 = clock domain
AstNode* stmtsp() const { return op4p(); } // op4 = statements
AstNode* passsp() const { return op4p(); } // op4 = statements (assert/cover passes)
bool immediate() const { return m_immediate; }
};

class AstPslAssert : public AstNodePslCoverOrAssert {
class AstAssert : public AstNodeCoverOrAssert {
public:
ASTNODE_NODE_FUNCS(PslAssert)
AstPslAssert(FileLine* fl, AstNode* propp, AstNode* stmtsp, const string& name="")
: AstNodePslCoverOrAssert(fl, propp, stmtsp, name) {}
ASTNODE_NODE_FUNCS(Assert)
AstAssert(FileLine* fl, AstNode* propp, AstNode* passsp, AstNode* failsp,
bool immediate, const string& name = "")
: AstNodeCoverOrAssert(fl, propp, passsp, immediate, name) {
addNOp3p(failsp);
}
AstNode* failsp() const { return op3p(); } // op3 = if assertion fails
};

class AstPslCover : public AstNodePslCoverOrAssert {
class AstCover : public AstNodeCoverOrAssert {
public:
ASTNODE_NODE_FUNCS(PslCover)
AstPslCover(FileLine* fl, AstNode* propp, AstNode* stmtsp, const string& name="")
: AstNodePslCoverOrAssert(fl, propp, stmtsp, name) {}
ASTNODE_NODE_FUNCS(Cover)
AstCover(FileLine* fl, AstNode* propp, AstNode* stmtsp,
bool immediate, const string& name = "")
: AstNodeCoverOrAssert(fl, propp, stmtsp, immediate, name) {}
AstNode* coverincp() const { return op3p(); } // op3 = coverage node
void coverincp(AstCoverInc* nodep) { addOp3p(nodep); } // op3 = coverage node
virtual bool immediate() const { return false; }
};

class AstPslRestrict : public AstNodePslCoverOrAssert {
class AstRestrict : public AstNodeCoverOrAssert {
public:
ASTNODE_NODE_FUNCS(PslRestrict)
AstPslRestrict(FileLine* fl, AstNode* propp)
: AstNodePslCoverOrAssert(fl, propp, NULL, "") {}
ASTNODE_NODE_FUNCS(Restrict)
AstRestrict(FileLine* fl, AstNode* propp)
: AstNodeCoverOrAssert(fl, propp, NULL, false, "") {}
};

//======================================================================
Expand Down
4 changes: 2 additions & 2 deletions src/V3Coverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,8 @@ class CoverageVisitor : public AstNVisitor {
m_checkBlock = true; // Reset as a child may have cleared it
}
}
virtual void visit(AstPslCover* nodep) {
UINFO(4," PSLCOVER: "<<nodep<<endl);
virtual void visit(AstCover* nodep) {
UINFO(4," COVER: "<<nodep<<endl);
m_checkBlock = true; // Always do cover blocks, even if there's a $stop
iterateChildren(nodep);
if (!nodep->coverincp()) {
Expand Down
4 changes: 2 additions & 2 deletions src/V3LinkParse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -459,10 +459,10 @@ class LinkParseVisitor : public AstNVisitor {
visitIterateNoValueMod(nodep);
m_inAlways = false;
}
virtual void visit(AstPslCover* nodep) {
virtual void visit(AstCover* nodep) {
visitIterateNoValueMod(nodep);
}
virtual void visit(AstPslRestrict* nodep) {
virtual void visit(AstRestrict* nodep) {
visitIterateNoValueMod(nodep);
}

Expand Down
6 changes: 3 additions & 3 deletions src/V3LinkResolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ class LinkResolveVisitor : public AstNVisitor {
// Below state needs to be preserved between each module call.
AstNodeModule* m_modp; // Current module
AstNodeFTask* m_ftaskp; // Function or task we're inside
AstVAssert* m_assertp; // Current assertion
int m_senitemCvtNum; // Temporary signal counter
AstNodeCoverOrAssert* m_assertp; // Current assertion
int m_senitemCvtNum; // Temporary signal counter

// METHODS
VL_DEBUG_FUNC; // Declare debug()
Expand All @@ -82,7 +82,7 @@ class LinkResolveVisitor : public AstNVisitor {
nodep->replaceWith(nodep->bodysp()->unlinkFrBackWithNext()); VL_DANGLING(nodep);
}
}
virtual void visit(AstVAssert* nodep) {
virtual void visit(AstNodeCoverOrAssert* nodep) {
if (m_assertp) nodep->v3error("Assert not allowed under another assert");
m_assertp = nodep;
iterateChildren(nodep);
Expand Down
Loading

0 comments on commit 83a1bd0

Please sign in to comment.