mirror of
https://github.com/verilator/verilator.git
synced 2026-08-22 05:57:29 +02:00
Support clocking blocks in virtual interfaces (#5235)
This commit is contained in:
@@ -142,6 +142,7 @@ set(HEADERS
|
|||||||
V3Randomize.h
|
V3Randomize.h
|
||||||
V3Reloop.h
|
V3Reloop.h
|
||||||
V3Rtti.h
|
V3Rtti.h
|
||||||
|
V3Sampled.h
|
||||||
V3Sched.h
|
V3Sched.h
|
||||||
V3Scope.h
|
V3Scope.h
|
||||||
V3Scoreboard.h
|
V3Scoreboard.h
|
||||||
@@ -288,6 +289,7 @@ set(COMMON_SOURCES
|
|||||||
V3ProtectLib.cpp
|
V3ProtectLib.cpp
|
||||||
V3Randomize.cpp
|
V3Randomize.cpp
|
||||||
V3Reloop.cpp
|
V3Reloop.cpp
|
||||||
|
V3Sampled.cpp
|
||||||
V3Sched.cpp
|
V3Sched.cpp
|
||||||
V3SchedAcyclic.cpp
|
V3SchedAcyclic.cpp
|
||||||
V3SchedPartition.cpp
|
V3SchedPartition.cpp
|
||||||
|
|||||||
@@ -284,6 +284,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||||||
V3ProtectLib.o \
|
V3ProtectLib.o \
|
||||||
V3Randomize.o \
|
V3Randomize.o \
|
||||||
V3Reloop.o \
|
V3Reloop.o \
|
||||||
|
V3Sampled.o \
|
||||||
V3Sched.o \
|
V3Sched.o \
|
||||||
V3SchedAcyclic.o \
|
V3SchedAcyclic.o \
|
||||||
V3SchedPartition.o \
|
V3SchedPartition.o \
|
||||||
|
|||||||
@@ -440,6 +440,7 @@ class AssertVisitor final : public VNVisitor {
|
|||||||
AstSampled* const newp = newSampledExpr(nodep);
|
AstSampled* const newp = newSampledExpr(nodep);
|
||||||
relinkHandle.relink(newp);
|
relinkHandle.relink(newp);
|
||||||
newp->user1(1);
|
newp->user1(1);
|
||||||
|
v3Global.setHasSampled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -357,6 +357,26 @@ private:
|
|||||||
nodep->user1(true);
|
nodep->user1(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstMemberSel* nodep) override {
|
||||||
|
if (AstClockingItem* const itemp = VN_CAST(
|
||||||
|
nodep->varp()->user1p() ? nodep->varp()->user1p() : nodep->varp()->firstAbovep(),
|
||||||
|
ClockingItem)) {
|
||||||
|
if (nodep->access().isReadOrRW() && itemp->direction() == VDirection::OUTPUT) {
|
||||||
|
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2023 14.3)");
|
||||||
|
}
|
||||||
|
if (nodep->access().isWriteOrRW()) {
|
||||||
|
if (itemp->direction() == VDirection::OUTPUT) {
|
||||||
|
if (!m_inAssignDlyLhs) {
|
||||||
|
nodep->v3error("Only non-blocking assignments can write "
|
||||||
|
"to clockvars (IEEE 1800-2023 14.16)");
|
||||||
|
}
|
||||||
|
if (m_inAssign) m_inSynchDrive = true;
|
||||||
|
} else if (itemp->direction() == VDirection::INPUT) {
|
||||||
|
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
void visit(AstNodeAssign* nodep) override {
|
void visit(AstNodeAssign* nodep) override {
|
||||||
if (nodep->user1()) return;
|
if (nodep->user1()) return;
|
||||||
VL_RESTORER(m_inAssign);
|
VL_RESTORER(m_inAssign);
|
||||||
|
|||||||
+9
-49
@@ -68,14 +68,10 @@ public:
|
|||||||
|
|
||||||
class ClockVisitor final : public VNVisitor {
|
class ClockVisitor final : public VNVisitor {
|
||||||
// NODE STATE
|
// NODE STATE
|
||||||
// ???
|
|
||||||
const VNUser1InUse m_user1InUse;
|
|
||||||
// STATE
|
// STATE
|
||||||
AstCFunc* m_evalp = nullptr; // The '_eval' function
|
AstCFunc* m_evalp = nullptr; // The '_eval' function
|
||||||
AstScope* m_scopep = nullptr; // Current scope
|
|
||||||
AstSenTree* m_lastSenp = nullptr; // Last sensitivity match, so we can detect duplicates.
|
AstSenTree* m_lastSenp = nullptr; // Last sensitivity match, so we can detect duplicates.
|
||||||
AstIf* m_lastIfp = nullptr; // Last sensitivity if active to add more under
|
AstIf* m_lastIfp = nullptr; // Last sensitivity if active to add more under
|
||||||
bool m_inSampled = false; // True inside a sampled expression
|
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
@@ -88,24 +84,6 @@ class ClockVisitor final : public VNVisitor {
|
|||||||
}
|
}
|
||||||
return senEqnp;
|
return senEqnp;
|
||||||
}
|
}
|
||||||
AstVarScope* createSampledVar(AstVarScope* vscp) {
|
|
||||||
if (vscp->user1p()) return VN_AS(vscp->user1p(), VarScope);
|
|
||||||
const AstVar* const varp = vscp->varp();
|
|
||||||
const string newvarname
|
|
||||||
= string{"__Vsampled__"} + vscp->scopep()->nameDotless() + "__" + varp->name();
|
|
||||||
FileLine* const flp = vscp->fileline();
|
|
||||||
AstVar* const newvarp = new AstVar{flp, VVarType::MODULETEMP, newvarname, varp->dtypep()};
|
|
||||||
m_scopep->modp()->addStmtsp(newvarp);
|
|
||||||
AstVarScope* const newvscp = new AstVarScope{flp, m_scopep, newvarp};
|
|
||||||
vscp->user1p(newvscp);
|
|
||||||
m_scopep->addVarsp(newvscp);
|
|
||||||
// At the top of _eval, assign them
|
|
||||||
AstAssign* const finalp = new AstAssign{flp, new AstVarRef{flp, newvscp, VAccess::WRITE},
|
|
||||||
new AstVarRef{flp, vscp, VAccess::READ}};
|
|
||||||
m_evalp->addInitsp(finalp);
|
|
||||||
UINFO(4, "New Sampled: " << newvscp << endl);
|
|
||||||
return newvscp;
|
|
||||||
}
|
|
||||||
AstIf* makeActiveIf(AstSenTree* sensesp) {
|
AstIf* makeActiveIf(AstSenTree* sensesp) {
|
||||||
AstNodeExpr* const senEqnp = createSenseEquation(sensesp->sensesp());
|
AstNodeExpr* const senEqnp = createSenseEquation(sensesp->sensesp());
|
||||||
UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation.");
|
UASSERT_OBJ(senEqnp, sensesp, "No sense equation, shouldn't be in sequent activation.");
|
||||||
@@ -179,33 +157,15 @@ class ClockVisitor final : public VNVisitor {
|
|||||||
clearLastSen();
|
clearLastSen();
|
||||||
}
|
}
|
||||||
|
|
||||||
//========== Create sampled values
|
//========== Move sampled assignments
|
||||||
void visit(AstScope* nodep) override {
|
void visit(AstVarScope* nodep) override {
|
||||||
VL_RESTORER(m_scopep);
|
AstVar* varp = nodep->varp();
|
||||||
{
|
if (varp->valuep() && varp->name().substr(0, strlen("__Vsampled")) == "__Vsampled") {
|
||||||
m_scopep = nodep;
|
m_evalp->addInitsp(new AstAssign{
|
||||||
iterateChildren(nodep);
|
nodep->fileline(), new AstVarRef{nodep->fileline(), nodep, VAccess::WRITE},
|
||||||
}
|
VN_AS(varp->valuep()->unlinkFrBack(), NodeExpr)});
|
||||||
}
|
varp->direction(VDirection::NONE); // Restore defaults
|
||||||
void visit(AstSampled* nodep) override {
|
varp->primaryIO(false);
|
||||||
VL_RESTORER(m_inSampled);
|
|
||||||
{
|
|
||||||
m_inSampled = true;
|
|
||||||
iterateChildren(nodep);
|
|
||||||
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
|
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void visit(AstVarRef* nodep) override {
|
|
||||||
iterateChildren(nodep);
|
|
||||||
if (m_inSampled && !nodep->user1SetOnce()) {
|
|
||||||
UASSERT_OBJ(nodep->access().isReadOnly(), nodep, "Should have failed in V3Access");
|
|
||||||
AstVarScope* const varscp = nodep->varScopep();
|
|
||||||
AstVarScope* const lastscp = createSampledVar(varscp);
|
|
||||||
AstNode* const newp = new AstVarRef{nodep->fileline(), lastscp, VAccess::READ};
|
|
||||||
newp->user1SetOnce(); // Don't sample this one
|
|
||||||
nodep->replaceWith(newp);
|
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,6 +114,7 @@ class V3Global final {
|
|||||||
bool m_dpi = false; // Need __Dpi include files
|
bool m_dpi = false; // Need __Dpi include files
|
||||||
bool m_hasEvents = false; // Design uses SystemVerilog named events
|
bool m_hasEvents = false; // Design uses SystemVerilog named events
|
||||||
bool m_hasClasses = false; // Design uses SystemVerilog classes
|
bool m_hasClasses = false; // Design uses SystemVerilog classes
|
||||||
|
bool m_hasSampled = false; // Design uses SAMPLED expresions
|
||||||
bool m_hasVirtIfaces = false; // Design uses virtual interfaces
|
bool m_hasVirtIfaces = false; // Design uses virtual interfaces
|
||||||
bool m_usesProbDist = false; // Uses $dist_*
|
bool m_usesProbDist = false; // Uses $dist_*
|
||||||
bool m_usesStdPackage = false; // Design uses the std package
|
bool m_usesStdPackage = false; // Design uses the std package
|
||||||
@@ -168,6 +169,8 @@ public:
|
|||||||
void setHasEvents() { m_hasEvents = true; }
|
void setHasEvents() { m_hasEvents = true; }
|
||||||
bool hasClasses() const { return m_hasClasses; }
|
bool hasClasses() const { return m_hasClasses; }
|
||||||
void setHasClasses() { m_hasClasses = true; }
|
void setHasClasses() { m_hasClasses = true; }
|
||||||
|
bool hasSampled() const { return m_hasSampled; }
|
||||||
|
void setHasSampled() { m_hasSampled = true; }
|
||||||
bool hasVirtIfaces() const { return m_hasVirtIfaces; }
|
bool hasVirtIfaces() const { return m_hasVirtIfaces; }
|
||||||
void setHasVirtIfaces() { m_hasVirtIfaces = true; }
|
void setHasVirtIfaces() { m_hasVirtIfaces = true; }
|
||||||
bool usesProbDist() const { return m_usesProbDist; }
|
bool usesProbDist() const { return m_usesProbDist; }
|
||||||
|
|||||||
+17
-8
@@ -2209,9 +2209,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||||||
new AstVarRef{clockingp->fileline(), eventp, VAccess::WRITE},
|
new AstVarRef{clockingp->fileline(), eventp, VAccess::WRITE},
|
||||||
false}});
|
false}});
|
||||||
v3Global.setHasEvents();
|
v3Global.setHasEvents();
|
||||||
eventp->user1p(new VSymEnt{m_statep->symsp(), eventp});
|
|
||||||
}
|
}
|
||||||
return reinterpret_cast<VSymEnt*>(clockingp->eventp()->user1p());
|
AstVar* const eventp = clockingp->eventp();
|
||||||
|
if (!eventp->user1p()) eventp->user1p(new VSymEnt{m_statep->symsp(), eventp});
|
||||||
|
return reinterpret_cast<VSymEnt*>(eventp->user1p());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isParamedClassRefDType(const AstNode* classp) {
|
bool isParamedClassRefDType(const AstNode* classp) {
|
||||||
@@ -3045,7 +3046,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||||||
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
|
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
|
||||||
okSymp); // Maybe nullptr
|
okSymp); // Maybe nullptr
|
||||||
if (!m_statep->forScopeCreation()) {
|
if (!m_statep->forScopeCreation()) {
|
||||||
VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
|
||||||
|
if (m_inSens && foundp) {
|
||||||
|
if (AstClocking* const clockingp = VN_CAST(foundp->nodep(), Clocking)) {
|
||||||
|
foundp = getCreateClockingEventSymEnt(clockingp);
|
||||||
|
}
|
||||||
|
}
|
||||||
AstVar* const varp
|
AstVar* const varp
|
||||||
= foundp ? foundToVarp(foundp, nodep, nodep->access()) : nullptr;
|
= foundp ? foundToVarp(foundp, nodep, nodep->access()) : nullptr;
|
||||||
nodep->varp(varp);
|
nodep->varp(varp);
|
||||||
@@ -3062,11 +3068,14 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||||||
// AstVarXRef's even though they are in the same module detect
|
// AstVarXRef's even though they are in the same module detect
|
||||||
// this and convert to normal VarRefs
|
// this and convert to normal VarRefs
|
||||||
if (!m_statep->forPrearray() && !m_statep->forScopeCreation()) {
|
if (!m_statep->forPrearray() && !m_statep->forScopeCreation()) {
|
||||||
if (VN_IS(nodep->dtypep(), IfaceRefDType)) {
|
if (const AstIfaceRefDType* const ifaceDtp
|
||||||
AstVarRef* const newrefp
|
= VN_CAST(nodep->dtypep(), IfaceRefDType)) {
|
||||||
= new AstVarRef{nodep->fileline(), nodep->varp(), nodep->access()};
|
if (!ifaceDtp->isVirtual()) {
|
||||||
nodep->replaceWith(newrefp);
|
AstVarRef* const newrefp
|
||||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
= new AstVarRef{nodep->fileline(), nodep->varp(), nodep->access()};
|
||||||
|
nodep->replaceWith(newrefp);
|
||||||
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ class LinkLValueVisitor final : public VNVisitor {
|
|||||||
// so it is needed to check only if m_setContinuously is true
|
// so it is needed to check only if m_setContinuously is true
|
||||||
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
|
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
|
||||||
}
|
}
|
||||||
if (const AstClockingItem* itemp = VN_CAST(nodep->varp()->backp(), ClockingItem)) {
|
if (const AstClockingItem* const itemp
|
||||||
|
= VN_CAST(nodep->varp()->backp(), ClockingItem)) {
|
||||||
UINFO(5, "ClkOut " << nodep << endl);
|
UINFO(5, "ClkOut " << nodep << endl);
|
||||||
if (itemp->outputp()) nodep->varp(itemp->outputp()->varp());
|
if (itemp->outputp()) nodep->varp(itemp->outputp()->varp());
|
||||||
}
|
}
|
||||||
@@ -285,6 +286,13 @@ class LinkLValueVisitor final : public VNVisitor {
|
|||||||
void visit(AstMemberSel* nodep) override {
|
void visit(AstMemberSel* nodep) override {
|
||||||
if (m_setRefLvalue != VAccess::NOCHANGE) {
|
if (m_setRefLvalue != VAccess::NOCHANGE) {
|
||||||
nodep->access(m_setRefLvalue);
|
nodep->access(m_setRefLvalue);
|
||||||
|
if (nodep->varp() && nodep->access().isWriteOrRW()) {
|
||||||
|
if (const AstClockingItem* const itemp
|
||||||
|
= VN_CAST(nodep->varp()->backp(), ClockingItem)) {
|
||||||
|
UINFO(5, "ClkOut " << nodep << endl);
|
||||||
|
if (itemp->outputp()) nodep->varp(itemp->outputp()->varp());
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// It is the only place where the access is set to member select nodes.
|
// It is the only place where the access is set to member select nodes.
|
||||||
// If it doesn't have to be set to WRITE, it means that it is READ.
|
// If it doesn't have to be set to WRITE, it means that it is READ.
|
||||||
|
|||||||
@@ -58,6 +58,15 @@ class V3OrderProcessDomains final {
|
|||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
|
// Dump a domain for debugging
|
||||||
|
std::string debugDomain(AstSenTree* domainp) {
|
||||||
|
return cvtToHex(domainp)
|
||||||
|
+ (domainp == m_deleteDomainp ? " [DEL]"
|
||||||
|
: domainp->hasCombo() ? " [COMB]"
|
||||||
|
: domainp->isMulti() ? " [MULT]"
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
|
||||||
// Make a domain that merges the two domains
|
// Make a domain that merges the two domains
|
||||||
AstSenTree* combineDomains(AstSenTree* ap, AstSenTree* bp) {
|
AstSenTree* combineDomains(AstSenTree* ap, AstSenTree* bp) {
|
||||||
if (ap == bp) return ap;
|
if (ap == bp) return ap;
|
||||||
@@ -93,6 +102,7 @@ class V3OrderProcessDomains final {
|
|||||||
// For logic, start with the explicit hybrid sensitivities
|
// For logic, start with the explicit hybrid sensitivities
|
||||||
OrderLogicVertex* const lvtxp = vtxp->cast<OrderLogicVertex>();
|
OrderLogicVertex* const lvtxp = vtxp->cast<OrderLogicVertex>();
|
||||||
if (lvtxp) domainp = lvtxp->hybridp();
|
if (lvtxp) domainp = lvtxp->hybridp();
|
||||||
|
if (domainp) UINFO(6, " hybr d=" << debugDomain(domainp) << " " << vtxp << endl);
|
||||||
|
|
||||||
// For each incoming edge, examine the source vertex
|
// For each incoming edge, examine the source vertex
|
||||||
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
for (V3GraphEdge& edge : vtxp->inEdges()) {
|
||||||
@@ -104,6 +114,7 @@ class V3OrderProcessDomains final {
|
|||||||
|
|
||||||
AstSenTree* fromDomainp = fromVtxp->domainp();
|
AstSenTree* fromDomainp = fromVtxp->domainp();
|
||||||
|
|
||||||
|
UINFO(6, " from d=" << debugDomain(fromDomainp) << " " << fromVtxp << endl);
|
||||||
UASSERT(fromDomainp == m_deleteDomainp || !fromDomainp->hasCombo(),
|
UASSERT(fromDomainp == m_deleteDomainp || !fromDomainp->hasCombo(),
|
||||||
"There should be no need for combinational domains");
|
"There should be no need for combinational domains");
|
||||||
|
|
||||||
@@ -113,6 +124,8 @@ class V3OrderProcessDomains final {
|
|||||||
externalDomainps.clear();
|
externalDomainps.clear();
|
||||||
m_externalDomains(vscp, externalDomainps);
|
m_externalDomains(vscp, externalDomainps);
|
||||||
for (AstSenTree* const externalDomainp : externalDomainps) {
|
for (AstSenTree* const externalDomainp : externalDomainps) {
|
||||||
|
UINFO(6, " xtrn d=" << debugDomain(externalDomainp) << " " << fromVtxp
|
||||||
|
<< " because of " << vscp << endl);
|
||||||
UASSERT_OBJ(!externalDomainp->hasCombo(), vscp,
|
UASSERT_OBJ(!externalDomainp->hasCombo(), vscp,
|
||||||
"There should be no need for combinational domains");
|
"There should be no need for combinational domains");
|
||||||
fromDomainp = combineDomains(fromDomainp, externalDomainp);
|
fromDomainp = combineDomains(fromDomainp, externalDomainp);
|
||||||
@@ -140,12 +153,7 @@ class V3OrderProcessDomains final {
|
|||||||
// Set the domain of the vertex
|
// Set the domain of the vertex
|
||||||
vtxp->domainp(domainp);
|
vtxp->domainp(domainp);
|
||||||
|
|
||||||
UINFO(5, " done d=" << cvtToHex(domainp)
|
UINFO(5, " done d=" << debugDomain(domainp) << " " << vtxp << endl);
|
||||||
<< (domainp == m_deleteDomainp ? " [DEL]"
|
|
||||||
: domainp->hasCombo() ? " [COMB]"
|
|
||||||
: domainp->isMulti() ? " [MULT]"
|
|
||||||
: "")
|
|
||||||
<< " " << vtxp << endl);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||||||
std::vector<AstActive*> V3Order::createSerial(OrderGraph& graph, const std::string& tag,
|
std::vector<AstActive*> V3Order::createSerial(OrderGraph& graph, const std::string& tag,
|
||||||
const TrigToSenMap& trigToSen, bool slow) {
|
const TrigToSenMap& trigToSen, bool slow) {
|
||||||
|
|
||||||
UINFO(2, " Constructing serial code for '" + tag + "'");
|
UINFO(2, " Constructing serial code for '" + tag + "'\n");
|
||||||
|
|
||||||
// Build the move graph
|
// Build the move graph
|
||||||
OrderMoveDomScope::clear();
|
OrderMoveDomScope::clear();
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
//*************************************************************************
|
||||||
|
// DESCRIPTION: Verilator: Removal of SAMPLED
|
||||||
|
//
|
||||||
|
// Code available from: https://verilator.org
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Copyright 2003-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
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
// V3Sampled's Transformations:
|
||||||
|
//
|
||||||
|
// Top Scope:
|
||||||
|
// Replace each variable reference under SAMPLED with a new variable.
|
||||||
|
// Remove SAMPLED.
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
|
||||||
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||||
|
|
||||||
|
#include "V3Sampled.h"
|
||||||
|
|
||||||
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// Clock state, as a visitor of each AstNode
|
||||||
|
|
||||||
|
class SampledVisitor final : public VNVisitor {
|
||||||
|
// NODE STATE
|
||||||
|
// AstVarScope::user1() -> AstVarScope*. The VarScope that stores sampled value
|
||||||
|
// AstVarRef::user1() -> bool. Whether already converted
|
||||||
|
const VNUser1InUse m_user1InUse;
|
||||||
|
// STATE
|
||||||
|
AstScope* m_scopep = nullptr; // Current scope
|
||||||
|
bool m_inSampled = false; // True inside a sampled expression
|
||||||
|
|
||||||
|
// METHODS
|
||||||
|
|
||||||
|
AstVarScope* createSampledVar(AstVarScope* vscp) {
|
||||||
|
if (vscp->user1p()) return VN_AS(vscp->user1p(), VarScope);
|
||||||
|
const AstVar* const varp = vscp->varp();
|
||||||
|
const string newvarname
|
||||||
|
= "__Vsampled_" + vscp->scopep()->nameDotless() + "__" + varp->name();
|
||||||
|
FileLine* const flp = vscp->fileline();
|
||||||
|
AstVar* const newvarp = new AstVar{flp, VVarType::MODULETEMP, newvarname, varp->dtypep()};
|
||||||
|
m_scopep->modp()->addStmtsp(newvarp);
|
||||||
|
AstVarScope* const newvscp = new AstVarScope{flp, m_scopep, newvarp};
|
||||||
|
newvarp->direction(VDirection::INPUT); // Inform V3Sched that it will be driven later
|
||||||
|
newvarp->primaryIO(true);
|
||||||
|
vscp->user1p(newvscp);
|
||||||
|
m_scopep->addVarsp(newvscp);
|
||||||
|
// At the top of _eval, assign them (use valuep here as temporary storage during V3Sched)
|
||||||
|
newvarp->valuep(new AstVarRef{flp, vscp, VAccess::READ});
|
||||||
|
UINFO(4, "New Sampled: " << newvscp << endl);
|
||||||
|
return newvscp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VISITORS
|
||||||
|
void visit(AstScope* nodep) override {
|
||||||
|
VL_RESTORER(m_scopep);
|
||||||
|
m_scopep = nodep;
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
|
void visit(AstSampled* nodep) override {
|
||||||
|
VL_RESTORER(m_inSampled);
|
||||||
|
m_inSampled = true;
|
||||||
|
iterateChildren(nodep);
|
||||||
|
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
}
|
||||||
|
void visit(AstVarRef* nodep) override {
|
||||||
|
iterateChildren(nodep);
|
||||||
|
if (m_inSampled && !nodep->user1SetOnce()) {
|
||||||
|
UASSERT_OBJ(nodep->access().isReadOnly(), nodep, "Should have failed in V3Access");
|
||||||
|
AstVarScope* const varscp = nodep->varScopep();
|
||||||
|
AstVarScope* const lastscp = createSampledVar(varscp);
|
||||||
|
AstNode* const newp = new AstVarRef{nodep->fileline(), lastscp, VAccess::READ};
|
||||||
|
newp->user1SetOnce(); // Don't sample this one
|
||||||
|
nodep->replaceWith(newp);
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------
|
||||||
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
// CONSTRUCTORS
|
||||||
|
explicit SampledVisitor(AstNetlist* netlistp) { iterate(netlistp); }
|
||||||
|
~SampledVisitor() override = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
//######################################################################
|
||||||
|
// Sampled class functions
|
||||||
|
|
||||||
|
void V3Sampled::sampledAll(AstNetlist* nodep) {
|
||||||
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||||
|
{ SampledVisitor{nodep}; } // Destruct before checking
|
||||||
|
V3Global::dumpCheckGlobalTree("sampled", 0, dumpTreeEitherLevel() >= 3);
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||||
|
//*************************************************************************
|
||||||
|
// DESCRIPTION: Verilator: Removal of SAMPLED
|
||||||
|
//
|
||||||
|
// Code available from: https://verilator.org
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
//
|
||||||
|
// Copyright 2003-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
|
||||||
|
//
|
||||||
|
//*************************************************************************
|
||||||
|
|
||||||
|
#ifndef VERILATOR_V3SAMPLED_H_
|
||||||
|
#define VERILATOR_V3SAMPLED_H_
|
||||||
|
|
||||||
|
#include "config_build.h"
|
||||||
|
#include "verilatedos.h"
|
||||||
|
|
||||||
|
#include "V3ThreadSafety.h"
|
||||||
|
|
||||||
|
class AstNetlist;
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
class V3Sampled final {
|
||||||
|
public:
|
||||||
|
static void sampledAll(AstNetlist* nodep) VL_MT_DISABLED;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // Guard
|
||||||
+18
-8
@@ -1170,10 +1170,14 @@ void schedule(AstNetlist* netlistp) {
|
|||||||
// (including combinationally generated signals) are computed within the act region.
|
// (including combinationally generated signals) are computed within the act region.
|
||||||
LogicRegions logicRegions
|
LogicRegions logicRegions
|
||||||
= partition(logicClasses.m_clocked, logicClasses.m_comb, logicClasses.m_hybrid);
|
= partition(logicClasses.m_clocked, logicClasses.m_comb, logicClasses.m_hybrid);
|
||||||
|
logicRegions.m_obs = logicClasses.m_observed;
|
||||||
|
logicRegions.m_react = logicClasses.m_reactive;
|
||||||
if (v3Global.opt.stats()) {
|
if (v3Global.opt.stats()) {
|
||||||
addSizeStat("size of region: Active Pre", logicRegions.m_pre);
|
addSizeStat("size of region: Active Pre", logicRegions.m_pre);
|
||||||
addSizeStat("size of region: Active", logicRegions.m_act);
|
addSizeStat("size of region: Active", logicRegions.m_act);
|
||||||
addSizeStat("size of region: NBA", logicRegions.m_nba);
|
addSizeStat("size of region: NBA", logicRegions.m_nba);
|
||||||
|
addSizeStat("size of region: Observed", logicRegions.m_obs);
|
||||||
|
addSizeStat("size of region: Reactive", logicRegions.m_react);
|
||||||
V3Stats::statsStage("sched-partition");
|
V3Stats::statsStage("sched-partition");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1183,6 +1187,8 @@ void schedule(AstNetlist* netlistp) {
|
|||||||
addSizeStat("size of replicated logic: Input", logicReplicas.m_ico);
|
addSizeStat("size of replicated logic: Input", logicReplicas.m_ico);
|
||||||
addSizeStat("size of replicated logic: Active", logicReplicas.m_act);
|
addSizeStat("size of replicated logic: Active", logicReplicas.m_act);
|
||||||
addSizeStat("size of replicated logic: NBA", logicReplicas.m_nba);
|
addSizeStat("size of replicated logic: NBA", logicReplicas.m_nba);
|
||||||
|
addSizeStat("size of replicated logic: Observed", logicReplicas.m_obs);
|
||||||
|
addSizeStat("size of replicated logic: Reactive", logicReplicas.m_react);
|
||||||
V3Stats::statsStage("sched-replicate");
|
V3Stats::statsStage("sched-replicate");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1207,8 +1213,8 @@ void schedule(AstNetlist* netlistp) {
|
|||||||
const auto& senTreeps = getSenTreesUsedBy({&logicRegions.m_pre, //
|
const auto& senTreeps = getSenTreesUsedBy({&logicRegions.m_pre, //
|
||||||
&logicRegions.m_act, //
|
&logicRegions.m_act, //
|
||||||
&logicRegions.m_nba, //
|
&logicRegions.m_nba, //
|
||||||
&logicClasses.m_observed, //
|
&logicRegions.m_obs, //
|
||||||
&logicClasses.m_reactive, //
|
&logicRegions.m_react, //
|
||||||
&timingKit.m_lbs});
|
&timingKit.m_lbs});
|
||||||
const TriggerKit& actTrig
|
const TriggerKit& actTrig
|
||||||
= createTriggers(netlistp, initp, senExprBuilder, senTreeps, "act", extraTriggers);
|
= createTriggers(netlistp, initp, senExprBuilder, senTreeps, "act", extraTriggers);
|
||||||
@@ -1225,7 +1231,7 @@ void schedule(AstNetlist* netlistp) {
|
|||||||
AstVarScope* const preTrigVscp = scopeTopp->createTempLike("__VpreTriggered", actTrigVscp);
|
AstVarScope* const preTrigVscp = scopeTopp->createTempLike("__VpreTriggered", actTrigVscp);
|
||||||
|
|
||||||
const auto cloneMapWithNewTriggerReferences
|
const auto cloneMapWithNewTriggerReferences
|
||||||
= [=](std::unordered_map<const AstSenTree*, AstSenTree*> map, AstVarScope* vscp) {
|
= [=](const std::unordered_map<const AstSenTree*, AstSenTree*>& map, AstVarScope* vscp) {
|
||||||
// Copy map
|
// Copy map
|
||||||
auto newMap{map};
|
auto newMap{map};
|
||||||
// Replace references in each mapped value with a reference to the given vscp
|
// Replace references in each mapped value with a reference to the given vscp
|
||||||
@@ -1293,6 +1299,7 @@ void schedule(AstNetlist* netlistp) {
|
|||||||
// Orders a region's logic and creates the region eval function
|
// Orders a region's logic and creates the region eval function
|
||||||
const auto order = [&](const std::string& name,
|
const auto order = [&](const std::string& name,
|
||||||
const std::vector<V3Sched::LogicByScope*>& logic) -> EvalKit {
|
const std::vector<V3Sched::LogicByScope*>& logic) -> EvalKit {
|
||||||
|
UINFO(2, "Scheduling " << name << " #logic = " << logic.size() << endl);
|
||||||
AstVarScope* const trigVscp
|
AstVarScope* const trigVscp
|
||||||
= scopeTopp->createTempLike("__V" + name + "Triggered", actTrigVscp);
|
= scopeTopp->createTempLike("__V" + name + "Triggered", actTrigVscp);
|
||||||
const auto trigMap = cloneMapWithNewTriggerReferences(actTrigMap, trigVscp);
|
const auto trigMap = cloneMapWithNewTriggerReferences(actTrigMap, trigVscp);
|
||||||
@@ -1350,18 +1357,21 @@ void schedule(AstNetlist* netlistp) {
|
|||||||
|
|
||||||
// Orders a region's logic and creates the region eval function (only if there is any logic in
|
// Orders a region's logic and creates the region eval function (only if there is any logic in
|
||||||
// the region)
|
// the region)
|
||||||
const auto orderIfNonEmpty = [&](const std::string& name, LogicByScope& lbs) -> EvalKit {
|
const auto orderIfNonEmpty
|
||||||
if (lbs.empty()) return {};
|
= [&](const std::string& name, const std::vector<LogicByScope*>& logic) -> EvalKit {
|
||||||
const auto& kit = order(name, {&lbs});
|
if (logic[0]->empty())
|
||||||
|
return {}; // if region is empty, replica is supposed to be empty as well
|
||||||
|
const auto& kit = order(name, logic);
|
||||||
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-" + name);
|
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-" + name);
|
||||||
return kit;
|
return kit;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 11: Create the 'obs' region evaluation function
|
// Step 11: Create the 'obs' region evaluation function
|
||||||
const EvalKit& obsKit = orderIfNonEmpty("obs", logicClasses.m_observed);
|
const EvalKit& obsKit = orderIfNonEmpty("obs", {&logicRegions.m_obs, &logicReplicas.m_obs});
|
||||||
|
|
||||||
// Step 12: Create the 're' region evaluation function
|
// Step 12: Create the 're' region evaluation function
|
||||||
const EvalKit& reactKit = orderIfNonEmpty("react", logicClasses.m_reactive);
|
const EvalKit& reactKit
|
||||||
|
= orderIfNonEmpty("react", {&logicRegions.m_react, &logicReplicas.m_react});
|
||||||
|
|
||||||
// Step 13: Create the 'postponed' region evaluation function
|
// Step 13: Create the 'postponed' region evaluation function
|
||||||
auto* const postponedFuncp = createPostponed(netlistp, logicClasses);
|
auto* const postponedFuncp = createPostponed(netlistp, logicClasses);
|
||||||
|
|||||||
@@ -98,6 +98,8 @@ struct LogicRegions final {
|
|||||||
LogicByScope m_pre; // AstAssignPre logic in 'act' region
|
LogicByScope m_pre; // AstAssignPre logic in 'act' region
|
||||||
LogicByScope m_act; // 'act' region logic
|
LogicByScope m_act; // 'act' region logic
|
||||||
LogicByScope m_nba; // 'nba' region logic
|
LogicByScope m_nba; // 'nba' region logic
|
||||||
|
LogicByScope m_obs; // AstAlwaysObserved logic in 'obs' region
|
||||||
|
LogicByScope m_react; // AstAlwaysReactive logic in 're' region
|
||||||
|
|
||||||
LogicRegions() = default;
|
LogicRegions() = default;
|
||||||
VL_UNCOPYABLE(LogicRegions);
|
VL_UNCOPYABLE(LogicRegions);
|
||||||
@@ -111,6 +113,8 @@ struct LogicReplicas final {
|
|||||||
LogicByScope m_ico; // Logic replicated into the 'ico' (Input Combinational) region
|
LogicByScope m_ico; // Logic replicated into the 'ico' (Input Combinational) region
|
||||||
LogicByScope m_act; // Logic replicated into the 'act' region
|
LogicByScope m_act; // Logic replicated into the 'act' region
|
||||||
LogicByScope m_nba; // Logic replicated into the 'nba' region
|
LogicByScope m_nba; // Logic replicated into the 'nba' region
|
||||||
|
LogicByScope m_obs; // Logic replicated into the 'obs' region
|
||||||
|
LogicByScope m_react; // Logic replicated into the 're' region
|
||||||
|
|
||||||
LogicReplicas() = default;
|
LogicReplicas() = default;
|
||||||
VL_UNCOPYABLE(LogicReplicas);
|
VL_UNCOPYABLE(LogicReplicas);
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ enum RegionFlags : uint8_t {
|
|||||||
NONE = 0x0, //
|
NONE = 0x0, //
|
||||||
INPUT = 0x1, // Variable/logic is driven from top level input
|
INPUT = 0x1, // Variable/logic is driven from top level input
|
||||||
ACTIVE = 0x2, // Variable/logic is driven from 'act' region logic
|
ACTIVE = 0x2, // Variable/logic is driven from 'act' region logic
|
||||||
NBA = 0x4 // Variable/logic is driven from 'nba' region logic
|
NBA = 0x4, // Variable/logic is driven from 'nba' region logic
|
||||||
|
OBSERVED = 0x8, // Variable/logic is driven from 'obs' region logic
|
||||||
|
REACTIVE = 0x10, // Variable/logic is driven from 're' region logic
|
||||||
};
|
};
|
||||||
|
|
||||||
//##############################################################################
|
//##############################################################################
|
||||||
@@ -79,7 +81,34 @@ public:
|
|||||||
case INPUT | NBA: return "magenta";
|
case INPUT | NBA: return "magenta";
|
||||||
case ACTIVE | NBA: return "cyan";
|
case ACTIVE | NBA: return "cyan";
|
||||||
case INPUT | ACTIVE | NBA: return "gray80"; // don't want white on white background
|
case INPUT | ACTIVE | NBA: return "gray80"; // don't want white on white background
|
||||||
default: v3fatal("There are only 3 region bits"); return "";
|
|
||||||
|
case REACTIVE: return "gray60";
|
||||||
|
case REACTIVE | INPUT: return "lightcoral";
|
||||||
|
case REACTIVE | ACTIVE: return "lightgreen";
|
||||||
|
case REACTIVE | NBA: return "lightblue";
|
||||||
|
case REACTIVE | INPUT | ACTIVE: return "lightyellow";
|
||||||
|
case REACTIVE | INPUT | NBA: return "lightpink";
|
||||||
|
case REACTIVE | ACTIVE | NBA: return "lightcyan";
|
||||||
|
case REACTIVE | INPUT | ACTIVE | NBA: return "gray90";
|
||||||
|
|
||||||
|
case OBSERVED: return "gray20";
|
||||||
|
case OBSERVED | INPUT: return "darkred";
|
||||||
|
case OBSERVED | ACTIVE: return "darkgreen";
|
||||||
|
case OBSERVED | NBA: return "darkblue";
|
||||||
|
case OBSERVED | INPUT | ACTIVE: return "gold";
|
||||||
|
case OBSERVED | INPUT | NBA: return "purple";
|
||||||
|
case OBSERVED | ACTIVE | NBA: return "darkcyan";
|
||||||
|
case OBSERVED | INPUT | ACTIVE | NBA: return "gray30";
|
||||||
|
|
||||||
|
case REACTIVE | OBSERVED: return "gray40";
|
||||||
|
case REACTIVE | OBSERVED | INPUT: return "indianred";
|
||||||
|
case REACTIVE | OBSERVED | ACTIVE: return "olive";
|
||||||
|
case REACTIVE | OBSERVED | NBA: return "dodgerBlue";
|
||||||
|
case REACTIVE | OBSERVED | INPUT | ACTIVE: return "khaki";
|
||||||
|
case REACTIVE | OBSERVED | INPUT | NBA: return "plum";
|
||||||
|
case REACTIVE | OBSERVED | ACTIVE | NBA: return "lightSeaGreen";
|
||||||
|
case REACTIVE | OBSERVED | INPUT | ACTIVE | NBA: return "gray50";
|
||||||
|
default: v3fatal("There are only 5 region bits"); return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// LCOV_EXCL_STOP
|
// LCOV_EXCL_STOP
|
||||||
@@ -213,6 +242,8 @@ std::unique_ptr<Graph> buildGraph(const LogicRegions& logicRegions) {
|
|||||||
for (const auto& pair : logicRegions.m_pre) addLogic(ACTIVE, pair.first, pair.second);
|
for (const auto& pair : logicRegions.m_pre) addLogic(ACTIVE, pair.first, pair.second);
|
||||||
for (const auto& pair : logicRegions.m_act) addLogic(ACTIVE, pair.first, pair.second);
|
for (const auto& pair : logicRegions.m_act) addLogic(ACTIVE, pair.first, pair.second);
|
||||||
for (const auto& pair : logicRegions.m_nba) addLogic(NBA, pair.first, pair.second);
|
for (const auto& pair : logicRegions.m_nba) addLogic(NBA, pair.first, pair.second);
|
||||||
|
for (const auto& pair : logicRegions.m_obs) addLogic(OBSERVED, pair.first, pair.second);
|
||||||
|
for (const auto& pair : logicRegions.m_react) addLogic(REACTIVE, pair.first, pair.second);
|
||||||
|
|
||||||
return graphp;
|
return graphp;
|
||||||
}
|
}
|
||||||
@@ -251,6 +282,8 @@ LogicReplicas replicate(Graph* graphp) {
|
|||||||
if (targetRegions & INPUT) replicateTo(result.m_ico);
|
if (targetRegions & INPUT) replicateTo(result.m_ico);
|
||||||
if (targetRegions & ACTIVE) replicateTo(result.m_act);
|
if (targetRegions & ACTIVE) replicateTo(result.m_act);
|
||||||
if (targetRegions & NBA) replicateTo(result.m_nba);
|
if (targetRegions & NBA) replicateTo(result.m_nba);
|
||||||
|
if (targetRegions & OBSERVED) replicateTo(result.m_obs);
|
||||||
|
if (targetRegions & REACTIVE) replicateTo(result.m_react);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+65
-2
@@ -2792,6 +2792,7 @@ class WidthVisitor final : public VNVisitor {
|
|||||||
}
|
}
|
||||||
void visit(AstMemberSel* nodep) override {
|
void visit(AstMemberSel* nodep) override {
|
||||||
UINFO(5, " MEMBERSEL " << nodep << endl);
|
UINFO(5, " MEMBERSEL " << nodep << endl);
|
||||||
|
if (nodep->didWidth()) return;
|
||||||
if (debug() >= 9) nodep->dumpTree("- mbs-in: ");
|
if (debug() >= 9) nodep->dumpTree("- mbs-in: ");
|
||||||
userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
|
userIterateChildren(nodep, WidthVP{SELF, BOTH}.p());
|
||||||
if (debug() >= 9) nodep->dumpTree("- mbs-ic: ");
|
if (debug() >= 9) nodep->dumpTree("- mbs-ic: ");
|
||||||
@@ -2799,19 +2800,65 @@ class WidthVisitor final : public VNVisitor {
|
|||||||
UASSERT_OBJ(nodep->fromp()->dtypep(), nodep->fromp(), "Unlinked data type");
|
UASSERT_OBJ(nodep->fromp()->dtypep(), nodep->fromp(), "Unlinked data type");
|
||||||
AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
|
AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefToEnump();
|
||||||
UINFO(9, " from dt " << fromDtp << endl);
|
UINFO(9, " from dt " << fromDtp << endl);
|
||||||
if (AstNodeUOrStructDType* const adtypep = VN_CAST(fromDtp, NodeUOrStructDType)) {
|
const AstMemberSel* const fromSel = VN_CAST(nodep->fromp(), MemberSel);
|
||||||
|
if (AstClocking* const clockingp = fromSel && fromSel->varp()
|
||||||
|
? VN_CAST(fromSel->varp()->firstAbovep(), Clocking)
|
||||||
|
: nullptr) {
|
||||||
|
// In: MEMBERSEL{MEMBERSEL{vifaceref, "cb", cb_event}, "clockvar", null}
|
||||||
|
// Out: MEMBERSEL{vifaceref, "clockvar", clockvar}
|
||||||
|
UINFO(9, " from clocking " << clockingp << endl);
|
||||||
|
if (AstVar* const varp = memberSelClocking(nodep, clockingp)) {
|
||||||
|
if (!varp->didWidth()) userIterate(varp, nullptr);
|
||||||
|
AstMemberSel* fromp = VN_AS(nodep->fromp(), MemberSel);
|
||||||
|
fromp->replaceWith(fromp->fromp()->unlinkFrBack());
|
||||||
|
VL_DO_DANGLING(fromp->deleteTree(), fromp);
|
||||||
|
nodep->dtypep(varp->dtypep());
|
||||||
|
nodep->varp(varp);
|
||||||
|
if (nodep->access().isWriteOrRW()) V3LinkLValue::linkLValueSet(nodep);
|
||||||
|
if (AstIfaceRefDType* const adtypep
|
||||||
|
= VN_CAST(nodep->fromp()->dtypep(), IfaceRefDType)) {
|
||||||
|
nodep->varp()->sensIfacep(adtypep->ifacep());
|
||||||
|
}
|
||||||
|
UINFO(9, " done clocking msel " << nodep << endl);
|
||||||
|
nodep->didWidth(true); // Must not visit again: will confuse scopes
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (AstNodeUOrStructDType* const adtypep = VN_CAST(fromDtp, NodeUOrStructDType)) {
|
||||||
if (memberSelStruct(nodep, adtypep)) return;
|
if (memberSelStruct(nodep, adtypep)) return;
|
||||||
} else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
|
} else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
|
||||||
if (memberSelClass(nodep, adtypep)) return;
|
if (memberSelClass(nodep, adtypep)) return;
|
||||||
} else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
|
} else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
|
||||||
if (AstNode* const foundp = memberSelIface(nodep, adtypep)) {
|
if (AstNode* foundp = memberSelIface(nodep, adtypep)) {
|
||||||
|
if (AstClocking* const clockingp = VN_CAST(foundp, Clocking)) {
|
||||||
|
foundp = clockingp->eventp();
|
||||||
|
if (!foundp) {
|
||||||
|
AstVar* const eventp = new AstVar{
|
||||||
|
clockingp->fileline(), VVarType::MODULETEMP, clockingp->name(),
|
||||||
|
clockingp->findBasicDType(VBasicDTypeKwd::EVENT)};
|
||||||
|
eventp->lifetime(VLifetime::STATIC);
|
||||||
|
clockingp->eventp(eventp);
|
||||||
|
// Trigger the clocking event in Observed (IEEE 1800-2023 14.13)
|
||||||
|
clockingp->addNextHere(new AstAlwaysObserved{
|
||||||
|
clockingp->fileline(),
|
||||||
|
new AstSenTree{clockingp->fileline(),
|
||||||
|
clockingp->sensesp()->cloneTree(false)},
|
||||||
|
new AstFireEvent{
|
||||||
|
clockingp->fileline(),
|
||||||
|
new AstVarRef{clockingp->fileline(), eventp, VAccess::WRITE},
|
||||||
|
false}});
|
||||||
|
v3Global.setHasEvents();
|
||||||
|
foundp = eventp;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (AstVar* const varp = VN_CAST(foundp, Var)) {
|
if (AstVar* const varp = VN_CAST(foundp, Var)) {
|
||||||
|
if (!varp->didWidth()) userIterate(varp, nullptr);
|
||||||
nodep->dtypep(foundp->dtypep());
|
nodep->dtypep(foundp->dtypep());
|
||||||
nodep->varp(varp);
|
nodep->varp(varp);
|
||||||
AstIface* const ifacep = adtypep->ifacep();
|
AstIface* const ifacep = adtypep->ifacep();
|
||||||
varp->sensIfacep(ifacep);
|
varp->sensIfacep(ifacep);
|
||||||
nodep->fromp()->foreach(
|
nodep->fromp()->foreach(
|
||||||
[ifacep](AstVarRef* const refp) { refp->varp()->sensIfacep(ifacep); });
|
[ifacep](AstVarRef* const refp) { refp->varp()->sensIfacep(ifacep); });
|
||||||
|
nodep->didWidth(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
UINFO(1, "found object " << foundp << endl);
|
UINFO(1, "found object " << foundp << endl);
|
||||||
@@ -2865,6 +2912,7 @@ class WidthVisitor final : public VNVisitor {
|
|||||||
}
|
}
|
||||||
nodep->dtypep(foundp->dtypep());
|
nodep->dtypep(foundp->dtypep());
|
||||||
nodep->varp(varp);
|
nodep->varp(varp);
|
||||||
|
nodep->didWidth(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
|
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
|
||||||
@@ -2930,6 +2978,21 @@ class WidthVisitor final : public VNVisitor {
|
|||||||
<< (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
|
<< (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
|
||||||
return nullptr; // Caller handles error
|
return nullptr; // Caller handles error
|
||||||
}
|
}
|
||||||
|
AstVar* memberSelClocking(AstMemberSel* nodep, AstClocking* clockingp) {
|
||||||
|
// Returns node if ok
|
||||||
|
VSpellCheck speller;
|
||||||
|
for (AstClockingItem* itemp = clockingp->itemsp(); itemp;
|
||||||
|
itemp = VN_AS(itemp->nextp(), ClockingItem)) {
|
||||||
|
if (itemp->varp()->name() == nodep->name()) return itemp->varp();
|
||||||
|
speller.pushCandidate(itemp->varp()->prettyName());
|
||||||
|
}
|
||||||
|
const string suggest = speller.bestCandidateMsg(nodep->prettyName());
|
||||||
|
nodep->v3error(
|
||||||
|
"Member " << nodep->prettyNameQ() << " not found in clocking block "
|
||||||
|
<< clockingp->prettyNameQ() << "\n"
|
||||||
|
<< (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
|
||||||
|
return nullptr; // Caller handles error
|
||||||
|
}
|
||||||
bool memberSelStruct(AstMemberSel* nodep, AstNodeUOrStructDType* adtypep) {
|
bool memberSelStruct(AstMemberSel* nodep, AstNodeUOrStructDType* adtypep) {
|
||||||
// Returns true if ok
|
// Returns true if ok
|
||||||
if (AstMemberDType* const memberp
|
if (AstMemberDType* const memberp
|
||||||
|
|||||||
@@ -81,6 +81,7 @@
|
|||||||
#include "V3ProtectLib.h"
|
#include "V3ProtectLib.h"
|
||||||
#include "V3Randomize.h"
|
#include "V3Randomize.h"
|
||||||
#include "V3Reloop.h"
|
#include "V3Reloop.h"
|
||||||
|
#include "V3Sampled.h"
|
||||||
#include "V3Sched.h"
|
#include "V3Sched.h"
|
||||||
#include "V3Scope.h"
|
#include "V3Scope.h"
|
||||||
#include "V3Scoreboard.h"
|
#include "V3Scoreboard.h"
|
||||||
@@ -437,6 +438,9 @@ static void process() {
|
|||||||
// down to a module and now be identical
|
// down to a module and now be identical
|
||||||
V3ActiveTop::activeTopAll(v3Global.rootp());
|
V3ActiveTop::activeTopAll(v3Global.rootp());
|
||||||
|
|
||||||
|
// Remove SAMPLED
|
||||||
|
if (v3Global.hasSampled()) V3Sampled::sampledAll(v3Global.rootp());
|
||||||
|
|
||||||
if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "PreOrder");
|
if (v3Global.opt.stats()) V3Stats::statsStageAll(v3Global.rootp(), "PreOrder");
|
||||||
|
|
||||||
// Schedule the logic
|
// Schedule the logic
|
||||||
|
|||||||
Executable
+22
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 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
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ["--exe --main --timing"],
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2024 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
interface Iface;
|
||||||
|
logic clk = 1'b0, inp = 1'b0, io = 1'b0, out = 1'b0, out2 = 1'b0;
|
||||||
|
clocking cb @(posedge clk);
|
||||||
|
input #7 inp;
|
||||||
|
output out;
|
||||||
|
inout io;
|
||||||
|
endclocking
|
||||||
|
always @(posedge clk) inp <= 1'b1;
|
||||||
|
always #5 clk <= ~clk;
|
||||||
|
assign out2 = out;
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
module main;
|
||||||
|
initial begin
|
||||||
|
#6;
|
||||||
|
t.mod1.cb.io <= 1'b1;
|
||||||
|
t.mod1.cb.out <= 1'b1;
|
||||||
|
if (t.mod0.io != 1'b0) $stop;
|
||||||
|
if (t.mod1.cb.io != 1'b0) $stop;
|
||||||
|
if (t.mod1.cb.inp != 1'b0) $stop;
|
||||||
|
@(posedge t.mod0.io)
|
||||||
|
if ($time != 15) $stop;
|
||||||
|
if (t.mod0.io != 1'b1) $stop;
|
||||||
|
if (t.mod1.cb.io != 1'b0) $stop;
|
||||||
|
#1
|
||||||
|
if (t.mod0.cb.io != 1'b1) $stop;
|
||||||
|
if (t.mod1.cb.io != 1'b1) $stop;
|
||||||
|
if (t.mod1.cb.inp != 1'b1) $stop;
|
||||||
|
#8;
|
||||||
|
t.mod0.inp = 1'b0;
|
||||||
|
if (t.mod0.cb.inp != 1'b1) $stop;
|
||||||
|
@(t.mod1.cb)
|
||||||
|
if ($time != 25) $stop;
|
||||||
|
if (t.mod0.cb.inp != 1'b1) $stop;
|
||||||
|
t.mod0.inp = 1'b0;
|
||||||
|
@(t.mod0.cb)
|
||||||
|
if ($time != 35) $stop;
|
||||||
|
if (t.mod0.cb.inp != 1'b0) $stop;
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
initial begin
|
||||||
|
@(posedge t.mod0.out)
|
||||||
|
if ($time != 15) $stop;
|
||||||
|
if (t.mod1.out2 != 1'b1) $stop;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t;
|
||||||
|
main main1();
|
||||||
|
Iface mod0();
|
||||||
|
virtual Iface mod1 = mod0;
|
||||||
|
endmodule
|
||||||
Reference in New Issue
Block a user