mirror of
https://github.com/verilator/verilator.git
synced 2025-04-29 03:56:58 +00:00
Add support for expressions in event controls (#3550)
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
This commit is contained in:
parent
1404319b28
commit
10cf492946
@ -2717,9 +2717,7 @@ private:
|
|||||||
}
|
}
|
||||||
UINFO(8, "senItem(NOT...) " << nodep << " " << invert << endl);
|
UINFO(8, "senItem(NOT...) " << nodep << " " << invert << endl);
|
||||||
if (invert) nodep->edgeType(nodep->edgeType().invert());
|
if (invert) nodep->edgeType(nodep->edgeType().invert());
|
||||||
AstNodeVarRef* const senvarp = VN_AS(lastSensp->unlinkFrBack(), NodeVarRef);
|
sensp->replaceWith(lastSensp->unlinkFrBack());
|
||||||
UASSERT_OBJ(senvarp, sensp, "Non-varref sensitivity variable");
|
|
||||||
sensp->replaceWith(senvarp);
|
|
||||||
VL_DO_DANGLING(sensp->deleteTree(), sensp);
|
VL_DO_DANGLING(sensp->deleteTree(), sensp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,42 +145,6 @@ private:
|
|||||||
nodep->scopeNamep(new AstScopeName{nodep->fileline(), false});
|
nodep->scopeNamep(new AstScopeName{nodep->fileline(), false});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void visit(AstSenItem* nodep) override {
|
|
||||||
// Remove bit selects, and bark if it's not a simple variable
|
|
||||||
iterateChildren(nodep);
|
|
||||||
if (!nodep->isClocked()) {
|
|
||||||
// Old V1995 sensitivity list; we'll probably mostly ignore
|
|
||||||
bool did = true;
|
|
||||||
while (did) {
|
|
||||||
did = false;
|
|
||||||
if (AstNodeSel* const selp = VN_CAST(nodep->sensp(), NodeSel)) {
|
|
||||||
AstNode* const fromp = selp->fromp()->unlinkFrBack();
|
|
||||||
selp->replaceWith(fromp);
|
|
||||||
VL_DO_DANGLING(selp->deleteTree(), selp);
|
|
||||||
did = true;
|
|
||||||
}
|
|
||||||
// NodeSel doesn't include AstSel....
|
|
||||||
if (AstSel* const selp = VN_CAST(nodep->sensp(), Sel)) {
|
|
||||||
AstNode* const fromp = selp->fromp()->unlinkFrBack();
|
|
||||||
selp->replaceWith(fromp);
|
|
||||||
VL_DO_DANGLING(selp->deleteTree(), selp);
|
|
||||||
did = true;
|
|
||||||
}
|
|
||||||
if (AstNodePreSel* const selp = VN_CAST(nodep->sensp(), NodePreSel)) {
|
|
||||||
AstNode* const fromp = selp->fromp()->unlinkFrBack();
|
|
||||||
selp->replaceWith(fromp);
|
|
||||||
VL_DO_DANGLING(selp->deleteTree(), selp);
|
|
||||||
did = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (nodep->isIllegal()) {
|
|
||||||
if (debug()) nodep->dumpTree(cout, "-tree: ");
|
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Complex statement in sensitivity list");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void visit(AstNodePreSel* nodep) override {
|
virtual void visit(AstNodePreSel* nodep) override {
|
||||||
if (!nodep->attrp()) {
|
if (!nodep->attrp()) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
116
src/V3Sched.cpp
116
src/V3Sched.cpp
@ -268,13 +268,19 @@ class SenExprBuilder final {
|
|||||||
AstCFunc* const m_initp; // The initialization function
|
AstCFunc* const m_initp; // The initialization function
|
||||||
AstScope* const m_scopeTopp; // Top level scope
|
AstScope* const m_scopeTopp; // Top level scope
|
||||||
|
|
||||||
std::vector<AstNodeStmt*> m_updates; // Update assignments
|
std::vector<AstVar*> m_locals; // Trigger eval local variables
|
||||||
|
std::vector<AstNodeStmt*> m_preUpdates; // Pre update assignments
|
||||||
|
std::vector<AstNodeStmt*> m_postUpdates; // Post update assignments
|
||||||
|
|
||||||
std::unordered_map<VNRef<AstNode>, AstVarScope*> m_prev; // The 'previous value' signals
|
std::unordered_map<VNRef<AstNode>, AstVarScope*> m_prev; // The 'previous value' signals
|
||||||
std::unordered_set<VNRef<AstNode>> m_hasUpdate; // Whether the given sen expression already
|
std::unordered_map<VNRef<AstNode>, AstVarScope*> m_curr; // The 'current value' signals
|
||||||
// has an update statement in m_updates
|
std::unordered_set<VNRef<AstNode>> m_hasPreUpdate; // Whether the given sen expression already
|
||||||
|
// has an update statement in m_preUpdates
|
||||||
|
std::unordered_set<VNRef<AstNode>> m_hasPostUpdate; // Likewis for m_postUpdates
|
||||||
|
|
||||||
V3UniqueNames m_uniqueNames{"__Vtrigprev__expression"}; // For generating unique signal names
|
V3UniqueNames m_currNames{"__Vtrigcurr__expression"}; // For generating unique current value
|
||||||
|
// signal names
|
||||||
|
V3UniqueNames m_prevNames{"__Vtrigprev__expression"}; // Likewise for previous values
|
||||||
|
|
||||||
static bool isSupportedDType(AstNodeDType* dtypep) {
|
static bool isSupportedDType(AstNodeDType* dtypep) {
|
||||||
dtypep = dtypep->skipRefp();
|
dtypep = dtypep->skipRefp();
|
||||||
@ -285,29 +291,62 @@ class SenExprBuilder final {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isSimpleExpr(const AstNode* const exprp) {
|
||||||
|
return exprp->forall<AstNode>([](const AstNode* const nodep) {
|
||||||
|
return VN_IS(nodep, Const) || VN_IS(nodep, NodeVarRef) || VN_IS(nodep, Sel)
|
||||||
|
|| VN_IS(nodep, NodeSel) || VN_IS(nodep, MemberSel)
|
||||||
|
|| VN_IS(nodep, CMethodHard);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
AstVarScope* getPrev(AstNode* currp) {
|
AstNode* getCurr(AstNode* exprp) {
|
||||||
FileLine* const flp = currp->fileline();
|
// For simple expressions like varrefs or selects, just use them directly
|
||||||
const auto rdCurr = [=]() { return currp->cloneTree(false); };
|
if (isSimpleExpr(exprp)) return exprp->cloneTree(false);
|
||||||
|
|
||||||
|
// Create the 'current value' variable
|
||||||
|
FileLine* const flp = exprp->fileline();
|
||||||
|
auto result = m_curr.emplace(*exprp, nullptr);
|
||||||
|
if (result.second) {
|
||||||
|
AstVar* const varp
|
||||||
|
= new AstVar{flp, VVarType::BLOCKTEMP, m_currNames.get(exprp), exprp->dtypep()};
|
||||||
|
varp->funcLocal(true);
|
||||||
|
m_locals.push_back(varp);
|
||||||
|
AstVarScope* vscp = new AstVarScope{flp, m_scopeTopp, varp};
|
||||||
|
m_scopeTopp->addVarp(vscp);
|
||||||
|
result.first->second = vscp;
|
||||||
|
}
|
||||||
|
AstVarScope* const currp = result.first->second;
|
||||||
|
|
||||||
|
// Add pre update if it does not exist yet in this round
|
||||||
|
if (m_hasPreUpdate.emplace(*currp).second) {
|
||||||
|
m_preUpdates.push_back(new AstAssign{flp, new AstVarRef{flp, currp, VAccess::WRITE},
|
||||||
|
exprp->cloneTree(false)});
|
||||||
|
}
|
||||||
|
return new AstVarRef{flp, currp, VAccess::READ};
|
||||||
|
}
|
||||||
|
AstVarScope* getPrev(AstNode* exprp) {
|
||||||
|
FileLine* const flp = exprp->fileline();
|
||||||
|
const auto rdCurr = [=]() { return getCurr(exprp); };
|
||||||
|
|
||||||
// Create the 'previous value' variable
|
// Create the 'previous value' variable
|
||||||
auto it = m_prev.find(*currp);
|
auto it = m_prev.find(*exprp);
|
||||||
if (it == m_prev.end()) {
|
if (it == m_prev.end()) {
|
||||||
// For readability, use the scoped signal name if the trigger is a simple AstVarRef
|
// For readability, use the scoped signal name if the trigger is a simple AstVarRef
|
||||||
string name;
|
string name;
|
||||||
if (AstVarRef* const refp = VN_CAST(currp, VarRef)) {
|
if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) {
|
||||||
AstVarScope* vscp = refp->varScopep();
|
AstVarScope* vscp = refp->varScopep();
|
||||||
name = "__Vtrigrprev__" + vscp->scopep()->nameDotless() + "__"
|
name = "__Vtrigrprev__" + vscp->scopep()->nameDotless() + "__"
|
||||||
+ vscp->varp()->name();
|
+ vscp->varp()->name();
|
||||||
} else {
|
} else {
|
||||||
name = m_uniqueNames.get(currp);
|
name = m_prevNames.get(exprp);
|
||||||
}
|
}
|
||||||
|
|
||||||
AstVarScope* const prevp = m_scopeTopp->createTemp(name, currp->dtypep());
|
AstVarScope* const prevp = m_scopeTopp->createTemp(name, exprp->dtypep());
|
||||||
it = m_prev.emplace(*currp, prevp).first;
|
it = m_prev.emplace(*exprp, prevp).first;
|
||||||
|
|
||||||
// Add the initializer init
|
// Add the initializer init
|
||||||
AstNode* const initp = rdCurr();
|
AstNode* const initp = exprp->cloneTree(false);
|
||||||
m_initp->addStmtsp(
|
m_initp->addStmtsp(
|
||||||
new AstAssign{flp, new AstVarRef{flp, prevp, VAccess::WRITE}, initp});
|
new AstAssign{flp, new AstVarRef{flp, prevp, VAccess::WRITE}, initp});
|
||||||
}
|
}
|
||||||
@ -316,22 +355,22 @@ class SenExprBuilder final {
|
|||||||
|
|
||||||
const auto wrPrev = [=]() { return new AstVarRef{flp, prevp, VAccess::WRITE}; };
|
const auto wrPrev = [=]() { return new AstVarRef{flp, prevp, VAccess::WRITE}; };
|
||||||
|
|
||||||
// Add update if it does not exist yet
|
// Add post update if it does not exist yet
|
||||||
if (m_hasUpdate.emplace(*currp).second) {
|
if (m_hasPostUpdate.emplace(*exprp).second) {
|
||||||
if (!isSupportedDType(currp->dtypep())) {
|
if (!isSupportedDType(exprp->dtypep())) {
|
||||||
currp->v3warn(E_UNSUPPORTED,
|
exprp->v3warn(E_UNSUPPORTED,
|
||||||
"Unsupported: Cannot detect changes on expression of complex type"
|
"Unsupported: Cannot detect changes on expression of complex type"
|
||||||
" (see combinational cycles reported by UNOPTFLAT)");
|
" (see combinational cycles reported by UNOPTFLAT)");
|
||||||
return prevp;
|
return prevp;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AstUnpackArrayDType* const dtypep = VN_CAST(currp->dtypep(), UnpackArrayDType)) {
|
if (AstUnpackArrayDType* const dtypep = VN_CAST(exprp->dtypep(), UnpackArrayDType)) {
|
||||||
AstCMethodHard* const cmhp = new AstCMethodHard{flp, wrPrev(), "assign", rdCurr()};
|
AstCMethodHard* const cmhp = new AstCMethodHard{flp, wrPrev(), "assign", rdCurr()};
|
||||||
cmhp->dtypeSetVoid();
|
cmhp->dtypeSetVoid();
|
||||||
cmhp->statement(true);
|
cmhp->statement(true);
|
||||||
m_updates.push_back(cmhp);
|
m_postUpdates.push_back(cmhp);
|
||||||
} else {
|
} else {
|
||||||
m_updates.push_back(new AstAssign{flp, wrPrev(), rdCurr()});
|
m_postUpdates.push_back(new AstAssign{flp, wrPrev(), rdCurr()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +381,7 @@ class SenExprBuilder final {
|
|||||||
FileLine* const flp = senItemp->fileline();
|
FileLine* const flp = senItemp->fileline();
|
||||||
AstNode* const senp = senItemp->sensp();
|
AstNode* const senp = senItemp->sensp();
|
||||||
|
|
||||||
const auto currp = [=]() { return senp->cloneTree(false); };
|
const auto currp = [=]() { return getCurr(senp); };
|
||||||
const auto prevp = [=]() { return new AstVarRef{flp, getPrev(senp), VAccess::READ}; };
|
const auto prevp = [=]() { return new AstVarRef{flp, getPrev(senp), VAccess::READ}; };
|
||||||
const auto lsb = [=](AstNodeMath* opp) { return new AstSel{flp, opp, 0, 1}; };
|
const auto lsb = [=](AstNodeMath* opp) { return new AstSel{flp, opp, 0, 1}; };
|
||||||
|
|
||||||
@ -371,7 +410,7 @@ class SenExprBuilder final {
|
|||||||
AstCMethodHard* const callp = new AstCMethodHard{flp, currp(), "isFired"};
|
AstCMethodHard* const callp = new AstCMethodHard{flp, currp(), "isFired"};
|
||||||
callp->dtypeSetBit();
|
callp->dtypeSetBit();
|
||||||
AstIf* const ifp = new AstIf{flp, callp};
|
AstIf* const ifp = new AstIf{flp, callp};
|
||||||
m_updates.push_back(ifp);
|
m_postUpdates.push_back(ifp);
|
||||||
|
|
||||||
// Clear 'fired' state when done
|
// Clear 'fired' state when done
|
||||||
AstCMethodHard* const clearp = new AstCMethodHard{flp, currp(), "clearFired"};
|
AstCMethodHard* const clearp = new AstCMethodHard{flp, currp(), "clearFired"};
|
||||||
@ -417,9 +456,16 @@ public:
|
|||||||
return {resultp, firedAtInitialization};
|
return {resultp, firedAtInitialization};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<AstNodeStmt*> getAndClearUpdates() {
|
std::vector<AstVar*> getAndClearLocals() { return std::move(m_locals); }
|
||||||
m_hasUpdate.clear();
|
|
||||||
return std::move(m_updates);
|
std::vector<AstNodeStmt*> getAndClearPreUpdates() {
|
||||||
|
m_hasPreUpdate.clear();
|
||||||
|
return std::move(m_preUpdates);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<AstNodeStmt*> getAndClearPostUpdates() {
|
||||||
|
m_hasPostUpdate.clear();
|
||||||
|
return std::move(m_postUpdates);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CONSTRUCTOR
|
// CONSTRUCTOR
|
||||||
@ -606,7 +652,25 @@ const TriggerKit createTriggers(AstNetlist* netlistp, SenExprBuilder& senExprBui
|
|||||||
++triggerNumber;
|
++triggerNumber;
|
||||||
}
|
}
|
||||||
// Add the update statements
|
// Add the update statements
|
||||||
for (AstNodeStmt* const nodep : senExprBuilder.getAndClearUpdates()) funcp->addStmtsp(nodep);
|
for (AstNodeStmt* const nodep : senExprBuilder.getAndClearPostUpdates()) {
|
||||||
|
funcp->addStmtsp(nodep);
|
||||||
|
}
|
||||||
|
const auto& preUpdates = senExprBuilder.getAndClearPreUpdates();
|
||||||
|
if (!preUpdates.empty()) {
|
||||||
|
for (AstNodeStmt* const nodep : vlstd::reverse_view(preUpdates)) {
|
||||||
|
UASSERT_OBJ(funcp->stmtsp(), funcp,
|
||||||
|
"No statements in trigger eval function, but there are pre updates");
|
||||||
|
funcp->stmtsp()->addHereThisAsNext(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto& locals = senExprBuilder.getAndClearLocals();
|
||||||
|
if (!locals.empty()) {
|
||||||
|
UASSERT_OBJ(funcp->stmtsp(), funcp,
|
||||||
|
"No statements in trigger eval function, but there are locals");
|
||||||
|
for (AstVar* const nodep : vlstd::reverse_view(locals)) {
|
||||||
|
funcp->stmtsp()->addHereThisAsNext(nodep);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add the initialization statements
|
// Add the initialization statements
|
||||||
if (initialTrigsp) {
|
if (initialTrigsp) {
|
||||||
|
@ -360,6 +360,7 @@ private:
|
|||||||
AstScope* m_scopep = nullptr; // Current scope
|
AstScope* m_scopep = nullptr; // Current scope
|
||||||
InsertMode m_insMode = IM_BEFORE; // How to insert
|
InsertMode m_insMode = IM_BEFORE; // How to insert
|
||||||
AstNode* m_insStmtp = nullptr; // Where to insert statement
|
AstNode* m_insStmtp = nullptr; // Where to insert statement
|
||||||
|
bool m_inSensesp = false; // Are we under a senitem?
|
||||||
int m_modNCalls = 0; // Incrementing func # for making symbols
|
int m_modNCalls = 0; // Incrementing func # for making symbols
|
||||||
DpiCFuncs m_dpiNames; // Map of all created DPI functions
|
DpiCFuncs m_dpiNames; // Map of all created DPI functions
|
||||||
|
|
||||||
@ -1369,6 +1370,11 @@ private:
|
|||||||
m_scopep = nullptr;
|
m_scopep = nullptr;
|
||||||
}
|
}
|
||||||
virtual void visit(AstNodeFTaskRef* nodep) override {
|
virtual void visit(AstNodeFTaskRef* nodep) override {
|
||||||
|
if (m_inSensesp) {
|
||||||
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: function calls in sensitivity lists");
|
||||||
|
nodep->taskp(nullptr); // So V3Broken doesn't complain
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Includes handling AstMethodCall, AstNew
|
// Includes handling AstMethodCall, AstNew
|
||||||
UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked?");
|
UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked?");
|
||||||
iterateIntoFTask(nodep->taskp()); // First, do hierarchical funcs
|
iterateIntoFTask(nodep->taskp()); // First, do hierarchical funcs
|
||||||
@ -1521,6 +1527,12 @@ private:
|
|||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
m_insStmtp = nullptr; // Next thing should be new statement
|
m_insStmtp = nullptr; // Next thing should be new statement
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstSenItem* nodep) override {
|
||||||
|
UASSERT_OBJ(!m_inSensesp, nodep, "Senitem under senitem?");
|
||||||
|
VL_RESTORER(m_inSensesp);
|
||||||
|
m_inSensesp = true;
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
//--------------------
|
//--------------------
|
||||||
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
virtual void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||||
|
|
||||||
|
@ -5050,7 +5050,22 @@ private:
|
|||||||
m_procedurep = nullptr;
|
m_procedurep = nullptr;
|
||||||
}
|
}
|
||||||
virtual void visit(AstSenItem* nodep) override {
|
virtual void visit(AstSenItem* nodep) override {
|
||||||
userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
|
UASSERT_OBJ(nodep->isClocked(), nodep, "Invalid edge");
|
||||||
|
// Optimize concat/replicate senitems; this has to be done here at the latest, otherwise we
|
||||||
|
// emit WIDTHCONCAT if there are unsized constants
|
||||||
|
if (VN_IS(nodep->sensp(), Concat) || VN_IS(nodep->sensp(), Replicate)) {
|
||||||
|
auto* const concatOrReplp = VN_CAST(nodep->sensp(), NodeBiop);
|
||||||
|
auto* const rhsp = concatOrReplp->rhsp()->unlinkFrBack();
|
||||||
|
if (nodep->edgeType() == VEdgeType::ET_CHANGED) {
|
||||||
|
// If it's ET_CHANGED, split concatenations into multiple senitems
|
||||||
|
auto* const lhsp = concatOrReplp->lhsp()->unlinkFrBack();
|
||||||
|
nodep->addNextHere(new AstSenItem{lhsp->fileline(), nodep->edgeType(), lhsp});
|
||||||
|
} // Else only use the RHS
|
||||||
|
nodep->replaceWith(new AstSenItem{rhsp->fileline(), nodep->edgeType(), rhsp});
|
||||||
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
|
} else {
|
||||||
|
userIterateChildren(nodep, WidthVP(SELF, BOTH).p());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
virtual void visit(AstWith* nodep) override {
|
virtual void visit(AstWith* nodep) override {
|
||||||
// Should otherwise be underneath a method call
|
// Should otherwise be underneath a method call
|
||||||
|
@ -3027,15 +3027,8 @@ event_expression<senItemp>: // IEEE: event_expression - split over several
|
|||||||
|
|
||||||
senitem<senItemp>: // IEEE: part of event_expression, non-'OR' ',' terms
|
senitem<senItemp>: // IEEE: part of event_expression, non-'OR' ',' terms
|
||||||
senitemEdge { $$ = $1; }
|
senitemEdge { $$ = $1; }
|
||||||
| senitemVar { $$ = $1; }
|
| expr { $$ = new AstSenItem{$<fl>1, VEdgeType::ET_CHANGED, $1}; }
|
||||||
| '(' senitem ')' { $$ = $2; }
|
|
||||||
//UNSUP expr { UNSUP }
|
|
||||||
| '{' event_expression '}' { $$ = $2; }
|
|
||||||
| senitem yP_ANDAND senitem { $$ = new AstSenItem($2, AstSenItem::Illegal()); }
|
|
||||||
//UNSUP expr yIFF expr { UNSUP }
|
//UNSUP expr yIFF expr { UNSUP }
|
||||||
// Since expr is unsupported we allow and ignore constants (removed in V3Const)
|
|
||||||
| yaINTNUM { $$ = nullptr; }
|
|
||||||
| yaFLOATNUM { $$ = nullptr; }
|
|
||||||
;
|
;
|
||||||
|
|
||||||
senitemVar<senItemp>:
|
senitemVar<senItemp>:
|
||||||
@ -3043,19 +3036,11 @@ senitemVar<senItemp>:
|
|||||||
;
|
;
|
||||||
|
|
||||||
senitemEdge<senItemp>: // IEEE: part of event_expression
|
senitemEdge<senItemp>: // IEEE: part of event_expression
|
||||||
//UNSUP // Below are all removed
|
yPOSEDGE expr { $$ = new AstSenItem{$1, VEdgeType::ET_POSEDGE, $2}; }
|
||||||
yPOSEDGE idClassSel { $$ = new AstSenItem($1, VEdgeType::ET_POSEDGE, $2); }
|
| yNEGEDGE expr { $$ = new AstSenItem{$1, VEdgeType::ET_NEGEDGE, $2}; }
|
||||||
| yNEGEDGE idClassSel { $$ = new AstSenItem($1, VEdgeType::ET_NEGEDGE, $2); }
|
| yEDGE expr { $$ = new AstSenItem{$1, VEdgeType::ET_BOTHEDGE, $2}; }
|
||||||
| yEDGE idClassSel { $$ = new AstSenItem($1, VEdgeType::ET_BOTHEDGE, $2); }
|
|
||||||
| yPOSEDGE '(' idClassSel ')' { $$ = new AstSenItem($1, VEdgeType::ET_POSEDGE, $3); }
|
|
||||||
| yNEGEDGE '(' idClassSel ')' { $$ = new AstSenItem($1, VEdgeType::ET_NEGEDGE, $3); }
|
|
||||||
| yEDGE '(' idClassSel ')' { $$ = new AstSenItem($1, VEdgeType::ET_BOTHEDGE, $3); }
|
|
||||||
//UNSUP // Above are all removed, replace with:
|
|
||||||
//UNSUP yPOSEDGE expr { UNSUP }
|
|
||||||
//UNSUP yPOSEDGE expr yIFF expr { UNSUP }
|
//UNSUP yPOSEDGE expr yIFF expr { UNSUP }
|
||||||
//UNSUP yNEGEDGE expr { UNSUP }
|
|
||||||
//UNSUP yNEGEDGE expr yIFF expr { UNSUP }
|
//UNSUP yNEGEDGE expr yIFF expr { UNSUP }
|
||||||
//UNSUP yEDGE expr { UNSUP }
|
|
||||||
//UNSUP yEDGE expr yIFF expr { UNSUP }
|
//UNSUP yEDGE expr yIFF expr { UNSUP }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
33
test_regress/t/t_event_control_expr.pl
Executable file
33
test_regress/t/t_event_control_expr.pl
Executable file
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. 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(
|
||||||
|
# do not test classes for multithreaded, as V3InstrCount doesn't handle MemberSel
|
||||||
|
verilator_flags2 => $Self->{vltmt} ? ['-DNO_CLASS'] : [],
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
for my $file (glob_all("$Self->{obj_dir}/$Self->{VM_PREFIX}*.cpp")) {
|
||||||
|
# Check that these simple expressions are not stored in temp variables
|
||||||
|
file_grep_not($file, qr/__Vtrigcurr__expression_.* = vlSelf->clk;/);
|
||||||
|
file_grep_not($file, qr/__Vtrigcurr__expression_.* = vlSelf->t__DOT__q.at\(0U\);/);
|
||||||
|
file_grep_not($file, qr/__Vtrigcurr__expression_.* = .*vlSelf->t__DOT____Vcellinp__u_array__t/);
|
||||||
|
file_grep_not($file, qr/__Vtrigcurr__expression_.* = .*vlSymsp->TOP__t__DOT__u_class.__PVT__obj/);
|
||||||
|
# The line below should only be generated if concats/replicates aren't converted to separate senitems
|
||||||
|
file_grep_not($file, qr/__Vtrigcurr__expression_.* = .*vlSelf->t__DOT__a/);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
174
test_regress/t/t_event_control_expr.v
Normal file
174
test_regress/t/t_event_control_expr.v
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`ifdef TEST_VERBOSE
|
||||||
|
`define WRITE_VERBOSE(args) $write args
|
||||||
|
`else
|
||||||
|
`define WRITE_VERBOSE(args)
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`define STRINGIFY(text) `"text`"
|
||||||
|
|
||||||
|
//========================================================================
|
||||||
|
// Various expression tests. The macro generates a module with the desired
|
||||||
|
// input and tested expression.
|
||||||
|
//
|
||||||
|
`define EXPR_TEST(name, test_edges, inputs, expr) \
|
||||||
|
module t_``name inputs; \
|
||||||
|
logic[$bits(expr)-1:0] last = 0; \
|
||||||
|
always @(expr) begin \
|
||||||
|
if ($bits(expr) > 1) begin \
|
||||||
|
`WRITE_VERBOSE(("[%0t] %s [changed] %s=%0x, last=%0x\n", $time, `STRINGIFY(name), `STRINGIFY(expr), expr, last)); \
|
||||||
|
end \
|
||||||
|
if ($time > 0 && (expr) == last) $stop; \
|
||||||
|
last <= expr; \
|
||||||
|
end \
|
||||||
|
generate if (test_edges) begin \
|
||||||
|
always @(posedge expr) begin \
|
||||||
|
`WRITE_VERBOSE(("[%0t] %s [posedge] %s=%0x, last=%0x\n", $time, `STRINGIFY(name), `STRINGIFY(expr), expr, last)); \
|
||||||
|
if ($time > 0 && ({1'b0, ~(expr)}[0] || last[0])) $stop; \
|
||||||
|
end \
|
||||||
|
always @(negedge expr) begin \
|
||||||
|
`WRITE_VERBOSE(("[%0t] %s [negedge] %s=%0x, last=%0x\n", $time, `STRINGIFY(name), `STRINGIFY(expr), expr, last)); \
|
||||||
|
if ($time > 0 && ({1'b0, expr}[0] || ~last[0])) $stop; \
|
||||||
|
end \
|
||||||
|
end endgenerate \
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`EXPR_TEST(xor, 1, (input a, b), b^a)
|
||||||
|
`EXPR_TEST(nand, 1, (input a, b, c), ~(c&b&a))
|
||||||
|
`EXPR_TEST(concat1, 1, (input a, b, c), {{a, b},c,a,{2{a,b,c}}})
|
||||||
|
`EXPR_TEST(reduce, 1, (input[3:0] v), v[0]^v[1]^v[2]^v[3])
|
||||||
|
`EXPR_TEST(concat2, 1, (input[3:0] v), {{v[0]|v[1]},v[1]|v[2],{4{v[2]|v[3]}}})
|
||||||
|
`EXPR_TEST(add, 0, (input int i, j), i+j)
|
||||||
|
`EXPR_TEST(lt, 1, (input int i, j), i<j)
|
||||||
|
`EXPR_TEST(array, 0, (input int t[5]), t[4])
|
||||||
|
`EXPR_TEST(array_complex, 0, (input int t[5], int cyc), t[cyc / 4])
|
||||||
|
`EXPR_TEST(queue, 0, (input int q[$]), q[0])
|
||||||
|
`EXPR_TEST(queue_mul, 0, (input int q[$], int i), q[0]*i)
|
||||||
|
|
||||||
|
`ifdef UNSUP
|
||||||
|
function int id(int x); return x; endfunction
|
||||||
|
`EXPR_TEST(func, 0, (input int cyc), id(cyc))
|
||||||
|
`endif
|
||||||
|
|
||||||
|
//========================================================================
|
||||||
|
// Class tests (special case as V3Width doesn't always properly handle
|
||||||
|
// out-of-module classes
|
||||||
|
//
|
||||||
|
`ifndef NO_CLASS
|
||||||
|
`define CLASS_TEST(name, expr) \
|
||||||
|
module t_``name(input int k); \
|
||||||
|
class Cls; \
|
||||||
|
int k; \
|
||||||
|
function int get_k(); return k; endfunction \
|
||||||
|
endclass \
|
||||||
|
Cls obj = new; \
|
||||||
|
assign obj.k = k; \
|
||||||
|
int last = 0; \
|
||||||
|
always @(expr) begin \
|
||||||
|
`WRITE_VERBOSE(("[%0t] %s [changed] %s=%0x, last=%0x\n", $time, `STRINGIFY(name), `STRINGIFY(expr), expr, last)); \
|
||||||
|
if ($time > 0 && expr == last) $stop; \
|
||||||
|
last <= expr; \
|
||||||
|
end \
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
`CLASS_TEST(class, obj.k)
|
||||||
|
|
||||||
|
`ifdef UNSUP
|
||||||
|
`CLASS_TEST(method, obj.get_k())
|
||||||
|
`endif
|
||||||
|
`endif
|
||||||
|
|
||||||
|
//========================================================================
|
||||||
|
// $c test has to be written out explicitly as the STRINGIFY macro can't handle it
|
||||||
|
//
|
||||||
|
module t_cstmt;
|
||||||
|
logic last = 0;
|
||||||
|
always @($c("vlSelf->clk")) begin
|
||||||
|
if ($time > 0 && logic'($c("vlSelf->clk")) == last) $stop;
|
||||||
|
last <= logic'($c("vlSelf->clk"));
|
||||||
|
end
|
||||||
|
always @(posedge $c("vlSelf->clk")) begin
|
||||||
|
`WRITE_VERBOSE(("[%0t] cstmt [posedge] $c(\"vlSelf->clk\")=%0b, last=%b\n", $time, $c("vlSelf->clk"), last));
|
||||||
|
if ($time > 0 && (~logic'($c("vlSelf->clk")) || last)) $stop;
|
||||||
|
end
|
||||||
|
always @(negedge $c("vlSelf->clk")) begin
|
||||||
|
`WRITE_VERBOSE(("[%0t] cstmt [negedge] $c(\"vlSelf->clk\")=%0b, last=%b\n", $time, $c("vlSelf->clk"), last));
|
||||||
|
if ($time > 0 && (logic'($c("vlSelf->clk")) || !last)) $stop;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
logic a = 0, b = 0, c = 0;
|
||||||
|
t_xor u_xor(.*);
|
||||||
|
t_nand u_nand(.*);
|
||||||
|
t_concat1 u_concat1(.*);
|
||||||
|
|
||||||
|
logic[3:0] v = '0;
|
||||||
|
t_reduce u_reduce(.*);
|
||||||
|
t_concat2 u_concat2(.*);
|
||||||
|
|
||||||
|
int i = 0, j = 0;
|
||||||
|
t_add u_add(.*);
|
||||||
|
t_lt u_lt(.*);
|
||||||
|
|
||||||
|
int t[5] = {0, 1, 2, 3, 4};
|
||||||
|
t_array u_array(.*);
|
||||||
|
t_array_complex u_array_complex(.*);
|
||||||
|
|
||||||
|
int q[$];
|
||||||
|
t_queue u_queue(.*);
|
||||||
|
t_queue_mul u_queue_mul(.*);
|
||||||
|
|
||||||
|
`ifdef UNSUP
|
||||||
|
t_func u_func(.*);
|
||||||
|
`endif
|
||||||
|
|
||||||
|
int k;
|
||||||
|
assign k = i + j;
|
||||||
|
`ifndef NO_CLASS
|
||||||
|
t_class u_class(.*);
|
||||||
|
`ifdef UNSUP
|
||||||
|
t_method u_method(.*);
|
||||||
|
`endif
|
||||||
|
`endif
|
||||||
|
|
||||||
|
t_cstmt u_cstmt;
|
||||||
|
|
||||||
|
int cyc = 0;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
// a, b, c
|
||||||
|
a <= ~a;
|
||||||
|
if (cyc % 2 == 0) b <= ~b;
|
||||||
|
else c <= ~c;
|
||||||
|
// v
|
||||||
|
if (cyc % 3 == 0) v[0] <= 1;
|
||||||
|
else v <= v << 1;
|
||||||
|
// i, j
|
||||||
|
i <= i + 2;
|
||||||
|
if (cyc % 2 == 0) j <= j + 4;
|
||||||
|
// t
|
||||||
|
t[cyc % 5] <= t[cyc % 5] + cyc;
|
||||||
|
// q
|
||||||
|
q.push_front(cyc);
|
||||||
|
`WRITE_VERBOSE(("[%0t] values: clk=%b, cyc=%0d, a=%b, b=%b, v=%b, i=%0x, j=%0x, t=[%0x, %0x, %0x, %0x, %0x], obj.k=%0x\n",
|
||||||
|
$time, clk, cyc, a, b, v, i, j, t[0], t[1], t[2], t[3], t[4], k));
|
||||||
|
`WRITE_VERBOSE((" q=%p\n", q));
|
||||||
|
if (cyc == 20) begin
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
9
test_regress/t/t_event_control_expr_unsup.out
Normal file
9
test_regress/t/t_event_control_expr_unsup.out
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
%Error-UNSUPPORTED: t/t_event_control_expr.v:55:13: Unsupported: function calls in sensitivity lists
|
||||||
|
55 | always @(id(cyc)) begin
|
||||||
|
| ^~
|
||||||
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
|
%Error-UNSUPPORTED: t/t_event_control_expr.v:82:17: Unsupported: function calls in sensitivity lists
|
||||||
|
: ... In instance t.u_method
|
||||||
|
82 | always @(obj.get_k()) begin
|
||||||
|
| ^~~~~
|
||||||
|
%Error: Exiting due to
|
22
test_regress/t/t_event_control_expr_unsup.pl
Executable file
22
test_regress/t/t_event_control_expr_unsup.pl
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. 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 => 1); # no vltmt, as AstMemberSel is unhandled in V3InstrCount
|
||||||
|
|
||||||
|
top_filename("t_event_control_expr.v");
|
||||||
|
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ['-DUNSUP'],
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
Loading…
Reference in New Issue
Block a user