Support $urandom, $urandom_range without stability.

This commit is contained in:
Wilson Snyder 2020-08-23 08:42:50 -04:00
parent e10156548d
commit f4a72946eb
14 changed files with 198 additions and 14 deletions

View File

@ -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.

View File

@ -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

View File

@ -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 {

View File

@ -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

View File

@ -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:

View File

@ -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)");

View File

@ -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());

View File

@ -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; }

View File

@ -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)

View File

@ -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());

View File

@ -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

View File

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

View 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