mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 10:03:44 +02:00
192 lines
8.3 KiB
C++
192 lines
8.3 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
//*************************************************************************
|
|
// DESCRIPTION: Verilator: Resolve covergroup reference formal arguments for
|
|
// scheduling
|
|
//
|
|
// Code available from: https://verilator.org
|
|
//
|
|
//*************************************************************************
|
|
//
|
|
// 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-FileCopyrightText: 2003-2026 Wilson Snyder
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
|
//
|
|
//*************************************************************************
|
|
// V3SchedCovergroup's Transformations:
|
|
//
|
|
// None - this only gathers information.
|
|
//
|
|
// A covergroup 'ref'/'const ref' formal argument becomes a pointer member of the covergroup
|
|
// class, bound when the covergroup is constructed. A sample() reading through it therefore
|
|
// holds no AstVarRef naming the design variable it reads, and V3Order would see the sampling
|
|
// block as reading nothing. Record what each construction binds, so V3Order can attribute
|
|
// those reads to the blocks that call sample().
|
|
//
|
|
// Bindings are keyed by the constructed handle where that is exact, which it is when every
|
|
// write of the handle is a construction of the recognized shape 'handle = new(...)'. Any other
|
|
// way for a covergroup object to reach a handle - an aliasing assignment, a temporary
|
|
// introduced by V3Task, an array element, passing the handle to a function by reference - is
|
|
// itself a write of that handle, and taints it. A tainted handle falls back to the union over
|
|
// all constructions of its class, which is an over-approximation, but safe.
|
|
//
|
|
//*************************************************************************
|
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
#include "V3AstNodeExpr.h"
|
|
#include "V3Sched.h"
|
|
|
|
#include <unordered_set>
|
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
namespace V3Sched {
|
|
|
|
const CovergroupRefBindings::Bindings CovergroupRefBindings::s_none;
|
|
|
|
void CovergroupRefBindings::addConstruction(const AstClass* classp, const AstVarScope* instp,
|
|
const Bindings& bindings) {
|
|
Bindings& classBindings = m_byClass[classp];
|
|
classBindings.insert(classBindings.end(), bindings.begin(), bindings.end());
|
|
// Note this creates an entry even when 'bindings' is empty, so that a handle binding
|
|
// nothing stays distinguishable from a handle we know nothing about
|
|
if (instp) {
|
|
Bindings& instBindings = m_byInstance[instp];
|
|
instBindings.insert(instBindings.end(), bindings.begin(), bindings.end());
|
|
}
|
|
}
|
|
|
|
const CovergroupRefBindings::Bindings&
|
|
CovergroupRefBindings::forSample(const AstVarScope* instp, const AstClass* classp) const {
|
|
if (instp) {
|
|
const auto it = m_byInstance.find(instp);
|
|
if (it != m_byInstance.end()) {
|
|
++m_numExactCalls;
|
|
return it->second;
|
|
}
|
|
}
|
|
const auto it = m_byClass.find(classp);
|
|
if (it != m_byClass.end()) {
|
|
++m_numUnionCalls;
|
|
return it->second;
|
|
}
|
|
// A covergroup with no reference formal at all
|
|
return s_none;
|
|
}
|
|
|
|
class CovergroupRefBindVisitor final : public VNVisitorConst {
|
|
// STATE
|
|
CovergroupRefBindings m_bindings; // Result
|
|
// Covergroup handles written by something other than a construction we recognized
|
|
std::unordered_set<const AstVarScope*> m_tainted;
|
|
|
|
// METHODS
|
|
|
|
// The covergroup class this call constructs, or nullptr if it is not a covergroup
|
|
// construction
|
|
static AstClass* constructedCovergroup(const AstNodeCCall* nodep) {
|
|
const AstCFunc* const funcp = nodep->funcp();
|
|
if (!funcp->isConstructor()) return nullptr;
|
|
// A constructor is a class method, so it is scoped and its scope is that of a class
|
|
AstClass* const classp = VN_AS(funcp->scopep()->modp(), Class);
|
|
return classp->isCovergroup() ? classp : nullptr;
|
|
}
|
|
|
|
// True if this variable holds a handle to a covergroup object
|
|
static bool isCovergroupHandle(const AstVar* varp) {
|
|
const AstClassRefDType* const dtypep = VN_CAST(varp->dtypep()->skipRefp(), ClassRefDType);
|
|
return dtypep && dtypep->classp()->isCovergroup();
|
|
}
|
|
|
|
// True if this constructor takes a reference formal, and so has anything to bind
|
|
static bool hasRefFormal(const AstCFunc* funcp) {
|
|
for (AstNode* portp = funcp->argsp(); portp; portp = portp->nextp()) {
|
|
const AstVar* const varp = VN_AS(portp, Var);
|
|
if (varp->declDirection().isRef() || varp->declDirection().isConstRef()) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Record one construction of 'classp' assigning to 'instp' (nullptr if not identified).
|
|
// A covergroup with no reference formal has nothing to bind, and is left out entirely, so
|
|
// that an entry with no bindings means only 'this handle binds nothing a sample() reads'.
|
|
void recordConstruction(AstNodeCCall* nodep, const AstClass* classp,
|
|
const AstVarScope* instp) {
|
|
if (!hasRefFormal(nodep->funcp())) return;
|
|
CovergroupRefBindings::Bindings bindings;
|
|
// Actuals correspond one to one, in order, with the function's argument variables.
|
|
// A constructor returns void, so none of them is a return value variable.
|
|
AstNode* actualp = nodep->argsp();
|
|
for (AstNode* portp = nodep->funcp()->argsp(); portp; portp = portp->nextp()) {
|
|
AstNode* const thisActualp = actualp;
|
|
actualp = actualp->nextp();
|
|
const AstVar* const varp = VN_AS(portp, Var);
|
|
if (!varp->declDirection().isRef() && !varp->declDirection().isConstRef()) continue;
|
|
// A 'ref' actual is an lvalue expression, so it need not be a plain variable
|
|
// reference. Bind every variable it names: for 'sigs[0]' that is 'sigs', which is
|
|
// exact rather than approximate, as V3Order models the whole array as one
|
|
// VarScope. An index expression contributes its own variables as well, which is an
|
|
// over-approximation, and so safe.
|
|
thisActualp->foreach([&](AstVarRef* refp) {
|
|
AstVarScope* const vscp = refp->varScopep();
|
|
bindings.push_back(vscp);
|
|
});
|
|
}
|
|
m_bindings.addConstruction(classp, instp, bindings);
|
|
}
|
|
|
|
// VISITORS
|
|
void visit(AstNodeAssign* nodep) override {
|
|
AstCNew* const cnewp = VN_CAST(nodep->rhsp(), CNew);
|
|
AstVarRef* const lhsRefp = VN_CAST(nodep->lhsp(), VarRef);
|
|
AstClass* const classp = cnewp && lhsRefp ? constructedCovergroup(cnewp) : nullptr;
|
|
if (!classp) {
|
|
iterateChildrenConst(nodep);
|
|
return;
|
|
}
|
|
recordConstruction(cnewp, classp, lhsRefp->varScopep());
|
|
// Deliberately not iterating the destination: this write is the one shape that does not
|
|
// taint the handle. Do iterate the arguments, which may write handles of their own.
|
|
iterateChildrenConst(cnewp);
|
|
}
|
|
|
|
void visit(AstNodeCCall* nodep) override {
|
|
iterateChildrenConst(nodep);
|
|
// A construction reached only here is one whose destination we could not identify, so
|
|
// every handle of the class must assume it
|
|
if (const AstClass* const classp = constructedCovergroup(nodep)) {
|
|
recordConstruction(nodep, classp, nullptr);
|
|
}
|
|
}
|
|
|
|
void visit(AstVarRef* nodep) override {
|
|
if (!nodep->access().isWriteOrRW()) return;
|
|
if (!isCovergroupHandle(nodep->varp())) return;
|
|
m_tainted.emplace(nodep->varScopep());
|
|
}
|
|
|
|
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
|
|
|
public:
|
|
// CONSTRUCTORS
|
|
explicit CovergroupRefBindVisitor(AstNetlist* nodep) {
|
|
iterateConst(nodep);
|
|
for (const AstVarScope* const vscp : m_tainted) m_bindings.dropInstance(vscp);
|
|
}
|
|
~CovergroupRefBindVisitor() override = default;
|
|
|
|
// METHODS
|
|
CovergroupRefBindings take_bindings() { return std::move(m_bindings); }
|
|
};
|
|
|
|
const CovergroupRefBindings makeCovergroupRefBindings(AstNetlist* nodep) {
|
|
UINFO(2, __FUNCTION__ << ":");
|
|
CovergroupRefBindings bindings{};
|
|
if (v3Global.useCovergroup()) bindings = CovergroupRefBindVisitor{nodep}.take_bindings();
|
|
return bindings;
|
|
}
|
|
|
|
} // namespace V3Sched
|