Skip to content

Commit

Permalink
Add extra triggers for virtual interfaces
Browse files Browse the repository at this point in the history
Signed-off-by: Krzysztof Bieganski <[email protected]>
  • Loading branch information
kbieganski committed Nov 8, 2023
1 parent dc10118 commit 605f249
Show file tree
Hide file tree
Showing 18 changed files with 282 additions and 14 deletions.
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ set(HEADERS
V3Unknown.h
V3Unroll.h
V3VariableOrder.h
V3VirtIface.h
V3Waiver.h
V3Width.h
V3WidthCommit.h
Expand Down Expand Up @@ -302,6 +303,7 @@ set(COMMON_SOURCES
V3Undriven.cpp
V3Unknown.cpp
V3Unroll.cpp
V3VirtIface.cpp
V3VariableOrder.cpp
V3Waiver.cpp
V3Width.cpp
Expand Down
1 change: 1 addition & 0 deletions src/Makefile_obj.in
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Undriven.o \
V3Unknown.o \
V3Unroll.o \
V3VirtIface.o \
V3VariableOrder.o \
V3Width.o \
V3WidthCommit.o \
Expand Down
3 changes: 3 additions & 0 deletions src/V3AstNodeDType.h
Original file line number Diff line number Diff line change
Expand Up @@ -819,6 +819,7 @@ class AstIfaceRefDType final : public AstNodeDType {
// Reference to an interface, either for a port, or inside parent cell
// @astgen op1 := paramsp : List[AstPin]
private:
bool m_virtual = false; // True if virtual interface
FileLine* m_modportFileline; // Where modport token was
string m_cellName; // "" = no cell, such as when connects to 'input' iface
string m_ifaceName; // Interface name
Expand Down Expand Up @@ -861,6 +862,8 @@ class AstIfaceRefDType final : public AstNodeDType {
bool similarDType(const AstNodeDType* samep) const override { return this == samep; }
int widthAlignBytes() const override { return 1; }
int widthTotalBytes() const override { return 1; }
void isVirtual(bool flag) { m_virtual = flag; }
bool isVirtual() const { return m_virtual; }
FileLine* modportFileline() const { return m_modportFileline; }
string cellName() const { return m_cellName; }
void cellName(const string& name) { m_cellName = name; }
Expand Down
9 changes: 9 additions & 0 deletions src/V3AstNodeOther.h
Original file line number Diff line number Diff line change
Expand Up @@ -1198,6 +1198,7 @@ class AstNetlist final : public AstNode {
AstCFunc* m_evalp = nullptr; // The '_eval' function
AstCFunc* m_evalNbap = nullptr; // The '_eval__nba' function
AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export trigger variable
AstVarScope* m_virtIfaceTriggerp = nullptr; // The virtual interface trigger variable
AstVar* m_delaySchedulerp = nullptr; // The delay scheduler variable
AstVarScope* m_nbaEventp = nullptr; // The NBA event variable
AstVarScope* m_nbaEventTriggerp = nullptr; // If set to 1, the NBA event should get triggered
Expand Down Expand Up @@ -1228,6 +1229,8 @@ class AstNetlist final : public AstNode {
void evalNbap(AstCFunc* funcp) { m_evalNbap = funcp; }
AstVarScope* dpiExportTriggerp() const { return m_dpiExportTriggerp; }
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
AstVarScope* virtIfaceTriggerp() const { return m_virtIfaceTriggerp; }
void virtIfaceTriggerp(AstVarScope* varScopep) { m_virtIfaceTriggerp = varScopep; }
AstVar* delaySchedulerp() const { return m_delaySchedulerp; }
void delaySchedulerp(AstVar* const varScopep) { m_delaySchedulerp = varScopep; }
AstVarScope* nbaEventp() const { return m_nbaEventp; }
Expand Down Expand Up @@ -2001,6 +2004,12 @@ class AstVar final : public AstNode {
string verilogKwd() const override;
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
VLifetime lifetime() const { return m_lifetime; }
bool isVirtIface() const {
if (AstIfaceRefDType* ifaceRefDtp = VN_CAST(dtypep(), IfaceRefDType)) {
return ifaceRefDtp->isVirtual();
}
return false;
}
void propagateAttrFrom(const AstVar* fromp) {
// This is getting connected to fromp; keep attributes
// Note the method below too
Expand Down
1 change: 1 addition & 0 deletions src/V3AstNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1941,6 +1941,7 @@ const char* AstNetlist::broken() const {
BROKEN_RTN(m_dollarUnitPkgp && !m_dollarUnitPkgp->brokeExists());
BROKEN_RTN(m_evalp && !m_evalp->brokeExists());
BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists());
BROKEN_RTN(m_virtIfaceTriggerp && !m_virtIfaceTriggerp->brokeExists());
BROKEN_RTN(m_topScopep && !m_topScopep->brokeExists());
BROKEN_RTN(m_delaySchedulerp && !m_delaySchedulerp->brokeExists());
BROKEN_RTN(m_nbaEventp && !m_nbaEventp->brokeExists());
Expand Down
2 changes: 1 addition & 1 deletion src/V3Gate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ class GateVisitor final : public VNVisitor {
UINFO(6, "New vertex " << varscp << endl);
vertexp = new GateVarVertex{&m_graph, m_scopep, varscp};
varscp->user1p(vertexp);
if (varscp->varp()->isUsedVirtIface()) {
if (varscp->varp()->isUsedVirtIface() || varscp->varp()->isVirtIface()) {
// Can be used in a class method, which cannot be tracked statically
vertexp->clearReducibleAndDedupable("VirtIface");
vertexp->setConsumed("VirtIface");
Expand Down
3 changes: 3 additions & 0 deletions src/V3Global.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ class V3Global final {
bool m_dpi = false; // Need __Dpi include files
bool m_hasEvents = false; // Design uses SystemVerilog named events
bool m_hasClasses = false; // Design uses SystemVerilog classes
bool m_hasVirtIfaces = false; // Design uses virtual interfaces
bool m_usesProbDist = false; // Uses $dist_*
bool m_usesStdPackage = false; // Design uses the std package
bool m_usesTiming = false; // Design uses timing constructs
Expand Down Expand Up @@ -162,6 +163,8 @@ class V3Global final {
void setHasEvents() { m_hasEvents = true; }
bool hasClasses() const { return m_hasClasses; }
void setHasClasses() { m_hasClasses = true; }
bool hasVirtIfaces() const { return m_hasVirtIfaces; }
void setHasVirtIfaces() { m_hasVirtIfaces = true; }
bool usesProbDist() const { return m_usesProbDist; }
void setUsesProbDist() { m_usesProbDist = true; }
bool usesStdPackage() const { return m_usesStdPackage; }
Expand Down
1 change: 1 addition & 0 deletions src/V3Localize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ class LocalizeVisitor final : public VNVisitor {
&& !nodep->varp()->isFuncLocal() // Not already a function local (e.g.: argument)
&& !nodep->varp()->isStatic() // Not a static variable
&& !nodep->varp()->isClassMember() // Statically exists in design hierarchy
&& !nodep->varp()->isVirtIface() // Is not a virtual interface
&& !nodep->varp()->isUsedVirtIface() // Not used through a virtual interface
&& !nodep->varp()->valuep() // Does not have an initializer
) {
Expand Down
29 changes: 22 additions & 7 deletions src/V3Sched.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -507,16 +507,16 @@ struct TriggerKit {
m_funcp->stmtsp()->addHereThisAsNext(callp->makeStmt());
}

// Utility to set then clear the dpiExportTrigger trigger
void addDpiExportTriggerAssignment(AstVarScope* dpiExportTriggerVscp, uint32_t index) const {
FileLine* const flp = dpiExportTriggerVscp->fileline();
// Utility to set then clear an extra trigger
void addExtraTriggerAssignment(AstVarScope* extraTriggerVscp, uint32_t index) const {
FileLine* const flp = extraTriggerVscp->fileline();
AstVarRef* const vrefp = new AstVarRef{flp, m_vscp, VAccess::WRITE};
AstCMethodHard* const callp = new AstCMethodHard{flp, vrefp, "set"};
callp->addPinsp(new AstConst{flp, index});
callp->addPinsp(new AstVarRef{flp, dpiExportTriggerVscp, VAccess::READ});
callp->addPinsp(new AstVarRef{flp, extraTriggerVscp, VAccess::READ});
callp->dtypeSetVoid();
AstNode* const stmtp = callp->makeStmt();
stmtp->addNext(new AstAssign{flp, new AstVarRef{flp, dpiExportTriggerVscp, VAccess::WRITE},
stmtp->addNext(new AstAssign{flp, new AstVarRef{flp, extraTriggerVscp, VAccess::WRITE},
new AstConst{flp, AstConst::BitFalse{}}});
m_funcp->stmtsp()->addHereThisAsNext(stmtp);
}
Expand Down Expand Up @@ -840,7 +840,7 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
= createTriggers(netlistp, initFuncp, senExprBuilder, senTreeps, "ico", extraTriggers);

if (dpiExportTriggerVscp) {
trig.addDpiExportTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
trig.addExtraTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
}

// Remap sensitivities
Expand Down Expand Up @@ -1150,13 +1150,18 @@ void schedule(AstNetlist* netlistp) {

// Step 8: Create the pre/act/nba triggers
AstVarScope* const dpiExportTriggerVscp = netlistp->dpiExportTriggerp();
AstVarScope* const virtIfaceTriggerVscp = netlistp->virtIfaceTriggerp();

// We may have an extra trigger for variable updated in DPI exports
ExtraTriggers extraTriggers;
const size_t dpiExportTriggerIndex = dpiExportTriggerVscp
? extraTriggers.allocate("DPI export trigger")
: std::numeric_limits<unsigned>::max();

const size_t virtIfaceTriggerIndex = virtIfaceTriggerVscp
? extraTriggers.allocate("Virtual interface trigger")
: std::numeric_limits<unsigned>::max();

const auto& senTreeps = getSenTreesUsedBy({&logicRegions.m_pre, //
&logicRegions.m_act, //
&logicRegions.m_nba, //
Expand All @@ -1170,7 +1175,10 @@ void schedule(AstNetlist* netlistp) {
if (timingKit.m_postUpdates) actTrig.m_funcp->addStmtsp(timingKit.m_postUpdates);

if (dpiExportTriggerVscp) {
actTrig.addDpiExportTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
actTrig.addExtraTriggerAssignment(dpiExportTriggerVscp, dpiExportTriggerIndex);
}
if (virtIfaceTriggerVscp) {
actTrig.addExtraTriggerAssignment(virtIfaceTriggerVscp, virtIfaceTriggerIndex);
}

AstVarScope* const actTrigVscp = actTrig.m_vscp;
Expand Down Expand Up @@ -1223,12 +1231,18 @@ void schedule(AstNetlist* netlistp) {
? createTriggerSenTree(netlistp, actTrig.m_vscp, dpiExportTriggerIndex)
: nullptr;

AstSenTree* const virtIfaceTriggeredAct
= virtIfaceTriggerVscp
? createTriggerSenTree(netlistp, actTrig.m_vscp, virtIfaceTriggerIndex)
: nullptr;

AstCFunc* const actFuncp = V3Order::order(
netlistp, {&logicRegions.m_pre, &logicRegions.m_act, &logicReplicas.m_act}, trigToSenAct,
"act", false, false, [&](const AstVarScope* vscp, std::vector<AstSenTree*>& out) {
auto it = actTimingDomains.find(vscp);
if (it != actTimingDomains.end()) out = it->second;
if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggeredAct);
if (vscp->varp()->isUsedVirtIface()) out.push_back(virtIfaceTriggeredAct);
});
splitCheck(actFuncp);
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-act");
Expand Down Expand Up @@ -1314,6 +1328,7 @@ void schedule(AstNetlist* netlistp) {
splitCheck(initp);

netlistp->dpiExportTriggerp(nullptr);
netlistp->virtIfaceTriggerp(nullptr);

V3Global::dumpCheckGlobalTree("sched", 0, dumpTreeLevel() >= 3);
}
Expand Down
1 change: 1 addition & 0 deletions src/V3SchedReplicate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ class SchedReplicateVarVertex final : public SchedReplicateVertex {
// the act region, which means combinational logic driven from a suspendable
// processes must be present in the 'act' region
if (varp()->isWrittenBySuspendable()) addDrivingRegions(ACTIVE);
if (varp()->isUsedVirtIface()) addDrivingRegions(ACTIVE);
}
AstVarScope* vscp() const { return m_vscp; }
AstVar* varp() const { return m_vscp->varp(); }
Expand Down
114 changes: 114 additions & 0 deletions src/V3VirtIface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Create separate tasks for forked processes that
// can outlive their parents
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2023 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
//
//*************************************************************************
// V3VirtIface's Transformations:
//
//*************************************************************************

#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT

#include "V3VirtIface.h"

#include "V3AstNodeExpr.h"

VL_DEFINE_DEBUG_FUNCTIONS;

//######################################################################
//

class VirtIfaceVisitor final : public VNVisitor {
private:
// STATE
AstNetlist* const m_netlistp; // Root node
AstNodeAssign* m_trigAssignp = nullptr; // Previous/current trigger assignment

// METHODS
static bool writesToVirtIface(AstNodeStmt* nodep) {
return nodep->exists([](const AstVarRef* varrefp) {
return varrefp->access().isWriteOrRW() && varrefp->varp()->isVirtIface();
});
}
AstVarRef* createVirtIfaceTriggerRefp(FileLine* const flp) const {
if (!m_netlistp->virtIfaceTriggerp()) {
AstScope* const scopeTopp = m_netlistp->topScopep()->scopep();
m_netlistp->virtIfaceTriggerp(scopeTopp->createTemp("__VvirtIfaceTrigger", 1));
}
return new AstVarRef{flp, m_netlistp->virtIfaceTriggerp(), VAccess::WRITE};
}

// VISITORS
void visit(AstNodeProcedure* nodep) override {
m_trigAssignp = nullptr;
iterateChildren(nodep);
}
void visit(AstCFunc* nodep) override {
m_trigAssignp = nullptr;
iterateChildren(nodep);
}
void visit(AstAssignW* nodep) override {
if (writesToVirtIface(nodep)) { nodep->convertToAlways(); }
}
void visit(AstNodeIf* nodep) override {
// TODO: handle ->condp()
m_trigAssignp = nullptr;
iterateAndNextNull(nodep->thensp());
m_trigAssignp = nullptr;
iterateAndNextNull(nodep->elsesp());
}
void visit(AstWhile* nodep) override {
// TODO: handle ->condp() etc.
m_trigAssignp = nullptr;
iterateAndNextNull(nodep->stmtsp());
}
void visit(AstNodeStmt* nodep) override {
if (!VN_IS(nodep, NodeAssign) //
|| !VN_IS(nodep, StmtExpr) //
|| nodep->isTimingControl()) {
m_trigAssignp = nullptr;
}
if (!writesToVirtIface(nodep)) return;
FileLine* const flp = nodep->fileline();
if (m_trigAssignp) {
m_trigAssignp->unlinkFrBack();
} else if (VN_IS(nodep, AssignDly)) {
m_trigAssignp = new AstAssignDly{flp, createVirtIfaceTriggerRefp(flp),
new AstConst{flp, AstConst::BitTrue{}}};
} else {
m_trigAssignp = new AstAssign{flp, createVirtIfaceTriggerRefp(flp),
new AstConst{flp, AstConst::BitTrue{}}};
}
nodep->addNextHere(m_trigAssignp);
}
void visit(AstNodeExpr*) override {} // Accelerate
void visit(AstNode* nodep) override { iterateChildren(nodep); }

public:
// CONSTRUCTORS
explicit VirtIfaceVisitor(AstNetlist* nodep)
: m_netlistp{nodep} {
iterate(nodep);
}
~VirtIfaceVisitor() override = default;
};

//######################################################################
// VirtIface class functions

void V3VirtIface::makeTriggerAssignments(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ": " << endl);
{ VirtIfaceVisitor{nodep}; }
V3Global::dumpCheckGlobalTree("vif", 0, dumpTreeLevel() >= 3);
}
35 changes: 35 additions & 0 deletions src/V3VirtIface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Create separate tasks for VirtIfaceed processes that
// can outlive their parents
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2023 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_V3VIRTIFACE_H_
#define VERILATOR_V3VIRTIFACE_H_

#include "config_build.h"
#include "verilatedos.h"

#include "V3ThreadSafety.h"

class AstNetlist;

//============================================================================

class V3VirtIface final {
public:
static void makeTriggerAssignments(AstNetlist* nodep) VL_MT_DISABLED;
};

#endif // Guard
2 changes: 1 addition & 1 deletion src/V3Width.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2723,7 +2723,7 @@ class WidthVisitor final : public VNVisitor {
if (AstVar* const varp = VN_CAST(foundp, Var)) {
nodep->dtypep(foundp->dtypep());
nodep->varp(varp);
varp->usedVirtIface(true);
varp->usedVirtIface(adtypep->isVirtual());
return;
}
UINFO(1, "found object " << foundp << endl);
Expand Down
6 changes: 6 additions & 0 deletions src/Verilator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
#include "V3Unknown.h"
#include "V3Unroll.h"
#include "V3VariableOrder.h"
#include "V3VirtIface.h"
#include "V3Waiver.h"
#include "V3Width.h"
#include "V3WidthCommit.h"
Expand Down Expand Up @@ -397,6 +398,11 @@ static void process() {
// Reorder assignments in pipelined blocks
if (v3Global.opt.fReorder()) V3Split::splitReorderAll(v3Global.rootp());

if (v3Global.hasVirtIfaces()) {
// Create extra triggers for virtual interfaces
V3VirtIface::makeTriggerAssignments(v3Global.rootp());
}

if (v3Global.opt.timing().isSetTrue()) {
// Convert AST for timing if requested
// Needs to be after V3Gate, as that step modifies sentrees
Expand Down
Loading

0 comments on commit 605f249

Please sign in to comment.