Support $past.

This commit is contained in:
Wilson Snyder 2018-09-23 15:09:47 -04:00
parent a8519a7a53
commit 0e37747d2c
16 changed files with 328 additions and 9 deletions

View File

@ -4,6 +4,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
* Verilator 4.003 devel * Verilator 4.003 devel
*** Support $past. [Dan Gisselquist]
*** Fix Mac OSX 10.13.6 / LLVM 9.1 compile issues, bug1348. [Kevin Kiningham] *** Fix Mac OSX 10.13.6 / LLVM 9.1 compile issues, bug1348. [Kevin Kiningham]
*** Fix MinGW compile issues, msg2636. [HyungKi Jeong] *** Fix MinGW compile issues, msg2636. [HyungKi Jeong]

View File

@ -3731,6 +3731,17 @@ Change this to:
Verilator doesn't do this conversion for you, as some more complicated Verilator doesn't do this conversion for you, as some more complicated
cases would result in simulator mismatches. cases would result in simulator mismatches.
=item TICKCOUNT
Warns that the number of ticks to delay a $past variable is greater than
10. At present Verilator effectively creates a flop for each delayed
signals, and as such any large counts may lead to large design size
increases.
Ignoring this warning will only slow simulations, it will simulate
correctly.
=item UNDRIVEN =item UNDRIVEN
Warns that the specified signal is never sourced. Verilator is fairly Warns that the specified signal is never sourced. Verilator is fairly

View File

