Support --timing triggers for virtual interfaces (#4673)
This commit is contained in:
parent
5d99534d54
commit
ea2084392f
|
|
@ -282,6 +282,7 @@ set(COMMON_SOURCES
|
|||
V3SchedPartition.cpp
|
||||
V3SchedReplicate.cpp
|
||||
V3SchedTiming.cpp
|
||||
V3SchedVirtIface.cpp
|
||||
V3Scope.cpp
|
||||
V3Scoreboard.cpp
|
||||
V3Slice.cpp
|
||||
|
|
|
|||
|
|
@ -274,6 +274,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3SchedPartition.o \
|
||||
V3SchedReplicate.o \
|
||||
V3SchedTiming.o \
|
||||
V3SchedVirtIface.o \
|
||||
V3Scope.o \
|
||||
V3Scoreboard.o \
|
||||
V3Slice.o \
|
||||
|
|
|
|||
|
|
@ -818,6 +818,7 @@ class AstIfaceRefDType final : public AstNodeDType {
|
|||
// @astgen ptr := m_ifacep : Optional[AstIface] // Interface; cellp() should override
|
||||
// @astgen ptr := m_cellp : Optional[AstCell] // When exact parent cell known; not a guess
|
||||
// @astgen ptr := m_modportp : Optional[AstModport] // nullptr = unlinked or no modport
|
||||
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
|
||||
|
|
@ -855,6 +856,11 @@ public:
|
|||
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; }
|
||||
|
|
|
|||
|
|
@ -1713,6 +1713,8 @@ class AstVar final : public AstNode {
|
|||
// MTASKSTATE variables
|
||||
// @astgen op3 := valuep : Optional[AstNode] // May be a DType for type parameter defaults
|
||||
// @astgen op4 := attrsp : List[AstNode] // Attributes during early parse
|
||||
// @astgen ptr := m_sensIfacep : Optional[AstIface] // Interface type to which reads from this
|
||||
// var are sensitive
|
||||
|
||||
string m_name; // Name of variable
|
||||
string m_origName; // Original name before dot addition
|
||||
|
|
@ -1738,7 +1740,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
|
||||
|
|
@ -1780,7 +1781,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;
|
||||
|
|
@ -1909,6 +1909,7 @@ public:
|
|||
}
|
||||
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; }
|
||||
|
|
@ -1919,7 +1920,6 @@ public:
|
|||
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) {
|
||||
|
|
@ -2012,7 +2012,6 @@ public:
|
|||
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;
|
||||
|
|
@ -2040,6 +2039,7 @@ public:
|
|||
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; }
|
||||
|
|
|
|||
|
|
@ -2694,7 +2694,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())) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ public:
|
|||
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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -162,6 +163,8 @@ public:
|
|||
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; }
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ public:
|
|||
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
|
||||
|
|
@ -178,7 +178,7 @@ public:
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -576,6 +576,17 @@ public:
|
|||
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
|
||||
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
@ -834,6 +846,10 @@ 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});
|
||||
|
|
@ -841,8 +857,9 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
|
|||
= 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);
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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, //
|
||||
|
|
@ -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);
|
||||
|
|
@ -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");
|
||||
|
|
@ -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(
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -149,6 +149,27 @@ public:
|
|||
TimingKit& operator=(TimingKit&&) = default;
|
||||
};
|
||||
|
||||
class VirtIfaceTriggers final {
|
||||
using IfaceTrigger = std::pair<const AstIface*, AstVarScope*>;
|
||||
using IfaceTriggerVec = std::vector<IfaceTrigger>;
|
||||
using IfaceSensMap = std::map<const AstIface*, AstSenTree*>;
|
||||
IfaceTriggerVec m_triggers;
|
||||
|
||||
public:
|
||||
void emplace_back(IfaceTrigger&& p) { m_triggers.emplace_back(std::move(p)); }
|
||||
IfaceTriggerVec::const_iterator begin() const { return m_triggers.begin(); }
|
||||
IfaceTriggerVec::const_iterator end() const { return m_triggers.end(); }
|
||||
IfaceSensMap makeIfaceToSensMap(AstNetlist* netlistp, size_t vifTriggerIndex,
|
||||
AstVarScope* trigVscp) const;
|
||||
VL_UNCOPYABLE(VirtIfaceTriggers);
|
||||
VirtIfaceTriggers() = default;
|
||||
VirtIfaceTriggers(VirtIfaceTriggers&&) = default;
|
||||
VirtIfaceTriggers& operator=(VirtIfaceTriggers&&) = default;
|
||||
};
|
||||
|
||||
// Creates trigger vars for signals driven via virtual interfaces
|
||||
VirtIfaceTriggers makeVirtIfaceTriggers(AstNetlist* nodep) VL_MT_DISABLED;
|
||||
|
||||
// Creates the timing kit and marks variables written by suspendables
|
||||
TimingKit prepareTiming(AstNetlist* const netlistp) VL_MT_DISABLED;
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,8 @@ public:
|
|||
: SchedReplicateVertex{graphp}
|
||||
, m_vscp{vscp} {
|
||||
// Top level inputs are
|
||||
if (varp()->isPrimaryInish() || varp()->isSigUserRWPublic() || varp()->isWrittenByDpi()) {
|
||||
if (varp()->isPrimaryInish() || varp()->isSigUserRWPublic() || varp()->isWrittenByDpi()
|
||||
|| varp()->sensIfacep()) {
|
||||
addDrivingRegions(INPUT);
|
||||
}
|
||||
// Currently we always execute suspendable processes at the beginning of
|
||||
|
|
|
|||
|
|
@ -0,0 +1,234 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Create triggers necessary for scheduling across
|
||||
// virtual interfaces
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//*************************************************************************
|
||||
// V3SchedVirtIface's Transformations:
|
||||
//
|
||||
// Each interface type written to via virtual interface, or written to normally but read via
|
||||
// virtual interface:
|
||||
// Create a trigger var for it
|
||||
// Each AssignW, AssignPost:
|
||||
// If it writes to a virtual interface, or to a variable read via virtual interface:
|
||||
// Convert to an always
|
||||
// Each statement:
|
||||
// If it writes to a virtual interface, or to a variable read via virtual interface:
|
||||
// Set the corresponding trigger to 1
|
||||
// If the write is done by an AssignDly, the trigger is also set by AssignDly
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3AstNodeExpr.h"
|
||||
#include "V3Sched.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
namespace V3Sched {
|
||||
|
||||
namespace {
|
||||
|
||||
class VirtIfaceVisitor final : public VNVisitor {
|
||||
private:
|
||||
// NODE STATE
|
||||
// AstIface::user1() -> AstVarScope*. Trigger var for this interface
|
||||
const VNUser1InUse m_user1InUse;
|
||||
|
||||
// TYPES
|
||||
using OnWriteToVirtIface = std::function<void(AstVarRef*, AstIface*)>;
|
||||
|
||||
// STATE
|
||||
AstNetlist* const m_netlistp; // Root node
|
||||
AstAssign* m_trigAssignp = nullptr; // Previous/current trigger assignment
|
||||
AstIface* m_trigAssignIfacep = nullptr; // Interface type whose trigger is assigned
|
||||
// by m_trigAssignp
|
||||
V3UniqueNames m_vifTriggerNames{"__VvifTrigger"}; // Unique names for virt iface
|
||||
// triggers
|
||||
VirtIfaceTriggers m_triggers; // Interfaces and corresponding trigger vars
|
||||
|
||||
// METHODS
|
||||
// For each write across a virtual interface boundary
|
||||
static void foreachWrittenVirtIface(AstNode* const nodep, const OnWriteToVirtIface& onWrite) {
|
||||
nodep->foreach([&](AstVarRef* const refp) {
|
||||
if (refp->access().isReadOnly()) return;
|
||||
if (AstIfaceRefDType* const dtypep = VN_CAST(refp->varp()->dtypep(), IfaceRefDType)) {
|
||||
if (dtypep->isVirtual() && VN_IS(refp->firstAbovep(), MemberSel)) {
|
||||
onWrite(refp, dtypep->ifacep());
|
||||
}
|
||||
} else if (AstIface* const ifacep = refp->varp()->sensIfacep()) {
|
||||
onWrite(refp, ifacep);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Returns true if there is a write across a virtual interface boundary
|
||||
static bool writesToVirtIface(const AstNode* const nodep) {
|
||||
return nodep->exists([](const AstVarRef* const refp) {
|
||||
if (!refp->access().isWriteOrRW()) return false;
|
||||
AstIfaceRefDType* const dtypep = VN_CAST(refp->varp()->dtypep(), IfaceRefDType);
|
||||
const bool writesToVirtIfaceMember
|
||||
= (dtypep && dtypep->isVirtual() && VN_IS(refp->firstAbovep(), MemberSel));
|
||||
const bool writesToIfaceSensVar = refp->varp()->sensIfacep();
|
||||
return writesToVirtIfaceMember || writesToIfaceSensVar;
|
||||
});
|
||||
}
|
||||
// Error on write across a virtual interface boundary
|
||||
static void unsupportedWriteToVirtIface(AstNode* nodep, const char* locationp) {
|
||||
if (!nodep) return;
|
||||
foreachWrittenVirtIface(nodep, [locationp](AstVarRef* const selp, AstIface*) {
|
||||
selp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: write to virtual interface in " << locationp);
|
||||
});
|
||||
}
|
||||
// Create trigger var for the given interface if it doesn't exist; return a write ref to it
|
||||
AstVarRef* createVirtIfaceTriggerRefp(FileLine* const flp, AstIface* ifacep) {
|
||||
if (!ifacep->user1()) {
|
||||
AstScope* const scopeTopp = m_netlistp->topScopep()->scopep();
|
||||
AstVarScope* const vscp = scopeTopp->createTemp(m_vifTriggerNames.get(ifacep), 1);
|
||||
ifacep->user1p(vscp);
|
||||
m_triggers.emplace_back(std::make_pair(ifacep, vscp));
|
||||
}
|
||||
return new AstVarRef{flp, VN_AS(ifacep->user1p(), VarScope), VAccess::WRITE};
|
||||
}
|
||||
|
||||
// VISITORS
|
||||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
m_trigAssignp = nullptr;
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
m_trigAssignIfacep = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCFunc* nodep) override {
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
m_trigAssignp = nullptr;
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
m_trigAssignIfacep = nullptr;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstAssignW* nodep) override {
|
||||
if (writesToVirtIface(nodep)) {
|
||||
// Convert to always, as we have to assign the trigger var
|
||||
nodep->convertToAlways();
|
||||
}
|
||||
}
|
||||
void visit(AstAssignPost* nodep) override {
|
||||
if (writesToVirtIface(nodep)) {
|
||||
// Convert to always, as we have to assign the trigger var
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstAlwaysPost* const postp = new AstAlwaysPost{flp, nullptr, nullptr};
|
||||
nodep->replaceWith(postp);
|
||||
postp->addStmtsp(
|
||||
new AstAssign{flp, nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()});
|
||||
nodep->deleteTree();
|
||||
}
|
||||
}
|
||||
void visit(AstNodeIf* nodep) override {
|
||||
unsupportedWriteToVirtIface(nodep->condp(), "if condition");
|
||||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
iterateAndNextNull(nodep->thensp());
|
||||
}
|
||||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
iterateAndNextNull(nodep->elsesp());
|
||||
}
|
||||
if (v3Global.usesTiming()) {
|
||||
// Clear the trigger assignment, as there could have been timing controls in either
|
||||
// branch
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstWhile* nodep) override {
|
||||
unsupportedWriteToVirtIface(nodep->precondsp(), "loop condition");
|
||||
unsupportedWriteToVirtIface(nodep->condp(), "loop condition");
|
||||
unsupportedWriteToVirtIface(nodep->incsp(), "loop increment statement");
|
||||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
iterateAndNextNull(nodep->stmtsp());
|
||||
}
|
||||
if (v3Global.usesTiming()) {
|
||||
// Clear the trigger assignment, as there could have been timing controls in the loop
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
{
|
||||
VL_RESTORER(m_trigAssignp);
|
||||
VL_RESTORER(m_trigAssignIfacep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
if (v3Global.usesTiming()) {
|
||||
// Clear the trigger assignment, as there could have been timing controls in the jump
|
||||
// block
|
||||
m_trigAssignp = nullptr;
|
||||
m_trigAssignIfacep = nullptr;
|
||||
}
|
||||
}
|
||||
void visit(AstNodeStmt* nodep) override {
|
||||
if (v3Global.usesTiming()
|
||||
&& nodep->exists([](AstNode* nodep) { return nodep->isTimingControl(); })) {
|
||||
m_trigAssignp = nullptr; // Could be after a delay - need new trigger assignment
|
||||
m_trigAssignIfacep = nullptr;
|
||||
// No restorer, as following statements should not reuse the old assignment
|
||||
}
|
||||
FileLine* const flp = nodep->fileline();
|
||||
foreachWrittenVirtIface(nodep, [&](AstVarRef*, AstIface* ifacep) {
|
||||
if (ifacep != m_trigAssignIfacep) {
|
||||
// Write to different interface type than before - need new trigger assignment
|
||||
// No restorer, as following statements should not reuse the old assignment
|
||||
m_trigAssignIfacep = ifacep;
|
||||
m_trigAssignp = nullptr;
|
||||
}
|
||||
if (!m_trigAssignp) {
|
||||
m_trigAssignp = new AstAssign{flp, createVirtIfaceTriggerRefp(flp, ifacep),
|
||||
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;
|
||||
|
||||
// METHODS
|
||||
VirtIfaceTriggers take_triggers() { return std::move(m_triggers); }
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
VirtIfaceTriggers makeVirtIfaceTriggers(AstNetlist* nodep) {
|
||||
UINFO(2, __FUNCTION__ << ": " << endl);
|
||||
if (v3Global.hasVirtIfaces()) {
|
||||
VirtIfaceVisitor visitor{nodep};
|
||||
V3Global::dumpCheckGlobalTree("sched_vif", 0, dumpTreeLevel() >= 6);
|
||||
return visitor.take_triggers();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} //namespace V3Sched
|
||||
|
|
@ -2741,7 +2741,10 @@ class WidthVisitor final : public VNVisitor {
|
|||
if (AstVar* const varp = VN_CAST(foundp, Var)) {
|
||||
nodep->dtypep(foundp->dtypep());
|
||||
nodep->varp(varp);
|
||||
varp->usedVirtIface(true);
|
||||
AstIface* const ifacep = adtypep->ifacep();
|
||||
varp->sensIfacep(ifacep);
|
||||
nodep->fromp()->foreach(
|
||||
[ifacep](AstVarRef* const refp) { refp->varp()->sensIfacep(ifacep); });
|
||||
return;
|
||||
}
|
||||
UINFO(1, "found object " << foundp << endl);
|
||||
|
|
|
|||
|
|
@ -2134,12 +2134,22 @@ data_typeNoRef<nodeDTypep>: // ==IEEE: data_type, excluding class_ty
|
|||
|
||||
data_typeVirtual<nodeDTypep>: // ==IEEE: data_type after yVIRTUAL [ yINTERFACE ]
|
||||
// // Parameters here are SV2009
|
||||
id/*interface*/ { $$ = new AstIfaceRefDType{$<fl>1, "", *$1}; }
|
||||
| id/*interface*/ '.' id/*modport*/ { $$ = new AstIfaceRefDType{$<fl>1, $<fl>3, "", *$1, *$3}; }
|
||||
id/*interface*/
|
||||
{ AstIfaceRefDType* const ifrefp = new AstIfaceRefDType{$<fl>1, "", *$1};
|
||||
ifrefp->isVirtual(true);
|
||||
$$ = ifrefp; }
|
||||
| id/*interface*/ '.' id/*modport*/
|
||||
{ AstIfaceRefDType* const ifrefp = new AstIfaceRefDType{$<fl>1, $<fl>3, "", *$1, *$3};
|
||||
ifrefp->isVirtual(true);
|
||||
$$ = ifrefp; }
|
||||
| id/*interface*/ parameter_value_assignmentClass
|
||||
{ $$ = new AstIfaceRefDType{$<fl>1, nullptr, "", *$1, "", $2}; }
|
||||
{ AstIfaceRefDType* const ifrefp = new AstIfaceRefDType{$<fl>1, nullptr, "", *$1, "", $2};
|
||||
ifrefp->isVirtual(true);
|
||||
$$ = ifrefp; }
|
||||
| id/*interface*/ parameter_value_assignmentClass '.' id/*modport*/
|
||||
{ $$ = new AstIfaceRefDType{$<fl>1, $<fl>4, "", *$1, *$4, $2}; }
|
||||
{ AstIfaceRefDType* const ifrefp = new AstIfaceRefDType{$<fl>1, $<fl>4, "", *$1, *$4, $2};
|
||||
ifrefp->isVirtual(true);
|
||||
$$ = ifrefp; }
|
||||
;
|
||||
|
||||
data_type_or_void<nodeDTypep>: // ==IEEE: data_type_or_void
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
[0] vif1.data==0000
|
||||
[0] intf2.data==0000
|
||||
[0] vif4.data==0000
|
||||
[10] intf2.data==beef
|
||||
[20] vif1.data==dead
|
||||
[20] vif4.data==face
|
||||
[30] intf2.data==beef
|
||||
[40] vif1.data==dead
|
||||
[40] vif4.data==face
|
||||
[50] intf2.data==beef
|
||||
[60] vif1.data==dead
|
||||
[60] vif4.data==face
|
||||
[70] intf2.data==beef
|
||||
[80] intf2.data==beef
|
||||
[80] vif4.data==cafe
|
||||
[90] intf2.data==beef
|
||||
[100] intf2.data==beef
|
||||
[100] vif4.data==deaf
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2020 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 => ["-fno-reorder"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface Bus1;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
interface Bus2;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
interface Bus3;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
|
||||
module t (
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
integer cyc = 0;
|
||||
Bus1 intf1();
|
||||
Bus2 intf2();
|
||||
Bus3 intf3(), intf4();
|
||||
virtual Bus1 vif1 = intf1;
|
||||
virtual Bus2 vif2 = intf2;
|
||||
virtual Bus3 vif3 = intf3, vif4 = intf4;
|
||||
|
||||
// Finish on negedge so that $finish is last
|
||||
always @(negedge clk)
|
||||
if (cyc >= 10) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
function void assign_to_intf3();
|
||||
if ($c("1")) return;
|
||||
intf3.data = 'hcafe;
|
||||
endfunction
|
||||
|
||||
always @(posedge clk) begin
|
||||
logic foo = 1;
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1 || cyc == 3 || cyc == 5) intf1.data = 'hdead;
|
||||
else vif2.data = 'hbeef;
|
||||
if (cyc == 1 || cyc == 3 || cyc == 5) begin
|
||||
if (cyc >= 3) $c("// dummy statement");
|
||||
else intf3.data = 'hfafa;
|
||||
intf4.data = 'hface;
|
||||
end
|
||||
if (cyc == 7) begin
|
||||
while ($c("0")) begin
|
||||
foo = 0;
|
||||
intf3.data = 'hbebe;
|
||||
end
|
||||
intf4.data = 'hcafe;
|
||||
end
|
||||
if (cyc == 9) begin
|
||||
assign_to_intf3;
|
||||
intf4.data = 'hdeaf;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb $write("[%0t] vif1.data==%h\n", $time, vif1.data);
|
||||
always_comb $write("[%0t] intf2.data==%h\n", $time, intf2.data);
|
||||
always_comb $write("[%0t] vif4.data==%h\n", $time, vif4.data);
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
[0] data==0000
|
||||
[0] data==0000
|
||||
[10] data==0000
|
||||
[20] data==dead
|
||||
[20] data==beef
|
||||
[20] data==beef
|
||||
[30] data==beef
|
||||
[40] data==face
|
||||
[40] data==cafe
|
||||
[40] data==cafe
|
||||
[50] data==cafe
|
||||
*-* All Finished *-*
|
||||
|
|
@ -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 2020 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(
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface Bus;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
|
||||
module t (
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
integer cyc = 0;
|
||||
Bus intf();
|
||||
virtual Bus vif = intf;
|
||||
logic [15:0] data;
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
end
|
||||
|
||||
// Finish on negedge so that $finish is last
|
||||
always @(negedge clk)
|
||||
if (cyc >= 5) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
always @(posedge clk or data) begin
|
||||
if (cyc == 1) intf.data <= 'hdead;
|
||||
else if (cyc == 2) intf.data <= 'hbeef;
|
||||
else if (cyc == 3) intf.data <= 'hface;
|
||||
else if (cyc == 4) intf.data <= 'hcafe;
|
||||
end
|
||||
|
||||
assign data = vif.data;
|
||||
always_comb $write("[%0t] data==%h\n", $time, data);
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
//
|
||||
// Copyright 2023 by Geza Lore. 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
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#include "verilated.h"
|
||||
|
||||
#include "Vt_interface_virtual_sched_ico.h"
|
||||
#include "Vt_interface_virtual_sched_ico__Syms.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
|
||||
contextp->debug(0);
|
||||
contextp->commandArgs(argc, argv);
|
||||
srand48(5);
|
||||
|
||||
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX};
|
||||
topp->inc = 1;
|
||||
topp->clk = false;
|
||||
topp->eval();
|
||||
|
||||
while (!contextp->gotFinish() && contextp->time() < 100000) {
|
||||
contextp->timeInc(5);
|
||||
if (topp->clk) topp->inc += 1;
|
||||
topp->clk = !topp->clk;
|
||||
topp->eval();
|
||||
}
|
||||
|
||||
if (!contextp->gotFinish()) {
|
||||
vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
[0] intf1.inc==0
|
||||
[0] vif2.inc==0
|
||||
[0] intf1.inc==1
|
||||
[0] vif2.inc==1
|
||||
[0] intf1.inc==1
|
||||
[0] vif2.inc==1
|
||||
[5] intf1.inc==1
|
||||
[5] vif2.inc==1
|
||||
[10] intf1.inc==2
|
||||
[10] vif2.inc==2
|
||||
[15] intf1.inc==2
|
||||
[15] vif2.inc==2
|
||||
[20] intf1.inc==3
|
||||
[20] vif2.inc==3
|
||||
[25] intf1.inc==3
|
||||
[25] vif2.inc==3
|
||||
[30] intf1.inc==4
|
||||
[30] vif2.inc==4
|
||||
[35] intf1.inc==4
|
||||
[35] vif2.inc==4
|
||||
[40] intf1.inc==5
|
||||
[40] vif2.inc==5
|
||||
[45] intf1.inc==5
|
||||
[45] vif2.inc==5
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 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
|
||||
|
||||
scenarios(vlt_all => 1);
|
||||
|
||||
compile(
|
||||
make_main => 0,
|
||||
v_flags2 => ["--exe", "$Self->{t_dir}/$Self->{name}.cpp"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2023 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface If;
|
||||
logic [31:0] inc;
|
||||
endinterface
|
||||
|
||||
module top (
|
||||
clk,
|
||||
inc
|
||||
);
|
||||
|
||||
input clk;
|
||||
input [31:0] inc;
|
||||
int cyc = 0;
|
||||
|
||||
If intf1();
|
||||
If intf2();
|
||||
virtual If vif1 = intf1;
|
||||
virtual If vif2 = intf2;
|
||||
|
||||
assign vif1.inc = inc;
|
||||
assign intf2.inc = inc;
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc >= 4) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
always_comb $write("[%0t] intf1.inc==%0h\n", $time, intf1.inc);
|
||||
always_comb $write("[%0t] vif2.inc==%0h\n", $time, vif2.inc);
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
[0] intf1.data==0000
|
||||
[0] intf2.data==0000
|
||||
[0] vif3.data==0000
|
||||
[0] intf2.data==0000
|
||||
[10] intf2.data==0000
|
||||
[10] vif3.data==0000
|
||||
[20] intf1.data==dead
|
||||
[20] intf2.data==0000
|
||||
[20] vif3.data==0000
|
||||
[30] intf2.data==dead
|
||||
[30] vif3.data==0000
|
||||
[40] intf1.data==beef
|
||||
[40] intf2.data==dead
|
||||
[40] vif3.data==0000
|
||||
[50] intf2.data==beef
|
||||
[50] vif3.data==0000
|
||||
[60] intf2.data==beef
|
||||
[60] vif3.data==face
|
||||
[70] intf2.data==beef
|
||||
[70] vif3.data==cafe
|
||||
*-* All Finished *-*
|
||||
|
|
@ -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 2020 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(
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface Bus1;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
|
||||
interface Bus2;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
|
||||
interface Bus3;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
|
||||
module t (
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
integer cyc = 0;
|
||||
Bus1 intf1();
|
||||
Bus2 intf2();
|
||||
Bus3 intf3();
|
||||
virtual Bus1 vif1 = intf1;
|
||||
virtual Bus2 vif2 = intf2;
|
||||
virtual Bus3 vif3 = intf3;
|
||||
|
||||
logic [15:0] data;
|
||||
assign vif2.data = data;
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 1)
|
||||
vif1.data = 'hdead;
|
||||
else if (cyc == 2)
|
||||
data = vif1.data;
|
||||
else if (cyc == 3)
|
||||
vif1.data = 'hbeef;
|
||||
else if (cyc == 4)
|
||||
data = vif1.data;
|
||||
else if (cyc == 5)
|
||||
intf3.data <= 'hface;
|
||||
else if (cyc == 6)
|
||||
intf3.data <= 'hcafe;
|
||||
end
|
||||
|
||||
// Finish on negedge so that $finish is last
|
||||
always @(negedge clk)
|
||||
if (cyc >= 7) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
always_comb $write("[%0t] intf1.data==%h\n", $time, intf1.data);
|
||||
always_comb $write("[%0t] intf2.data==%h\n", $time, intf2.data);
|
||||
always_comb $write("[%0t] vif3.data==%h\n", $time, vif3.data);
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
vif1.data==dead
|
||||
intf2.data==0000
|
||||
vif1.data==dead
|
||||
intf2.data==0000
|
||||
intf2.data==beef
|
||||
vif1.data==dead
|
||||
intf2.data==beef
|
||||
intf2.data==beef
|
||||
vif1.data==cafe
|
||||
intf2.data==beef
|
||||
intf2.data==face
|
||||
vif1.data==cafe
|
||||
intf2.data==face
|
||||
intf2.data==face
|
||||
vif1.data==feed
|
||||
intf2.data==face
|
||||
intf2.data==deed
|
||||
vif1.data==feed
|
||||
intf2.data==deed
|
||||
intf2.data==deed
|
||||
vif1.data==deaf
|
||||
intf2.data==deed
|
||||
intf2.data==fafa
|
||||
vif1.data==deaf
|
||||
intf2.data==fafa
|
||||
intf2.data==fafa
|
||||
vif1.data==bebe
|
||||
intf2.data==fafa
|
||||
*-* All Finished *-*
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2020 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"],
|
||||
make_main => 0,
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface Bus;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
|
||||
module t;
|
||||
Bus intf1(), intf2();
|
||||
virtual Bus vif1 = intf1, vif2 = intf2;
|
||||
|
||||
task assign_to_vif2();
|
||||
if ($c("0")) return;
|
||||
#1 vif2.data = 'hfafa; #1;
|
||||
endtask
|
||||
|
||||
initial forever begin
|
||||
intf1.data = 'hdead;
|
||||
if ($c("1")) begin
|
||||
#1 vif2.data = 'hbeef; #1;
|
||||
end
|
||||
intf1.data = 'hcafe;
|
||||
if ($c("0")); else begin
|
||||
#1 vif2.data = 'hface; #1;
|
||||
end
|
||||
intf1.data = 'hfeed;
|
||||
while ($time < 5) begin
|
||||
#1 vif2.data = 'hdeed; #1;
|
||||
end
|
||||
intf1.data = 'hdeaf;
|
||||
assign_to_vif2;
|
||||
intf1.data = 'hbebe;
|
||||
#1 $write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
always_comb if ($time < 9) $write("vif1.data==%h\n", vif1.data);
|
||||
always_comb if ($time < 9) $write("intf2.data==%h\n", intf2.data);
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
%Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:22:22: Unsupported: write to virtual interface in if condition
|
||||
22 | if (write_data(vif.data)) $write("dummy op");
|
||||
| ^~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:23:25: Unsupported: write to virtual interface in loop condition
|
||||
23 | while (write_data(vif.data));
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:24:34: Unsupported: write to virtual interface in loop condition
|
||||
24 | for (int i = 0; write_data(vif.data); i += int'(write_data(vif.data)));
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:24:66: Unsupported: write to virtual interface in loop increment statement
|
||||
24 | for (int i = 0; write_data(vif.data); i += int'(write_data(vif.data)));
|
||||
| ^~~
|
||||
%Error-UNSUPPORTED: t/t_interface_virtual_unsup.v:25:34: Unsupported: write to virtual interface in loop condition
|
||||
25 | for (int i = 0; write_data(vif.data++); i++);
|
||||
| ^~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 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
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
fails => $Self->{vlt_all},
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
) if !$Self->{vlt_all};
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2023 by Wilson Snyder.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
interface Bus;
|
||||
logic [15:0] data;
|
||||
endinterface
|
||||
|
||||
module t;
|
||||
Bus intf();
|
||||
virtual Bus vif = intf;
|
||||
|
||||
function logic write_data(output logic[15:0] data);
|
||||
data = 'hdead;
|
||||
return 1;
|
||||
endfunction
|
||||
|
||||
// verilator lint_off INFINITELOOP
|
||||
initial begin
|
||||
if (write_data(vif.data)) $write("dummy op");
|
||||
while (write_data(vif.data));
|
||||
for (int i = 0; write_data(vif.data); i += int'(write_data(vif.data)));
|
||||
for (int i = 0; write_data(vif.data++); i++);
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue