forked from github/verilator
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 $urandom, $urandom_range without stability.
|
||||
|
||||
**** Fix false DECLFILENAME on black-boxed modules (#2430). [Philipp Wagner]
|
||||
|
||||
**** 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
|
||||
similar.
|
||||
|
||||
=item $random
|
||||
=item $random, $urandom, $urandom_range
|
||||
|
||||
$random does not support the optional argument to set the seed. Use the
|
||||
srand function in C to accomplish this, and note there is only one random
|
||||
number generator (not one per module).
|
||||
$random and $urandom do not support the optional argument to set the seed.
|
||||
Use +verilator+seed argument to set the seed. There is one random seed per
|
||||
C thread, not per module for $random, nor per object for random stability
|
||||
of $urandom/$urandom_range.
|
||||
|
||||
=item $readmemb, $readmemh
|
||||
|
||||
|
@ -315,8 +315,6 @@ vluint64_t vl_rand64() VL_MT_SAFE {
|
||||
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
|
||||
// LCOV_EXCL_START
|
||||
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
|
||||
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 QData VL_RANDOM_Q(int obits); ///< Randomize a signal
|
||||
extern vluint64_t vl_rand64() VL_MT_SAFE;
|
||||
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
|
||||
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
|
||||
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; }
|
||||
};
|
||||
|
||||
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 {
|
||||
VTimescale m_timeunit; // Parent module time unit
|
||||
public:
|
||||
|
@ -2209,8 +2209,8 @@ private:
|
||||
|
||||
TREEOP1("AstSel{warnSelect(nodep)}", "NEVER");
|
||||
// Generic constants on both side. Do this first to avoid other replacements
|
||||
TREEOPC("AstNodeBiop {$lhsp.castConst, $rhsp.castConst}", "replaceConst(nodep)");
|
||||
TREEOPC("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque()}", "replaceConst(nodep)");
|
||||
TREEOPC("AstNodeBiop {$lhsp.castConst, $rhsp.castConst, nodep->isPredictOptimizable()}", "replaceConst(nodep)");
|
||||
TREEOPC("AstNodeUniop{$lhsp.castConst, !nodep->isOpaque(), nodep->isPredictOptimizable()}", "replaceConst(nodep)");
|
||||
TREEOPC("AstNodeQuadop{$lhsp.castConst, $rhsp.castConst, $thsp.castConst, $fhsp.castConst}", "replaceConst(nodep)");
|
||||
// Zero on one side or the other
|
||||
TREEOP ("AstAdd {$lhsp.isZero, $rhsp}", "replaceWRhs(nodep)");
|
||||
|
@ -1101,6 +1101,19 @@ private:
|
||||
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 {
|
||||
nodep->dtypeSetSigned32(); // Used in int context
|
||||
if (!VN_IS(nodep->backp(), IsUnbounded) && !VN_IS(nodep->backp(), BracketArrayDType)
|
||||
@ -1793,8 +1806,7 @@ private:
|
||||
if (nodep->lvalue() && nodep->varp()->direction() == VDirection::CONSTREF) {
|
||||
nodep->v3error("Assigning to const ref variable: " << nodep->prettyNameQ());
|
||||
} else if (nodep->lvalue() && nodep->varp()->isConst() && !m_paramsOnly
|
||||
&& (!m_ftaskp || !m_ftaskp->isConstructor())
|
||||
&& !VN_IS(m_procedurep, Initial)) {
|
||||
&& (!m_ftaskp || !m_ftaskp->isConstructor()) && !VN_IS(m_procedurep, Initial)) {
|
||||
// Too loose, but need to allow our generated first assignment
|
||||
// Move this to a property of the AstInitial block
|
||||
nodep->v3error("Assigning to const variable: " << nodep->prettyNameQ());
|
||||
|
@ -256,10 +256,12 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
||||
"$tanh" { FL; return yD_TANH; }
|
||||
"$test$plusargs" { FL; return yD_TESTPLUSARGS; }
|
||||
"$time" { FL; return yD_TIME; }
|
||||
"$timeskew" { FL; return yaTIMINGSPEC; }
|
||||
"$timeformat" { FL; return yD_TIMEFORMAT; }
|
||||
"$timeskew" { FL; return yaTIMINGSPEC; }
|
||||
"$typename" { FL; return yD_TYPENAME; }
|
||||
"$ungetc" { FL; return yD_UNGETC; }
|
||||
"$urandom" { FL; return yD_URANDOM; }
|
||||
"$urandom_range" { FL; return yD_URANDOM_RANGE; }
|
||||
"$value$plusargs" { FL; return yD_VALUEPLUSARGS; }
|
||||
"$width" { FL; return yaTIMINGSPEC; }
|
||||
"$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_UNPACKED_DIMENSIONS "$unpacked_dimensions"
|
||||
%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_WARNING "$warning"
|
||||
%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_UNPACKED_DIMENSIONS '(' exprOrDataType ')' { $$ = new AstAttrOf($1,AstAttrType::DIM_UNPK_DIMENSIONS,$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)
|
||||
|
@ -34,6 +34,7 @@ module t(/*AUTOARG*/);
|
||||
if (0) p.await();
|
||||
if (0) p.suspend();
|
||||
if (0) p.resume();
|
||||
// See also t_urandom.pl
|
||||
p.srandom(0);
|
||||
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
|
||||
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
|
||||
|
@ -13,6 +13,9 @@ module t;
|
||||
valuea = $random(10);
|
||||
valueb = $random(10);
|
||||
if (valuea !== valueb) $stop;
|
||||
valuea = $urandom(10);
|
||||
valueb = $urandom(10);
|
||||
if (valuea !== valueb) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
21
test_regress/t/t_urandom.pl
Executable file
21
test_regress/t/t_urandom.pl
Executable file
@ -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;
|
79
test_regress/t/t_urandom.v
Normal file
79
test_regress/t/t_urandom.v
Normal file
@ -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
Block a user