Add support for expressions in event controls (#3550)

Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
This commit is contained in:
Krzysztof Bieganski 2022-08-19 20:18:38 +02:00 committed by GitHub
parent 1404319b28
commit 10cf492946
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 361 additions and 85 deletions

View File

@ -2717,9 +2717,7 @@ private:
}
UINFO(8, "senItem(NOT...) " << nodep << " " << invert << endl);
if (invert) nodep->edgeType(nodep->edgeType().invert());
AstNodeVarRef* const senvarp = VN_AS(lastSensp->unlinkFrBack(), NodeVarRef);
UASSERT_OBJ(senvarp, sensp, "Non-varref sensitivity variable");
sensp->replaceWith(senvarp);
sensp->replaceWith(lastSensp->unlinkFrBack());
VL_DO_DANGLING(sensp->deleteTree(), sensp);
}
}

View File

@ -145,42 +145,6 @@ private:
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 {
if (!nodep->attrp()) {
iterateChildren(nodep);

View File

@ -268,13 +268,19 @@ class SenExprBuilder final {
AstCFunc* const m_initp; // The initialization function
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_set<VNRef<AstNode>> m_hasUpdate; // Whether the given sen expression already
// has an update statement in m_updates
std::unordered_map<VNRef<AstNode>, AstVarScope*> m_curr; // The 'current value' signals
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) {
dtypep = dtypep->skipRefp();
@ -285,29 +291,62 @@ class SenExprBuilder final {
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
AstVarScope* getPrev(AstNode* currp) {
FileLine* const flp = currp->fileline();
const auto rdCurr = [=]() { return currp->cloneTree(false); };
AstNode* getCurr(AstNode* exprp) {
// For simple expressions like varrefs or selects, just use them directly
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
auto it = m_prev.find(*currp);
auto it = m_prev.find(*exprp);
if (it == m_prev.end()) {
// For readability, use the scoped signal name if the trigger is a simple AstVarRef
string name;
if (AstVarRef* const refp = VN_CAST(currp, VarRef)) {
if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) {
AstVarScope* vscp = refp->varScopep();
name = "__Vtrigrprev__" + vscp->scopep()->nameDotless() + "__"
+ vscp->varp()->name();
} else {
name = m_uniqueNames.get(currp);
name = m_prevNames.get(exprp);
}
AstVarScope* const prevp = m_scopeTopp->createTemp(name, currp->dtypep());
it = m_prev.emplace(*currp, prevp).first;
AstVarScope* const prevp = m_scopeTopp->createTemp(name, exprp->dtypep());
it = m_prev.emplace(*exprp, prevp).first;
// Add the initializer init
AstNode* const initp = rdCurr();
AstNode* const initp = exprp->cloneTree(false);
m_initp->addStmtsp(
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}; };
// Add update if it does not exist yet
if (m_hasUpdate.emplace(*currp).second) {
if (!isSupportedDType(currp->dtypep())) {
currp->v3warn(E_UNSUPPORTED,
// Add post update if it does not exist yet
if (m_hasPostUpdate.emplace(*exprp).second) {
if (!isSupportedDType(exprp->dtypep())) {
exprp->v3warn(E_UNSUPPORTED,
"Unsupported: Cannot detect changes on expression of complex type"
" (see combinational cycles reported by UNOPTFLAT)");
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()};
cmhp->dtypeSetVoid();
cmhp->statement(true);
m_updates.push_back(cmhp);
m_postUpdates.push_back(cmhp);
} 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();
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 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"};
callp->dtypeSetBit();
AstIf* const ifp = new AstIf{flp, callp};
m_updates.push_back(ifp);
m_postUpdates.push_back(ifp);
// Clear 'fired' state when done
AstCMethodHard* const clearp = new AstCMethodHard{flp, currp(), "clearFired"};
@ -417,9 +456,16 @@ public:
return {resultp, firedAtInitialization};
}
std::vector<AstNodeStmt*> getAndClearUpdates() {
m_hasUpdate.clear();
return std::move(m_updates);
std::vector<AstVar*> getAndClearLocals() { return std::move(m_locals); }
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
@ -606,7 +652,25 @@ const TriggerKit createTriggers(AstNetlist* netlistp, SenExprBuilder& senExprBui
++triggerNumber;
}
// 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
if (initialTrigsp) {

View File

@ -360,6 +360,7 @@ private:
AstScope* m_scopep = nullptr; // Current scope
InsertMode m_insMode = IM_BEFORE; // How to insert
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
DpiCFuncs m_dpiNames; // Map of all created DPI functions
@ -1369,6 +1370,11 @@ private:
m_scopep = nullptr;
}
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
UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked?");
iterateIntoFTask(nodep->taskp()); // First, do hierarchical funcs
@ -1521,6 +1527,12 @@ private:
iterateChildren(nodep);
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); }

View File

@ -5050,7 +5050,22 @@ private:
m_procedurep = nullptr;
}
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 {
// Should otherwise be underneath a method call

View File

@ -3027,15 +3027,8 @@ event_expression<senItemp>: // IEEE: event_expression - split over several
senitem<senItemp>: // IEEE: part of event_expression, non-'OR' ',' terms
senitemEdge { $$ = $1; }
| senitemVar { $$ = $1; }
| '(' senitem ')' { $$ = $2; }
//UNSUP expr { UNSUP }
| '{' event_expression '}' { $$ = $2; }
| senitem yP_ANDAND senitem { $$ = new AstSenItem($2, AstSenItem::Illegal()); }
| expr { $$ = new AstSenItem{$<fl>1, VEdgeType::ET_CHANGED, $1}; }
//UNSUP expr yIFF expr { UNSUP }
// Since expr is unsupported we allow and ignore constants (removed in V3Const)
| yaINTNUM { $$ = nullptr; }
| yaFLOATNUM { $$ = nullptr; }
;
senitemVar<senItemp>:
@ -3043,19 +3036,11 @@ senitemVar<senItemp>:
;
senitemEdge<senItemp>: // IEEE: part of event_expression
//UNSUP // Below are all removed
yPOSEDGE idClassSel { $$ = new AstSenItem($1, VEdgeType::ET_POSEDGE, $2); }
| yNEGEDGE idClassSel { $$ = new AstSenItem($1, VEdgeType::ET_NEGEDGE, $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 }
yPOSEDGE expr { $$ = new AstSenItem{$1, VEdgeType::ET_POSEDGE, $2}; }
| yNEGEDGE expr { $$ = new AstSenItem{$1, VEdgeType::ET_NEGEDGE, $2}; }
| yEDGE expr { $$ = new AstSenItem{$1, VEdgeType::ET_BOTHEDGE, $2}; }
//UNSUP yPOSEDGE expr yIFF expr { UNSUP }
//UNSUP yNEGEDGE expr { UNSUP }
//UNSUP yNEGEDGE expr yIFF expr { UNSUP }
//UNSUP yEDGE expr { UNSUP }
//UNSUP yEDGE expr yIFF expr { UNSUP }
;

View 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;

View 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

View 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

View 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;