2025-10-13 11:26:25 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
2025-11-05 14:40:13 +01:00
|
|
|
// DESCRIPTION: Verilator: Implementation of assertion properties
|
2025-10-13 11:26:25 +02:00
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
|
|
|
|
// Copyright 2005-2025 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2025-11-06 11:36:56 +01:00
|
|
|
// Each sequence is translated into a decision tree in form of deterministic
|
|
|
|
|
// finite automaton (DFA) with bipartite structure. Each cycle delay is connected
|
|
|
|
|
// with an expression that depending on an evaluation result, proceeds to the next
|
|
|
|
|
// evaluation state. The structure is rooted with original sequence expression for
|
|
|
|
|
// simplifying further transformation back to AST.
|
|
|
|
|
//
|
|
|
|
|
// The graph consists of the following nodes:
|
|
|
|
|
//
|
|
|
|
|
// DfaStmtVertex: Statements to be executed to traverse from one state to another
|
|
|
|
|
// DfaExprVertex: Property expression that is checked and based on that a branch
|
|
|
|
|
// is taken.
|
|
|
|
|
// DfaConditionEdge: Branch edge that connects statements and expressions.
|
|
|
|
|
//
|
2025-10-13 11:26:25 +02:00
|
|
|
// Properties steps:
|
|
|
|
|
// Ensemble a property decision tree from sequence expressions.
|
2025-11-06 11:36:56 +01:00
|
|
|
// Transform property decision tree into AST, remove source sequence expression
|
|
|
|
|
// Property blocks are wrapped with AstPExpr that are transformed
|
2025-10-13 11:26:25 +02:00
|
|
|
// further by V3AssertPre and V3Assert.
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
|
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2025-11-05 14:40:13 +01:00
|
|
|
#include "V3AssertProp.h"
|
2025-10-13 11:26:25 +02:00
|
|
|
|
|
|
|
|
#include "V3Graph.h"
|
|
|
|
|
#include "V3UniqueNames.h"
|
|
|
|
|
|
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Data structures (graph types)
|
|
|
|
|
|
|
|
|
|
class DfaVertex VL_NOT_FINAL : public V3GraphVertex {
|
|
|
|
|
VL_RTTI_IMPL(DfaVertex, V3GraphVertex)
|
|
|
|
|
// STATE
|
|
|
|
|
AstNode* const m_nodep; // Underlying node
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
explicit DfaVertex(V3Graph* graphp, AstNode* nodep) VL_MT_DISABLED : V3GraphVertex{graphp},
|
|
|
|
|
m_nodep{nodep} {}
|
|
|
|
|
AstNode* nodep() const { return m_nodep; }
|
|
|
|
|
string name() const override VL_MT_STABLE {
|
|
|
|
|
return cvtToHex(m_nodep) + "\\n " + cvtToStr(m_nodep->typeName()) + "\\n"s
|
|
|
|
|
+ m_nodep->fileline()->ascii();
|
|
|
|
|
};
|
|
|
|
|
string dotShape() const override {
|
|
|
|
|
if (inEmpty()) return "tripleoctagon";
|
|
|
|
|
if (outEmpty()) return "doubleoctagon";
|
|
|
|
|
return "oval";
|
|
|
|
|
}
|
|
|
|
|
bool isStart() const { return inEmpty(); }
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-05 14:40:13 +01:00
|
|
|
class DfaStmtVertex final : public DfaVertex {
|
|
|
|
|
VL_RTTI_IMPL(DfaStmtVertex, V3GraphEdge)
|
2025-10-13 11:26:25 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2025-11-05 14:40:13 +01:00
|
|
|
explicit DfaStmtVertex(V3Graph* graphp, AstNodeStmt* stmtp) VL_MT_DISABLED
|
2025-10-13 11:26:25 +02:00
|
|
|
: DfaVertex{graphp, stmtp} {}
|
|
|
|
|
string dotColor() const override { return "green"; }
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-05 14:40:13 +01:00
|
|
|
class DfaExprVertex final : public DfaVertex {
|
|
|
|
|
VL_RTTI_IMPL(DfaExprVertex, V3GraphEdge)
|
2025-10-13 11:26:25 +02:00
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2025-11-05 14:40:13 +01:00
|
|
|
explicit DfaExprVertex(V3Graph* graphp, AstNodeExpr* exprp) VL_MT_DISABLED
|
2025-10-13 11:26:25 +02:00
|
|
|
: DfaVertex{graphp, exprp} {}
|
|
|
|
|
string dotColor() const override { return "blue"; }
|
|
|
|
|
};
|
|
|
|
|
|
2025-11-05 14:40:13 +01:00
|
|
|
class DfaConditionEdge final : public V3GraphEdge {
|
|
|
|
|
VL_RTTI_IMPL(DfaConditionEdge, V3GraphEdge)
|
2025-10-13 11:26:25 +02:00
|
|
|
// STATE
|
|
|
|
|
const bool m_ifBranch; // Whether this branch is taken for fulfilled condition
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2025-11-05 14:40:13 +01:00
|
|
|
explicit DfaConditionEdge(V3Graph* graphp, DfaVertex* fromp, DfaVertex* top,
|
|
|
|
|
bool ifBranch) VL_MT_DISABLED : V3GraphEdge{graphp, fromp, top, 1},
|
|
|
|
|
m_ifBranch{ifBranch} {}
|
|
|
|
|
~DfaConditionEdge() override = default;
|
2025-10-13 11:26:25 +02:00
|
|
|
|
|
|
|
|
bool ifBranch() const { return m_ifBranch; }
|
|
|
|
|
string dotColor() const override { return m_ifBranch ? "green" : "red"; }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Parse properties and ensemble a property tree graph
|
2025-11-05 14:40:13 +01:00
|
|
|
class AssertPropBuildVisitor final : public VNVisitorConst {
|
2025-10-13 11:26:25 +02:00
|
|
|
// STATE
|
2025-11-06 10:54:05 +01:00
|
|
|
V3Graph& m_graph; // Property tree
|
2025-11-06 10:19:49 +01:00
|
|
|
DfaVertex* m_lastVtxp = nullptr; // Last encountered vertex
|
2025-11-05 14:25:10 +01:00
|
|
|
bool m_underSExpr = false; // Is under sequence expression, for creating a start node
|
|
|
|
|
size_t m_underLogNots = 0; // Number of 'not' operators before sequence
|
2025-10-13 11:26:25 +02:00
|
|
|
|
2025-11-06 10:19:49 +01:00
|
|
|
DfaStmtVertex* makeClause(AstSExpr* nodep, bool pass) {
|
|
|
|
|
return new DfaStmtVertex{
|
2025-11-06 10:54:05 +01:00
|
|
|
&m_graph,
|
2025-11-06 10:19:49 +01:00
|
|
|
new AstPExprClause{nodep->fileline(), m_underLogNots % 2 == 0 ? pass : !pass}};
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-13 11:26:25 +02:00
|
|
|
// VISITORS
|
|
|
|
|
void visit(AstNodeCoverOrAssert* nodep) override { iterateChildrenConst(nodep); }
|
|
|
|
|
void visit(AstLogNot* nodep) override {
|
|
|
|
|
VL_RESTORER(m_underLogNots);
|
|
|
|
|
++m_underLogNots;
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstSExpr* nodep) override {
|
|
|
|
|
|
|
|
|
|
if (VN_IS(nodep->exprp(), SExpr)) {
|
|
|
|
|
VL_RESTORER(m_underSExpr);
|
|
|
|
|
m_underSExpr = true;
|
|
|
|
|
iterateConst(nodep->exprp());
|
|
|
|
|
} else {
|
2025-11-05 14:40:13 +01:00
|
|
|
DfaExprVertex* const exprVtxp
|
2025-11-06 10:54:05 +01:00
|
|
|
= new DfaExprVertex{&m_graph, nodep->exprp()->unlinkFrBack()};
|
|
|
|
|
new DfaConditionEdge{&m_graph, exprVtxp, makeClause(nodep, true), true};
|
|
|
|
|
new DfaConditionEdge{&m_graph, exprVtxp, makeClause(nodep, false), false};
|
2025-11-06 10:19:49 +01:00
|
|
|
m_lastVtxp = exprVtxp;
|
2025-10-13 11:26:25 +02:00
|
|
|
}
|
|
|
|
|
|
2025-11-06 10:19:49 +01:00
|
|
|
DfaExprVertex* const startVtxp
|
2025-11-06 10:54:05 +01:00
|
|
|
= m_underSExpr ? nullptr : new DfaExprVertex{&m_graph, nodep};
|
2025-10-13 11:26:25 +02:00
|
|
|
|
2025-11-05 14:40:13 +01:00
|
|
|
DfaStmtVertex* const dlyVtxp
|
2025-11-06 10:54:05 +01:00
|
|
|
= new DfaStmtVertex{&m_graph, nodep->delayp()->unlinkFrBack()};
|
2025-10-13 11:26:25 +02:00
|
|
|
|
|
|
|
|
if (AstSExpr* const sexprp = VN_CAST(nodep->preExprp(), SExpr)) {
|
|
|
|
|
UASSERT_OBJ(!sexprp->preExprp() && !VN_IS(sexprp->exprp(), SExpr), sexprp,
|
|
|
|
|
"Incorrect sexpr tree");
|
2025-11-05 14:40:13 +01:00
|
|
|
DfaStmtVertex* const sdlyVtxp
|
2025-11-06 10:54:05 +01:00
|
|
|
= new DfaStmtVertex{&m_graph, sexprp->delayp()->unlinkFrBack()};
|
2025-11-05 14:40:13 +01:00
|
|
|
DfaExprVertex* const exprVtxp
|
2025-11-06 10:54:05 +01:00
|
|
|
= new DfaExprVertex{&m_graph, sexprp->exprp()->unlinkFrBack()};
|
2025-10-13 11:26:25 +02:00
|
|
|
|
2025-11-06 10:54:05 +01:00
|
|
|
if (startVtxp) new DfaConditionEdge{&m_graph, startVtxp, sdlyVtxp, true};
|
|
|
|
|
new DfaConditionEdge{&m_graph, sdlyVtxp, exprVtxp, true};
|
|
|
|
|
new DfaConditionEdge{&m_graph, exprVtxp, dlyVtxp, true};
|
|
|
|
|
new DfaConditionEdge{&m_graph, dlyVtxp, m_lastVtxp, true};
|
|
|
|
|
new DfaConditionEdge{&m_graph, exprVtxp, makeClause(nodep, false), false};
|
2025-10-13 11:26:25 +02:00
|
|
|
|
|
|
|
|
// This case only occurs when multi-delay sequence starts with an expression,
|
|
|
|
|
// don't set last as this is never a last expression.
|
|
|
|
|
} else if (nodep->preExprp()) {
|
2025-11-05 14:40:13 +01:00
|
|
|
DfaExprVertex* const preVtxp
|
2025-11-06 10:54:05 +01:00
|
|
|
= new DfaExprVertex{&m_graph, nodep->preExprp()->unlinkFrBack()};
|
|
|
|
|
if (startVtxp) new DfaConditionEdge{&m_graph, startVtxp, preVtxp, true};
|
|
|
|
|
new DfaConditionEdge{&m_graph, preVtxp, dlyVtxp, true};
|
|
|
|
|
new DfaConditionEdge{&m_graph, dlyVtxp, m_lastVtxp, true};
|
|
|
|
|
new DfaConditionEdge{&m_graph, preVtxp, makeClause(nodep, false), false};
|
2025-11-06 10:19:49 +01:00
|
|
|
m_lastVtxp = preVtxp;
|
2025-10-13 11:26:25 +02:00
|
|
|
} else {
|
2025-11-06 10:54:05 +01:00
|
|
|
if (startVtxp) new DfaConditionEdge{&m_graph, startVtxp, dlyVtxp, true};
|
|
|
|
|
new DfaConditionEdge{&m_graph, dlyVtxp, m_lastVtxp, true};
|
2025-11-06 10:19:49 +01:00
|
|
|
m_lastVtxp = dlyVtxp;
|
2025-10-13 11:26:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
|
|
|
|
void visit(AstConstPool* nodep) override {}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2025-11-06 10:54:05 +01:00
|
|
|
explicit AssertPropBuildVisitor(AstNetlist* nodep, V3Graph& graph)
|
|
|
|
|
: m_graph{graph} {
|
2025-10-13 11:26:25 +02:00
|
|
|
iterateConst(nodep);
|
2025-11-06 10:54:05 +01:00
|
|
|
if (dumpGraphLevel() >= 6) m_graph.dumpDotFilePrefixedAlways("properties", true);
|
2025-10-13 11:26:25 +02:00
|
|
|
}
|
2025-11-05 14:40:13 +01:00
|
|
|
~AssertPropBuildVisitor() override = default;
|
2025-10-13 11:26:25 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Transform property graph into AST
|
2025-11-05 14:40:13 +01:00
|
|
|
class AssertPropTransformer final {
|
2025-10-13 11:26:25 +02:00
|
|
|
// STATE
|
|
|
|
|
V3UniqueNames m_assertCycleDelayNames{"__Vassert"}; // Names for assertion properties
|
2025-11-06 10:54:05 +01:00
|
|
|
V3Graph& m_graph; // Property tree
|
2025-10-13 11:26:25 +02:00
|
|
|
AstPExpr* m_pexprp = nullptr; // Currently built property sequence
|
|
|
|
|
AstBegin* m_current = nullptr; // Currently built block
|
|
|
|
|
|
|
|
|
|
V3GraphVertex* processVtx(V3GraphVertex* vtxp) {
|
2025-11-05 14:40:13 +01:00
|
|
|
if (DfaStmtVertex* const stmtp = vtxp->cast<DfaStmtVertex>()) return processVtx(stmtp);
|
|
|
|
|
if (DfaExprVertex* const exprp = vtxp->cast<DfaExprVertex>()) return processVtx(exprp);
|
2025-10-13 11:26:25 +02:00
|
|
|
// TODO use C++17 std::variant and std::visit
|
2025-11-06 10:12:23 +01:00
|
|
|
v3fatalSrc("Unexpected vertex type");
|
2025-10-13 11:26:25 +02:00
|
|
|
return nullptr;
|
|
|
|
|
}
|
2025-11-05 14:40:13 +01:00
|
|
|
V3GraphVertex* processVtx(DfaStmtVertex* vtxp) {
|
2025-10-13 11:26:25 +02:00
|
|
|
UASSERT_OBJ(!vtxp->isStart(), vtxp->nodep(),
|
|
|
|
|
"Starting node should be a property expression");
|
|
|
|
|
UASSERT_OBJ(m_current, vtxp->nodep(), "Should be under a block");
|
|
|
|
|
m_current->addStmtsp(vtxp->nodep());
|
|
|
|
|
return processEdge(vtxp->outEdges().frontp());
|
|
|
|
|
}
|
2025-11-05 14:40:13 +01:00
|
|
|
V3GraphVertex* processVtx(DfaExprVertex* vtxp) {
|
2025-10-13 11:26:25 +02:00
|
|
|
AstNode* const nodep = vtxp->nodep();
|
|
|
|
|
if (vtxp->isStart()) {
|
|
|
|
|
AstBegin* const bodyp = new AstBegin{
|
|
|
|
|
nodep->fileline(), m_assertCycleDelayNames.get(nodep) + "__block", nullptr, true};
|
|
|
|
|
m_pexprp = new AstPExpr{nodep->fileline(), bodyp, nodep->dtypep()};
|
2025-11-06 11:51:46 +01:00
|
|
|
UASSERT_OBJ(vtxp->outSize1(), nodep, "Starting node must have one out edge");
|
2025-10-13 11:26:25 +02:00
|
|
|
m_current = m_pexprp->bodyp();
|
|
|
|
|
return processEdge(vtxp->outEdges().frontp());
|
|
|
|
|
}
|
2025-11-06 11:51:46 +01:00
|
|
|
UASSERT_OBJ(vtxp->outEdges().size() == 2, nodep, "Each expression must have two branches");
|
2025-10-13 11:26:25 +02:00
|
|
|
AstBegin* const passsp = new AstBegin{
|
|
|
|
|
nodep->fileline(), m_assertCycleDelayNames.get(nodep) + "__block_pass", nullptr, true};
|
2025-11-06 11:51:46 +01:00
|
|
|
AstNode* const failsp = [vtxp]() {
|
|
|
|
|
V3GraphVertex* const failVtxp = vtxp->outEdges().backp()->top();
|
|
|
|
|
return failVtxp->as<DfaStmtVertex>()->nodep();
|
2025-10-13 11:26:25 +02:00
|
|
|
}();
|
|
|
|
|
|
|
|
|
|
AstSampled* const sampledp
|
|
|
|
|
= new AstSampled{nodep->fileline(), VN_AS(vtxp->nodep(), NodeExpr)};
|
|
|
|
|
sampledp->dtypeFrom(vtxp->nodep());
|
|
|
|
|
AstIf* const ifp = new AstIf{nodep->fileline(), sampledp, passsp, failsp};
|
|
|
|
|
m_current->addStmtsp(ifp);
|
|
|
|
|
m_current = passsp;
|
|
|
|
|
return processEdge(vtxp->outEdges().frontp());
|
|
|
|
|
}
|
|
|
|
|
V3GraphVertex* processEdge(V3GraphEdge* edgep) {
|
|
|
|
|
if (edgep) return processVtx(edgep->top());
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2025-11-06 10:54:05 +01:00
|
|
|
explicit AssertPropTransformer(V3Graph& graph)
|
|
|
|
|
: m_graph{graph} {
|
|
|
|
|
for (V3GraphVertex& vtx : m_graph.vertices()) {
|
2025-11-05 14:40:13 +01:00
|
|
|
if (DfaVertex* const dVtxp = vtx.cast<DfaExprVertex>()) {
|
2025-10-13 11:26:25 +02:00
|
|
|
if (dVtxp->isStart()) {
|
|
|
|
|
VL_RESTORER(m_pexprp);
|
|
|
|
|
processVtx(&vtx);
|
|
|
|
|
AstSExpr* const propp = VN_AS(dVtxp->nodep(), SExpr);
|
|
|
|
|
propp->replaceWith(m_pexprp);
|
2025-11-06 10:13:58 +01:00
|
|
|
VL_DO_DANGLING(propp->deleteTree(), propp);
|
2025-10-13 11:26:25 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2025-11-05 14:40:13 +01:00
|
|
|
// Top AssertProp class
|
2025-10-13 11:26:25 +02:00
|
|
|
|
2025-11-05 14:40:13 +01:00
|
|
|
void V3AssertProp::assertPropAll(AstNetlist* nodep) {
|
2025-10-13 11:26:25 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
|
|
|
|
{
|
2025-11-06 10:54:05 +01:00
|
|
|
V3Graph graph;
|
|
|
|
|
{ AssertPropBuildVisitor{nodep, graph}; }
|
|
|
|
|
AssertPropTransformer{graph};
|
2025-10-13 11:26:25 +02:00
|
|
|
}
|
|
|
|
|
V3Global::dumpCheckGlobalTree("assertproperties", 0, dumpTreeEitherLevel() >= 3);
|
|
|
|
|
}
|