parent
c7a0613c57
commit
7b12f6a1dd
|
|
@ -1195,6 +1195,8 @@ class AstNetlist final : public AstNode {
|
|||
AstCFunc* m_evalNbap = nullptr; // The '_eval__nba' function
|
||||
AstVarScope* m_dpiExportTriggerp = nullptr; // The DPI export 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
|
||||
AstTopScope* m_topScopep = nullptr; // The singleton AstTopScope under the top module
|
||||
VTimescale m_timeunit; // Global time unit
|
||||
VTimescale m_timeprecision; // Global time precision
|
||||
|
|
@ -1224,6 +1226,10 @@ public:
|
|||
void dpiExportTriggerp(AstVarScope* varScopep) { m_dpiExportTriggerp = varScopep; }
|
||||
AstVar* delaySchedulerp() const { return m_delaySchedulerp; }
|
||||
void delaySchedulerp(AstVar* const varScopep) { m_delaySchedulerp = varScopep; }
|
||||
AstVarScope* nbaEventp() const { return m_nbaEventp; }
|
||||
void nbaEventp(AstVarScope* const varScopep) { m_nbaEventp = varScopep; }
|
||||
AstVarScope* nbaEventTriggerp() const { return m_nbaEventTriggerp; }
|
||||
void nbaEventTriggerp(AstVarScope* const varScopep) { m_nbaEventTriggerp = varScopep; }
|
||||
void stdPackagep(AstPackage* const packagep) { m_stdPackagep = packagep; }
|
||||
AstPackage* stdPackagep() const { return m_stdPackagep; }
|
||||
AstTopScope* topScopep() const { return m_topScopep; }
|
||||
|
|
|
|||
|
|
@ -1936,6 +1936,8 @@ const char* AstNetlist::broken() const {
|
|||
BROKEN_RTN(m_dpiExportTriggerp && !m_dpiExportTriggerp->brokeExists());
|
||||
BROKEN_RTN(m_topScopep && !m_topScopep->brokeExists());
|
||||
BROKEN_RTN(m_delaySchedulerp && !m_delaySchedulerp->brokeExists());
|
||||
BROKEN_RTN(m_nbaEventp && !m_nbaEventp->brokeExists());
|
||||
BROKEN_RTN(m_nbaEventTriggerp && !m_nbaEventTriggerp->brokeExists());
|
||||
return nullptr;
|
||||
}
|
||||
AstPackage* AstNetlist::dollarUnitPkgAddp() {
|
||||
|
|
|
|||
|
|
@ -520,8 +520,12 @@ private:
|
|||
m_nextDlyp
|
||||
= VN_CAST(nodep->nextp(), AssignDly); // Next assignment in same block, maybe nullptr.
|
||||
if (m_cfuncp) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Delayed assignment inside public function/task");
|
||||
if (!v3Global.rootp()->nbaEventp()) {
|
||||
nodep->v3warn(
|
||||
E_NOTIMING,
|
||||
"Delayed assignment in a non-inlined function/task requires --timing");
|
||||
}
|
||||
return;
|
||||
}
|
||||
UASSERT_OBJ(m_procp, nodep, "Delayed assignment not under process");
|
||||
const bool isArray = VN_IS(nodep->lhsp(), ArraySel)
|
||||
|
|
|
|||
187
src/V3Sched.cpp
187
src/V3Sched.cpp
|
|
@ -587,18 +587,27 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp,
|
|||
return {vscp, funcp, dumpp, map};
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// EvalLoop contains elements of an evaluation loop created by makeEvalLoop()
|
||||
|
||||
struct EvalLoop {
|
||||
// Loop iteration counter for enforcing the converge limit
|
||||
AstVarScope* counterp = nullptr;
|
||||
// The loop condition
|
||||
AstVarScope* continuep = nullptr;
|
||||
// The loop itself and statements around it
|
||||
AstNodeStmt* stmtsp = nullptr;
|
||||
};
|
||||
|
||||
//============================================================================
|
||||
// Helpers to construct an evaluation loop.
|
||||
|
||||
AstNodeStmt* buildLoop(AstNetlist* netlistp, const string& name,
|
||||
const std::function<void(AstVarScope*, AstWhile*)>& build) //
|
||||
AstNodeStmt* buildLoop(AstNetlist* netlistp, AstVarScope* const condp,
|
||||
const std::function<void(AstWhile*)>& build) //
|
||||
{
|
||||
AstTopScope* const topScopep = netlistp->topScopep();
|
||||
AstScope* const scopeTopp = topScopep->scopep();
|
||||
FileLine* const flp = scopeTopp->fileline();
|
||||
// Create the loop condition variable
|
||||
AstVarScope* const condp = scopeTopp->createTemp("__V" + name + "Continue", 1);
|
||||
condp->varp()->noReset(true);
|
||||
// Initialize the loop condition variable to true
|
||||
AstNodeStmt* const resp = setVar(condp, 1);
|
||||
// Add the loop
|
||||
|
|
@ -607,16 +616,15 @@ AstNodeStmt* buildLoop(AstNetlist* netlistp, const string& name,
|
|||
// Clear the loop condition variable in the loop
|
||||
loopp->addStmtsp(setVar(condp, 0));
|
||||
// Build the body
|
||||
build(condp, loopp);
|
||||
build(loopp);
|
||||
// Done
|
||||
return resp;
|
||||
};
|
||||
|
||||
std::pair<AstVarScope*, AstNodeStmt*> makeEvalLoop(AstNetlist* netlistp, const string& tag,
|
||||
const string& name, AstVarScope* trigVscp,
|
||||
AstCFunc* trigDumpp,
|
||||
std::function<AstNodeStmt*()> computeTriggers,
|
||||
std::function<AstNodeStmt*()> makeBody) {
|
||||
EvalLoop makeEvalLoop(AstNetlist* netlistp, const string& tag, const string& name,
|
||||
AstVarScope* trigVscp, AstCFunc* trigDumpp,
|
||||
std::function<AstNodeStmt*()> computeTriggers,
|
||||
std::function<AstNodeStmt*()> makeBody) {
|
||||
UASSERT_OBJ(trigVscp->dtypep()->basicp()->isTriggerVec(), trigVscp, "Not TRIGGERVEC");
|
||||
AstTopScope* const topScopep = netlistp->topScopep();
|
||||
AstScope* const scopeTopp = topScopep->scopep();
|
||||
|
|
@ -625,8 +633,11 @@ std::pair<AstVarScope*, AstNodeStmt*> makeEvalLoop(AstNetlist* netlistp, const s
|
|||
AstVarScope* const counterp = scopeTopp->createTemp("__V" + tag + "IterCount", 32);
|
||||
counterp->varp()->noReset(true);
|
||||
|
||||
AstVarScope* const continuep = scopeTopp->createTemp("__V" + tag + "Continue", 1);
|
||||
continuep->varp()->noReset(true);
|
||||
|
||||
AstNodeStmt* nodep = setVar(counterp, 0);
|
||||
nodep->addNext(buildLoop(netlistp, tag, [&](AstVarScope* continuep, AstWhile* loopp) {
|
||||
nodep->addNext(buildLoop(netlistp, continuep, [&](AstWhile* loopp) {
|
||||
// Compute triggers
|
||||
loopp->addStmtsp(computeTriggers());
|
||||
// Invoke body if triggered
|
||||
|
|
@ -678,7 +689,7 @@ std::pair<AstVarScope*, AstNodeStmt*> makeEvalLoop(AstNetlist* netlistp, const s
|
|||
}
|
||||
}));
|
||||
|
||||
return {counterp, nodep};
|
||||
return {counterp, continuep, nodep};
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
|
@ -723,7 +734,7 @@ void createSettle(AstNetlist* netlistp, AstCFunc* const initFuncp, SenExprBuilde
|
|||
splitCheck(stlFuncp);
|
||||
|
||||
// Create the eval loop
|
||||
const auto& pair = makeEvalLoop(
|
||||
const auto& loop = makeEvalLoop(
|
||||
netlistp, "stl", "Settle", trig.m_vscp, trig.m_dumpp,
|
||||
[&]() { // Trigger
|
||||
AstCCall* const callp = new AstCCall{stlFuncp->fileline(), trig.m_funcp};
|
||||
|
|
@ -737,10 +748,10 @@ void createSettle(AstNetlist* netlistp, AstCFunc* const initFuncp, SenExprBuilde
|
|||
});
|
||||
|
||||
// Add the first iteration trigger to the trigger computation function
|
||||
trig.addFirstIterationTriggerAssignment(pair.first, firstIterationTrigger);
|
||||
trig.addFirstIterationTriggerAssignment(loop.counterp, firstIterationTrigger);
|
||||
|
||||
// Add the eval loop to the top function
|
||||
funcp->addStmtsp(pair.second);
|
||||
funcp->addStmtsp(loop.stmtsp);
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
|
@ -812,7 +823,7 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
|
|||
splitCheck(icoFuncp);
|
||||
|
||||
// Create the eval loop
|
||||
const auto& pair = makeEvalLoop(
|
||||
const auto& loop = makeEvalLoop(
|
||||
netlistp, "ico", "Input combinational", trig.m_vscp, trig.m_dumpp,
|
||||
[&]() { // Trigger
|
||||
AstCCall* const callp = new AstCCall{icoFuncp->fileline(), trig.m_funcp};
|
||||
|
|
@ -826,10 +837,10 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
|
|||
});
|
||||
|
||||
// Add the first iteration trigger to the trigger computation function
|
||||
trig.addFirstIterationTriggerAssignment(pair.first, firstIterationTrigger);
|
||||
trig.addFirstIterationTriggerAssignment(loop.counterp, firstIterationTrigger);
|
||||
|
||||
// Return the eval loop itself
|
||||
return pair.second;
|
||||
return loop.stmtsp;
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
|
|
@ -884,72 +895,86 @@ void createEval(AstNetlist* netlistp, //
|
|||
if (icoLoop) funcp->addStmtsp(icoLoop);
|
||||
|
||||
// Create the active eval loop
|
||||
AstNodeStmt* const activeEvalLoopp
|
||||
= makeEvalLoop(
|
||||
netlistp, "act", "Active", actKit.m_vscp, actKit.m_dumpp,
|
||||
[&]() { // Trigger
|
||||
AstNodeStmt* resultp = nullptr;
|
||||
const auto& activeEvalLoop = makeEvalLoop(
|
||||
netlistp, "act", "Active", actKit.m_vscp, actKit.m_dumpp,
|
||||
[&]() { // Trigger
|
||||
AstNodeStmt* resultp = nullptr;
|
||||
|
||||
// Compute the current triggers
|
||||
{
|
||||
AstCCall* const trigsp = new AstCCall{flp, actKit.m_triggerComputep};
|
||||
trigsp->dtypeSetVoid();
|
||||
resultp = AstNode::addNext(resultp, trigsp->makeStmt());
|
||||
}
|
||||
// Compute the current triggers
|
||||
{
|
||||
AstCCall* const trigsp = new AstCCall{flp, actKit.m_triggerComputep};
|
||||
trigsp->dtypeSetVoid();
|
||||
resultp = AstNode::addNext(resultp, trigsp->makeStmt());
|
||||
}
|
||||
|
||||
// Commit trigger awaits from the previous iteration
|
||||
if (AstCCall* const commitp = timingKit.createCommit(netlistp)) {
|
||||
resultp = AstNode::addNext(resultp, commitp->makeStmt());
|
||||
}
|
||||
// Commit trigger awaits from the previous iteration
|
||||
if (AstCCall* const commitp = timingKit.createCommit(netlistp)) {
|
||||
resultp = AstNode::addNext(resultp, commitp->makeStmt());
|
||||
}
|
||||
|
||||
return resultp;
|
||||
},
|
||||
[&]() { // Body
|
||||
// Compute the pre triggers
|
||||
AstNodeStmt* resultp
|
||||
= createTriggerAndNotCall(flp, preTrigsp, actKit.m_vscp, nbaKit.m_vscp);
|
||||
// Latch the active trigger flags under the NBA trigger flags
|
||||
resultp = AstNode::addNext(
|
||||
resultp, createTriggerSetCall(flp, nbaKit.m_vscp, actKit.m_vscp));
|
||||
// Resume triggered timing schedulers
|
||||
if (AstCCall* const resumep = timingKit.createResume(netlistp)) {
|
||||
resultp = AstNode::addNext(resultp, resumep->makeStmt());
|
||||
}
|
||||
// Invoke body function
|
||||
{
|
||||
AstCCall* const callp = new AstCCall{flp, actKit.m_funcp};
|
||||
callp->dtypeSetVoid();
|
||||
resultp = AstNode::addNext(resultp, callp->makeStmt());
|
||||
}
|
||||
return resultp;
|
||||
},
|
||||
[&]() { // Body
|
||||
// Compute the pre triggers
|
||||
AstNodeStmt* resultp
|
||||
= createTriggerAndNotCall(flp, preTrigsp, actKit.m_vscp, nbaKit.m_vscp);
|
||||
// Latch the active trigger flags under the NBA trigger flags
|
||||
resultp = AstNode::addNext(resultp,
|
||||
createTriggerSetCall(flp, nbaKit.m_vscp, actKit.m_vscp));
|
||||
// Resume triggered timing schedulers
|
||||
if (AstCCall* const resumep = timingKit.createResume(netlistp)) {
|
||||
resultp = AstNode::addNext(resultp, resumep->makeStmt());
|
||||
}
|
||||
// Invoke body function
|
||||
{
|
||||
AstCCall* const callp = new AstCCall{flp, actKit.m_funcp};
|
||||
callp->dtypeSetVoid();
|
||||
resultp = AstNode::addNext(resultp, callp->makeStmt());
|
||||
}
|
||||
|
||||
return resultp;
|
||||
})
|
||||
.second;
|
||||
return resultp;
|
||||
});
|
||||
|
||||
// Create the NBA eval loop. This uses the Active eval loop in the trigger section.
|
||||
AstNodeStmt* topEvalLoopp
|
||||
= makeEvalLoop(
|
||||
netlistp, "nba", "NBA", nbaKit.m_vscp, nbaKit.m_dumpp,
|
||||
[&]() { // Trigger
|
||||
// Reset NBA triggers
|
||||
AstNodeStmt* resultp = createTriggerClearCall(flp, nbaKit.m_vscp);
|
||||
// Run the Active eval loop
|
||||
resultp = AstNode::addNext(resultp, activeEvalLoopp);
|
||||
return resultp;
|
||||
},
|
||||
[&]() { // Body
|
||||
AstCCall* const callp = new AstCCall{flp, nbaKit.m_funcp};
|
||||
callp->dtypeSetVoid();
|
||||
AstNodeStmt* resultp = callp->makeStmt();
|
||||
// Latch the NBA trigger flags under the following region's trigger flags
|
||||
AstVarScope* const nextVscp = obsKit.m_vscp ? obsKit.m_vscp : reactKit.m_vscp;
|
||||
if (nextVscp) {
|
||||
resultp = AstNode::addNext(
|
||||
resultp, createTriggerSetCall(flp, nextVscp, nbaKit.m_vscp));
|
||||
}
|
||||
return resultp;
|
||||
})
|
||||
.second;
|
||||
const auto& nbaEvalLoop = makeEvalLoop(
|
||||
netlistp, "nba", "NBA", nbaKit.m_vscp, nbaKit.m_dumpp,
|
||||
[&]() { // Trigger
|
||||
// Reset NBA triggers
|
||||
AstNodeStmt* resultp = createTriggerClearCall(flp, nbaKit.m_vscp);
|
||||
// Run the Active eval loop
|
||||
resultp = AstNode::addNext(resultp, activeEvalLoop.stmtsp);
|
||||
return resultp;
|
||||
},
|
||||
[&]() { // Body
|
||||
AstCCall* const callp = new AstCCall{flp, nbaKit.m_funcp};
|
||||
callp->dtypeSetVoid();
|
||||
AstNodeStmt* resultp = callp->makeStmt();
|
||||
// Latch the NBA trigger flags under the following region's trigger flags
|
||||
AstVarScope* const nextVscp = obsKit.m_vscp ? obsKit.m_vscp : reactKit.m_vscp;
|
||||
if (nextVscp) {
|
||||
resultp = AstNode::addNext(resultp,
|
||||
createTriggerSetCall(flp, nextVscp, nbaKit.m_vscp));
|
||||
}
|
||||
return resultp;
|
||||
});
|
||||
|
||||
// If the NBA event exists, trigger it in 'nba'
|
||||
if (netlistp->nbaEventp()) {
|
||||
UASSERT(netlistp->nbaEventTriggerp(), "NBA event trigger var should exist");
|
||||
AstIf* const ifp
|
||||
= new AstIf{flp, new AstVarRef{flp, netlistp->nbaEventTriggerp(), VAccess::READ}};
|
||||
ifp->addThensp(setVar(nbaEvalLoop.continuep, 1));
|
||||
ifp->addThensp(setVar(netlistp->nbaEventTriggerp(), 0));
|
||||
AstCMethodHard* const firep = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, netlistp->nbaEventp(), VAccess::WRITE}, "fire"};
|
||||
firep->dtypeSetVoid();
|
||||
ifp->addThensp(firep->makeStmt());
|
||||
activeEvalLoop.stmtsp->addNext(ifp);
|
||||
netlistp->nbaEventp(nullptr);
|
||||
netlistp->nbaEventTriggerp(nullptr);
|
||||
}
|
||||
|
||||
AstNodeStmt* topEvalLoopp = nbaEvalLoop.stmtsp;
|
||||
|
||||
if (obsKit.m_funcp) {
|
||||
// Create the Observed eval loop. This uses the NBA eval loop in the trigger section.
|
||||
|
|
@ -974,7 +999,7 @@ void createEval(AstNetlist* netlistp, //
|
|||
}
|
||||
return resultp;
|
||||
})
|
||||
.second;
|
||||
.stmtsp;
|
||||
}
|
||||
|
||||
if (reactKit.m_funcp) {
|
||||
|
|
@ -993,7 +1018,7 @@ void createEval(AstNetlist* netlistp, //
|
|||
callp->dtypeSetVoid();
|
||||
return callp->makeStmt();
|
||||
})
|
||||
.second;
|
||||
.stmtsp;
|
||||
}
|
||||
funcp->addStmtsp(topEvalLoopp);
|
||||
|
||||
|
|
|
|||
|
|
@ -365,6 +365,10 @@ private:
|
|||
m_underFork |= F_MIGHT_NEED_PROC;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstAssignDly* nodep) override {
|
||||
if (!VN_IS(m_procp, NodeProcedure)) v3Global.setUsesTiming();
|
||||
visit(static_cast<AstNode*>(nodep));
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
if (nodep->isTimingControl()) {
|
||||
v3Global.setUsesTiming();
|
||||
|
|
@ -451,8 +455,10 @@ private:
|
|||
double m_timescaleFactor = 1.0; // Factor to scale delays by
|
||||
int m_forkCnt = 0; // Number of forks inside a module
|
||||
bool m_underJumpBlock = false; // True if we are inside of a jump-block
|
||||
bool m_underProcedure = false; // True if we are under an always or initial
|
||||
|
||||
// Unique names
|
||||
V3UniqueNames m_dlyforkNames{"__Vdlyfork"}; // Names for temp AssignW vars
|
||||
V3UniqueNames m_contAssignVarNames{"__VassignWtmp"}; // Names for temp AssignW vars
|
||||
V3UniqueNames m_intraValueNames{"__Vintraval"}; // Intra assign delay value var names
|
||||
V3UniqueNames m_intraIndexNames{"__Vintraidx"}; // Intra assign delay index var names
|
||||
|
|
@ -574,6 +580,31 @@ private:
|
|||
m_netlistp->topScopep()->addSenTreesp(m_dynamicSensesp);
|
||||
return m_dynamicSensesp;
|
||||
}
|
||||
// Creates the event variable to trigger in NBA region
|
||||
AstEventControl* createNbaEventControl(FileLine* flp) {
|
||||
if (!m_netlistp->nbaEventp()) {
|
||||
auto* const nbaEventDtp = new AstBasicDType{m_scopeTopp->fileline(),
|
||||
VBasicDTypeKwd::EVENT, VSigning::UNSIGNED};
|
||||
m_netlistp->typeTablep()->addTypesp(nbaEventDtp);
|
||||
m_netlistp->nbaEventp(m_scopeTopp->createTemp("__VnbaEvent", nbaEventDtp));
|
||||
v3Global.setHasEvents();
|
||||
}
|
||||
return new AstEventControl{
|
||||
flp,
|
||||
new AstSenTree{
|
||||
flp, new AstSenItem{flp, VEdgeType::ET_EVENT,
|
||||
new AstVarRef{flp, m_netlistp->nbaEventp(), VAccess::READ}}},
|
||||
nullptr};
|
||||
}
|
||||
// Creates the variable that, if set, causes the NBA event to be triggered
|
||||
AstAssign* createNbaEventTriggerAssignment(FileLine* flp) {
|
||||
if (!m_netlistp->nbaEventTriggerp()) {
|
||||
m_netlistp->nbaEventTriggerp(m_scopeTopp->createTemp("__VnbaEventTrigger", 1));
|
||||
}
|
||||
return new AstAssign{flp,
|
||||
new AstVarRef{flp, m_netlistp->nbaEventTriggerp(), VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitTrue{}}};
|
||||
}
|
||||
// Returns true if we are under a class or the given tree has any references to locals. These
|
||||
// are cases where static, globally-evaluated triggers are not suitable.
|
||||
bool needDynamicTrigger(AstNode* const nodep) const {
|
||||
|
|
@ -755,6 +786,8 @@ private:
|
|||
void visit(AstNodeProcedure* nodep) override {
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
VL_RESTORER(m_underProcedure);
|
||||
m_underProcedure = true;
|
||||
iterateChildren(nodep);
|
||||
if (hasFlags(nodep, T_SUSPENDEE)) nodep->setSuspendable();
|
||||
if (hasFlags(nodep, T_HAS_PROC)) nodep->setNeedProcess();
|
||||
|
|
@ -775,7 +808,8 @@ private:
|
|||
if (nodep->user1SetOnce()) return;
|
||||
VL_RESTORER(m_procp);
|
||||
m_procp = nodep;
|
||||
|
||||
VL_RESTORER(m_underProcedure);
|
||||
m_underProcedure = true;
|
||||
// Workaround for killing `always` processes (doing that is pretty much UB)
|
||||
// TODO: Disallow killing `always` at runtime (throw an error)
|
||||
if (hasFlags(nodep, T_HAS_PROC)) addFlags(nodep, T_SUSPENDEE);
|
||||
|
|
@ -981,20 +1015,37 @@ private:
|
|||
void visit(AstNodeAssign* nodep) override {
|
||||
// Only process once to avoid infinite loops (due to the net delay)
|
||||
if (nodep->user1SetOnce()) return;
|
||||
AstNode* const controlp = factorOutTimingControl(nodep);
|
||||
if (!controlp) return;
|
||||
// Handle the intra assignment timing control
|
||||
FileLine* const flp = nodep->fileline();
|
||||
if (VN_IS(nodep, AssignDly)) {
|
||||
// If it's an NBA with an intra assignment delay, put it in a fork
|
||||
AstNode* controlp = factorOutTimingControl(nodep);
|
||||
const bool inAssignDly = VN_IS(nodep, AssignDly);
|
||||
// Handle the intra assignment timing control
|
||||
// Transform if:
|
||||
// * there's a timing control in the assignment
|
||||
// * the assignment is an AssignDly and it's in a non-inlined function
|
||||
if (!controlp && (!inAssignDly || m_underProcedure)) return;
|
||||
// Insert new vars before the timing control if we're in a function; in a process we can't
|
||||
// do that. These intra-assignment vars will later be passed to forked processes by value.
|
||||
AstNode* insertBeforep = m_underProcedure ? nullptr : controlp;
|
||||
// Special case for NBA
|
||||
if (inAssignDly) {
|
||||
// Put it in a fork so it doesn't block
|
||||
auto* const forkp = new AstFork{flp, "", nullptr};
|
||||
forkp->joinType(VJoinType::JOIN_NONE);
|
||||
if (!m_underProcedure) {
|
||||
// If it's in a function, it won't be handled by V3Delayed
|
||||
// Put it behind an additional named event that gets triggered in the NBA region
|
||||
AstEventControl* const nbaEventControlp = createNbaEventControl(flp);
|
||||
AstAssign* const trigAssignp = createNbaEventTriggerAssignment(flp);
|
||||
nodep->replaceWith(trigAssignp);
|
||||
trigAssignp->addNextHere(nbaEventControlp);
|
||||
nbaEventControlp->addStmtsp(nodep);
|
||||
insertBeforep = forkp;
|
||||
if (!controlp) controlp = nbaEventControlp;
|
||||
}
|
||||
controlp->replaceWith(forkp);
|
||||
forkp->addStmtsp(controlp);
|
||||
}
|
||||
// Insert new vars before the timing control if we're in a function; in a process we can't
|
||||
// do that. These intra-assignment vars will later be passed to forked processes by value.
|
||||
AstNode* const insertBeforep = m_classp ? controlp : nullptr;
|
||||
UASSERT_OBJ(nodep, controlp, "Assignment should have timing control");
|
||||
addCLocalScope(flp, insertBeforep);
|
||||
// Function for replacing values with intermediate variables
|
||||
const auto replaceWithIntermediate = [&](AstNodeExpr* const valuep,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2019 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,
|
||||
);
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ["--exe --main --timing +define+WITH_DELAY"],
|
||||
make_main => 0,
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
// 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
|
||||
|
||||
`ifdef WITH_DELAY
|
||||
`define DELAY #1
|
||||
`define TIME_AFTER_FIRST_WAIT 2
|
||||
`define TIME_AFTER_SECOND_WAIT 3
|
||||
`else
|
||||
`define DELAY
|
||||
`define TIME_AFTER_FIRST_WAIT 1
|
||||
`define TIME_AFTER_SECOND_WAIT 1
|
||||
`endif
|
||||
|
||||
class nba_waiter;
|
||||
// Task taken from UVM
|
||||
task wait_for_nba_region;
|
||||
int nba;
|
||||
int next_nba;
|
||||
next_nba++;
|
||||
nba <= `DELAY next_nba;
|
||||
@(nba);
|
||||
endtask
|
||||
endclass
|
||||
|
||||
module t;
|
||||
nba_waiter waiter = new;
|
||||
event e;
|
||||
int cnt = 0;
|
||||
|
||||
initial begin
|
||||
#1 ->e;
|
||||
if (cnt != 0) $stop;
|
||||
cnt++;
|
||||
waiter.wait_for_nba_region;
|
||||
->e;
|
||||
if (cnt != 2) $stop;
|
||||
if ($time != `TIME_AFTER_FIRST_WAIT) $stop;
|
||||
cnt++;
|
||||
waiter.wait_for_nba_region;
|
||||
if (cnt != 4) $stop;
|
||||
if ($time != `TIME_AFTER_SECOND_WAIT) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
@e if (cnt != 1) $stop;
|
||||
cnt++;
|
||||
@e if (cnt != 3) $stop;
|
||||
cnt++;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
%Error-NOTIMING: t/t_assigndly_dynamic_notiming.v:10:13: Delayed assignment in a non-inlined function/task requires --timing
|
||||
: ... note: In instance '$unit::foo'
|
||||
10 | qux <= '1;
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/NOTIMING?v=latest
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2019 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 => ["--no-timing"],
|
||||
fails => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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
|
||||
|
||||
class foo;
|
||||
task bar;
|
||||
int qux;
|
||||
qux <= '1;
|
||||
endtask
|
||||
endclass
|
||||
|
|
@ -918,9 +918,7 @@ task uvm_wait_for_nba_region;
|
|||
int nba;
|
||||
int next_nba;
|
||||
next_nba++;
|
||||
//TODO issue #4496 - Delayed assignment inside public function/task
|
||||
//TODO %Error-UNSUPPORTED: t/t_uvm_pkg_todo.vh:875:7: Unsupported: Delayed assignment inside public function/task
|
||||
//TODO nba <= next_nba;
|
||||
nba <= next_nba;
|
||||
@(nba);
|
||||
endtask
|
||||
function automatic void uvm_split_string (string str, byte sep, ref string values[$]);
|
||||
|
|
|
|||
Loading…
Reference in New Issue