Support $urandom, $urandom_range without stability.
This commit is contained in:
parent
e10156548d
commit
f4a72946eb
2
Changes
2
Changes
|
|
@ -11,6 +11,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
|
||||||
|
|
||||||
**** Support class extern.
|
**** Support class extern.
|
||||||
|
|
||||||
|
**** Support $urandom, $urandom_range without stability.
|
||||||
|
|
||||||
**** Fix false DECLFILENAME on black-boxed modules (#2430). [Philipp Wagner]
|
**** Fix false DECLFILENAME on black-boxed modules (#2430). [Philipp Wagner]
|
||||||
|
|
||||||
**** Fix naming of "id : begin" blocks.
|
**** Fix naming of "id : begin" blocks.
|
||||||
|
|
|
||||||
|
|
@ -4093,11 +4093,12 @@ All specify blocks and timing checks are ignored.
|
||||||
Monitor and strobe are not supported, convert to always_comb $display or
|
Monitor and strobe are not supported, convert to always_comb $display or
|
||||||
similar.
|
similar.
|
||||||
|
|
||||||
=item $random
|
=item $random, $urandom, $urandom_range
|
||||||
|
|
||||||
$random does not support the optional argument to set the seed. Use the
|
$random and $urandom do not support the optional argument to set the seed.
|
||||||
srand function in C to accomplish this, and note there is only one random
|
Use +verilator+seed argument to set the seed. There is one random seed per
|
||||||
number generator (not one per module).
|
C thread, not per module for $random, nor per object for random stability
|
||||||
|
of $urandom/$urandom_range.
|
||||||
|
|
||||||
=item $readmemb, $readmemh
|
=item $readmemb, $readmemh
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -315,8 +315,6 @@ vluint64_t vl_rand64() VL_MT_SAFE {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
IData VL_RANDOM_I(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_I(obits); }
|
|
||||||
QData VL_RANDOM_Q(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_Q(obits); }
|
|
||||||
// VL_RANDOM_W currently unused as $random always 32 bits, left for backwards compatibility
|
// VL_RANDOM_W currently unused as $random always 32 bits, left for backwards compatibility
|
||||||
// LCOV_EXCL_START
|
// LCOV_EXCL_START
|
||||||
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||||
|
|
|
||||||
|
|
@ -643,9 +643,19 @@ extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
||||||
/// Print a debug message from internals with standard prefix, with printf style format
|
/// Print a debug message from internals with standard prefix, with printf style format
|
||||||
extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
|
||||||
|
|
||||||
extern IData VL_RANDOM_I(int obits); ///< Randomize a signal
|
extern vluint64_t vl_rand64() VL_MT_SAFE;
|
||||||
extern QData VL_RANDOM_Q(int obits); ///< Randomize a signal
|
inline IData VL_RANDOM_I(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_I(obits); }
|
||||||
|
inline QData VL_RANDOM_Q(int obits) VL_MT_SAFE { return vl_rand64() & VL_MASK_Q(obits); }
|
||||||
extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp); ///< Randomize a signal
|
extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp); ///< Randomize a signal
|
||||||
|
inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) {
|
||||||
|
vluint64_t rnd = vl_rand64();
|
||||||
|
if (VL_LIKELY(hi > lo)) {
|
||||||
|
// Modulus isn't very fast but it's common that hi-low is power-of-two
|
||||||
|
return (rnd % (hi - lo)) + lo;
|
||||||
|
} else {
|
||||||
|
return (rnd % (lo - hi)) + hi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Init time only, so slow is fine
|
/// Init time only, so slow is fine
|
||||||
extern IData VL_RAND_RESET_I(int obits); ///< Random reset a signal
|
extern IData VL_RAND_RESET_I(int obits); ///< Random reset a signal
|
||||||
|
|
|
||||||
|
|
@ -5213,6 +5213,50 @@ public:
|
||||||
virtual bool same(const AstNode* samep) const override { return true; }
|
virtual bool same(const AstNode* samep) const override { return true; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class AstURandom : public AstNodeTermop {
|
||||||
|
// $urandom
|
||||||
|
public:
|
||||||
|
explicit AstURandom(FileLine* fl)
|
||||||
|
: ASTGEN_SUPER(fl) {
|
||||||
|
dtypeSetUInt32(); // Says IEEE
|
||||||
|
}
|
||||||
|
ASTNODE_NODE_FUNCS(URandom)
|
||||||
|
virtual string emitVerilog() override { return "%f$urandom"; }
|
||||||
|
virtual string emitC() override { return "VL_RANDOM_%nq(%nw)"; }
|
||||||
|
virtual bool cleanOut() const override { return true; }
|
||||||
|
virtual bool isGateOptimizable() const override { return false; }
|
||||||
|
virtual bool isPredictOptimizable() const override { return false; }
|
||||||
|
virtual int instrCount() const override { return instrCountPli(); }
|
||||||
|
virtual V3Hash sameHash() const override { return V3Hash(); }
|
||||||
|
virtual bool same(const AstNode* samep) const override { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class AstURandomRange : public AstNodeBiop {
|
||||||
|
// $urandom_range
|
||||||
|
public:
|
||||||
|
explicit AstURandomRange(FileLine* fl, AstNode* lhsp, AstNode* rhsp)
|
||||||
|
: ASTGEN_SUPER(fl, lhsp, rhsp) {
|
||||||
|
dtypeSetUInt32(); // Says IEEE
|
||||||
|
}
|
||||||
|
ASTNODE_NODE_FUNCS(URandomRange)
|
||||||
|
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
|
||||||
|
return new AstURandomRange(fileline(), lhsp, rhsp);
|
||||||
|
}
|
||||||
|
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||||
|
V3ERROR_NA;
|
||||||
|
}
|
||||||
|
virtual string emitVerilog() override { return "%f$urandom_range(%l, %r)"; }
|
||||||
|
virtual string emitC() override { return "VL_URANDOM_RANGE_%nq(%li, %ri)"; }
|
||||||
|
virtual bool cleanOut() const override { return true; }
|
||||||
|
virtual bool cleanLhs() const override { return true; }
|
||||||
|
virtual bool cleanRhs() const override { return true; }
|
||||||
|
virtual bool sizeMattersLhs() const override { return false; }
|
||||||
|
virtual bool sizeMattersRhs() const override { return false; }
|
||||||
|
virtual bool isGateOptimizable() const override { return false; }
|
||||||
|
virtual bool isPredictOptimizable() const override { return false; }
|
||||||
|
virtual int instrCount() const override { return instrCountPli(); }
|
||||||
|
};
|
||||||
|
|
||||||
class AstTime : public AstNodeTermop {
|
class AstTime : public AstNodeTermop {
|
||||||
VTimescale m_timeunit; // Parent module time unit
|
VTimescale m_timeunit; // Parent module time unit
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -2209,8 +2209,8 @@ private:
|
||||||
|
|
||||||
TREEOP1("AstSel{warnSelect(nodep)}", "NEVER");
|
TREEOP1("AstSel{warnSelect(nodep)}", "NEVER");
|
||||||
// Generic constants on both side. Do this first to avoid other replacements
|
// Generic constants on both side. Do this first to avoid other replacements
|
||||||
TREEOPC("AstNodeBiop {$lhsp.castConst, $rhsp.castConst}", "replaceConst(nodep)");
|
TREEOPC("AstNodeBiop {$lhsp.castConst, $rhsp.castConst, nodep->isPredictOptimizable()}", "replaceConst(nodep)");
|
||||||
TREEOPC("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque()}", "replaceConst(nodep)");
|
TREEOPC("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque(), nodep->isPredictOptimizable()}", "replaceConst(nodep)");
|
||||||
TREEOPC("AstNodeQuadop{$lhsp.castConst, $rhsp.castConst, $thsp.castConst, $fhsp.castConst}", "replaceConst(nodep)");
|
TREEOPC("AstNodeQuadop{$lhsp.castConst, $rhsp.castConst, $thsp.castConst, $fhsp.castConst}", "replaceConst(nodep)");
|
||||||
// Zero on one side or the other
|
// Zero on one side or the other
|
||||||
TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
||||||
|
|
|
||||||
|
|
@ -1101,6 +1101,19 @@ private:
|
||||||
nodep->dtypeSetSigned32(); // Says the spec
|
nodep->dtypeSetSigned32(); // Says the spec
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
virtual void visit(AstURandom* nodep) override {
|
||||||
|
if (m_vup->prelim()) {
|
||||||
|
nodep->dtypeSetUInt32(); // Says the spec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
virtual void visit(AstURandomRange* nodep) override {
|
||||||
|
if (m_vup->prelim()) {
|
||||||
|
nodep->dtypeSetUInt32(); // Says the spec
|
||||||
|
AstNodeDType* expDTypep = nodep->findUInt32DType();
|
||||||
|
iterateCheck(nodep, "LHS", nodep->lhsp(), SELF, FINAL, expDTypep, EXTEND_EXP);
|
||||||
|
iterateCheck(nodep, "RHS", nodep->rhsp(), SELF, FINAL, expDTypep, EXTEND_EXP);
|
||||||
|
}
|
||||||
|
}
|
||||||
virtual void visit(AstUnbounded* nodep) override {
|
virtual void visit(AstUnbounded* nodep) override {
|
||||||
nodep->dtypeSetSigned32(); // Used in int context
|
nodep->dtypeSetSigned32(); // Used in int context
|
||||||
if (!VN_IS(nodep->backp(), IsUnbounded) && !VN_IS(nodep->backp(), BracketArrayDType)
|
if (!VN_IS(nodep->backp(), IsUnbounded) && !VN_IS(nodep->backp(), BracketArrayDType)
|
||||||
|
|
@ -1793,8 +1806,7 @@ private:
|
||||||
if (nodep->lvalue() && nodep->varp()->direction() == VDirection::CONSTREF) {
|
if (nodep->lvalue() && nodep->varp()->direction() == VDirection::CONSTREF) {
|
||||||
nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ());
|
nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ());
|
||||||
} else if (nodep->lvalue() && nodep->varp()->isConst() && !m_paramsOnly
|
} else if (nodep->lvalue() && nodep->varp()->isConst() && !m_paramsOnly
|
||||||
&& (!m_ftaskp || !m_ftaskp->isConstructor())
|
&& (!m_ftaskp || !m_ftaskp->isConstructor()) && !VN_IS(m_procedurep, Initial)) {
|
||||||
&& !VN_IS(m_procedurep, Initial)) {
|
|
||||||
// Too loose, but need to allow our generated first assignment
|
// Too loose, but need to allow our generated first assignment
|
||||||
// Move this to a property of the AstInitial block
|
// Move this to a property of the AstInitial block
|
||||||
nodep->v3error("Assigning to const variable: " << nodep->prettyNameQ());
|
nodep->v3error("Assigning to const variable: " << nodep->prettyNameQ());
|
||||||
|
|
|
||||||
|
|
@ -256,10 +256,12 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
||||||
"$tanh" { FL; return yD_TANH; }
|
"$tanh" { FL; return yD_TANH; }
|
||||||
"$test$plusargs" { FL; return yD_TESTPLUSARGS; }
|
"$test$plusargs" { FL; return yD_TESTPLUSARGS; }
|
||||||
"$time" { FL; return yD_TIME; }
|
"$time" { FL; return yD_TIME; }
|
||||||
"$timeskew" { FL; return yaTIMINGSPEC; }
|
|
||||||
"$timeformat" { FL; return yD_TIMEFORMAT; }
|
"$timeformat" { FL; return yD_TIMEFORMAT; }
|
||||||
|
"$timeskew" { FL; return yaTIMINGSPEC; }
|
||||||
"$typename" { FL; return yD_TYPENAME; }
|
"$typename" { FL; return yD_TYPENAME; }
|
||||||
"$ungetc" { FL; return yD_UNGETC; }
|
"$ungetc" { FL; return yD_UNGETC; }
|
||||||
|
"$urandom" { FL; return yD_URANDOM; }
|
||||||
|
"$urandom_range" { FL; return yD_URANDOM_RANGE; }
|
||||||
"$value$plusargs" { FL; return yD_VALUEPLUSARGS; }
|
"$value$plusargs" { FL; return yD_VALUEPLUSARGS; }
|
||||||
"$width" { FL; return yaTIMINGSPEC; }
|
"$width" { FL; return yaTIMINGSPEC; }
|
||||||
"$write" { FL; return yD_WRITE; }
|
"$write" { FL; return yD_WRITE; }
|
||||||
|
|
|
||||||
|
|
@ -788,6 +788,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
||||||
%token<fl> yD_UNIT "$unit"
|
%token<fl> yD_UNIT "$unit"
|
||||||
%token<fl> yD_UNPACKED_DIMENSIONS "$unpacked_dimensions"
|
%token<fl> yD_UNPACKED_DIMENSIONS "$unpacked_dimensions"
|
||||||
%token<fl> yD_UNSIGNED "$unsigned"
|
%token<fl> yD_UNSIGNED "$unsigned"
|
||||||
|
%token<fl> yD_URANDOM "$urandom"
|
||||||
|
%token<fl> yD_URANDOM_RANGE "$urandom_range"
|
||||||
%token<fl> yD_VALUEPLUSARGS "$value$plusargs"
|
%token<fl> yD_VALUEPLUSARGS "$value$plusargs"
|
||||||
%token<fl> yD_WARNING "$warning"
|
%token<fl> yD_WARNING "$warning"
|
||||||
%token<fl> yD_WRITE "$write"
|
%token<fl> yD_WRITE "$write"
|
||||||
|
|
@ -3696,7 +3698,10 @@ system_f_call_or_t<nodep>: // IEEE: part of system_tf_call (can be task or func)
|
||||||
| yD_UNGETC '(' expr ',' expr ')' { $$ = new AstFUngetC($1, $5, $3); } // Arg swap to file first
|
| yD_UNGETC '(' expr ',' expr ')' { $$ = new AstFUngetC($1, $5, $3); } // Arg swap to file first
|
||||||
| yD_UNPACKED_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_UNPK_DIMENSIONS,$3); }
|
| yD_UNPACKED_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_UNPK_DIMENSIONS,$3); }
|
||||||
| yD_UNSIGNED '(' expr ')' { $$ = new AstUnsigned($1,$3); }
|
| yD_UNSIGNED '(' expr ')' { $$ = new AstUnsigned($1,$3); }
|
||||||
| yD_VALUEPLUSARGS '(' expr ',' expr ')' { $$ = new AstValuePlusArgs($1,$3,$5); }
|
| yD_URANDOM '(' expr ')' { $$ = new AstURandom($1); BBUNSUP($1, "Unsupported: Seed on $urandom. Suggest use +verilator+seed+ runtime flag"); }
|
||||||
|
| yD_URANDOM parenE { $$ = new AstURandom($1); }
|
||||||
|
| yD_URANDOM_RANGE '(' expr ',' expr ')' { $$ = new AstURandomRange($1, $3, $5); }
|
||||||
|
| yD_VALUEPLUSARGS '(' expr ',' expr ')' { $$ = new AstValuePlusArgs($1, $3, $5); }
|
||||||
;
|
;
|
||||||
|
|
||||||
elaboration_system_task<nodep>: // IEEE: elaboration_system_task (1800-2009)
|
elaboration_system_task<nodep>: // IEEE: elaboration_system_task (1800-2009)
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ module t(/*AUTOARG*/);
|
||||||
if (0) p.await();
|
if (0) p.await();
|
||||||
if (0) p.suspend();
|
if (0) p.suspend();
|
||||||
if (0) p.resume();
|
if (0) p.resume();
|
||||||
|
// See also t_urandom.pl
|
||||||
p.srandom(0);
|
p.srandom(0);
|
||||||
p.set_randstate(p.get_randstate());
|
p.set_randstate(p.get_randstate());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,4 +4,10 @@
|
||||||
%Error-UNSUPPORTED: t/t_sys_rand_seed.v:14:16: Unsupported: Seed on $random. Suggest use +verilator+seed+ runtime flag
|
%Error-UNSUPPORTED: t/t_sys_rand_seed.v:14:16: Unsupported: Seed on $random. Suggest use +verilator+seed+ runtime flag
|
||||||
14 | valueb = $random(10);
|
14 | valueb = $random(10);
|
||||||
| ^~~~~~~
|
| ^~~~~~~
|
||||||
|
%Error-UNSUPPORTED: t/t_sys_rand_seed.v:16:16: Unsupported: Seed on $urandom. Suggest use +verilator+seed+ runtime flag
|
||||||
|
16 | valuea = $urandom(10);
|
||||||
|
| ^~~~~~~~
|
||||||
|
%Error-UNSUPPORTED: t/t_sys_rand_seed.v:17:16: Unsupported: Seed on $urandom. Suggest use +verilator+seed+ runtime flag
|
||||||
|
17 | valueb = $urandom(10);
|
||||||
|
| ^~~~~~~~
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,9 @@ module t;
|
||||||
valuea = $random(10);
|
valuea = $random(10);
|
||||||
valueb = $random(10);
|
valueb = $random(10);
|
||||||
if (valuea !== valueb) $stop;
|
if (valuea !== valueb) $stop;
|
||||||
|
valuea = $urandom(10);
|
||||||
|
valueb = $urandom(10);
|
||||||
|
if (valuea !== valueb) $stop;
|
||||||
$write("*-* All Finished *-*\n");
|
$write("*-* All Finished *-*\n");
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2020 by Wilson Snyder. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
compile(
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2020 by Wilson Snyder.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
// Methods defined by IEEE:
|
||||||
|
// function int unsigned $urandom [ (int seed ) ] ;
|
||||||
|
// function int unsigned $urandom_range( int unsigned maxval,
|
||||||
|
// int unsigned minval = 0 );
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/);
|
||||||
|
`ifndef VERILATOR
|
||||||
|
`define PROC
|
||||||
|
`endif
|
||||||
|
`ifdef PROC
|
||||||
|
process p;
|
||||||
|
`endif
|
||||||
|
|
||||||
|
int unsigned v1;
|
||||||
|
int unsigned v2;
|
||||||
|
int unsigned v3;
|
||||||
|
string s;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
`ifdef PROC
|
||||||
|
if (p != null) $stop;
|
||||||
|
p = process::self();
|
||||||
|
`endif
|
||||||
|
|
||||||
|
v1 = $urandom;
|
||||||
|
v2 = $urandom;
|
||||||
|
v3 = $urandom();
|
||||||
|
if (v1 == v2 && v1 == v3) $stop; // Possible, but 2^-64
|
||||||
|
|
||||||
|
// Range
|
||||||
|
for (int test = 0; test < 20; ++test) begin
|
||||||
|
v1 = $urandom_range(0, 2);
|
||||||
|
if (v1 != 0 && v1 != 1) $stop;
|
||||||
|
v1 = $urandom_range(2, 0);
|
||||||
|
if (v1 != 0 && v1 != 1) $stop;
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifndef VERILATOR
|
||||||
|
// Seed stability
|
||||||
|
// Note UVM doesn't use $urandom seeding
|
||||||
|
v1 = $urandom(1);
|
||||||
|
v2 = $urandom(1);
|
||||||
|
if (v1 != v2) $stop;
|
||||||
|
v2 = $urandom(1);
|
||||||
|
if (v1 != v2) $stop;
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifdef PROC
|
||||||
|
// Seed stability via process.srandom
|
||||||
|
p.srandom(1);
|
||||||
|
v1 = $urandom();
|
||||||
|
p.srandom(1);
|
||||||
|
v2 = $urandom();
|
||||||
|
if (v1 != v2) $stop;
|
||||||
|
p.srandom(1);
|
||||||
|
v2 = $urandom();
|
||||||
|
if (v1 != v2) $stop;
|
||||||
|
|
||||||
|
// Seed stability via process.get_randstate
|
||||||
|
s = p.get_randstate();
|
||||||
|
v1 = $urandom();
|
||||||
|
p.set_randstate(s);
|
||||||
|
v2 = $urandom();
|
||||||
|
if (v1 != v2) $stop;
|
||||||
|
p.set_randstate(s);
|
||||||
|
v2 = $urandom();
|
||||||
|
if (v1 != v2) $stop;
|
||||||
|
`endif
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
Loading…
Reference in New Issue