@ -45,6 +45,7 @@ private:
// STATE // STATE
AstNodeModule* m_modp; // Last module AstNodeModule* m_modp; // Last module
AstBegin* m_beginp; // Last begin AstBegin* m_beginp; // Last begin
unsigned m_modPastNum; // Module past numbering
V3Double0 m_statAsCover; // Statistic tracking V3Double0 m_statAsCover; // Statistic tracking
V3Double0 m_statAsPsl; // Statistic tracking V3Double0 m_statAsPsl; // Statistic tracking
V3Double0 m_statAsFull; // Statistic tracking V3Double0 m_statAsFull; // Statistic tracking
@ -185,6 +186,7 @@ private:
pushDeletep(nodep); VL_DANGLING(nodep); pushDeletep(nodep); VL_DANGLING(nodep);
} }
// VISITORS
virtual void visit(AstIf* nodep) { virtual void visit(AstIf* nodep) {
if (nodep->user1SetOnce()) return; if (nodep->user1SetOnce()) return;
if (nodep->uniquePragma() || nodep->unique0Pragma()) { if (nodep->uniquePragma() || nodep->unique0Pragma()) {
@ -244,7 +246,7 @@ private:
} }
} }
// VISITORS //========== Case assertions //========== Case assertions
virtual void visit(AstCase* nodep) { virtual void visit(AstCase* nodep) {
iterateChildren(nodep); iterateChildren(nodep);
if (!nodep->user1SetOnce()) { if (!nodep->user1SetOnce()) {
@ -302,7 +304,38 @@ private:
} }
} }
// VISITORS //========== Statements //========== Past
virtual void visit(AstPast* nodep) {
iterateChildren(nodep);
uint32_t ticks = 1;
if (nodep->ticksp()) {
if (!VN_IS(nodep->ticksp(), Const)) nodep->v3fatalSrc("Expected constant ticks, checked in V3Width");
ticks = VN_CAST(nodep->ticksp(), Const)->toUInt();
}
if (ticks<1) nodep->v3fatalSrc("0 tick should have been checked in V3Width");
AstNode* inp = nodep->exprp()->unlinkFrBack();
AstVar* invarp = NULL;
AstSenTree* sentreep = nodep->sentreep(); sentreep->unlinkFrBack();
AstAlways* alwaysp = new AstAlways(nodep->fileline(), VAlwaysKwd::ALWAYS,
sentreep, NULL);
m_modp->addStmtp(alwaysp);
for (uint32_t i=0; i<ticks; ++i) {
AstVar* outvarp = new AstVar(nodep->fileline(), AstVarType::MODULETEMP,
"_Vpast_"+cvtToStr(m_modPastNum++)+"_"+cvtToStr(i),
inp->dtypep());
m_modp->addStmtp(outvarp);
AstNode* assp = new AstAssignDly(nodep->fileline(),
new AstVarRef(nodep->fileline(), outvarp, true),
inp);
alwaysp->addStmtp(assp);
//if (debug()>-9) assp->dumpTree(cout, "-ass: ");
invarp = outvarp;
inp = new AstVarRef(nodep->fileline(), invarp, false);
}
nodep->replaceWith(inp);
}
//========== Statements
virtual void visit(AstDisplay* nodep) { virtual void visit(AstDisplay* nodep) {
iterateChildren(nodep); iterateChildren(nodep);
// Replace the special types with standard text // Replace the special types with standard text
@ -330,6 +363,7 @@ private:
virtual void visit(AstNodeModule* nodep) { virtual void visit(AstNodeModule* nodep) {
m_modp = nodep; m_modp = nodep;
m_modPastNum = 0;
// //
iterateChildren(nodep); iterateChildren(nodep);
// Reset defaults // Reset defaults
@ -354,6 +388,7 @@ public:
explicit AssertVisitor(AstNetlist* nodep) { explicit AssertVisitor(AstNetlist* nodep) {
m_beginp = NULL; m_beginp = NULL;
m_modp = NULL; m_modp = NULL;
m_modPastNum = 0;
// Process // Process
iterate(nodep); iterate(nodep);
} }

View File

@ -44,9 +44,11 @@ private:
// NODE STATE/TYPES // NODE STATE/TYPES
// STATE // STATE
// Reset each module: // Reset each module:
AstNodeSenItem* m_seniDefaultp; // Default sensitivity (from AstDefClock) AstNodeSenItem* m_seniDefaultp; // Default sensitivity (from AstDefClock)
// Reset each assertion: // Reset each assertion:
AstNodeSenItem* m_senip; // Last sensitivity AstNodeSenItem* m_senip; // Last sensitivity
// Reset each always:
AstNodeSenItem* m_seniAlwaysp; // Last sensitivity in always
// METHODS // METHODS
VL_DEBUG_FUNC; // Declare debug() VL_DEBUG_FUNC; // Declare debug()
@ -55,7 +57,9 @@ private:
// Create sentree based on clocked or default clock // Create sentree based on clocked or default clock
// Return NULL for always // Return NULL for always
AstSenTree* newp = NULL; AstSenTree* newp = NULL;
AstNodeSenItem* senip = m_senip ? m_senip : m_seniDefaultp; AstNodeSenItem* senip = m_senip;
if (!senip) senip = m_seniDefaultp;
if (!senip) senip = m_seniAlwaysp;
if (!senip) { if (!senip) {
nodep->v3error("Unsupported: Unclocked assertion"); nodep->v3error("Unsupported: Unclocked assertion");
newp = new AstSenTree(nodep->fileline(), NULL); newp = new AstSenTree(nodep->fileline(), NULL);
@ -68,7 +72,8 @@ private:
m_senip = NULL; m_senip = NULL;
} }
// VISITORS //========== Statements // VISITORS
//========== Statements
virtual void visit(AstClocking* nodep) { virtual void visit(AstClocking* nodep) {
UINFO(8," CLOCKING"<<nodep<<endl); UINFO(8," CLOCKING"<<nodep<<endl);
// Store the new default clock, reset on new module // Store the new default clock, reset on new module
@ -81,16 +86,31 @@ private:
} }
pushDeletep(nodep); VL_DANGLING(nodep); pushDeletep(nodep); VL_DANGLING(nodep);
} }
virtual void visit(AstAlways* nodep) {
iterateAndNextNull(nodep->sensesp());
if (nodep->sensesp()) {
m_seniAlwaysp = nodep->sensesp()->sensesp();
}
iterateAndNextNull(nodep->bodysp());
m_seniAlwaysp = NULL;
}
virtual void visit(AstNodePslCoverOrAssert* nodep) { virtual void visit(AstNodePslCoverOrAssert* nodep) {
if (nodep->sentreep()) return; // Already processed if (nodep->sentreep()) return; // Already processed
clearAssertInfo(); clearAssertInfo();
// Find PslClocking's burried under nodep->exprsp
iterateChildren(nodep); iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep)); nodep->sentreep(newSenTree(nodep));
clearAssertInfo(); clearAssertInfo();
} }
virtual void visit(AstPslClocked* nodep) { virtual void visit(AstPast* nodep) {
if (nodep->sentreep()) return; // Already processed
iterateChildren(nodep); iterateChildren(nodep);
nodep->sentreep(newSenTree(nodep));
}
virtual void visit(AstPslClocked* nodep) {
// No need to iterate the body, once replace will get iterated
iterateAndNextNull(nodep->sensesp());
if (m_senip) { if (m_senip) {
nodep->v3error("Unsupported: Only one PSL clock allowed per assertion"); nodep->v3error("Unsupported: Only one PSL clock allowed per assertion");
} }
@ -120,6 +140,7 @@ public:
// CONSTRUCTORS // CONSTRUCTORS
explicit AssertPreVisitor(AstNetlist* nodep) { explicit AssertPreVisitor(AstNetlist* nodep) {
m_seniDefaultp = NULL; m_seniDefaultp = NULL;
m_seniAlwaysp = NULL;
clearAssertInfo(); clearAssertInfo();
// Process // Process
iterate(nodep); iterate(nodep);

View File

@ -2155,7 +2155,7 @@ public:
// //
virtual void dump(std::ostream& str); virtual void dump(std::ostream& str);
AstSenTree* sensesp() const { return VN_CAST(op1p(), SenTree); } // op1 = Sensitivity list AstSenTree* sensesp() const { return VN_CAST(op1p(), SenTree); } // op1 = Sensitivity list
AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate AstNode* bodysp() const { return op2p(); } // op2 = Statements to evaluate
void addStmtp(AstNode* nodep) { addOp2p(nodep); } void addStmtp(AstNode* nodep) { addOp2p(nodep); }
VAlwaysKwd keyword() const { return m_keyword; } VAlwaysKwd keyword() const { return m_keyword; }
// Special accessors // Special accessors
@ -5193,6 +5193,30 @@ public:
virtual string emitC() { return "hypot(%li,%ri)"; } virtual string emitC() { return "hypot(%li,%ri)"; }
}; };
class AstPast : public AstNodeMath {
// Verilog $past
// Parents: math
// Children: expression
public:
AstPast(FileLine* fl, AstNode* exprp, AstNode* ticksp) : AstNodeMath(fl) {
addOp1p(exprp);
addNOp2p(ticksp);
}
ASTNODE_NODE_FUNCS(Past)
virtual string emitVerilog() { V3ERROR_NA; return ""; }
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { V3ERROR_NA; }
virtual string emitC() { V3ERROR_NA; return "";}
virtual string emitSimpleOperator() { V3ERROR_NA; return "";}
virtual bool cleanOut() { V3ERROR_NA; return "";}
virtual int instrCount() const { return widthInstrs(); }
AstNode* exprp() const { return op1p(); } // op1 = expression
AstNode* ticksp() const { return op2p(); } // op2 = ticks or NULL means 1
AstSenTree* sentreep() const { return VN_CAST(op4p(), SenTree); } // op4 = clock domain
void sentreep(AstSenTree* sentreep) { addOp4p(sentreep); } // op4 = clock domain
virtual V3Hash sameHash() const { return V3Hash(); }
virtual bool same(const AstNode* samep) const { return true; }
};
class AstPattern : public AstNodeMath { class AstPattern : public AstNodeMath {
// Verilog '{a,b,c,d...} // Verilog '{a,b,c,d...}
// Parents: AstNodeAssign, AstPattern, ... // Parents: AstNodeAssign, AstPattern, ...

View File

@ -332,6 +332,15 @@ class EmitVBaseVisitor : public EmitCBaseVisitor {
} }
putqs(nodep,"end\n"); putqs(nodep,"end\n");
} }
virtual void visit(AstPast* nodep) {
putfs(nodep, "$past(");
iterateAndNextNull(nodep->exprp());
if (nodep->ticksp()) {
puts(",");
iterateAndNextNull(nodep->ticksp());
}
puts(")");
}
virtual void visit(AstReturn* nodep) { virtual void visit(AstReturn* nodep) {
putfs(nodep,"return "); putfs(nodep,"return ");
iterateAndNextNull(nodep->lhsp()); iterateAndNextNull(nodep->lhsp());

View File

@ -94,6 +94,7 @@ public:
STMTDLY, // Delayed statement STMTDLY, // Delayed statement
SYMRSVDWORD, // Symbol is Reserved Word SYMRSVDWORD, // Symbol is Reserved Word
SYNCASYNCNET, // Mixed sync + async reset SYNCASYNCNET, // Mixed sync + async reset
TICKCOUNT, // Too large tick count
UNDRIVEN, // No drivers UNDRIVEN, // No drivers
UNOPT, // Unoptimizable block UNOPT, // Unoptimizable block
UNOPTFLAT, // Unoptimizable block after flattening UNOPTFLAT, // Unoptimizable block after flattening
@ -140,6 +141,7 @@ public:
"PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY", "PINMISSING", "PINNOCONNECT", "PINCONNECTEMPTY",
"REALCVT", "REDEFMACRO", "REALCVT", "REDEFMACRO",
"SELRANGE", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "SELRANGE", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET",
"TICKCOUNT",
"UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS",
"UNPACKED", "UNSIGNED", "UNUSED", "UNPACKED", "UNSIGNED", "UNUSED",
"USERERROR", "USERFATAL", "USERINFO", "USERWARN", "USERERROR", "USERFATAL", "USERINFO", "USERWARN",

View File

@ -798,6 +798,26 @@ private:
// We don't size the constant until we commit the widths, as need parameters // We don't size the constant until we commit the widths, as need parameters
// to remain unsized, and numbers to remain unsized to avoid backp() warnings // to remain unsized, and numbers to remain unsized to avoid backp() warnings
} }
virtual void visit(AstPast* nodep) {
if (m_vup->prelim()) {
iterateCheckSizedSelf(nodep, "LHS", nodep->exprp(), SELF, BOTH);
nodep->dtypeFrom(nodep->exprp());
if (nodep->ticksp()) {
iterateCheckSizedSelf(nodep, "Ticks", nodep->ticksp(), SELF, BOTH);
V3Const::constifyParamsEdit(nodep->ticksp()); // ticksp may change
const AstConst* constp = VN_CAST(nodep->ticksp(), Const);
if (!constp || constp->toSInt() < 1) {
nodep->v3error("$past tick value must be constant and >= 1 (IEEE 2017 16.9.3)");
nodep->ticksp()->unlinkFrBack()->deleteTree();
} else {
if (constp->toSInt() > 10) {
nodep->v3warn(TICKCOUNT, "$past tick value of "<<constp->toSInt()
<<" may have a large performance cost");
}
}
}
}
}
virtual void visit(AstRand* nodep) { virtual void visit(AstRand* nodep) {
if (m_vup->prelim()) { if (m_vup->prelim()) {
nodep->dtypeSetSigned32(); // Says the spec nodep->dtypeSetSigned32(); // Says the spec

View File

@ -444,6 +444,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
"$low" { FL; return yD_LOW; } "$low" { FL; return yD_LOW; }
"$onehot" { FL; return yD_ONEHOT; } "$onehot" { FL; return yD_ONEHOT; }
"$onehot0" { FL; return yD_ONEHOT0; } "$onehot0" { FL; return yD_ONEHOT0; }
"$past" { FL; return yD_PAST; }
"$right" { FL; return yD_RIGHT; } "$right" { FL; return yD_RIGHT; }
"$size" { FL; return yD_SIZE; } "$size" { FL; return yD_SIZE; }
"$unpacked_dimensions" { FL; return yD_UNPACKED_DIMENSIONS; } "$unpacked_dimensions" { FL; return yD_UNPACKED_DIMENSIONS; }

View File

@ -485,6 +485,7 @@ class AstSenTree;
%token<fl> yD_LOW "$low" %token<fl> yD_LOW "$low"
%token<fl> yD_ONEHOT "$onehot" %token<fl> yD_ONEHOT "$onehot"
%token<fl> yD_ONEHOT0 "$onehot0" %token<fl> yD_ONEHOT0 "$onehot0"
%token<fl> yD_PAST "$past"
%token<fl> yD_POW "$pow" %token<fl> yD_POW "$pow"
%token<fl> yD_RANDOM "$random" %token<fl> yD_RANDOM "$random"
%token<fl> yD_READMEMB "$readmemb" %token<fl> yD_READMEMB "$readmemb"
@ -2776,8 +2777,12 @@ system_f_call_or_t<nodep>: // IEEE: part of system_tf_call (can be task or func)
| yD_LOW '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LOW,$3,$5); } | yD_LOW '(' exprOrDataType ',' expr ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_LOW,$3,$5); }
| yD_ONEHOT '(' expr ')' { $$ = new AstOneHot($1,$3); } | yD_ONEHOT '(' expr ')' { $$ = new AstOneHot($1,$3); }
| yD_ONEHOT0 '(' expr ')' { $$ = new AstOneHot0($1,$3); } | yD_ONEHOT0 '(' expr ')' { $$ = new AstOneHot0($1,$3); }
| yD_PAST '(' expr ')' { $$ = new AstPast($1,$3, NULL); }
| yD_PAST '(' expr ',' expr ')' { $$ = new AstPast($1,$3, $5); }
| yD_PAST '(' expr ',' expr ',' expr ')' { $1->v3error("Unsupported: $past expr2 and clock arguments"); $$ = $3; }
| yD_PAST '(' expr ',' expr ',' expr ',' expr')' { $1->v3error("Unsupported: $past expr2 and clock arguments"); $$ = $3; }
| yD_POW '(' expr ',' expr ')' { $$ = new AstPowD($1,$3,$5); } | yD_POW '(' expr ',' expr ')' { $$ = new AstPowD($1,$3,$5); }
| yD_RANDOM '(' expr ')' { $1->v3error("Unsupported: Seeding $random doesn't map to C++, use $c(\"srand\")"); } | yD_RANDOM '(' expr ')' { $1->v3error("Unsupported: Seeding $random doesn't map to C++, use $c(\"srand\")"); $$ = NULL; }
| yD_RANDOM parenE { $$ = new AstRand($1); } | yD_RANDOM parenE { $$ = new AstRand($1); }
| yD_REALTIME parenE { $$ = new AstTimeD($1); } | yD_REALTIME parenE { $$ = new AstTimeD($1); }
| yD_REALTOBITS '(' expr ')' { $$ = new AstRealToBits($1,$3); } | yD_REALTOBITS '(' expr ')' { $$ = new AstRealToBits($1,$3); }

20
test_regress/t/t_past.pl Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

98
test_regress/t/t_past.v Normal file
View File

@ -0,0 +1,98 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2018 by Wilson Snyder.
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=0;
reg [63:0] crc;
reg [63:0] sum;
// Take CRC data and apply to testblock inputs
wire [31:0] in = crc[31:0];
Test test (/*AUTOINST*/
// Inputs
.clk (clk),
.in (in[31:0]));
Test2 test2 (/*AUTOINST*/
// Inputs
.clk (clk),
.in (in[31:0]));
// Test loop
always @ (posedge clk) begin
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
if (cyc==0) begin
// Setup
crc <= 64'h5aef0c8d_d70a4497;
end
else if (cyc<10) begin
end
else if (cyc<90) begin
end
else if (cyc==99) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module Test (/*AUTOARG*/
// Inputs
clk, in
);
input clk;
input [31:0] in;
reg [31:0] dly0;
reg [31:0] dly1;
reg [31:0] dly2;
reg [31:0] dly3;
// If called in an assertion, sequence, or property, the appropriate clocking event.
// Otherwise, if called in a disable condition or a clock expression in an assertion, sequence, or prop, explicit.
// Otherwise, if called in an action block of an assertion, the leading clock of the assertion is used.
// Otherwise, if called in a procedure, the inferred clock
// Otherwise, default clocking
always @(posedge clk) begin
dly0 <= in;
dly1 <= dly0;
dly2 <= dly1;
dly3 <= dly2;
// $past(expression, ticks, expression, clocking)
// In clock expression
if (dly0 != $past(in)) $stop;
if (dly0 != $past(in,1)) $stop;
if (dly1 != $past(in,2)) $stop;
end
assert property (@(posedge clk) dly0 == $past(in));
endmodule
module Test2 (/*AUTOARG*/
// Inputs
clk, in
);
input clk;
input [31:0] in;
reg [31:0] dly0;
reg [31:0] dly1;
default clocking @(posedge clk); endclocking
assert property (@(posedge clk) dly1 == $past(in, 2));
endmodule

22
test_regress/t/t_past_bad.pl Executable file
View File

@ -0,0 +1,22 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
scenarios(vlt_all => 1);
compile(
fails => 1,
expect =>
'%Error: t/t_past_bad.v:\d+:.* \$past tick value must be constant and >= 1 \(IEEE 2017 16.9.3\)
%Warning-TICKCOUNT: t/t_past_bad.v:\d+: \$past tick value of 10000 may have a large performance cost
.*%Error: Exiting due to.*',
);
ok(1);
1;

View File

@ -0,0 +1,14 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2018 by Wilson Snyder.
module t (d, clk);
input d;
input clk;
always @ (posedge clk) begin
if ($past(d, 0)) $stop; // IEEE 16.9.3 must be >- 0
if ($past(d, 10000)) $stop; // TICKCOUNT
end
endmodule

View File

@ -0,0 +1,21 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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.
scenarios(vlt_all => 1);
compile(
fails => 1,
expect =>
'%Error: t/t_past_unsup_bad.v:12: Unsupported: \$past expr2 and clock arguments
%Error: Exiting due to.*',
);
ok(1);
1;

View File

@ -0,0 +1,14 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2018 by Wilson Snyder.
module t (d, clk);
input d;
input clk;
always @ (posedge clk) begin
// Unsupported
if ($past(d, 0, 0, 0)) $stop;
end
endmodule