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 <kbieganski@antmicro.com>
  • Loading branch information
kbieganski committed Dec 1, 2023
1 parent ead27db commit 5ddc9a4
Show file tree
Hide file tree
Showing 36 changed files with 952 additions and 27 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ set(COMMON_SOURCES
V3SchedPartition.cpp
V3SchedReplicate.cpp
V3SchedTiming.cpp
V3SchedVirtIface.cpp
V3Scope.cpp
V3Scoreboard.cpp
V3Slice.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 @@ -274,6 +274,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3SchedPartition.o \
V3SchedReplicate.o \
V3SchedTiming.o \
V3SchedVirtIface.o \
V3Scope.o \
V3Scoreboard.o \
V3Slice.o \
Expand Down
6 changes: 6 additions & 0 deletions src/V3AstNodeDType.h
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,7 @@ class AstEnumDType final : public AstNodeDType {
class AstIfaceRefDType final : public AstNodeDType {
// Reference to an interface, either for a port, or inside parent cell
// @astgen op1 := paramsp : List[AstPin]
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 @@ -876,6 +877,11 @@ 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;
if (flag) v3Global.setHasVirtIfaces();
}
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: 5 additions & 4 deletions src/V3AstNodeOther.h
Original file line number Diff line number Diff line change
Expand Up @@ -1727,6 +1727,7 @@ class AstVar final : public AstNode {
VLifetime m_lifetime; // Lifetime
VVarAttrClocker m_attrClocker;
MTaskIdSet m_mtaskIds; // MTaskID's that read or write this var
AstIface* m_sensIfacep = nullptr; // Interface type to which reads from this var are sensitive
int m_pinNum = 0; // For XML, if non-zero the connection pin number
bool m_ansi : 1; // Params or pins declared in the module header, rather than the body
bool m_declTyped : 1; // Declared as type (for dedup check)
Expand All @@ -1742,7 +1743,6 @@ class AstVar final : public AstNode {
bool m_usedClock : 1; // Signal used as a clock
bool m_usedParam : 1; // Parameter is referenced (on link; later signals not setup)
bool m_usedLoopIdx : 1; // Variable subject of for unrolling
bool m_usedVirtIface : 1; // Signal used through a virtual interface
bool m_funcLocal : 1; // Local variable for a function
bool m_funcLocalSticky : 1; // As m_funcLocal but remains set if var is moved to a static
bool m_funcReturn : 1; // Return variable for a function
Expand Down Expand Up @@ -1784,7 +1784,6 @@ class AstVar final : public AstNode {
m_usedClock = false;
m_usedParam = false;
m_usedLoopIdx = false;
m_usedVirtIface = false;
m_sigPublic = false;
m_sigModPublic = false;
m_sigUserRdPublic = false;
Expand Down Expand Up @@ -1866,6 +1865,8 @@ class AstVar final : public AstNode {
dtypeFrom(examplep);
}
ASTGEN_MEMBERS_AstVar;
void cloneRelink() override;
const char* broken() const override;
void dump(std::ostream& str) const override;
bool same(const AstNode* samep) const override;
string name() const override VL_MT_STABLE VL_MT_SAFE { return m_name; } // * = Var name
Expand Down Expand Up @@ -1913,6 +1914,7 @@ class AstVar final : public AstNode {
}
void ansi(bool flag) { m_ansi = flag; }
void declTyped(bool flag) { m_declTyped = flag; }
void sensIfacep(AstIface* nodep) { m_sensIfacep = nodep; }
void attrClocker(VVarAttrClocker flag) { m_attrClocker = flag; }
void attrFileDescr(bool flag) { m_fileDescr = flag; }
void attrScClocked(bool flag) { m_scClocked = flag; }
Expand All @@ -1923,7 +1925,6 @@ class AstVar final : public AstNode {
void usedClock(bool flag) { m_usedClock = flag; }
void usedParam(bool flag) { m_usedParam = flag; }
void usedLoopIdx(bool flag) { m_usedLoopIdx = flag; }
void usedVirtIface(bool flag) { m_usedVirtIface = flag; }
void sigPublic(bool flag) { m_sigPublic = flag; }
void sigModPublic(bool flag) { m_sigModPublic = flag; }
void sigUserRdPublic(bool flag) {
Expand Down Expand Up @@ -2016,7 +2017,6 @@ class AstVar final : public AstNode {
bool isUsedClock() const { return m_usedClock; }
bool isUsedParam() const { return m_usedParam; }
bool isUsedLoopIdx() const { return m_usedLoopIdx; }
bool isUsedVirtIface() const { return m_usedVirtIface; }
bool isSc() const VL_MT_SAFE { return m_sc; }
bool isScQuad() const;
bool isScBv() const;
Expand Down Expand Up @@ -2044,6 +2044,7 @@ class AstVar final : public AstNode {
bool attrSFormat() const { return m_attrSFormat; }
bool attrSplitVar() const { return m_attrSplitVar; }
bool attrIsolateAssign() const { return m_attrIsolateAssign; }
AstIface* sensIfacep() const { return m_sensIfacep; }
VVarAttrClocker attrClocker() const { return m_attrClocker; }
string verilogKwd() const override;
void lifetime(const VLifetime& flag) { m_lifetime = flag; }
Expand Down
7 changes: 7 additions & 0 deletions src/V3AstNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2168,6 +2168,13 @@ int AstVarRef::instrCount() const {
// Otherwise as a load/store
return widthInstrs() * (access().isReadOrRW() ? INSTR_COUNT_LD : 1);
}
void AstVar::cloneRelink() {
if (m_sensIfacep && m_sensIfacep->clonep()) m_sensIfacep = m_sensIfacep->clonep();
}
const char* AstVar::broken() const {
BROKEN_RTN(m_sensIfacep && !m_sensIfacep->brokeExists());
return nullptr;
}
void AstVar::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isSc()) str << " [SC]";
Expand Down
2 changes: 1 addition & 1 deletion src/V3Const.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2691,7 +2691,7 @@ class ConstVisitor final : public VNVisitor {
&& m_doNConst
&& v3Global.opt.fConst()
// Default value, not a "known" constant for this usage
&& !nodep->varp()->isClassMember() && !nodep->varp()->isUsedVirtIface()
&& !nodep->varp()->isClassMember() && !nodep->varp()->sensIfacep()
&& !(nodep->varp()->isFuncLocal() && nodep->varp()->isNonOutput())
&& !nodep->varp()->noSubst() && !nodep->varp()->isSigPublic())
|| nodep->varp()->isParam())) {
Expand Down
2 changes: 1 addition & 1 deletion src/V3Dead.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ class DeadVisitor final : public VNVisitor {
}
bool mightElimVar(AstVar* nodep) const {
if (nodep->isSigPublic()) return false; // Can't elim publics!
if (nodep->isIO() || nodep->isClassMember() || nodep->isUsedVirtIface()) return false;
if (nodep->isIO() || nodep->isClassMember() || nodep->sensIfacep()) return false;
if (nodep->isTemp() && !nodep->isTrace()) return true;
return m_elimUserVars; // Post-Trace can kill most anything
}
Expand Down
2 changes: 1 addition & 1 deletion src/V3Gate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ class GateGraph final : public V3Graph {
UINFO(6, "New vertex " << vscp << endl);
vVtxp = new GateVarVertex{this, vscp};
vscp->user1p(vVtxp);
if (vscp->varp()->isUsedVirtIface()) {
if (vscp->varp()->sensIfacep()) {
// Can be used in a class method, which cannot be tracked statically
vVtxp->clearReducibleAndDedupable("VirtIface");
vVtxp->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
4 changes: 2 additions & 2 deletions src/V3Life.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class LifeBlock final {
void checkRemoveAssign(const LifeMap::iterator& it) {
const AstVar* const varp = it->first->varp();
LifeVarEntry* const entp = &(it->second);
if (!varp->isSigPublic() && !varp->isUsedVirtIface()) {
if (!varp->isSigPublic() && !varp->sensIfacep()) {
// Rather than track what sigs AstUCFunc/AstUCStmt may change,
// we just don't optimize any public sigs
// Check the var entry, and remove if appropriate
Expand Down Expand Up @@ -178,7 +178,7 @@ class LifeBlock final {
const auto pair = m_map.emplace(nodep, LifeVarEntry::CONSUMED{});
if (!pair.second) {
if (AstConst* const constp = pair.first->second.constNodep()) {
if (!varrefp->varp()->isSigPublic() && !varrefp->varp()->isUsedVirtIface()) {
if (!varrefp->varp()->isSigPublic() && !varrefp->varp()->sensIfacep()) {
// Aha, variable is constant; substitute in.
// We'll later constant propagate
UINFO(4, " replaceconst: " << varrefp << endl);
Expand Down
2 changes: 1 addition & 1 deletion src/V3Localize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,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()->isUsedVirtIface() // Not used through a virtual interface
&& !nodep->varp()->sensIfacep() // Not sensitive to an interface
&& !nodep->varp()->valuep() // Does not have an initializer
) {
UINFO(4, "Consider for localization: " << nodep << endl);
Expand Down
81 changes: 70 additions & 11 deletions src/V3Sched.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,16 +508,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 @@ -576,6 +576,17 @@ class ExtraTriggers final {
const string& description(size_t index) const { return m_descriptions[index]; }
};

//============================================================================
// Helper that creates virtual interface trigger resets

void addVirtIfaceTriggerAssignments(const VirtIfaceTriggers& virtIfaceTriggers,
size_t vifTriggerIndex, const TriggerKit& actTrig) {
for (const auto& p : virtIfaceTriggers) {
actTrig.addExtraTriggerAssignment(p.second, vifTriggerIndex);
++vifTriggerIndex;
}
}

//============================================================================
// Create a TRIGGERVEC and the related TriggerKit for the given AstSenTree vector

Expand Down Expand Up @@ -808,7 +819,8 @@ void createSettle(AstNetlist* netlistp, AstCFunc* const initFuncp, SenExprBuilde
// Order the replicated combinational logic to create the 'ico' region

AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
SenExprBuilder& senExprBuilder, LogicByScope& logic) {
SenExprBuilder& senExprBuilder, LogicByScope& logic,
const VirtIfaceTriggers& virtIfaceTriggers) {
// Nothing to do if no combinational logic is sensitive to top level inputs
if (logic.empty()) return nullptr;

Expand All @@ -834,15 +846,20 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
const size_t dpiExportTriggerIndex = dpiExportTriggerVscp
? extraTriggers.allocate("DPI export trigger")
: std::numeric_limits<unsigned>::max();
const size_t firstVifTriggerIndex = extraTriggers.size();
for (const auto& p : virtIfaceTriggers) {
extraTriggers.allocate("virtual interface: " + p.first->name());
}

// Gather the relevant sensitivity expressions and create the trigger kit
const auto& senTreeps = getSenTreesUsedBy({&logic});
const TriggerKit& trig
= createTriggers(netlistp, initFuncp, senExprBuilder, senTreeps, "ico", extraTriggers);

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

// Remap sensitivities
remapSensitivities(logic, trig.m_map);
Expand All @@ -859,6 +876,8 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
AstSenTree* const dpiExportTriggered
= dpiExportTriggerVscp ? createTriggerSenTree(netlistp, trig.m_vscp, dpiExportTriggerIndex)
: nullptr;
const auto& vifTriggered
= virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, trig.m_vscp);

// Create and Order the body function
AstCFunc* const icoFuncp
Expand All @@ -869,6 +888,10 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
out.push_back(inputChanged);
}
if (varp->isWrittenByDpi()) out.push_back(dpiExportTriggered);
if (vscp->varp()->sensIfacep()) {
const auto it = vifTriggered.find(vscp->varp()->sensIfacep());
if (it != vifTriggered.end()) out.push_back(it->second);
}
});
splitCheck(icoFuncp);

Expand Down Expand Up @@ -1070,6 +1093,21 @@ void createEval(AstNetlist* netlistp, //

} // namespace

//============================================================================
// Helper that builds virtual interface trigger sentrees

VirtIfaceTriggers::IfaceSensMap
VirtIfaceTriggers::makeIfaceToSensMap(AstNetlist* const netlistp, size_t vifTriggerIndex,
AstVarScope* trigVscp) const {
std::map<const AstIface*, AstSenTree*> ifaceToSensMap;
for (const auto& p : *this) {
ifaceToSensMap.emplace(
std::make_pair(p.first, createTriggerSenTree(netlistp, trigVscp, vifTriggerIndex)));
++vifTriggerIndex;
}
return ifaceToSensMap;
}

//============================================================================
// Top level entry-point to scheduling

Expand All @@ -1080,7 +1118,10 @@ void schedule(AstNetlist* netlistp) {
V3Stats::addStat("Scheduling, " + name, size);
};

// Step 0. Prepare timing-related logic and external domains
// Step 0. Prepare external domains for timing and virtual interfaces
// Create extra triggers for virtual interfaces
const auto& virtIfaceTriggers = makeVirtIfaceTriggers(netlistp);
// Prepare timing-related logic and external domains
TimingKit timingKit = prepareTiming(netlistp);

// Step 1. Gather and classify all logic in the design
Expand Down Expand Up @@ -1145,8 +1186,8 @@ void schedule(AstNetlist* netlistp) {
}

// Step 7: Create input combinational logic loop
AstNode* const icoLoopp
= createInputCombLoop(netlistp, initp, senExprBuilder, logicReplicas.m_ico);
AstNode* const icoLoopp = createInputCombLoop(netlistp, initp, senExprBuilder,
logicReplicas.m_ico, virtIfaceTriggers);
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-ico");

// Step 8: Create the pre/act/nba triggers
Expand All @@ -1157,6 +1198,10 @@ void schedule(AstNetlist* netlistp) {
const size_t dpiExportTriggerIndex = dpiExportTriggerVscp
? extraTriggers.allocate("DPI export trigger")
: std::numeric_limits<unsigned>::max();
const size_t firstVifTriggerIndex = extraTriggers.size();
for (const auto& p : virtIfaceTriggers) {
extraTriggers.allocate("virtual interface: " + p.first->name());
}

const auto& senTreeps = getSenTreesUsedBy({&logicRegions.m_pre, //
&logicRegions.m_act, //
Expand All @@ -1171,8 +1216,9 @@ 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);
}
addVirtIfaceTriggerAssignments(virtIfaceTriggers, firstVifTriggerIndex, actTrig);

AstVarScope* const actTrigVscp = actTrig.m_vscp;
AstVarScope* const preTrigVscp = scopeTopp->createTempLike("__VpreTriggered", actTrigVscp);
Expand Down Expand Up @@ -1224,12 +1270,19 @@ void schedule(AstNetlist* netlistp) {
? createTriggerSenTree(netlistp, actTrig.m_vscp, dpiExportTriggerIndex)
: nullptr;

const auto& vifTriggeredAct
= virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, actTrig.m_vscp);

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()->sensIfacep()) {
const auto it = vifTriggeredAct.find(vscp->varp()->sensIfacep());
if (it != vifTriggeredAct.end()) out.push_back(it->second);
}
});
splitCheck(actFuncp);
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-act");
Expand All @@ -1253,6 +1306,8 @@ void schedule(AstNetlist* netlistp) {
= dpiExportTriggerVscp
? createTriggerSenTree(netlistp, trigVscp, dpiExportTriggerIndex)
: nullptr;
const auto& vifTriggered
= virtIfaceTriggers.makeIfaceToSensMap(netlistp, firstVifTriggerIndex, trigVscp);

const auto& timingDomains = timingKit.remapDomains(trigMap);
AstCFunc* const funcp = V3Order::order(
Expand All @@ -1261,6 +1316,10 @@ void schedule(AstNetlist* netlistp) {
auto it = timingDomains.find(vscp);
if (it != timingDomains.end()) out = it->second;
if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggered);
if (vscp->varp()->sensIfacep()) {
const auto it = vifTriggered.find(vscp->varp()->sensIfacep());
if (it != vifTriggered.end()) out.push_back(it->second);
}
});

// Create the trigger dumping function, which is the same as act trigger
Expand Down
Loading

0 comments on commit 5ddc9a4

Please sign in to comment.