From f4a72946eb5901fe7f56f04f4a4ce2a9abc1d2f2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 23 Aug 2020 08:42:50 -0400 Subject: [PATCH] Support $urandom, $urandom_range without stability. --- Changes | 2 + bin/verilator | 9 ++-- include/verilated.cpp | 2 - include/verilated.h | 14 +++++- src/V3AstNodes.h | 44 +++++++++++++++++ src/V3Const.cpp | 4 +- src/V3Width.cpp | 16 +++++- src/verilog.l | 4 +- src/verilog.y | 7 ++- test_regress/t/t_process.v | 1 + test_regress/t/t_sys_rand_seed.out | 6 +++ test_regress/t/t_sys_rand_seed.v | 3 ++ test_regress/t/t_urandom.pl | 21 ++++++++ test_regress/t/t_urandom.v | 79 ++++++++++++++++++++++++++++++ 14 files changed, 198 insertions(+), 14 deletions(-) create mode 100755 test_regress/t/t_urandom.pl create mode 100644 test_regress/t/t_urandom.v diff --git a/Changes b/Changes index dde4f618f..815c49c5b 100644 --- a/Changes +++ b/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. diff --git a/bin/verilator b/bin/verilator index 96f58771e..1a1354666 100755 --- a/bin/verilator +++ b/bin/verilator @@ -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 diff --git a/include/verilated.cpp b/include/verilated.cpp index 4566ef242..895118fca 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -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 { diff --git a/include/verilated.h b/include/verilated.h index b5c3fd173..4cc5cd6ee 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -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 diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index a1aba6567..564f2995f 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -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: diff --git a/src/V3Const.cpp b/src/V3Const.cpp index b93b4ef8b..5a31fe395 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -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)"); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 34cb1d0da..c289ecc1d 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -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()); diff --git a/src/verilog.l b/src/verilog.l index 9c9613988..30020dd94 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -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; } diff --git a/src/verilog.y b/src/verilog.y index 1692b3059..50ba6ebdb 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -788,6 +788,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yD_UNIT "$unit" %token yD_UNPACKED_DIMENSIONS "$unpacked_dimensions" %token yD_UNSIGNED "$unsigned" +%token yD_URANDOM "$urandom" +%token yD_URANDOM_RANGE "$urandom_range" %token yD_VALUEPLUSARGS "$value$plusargs" %token yD_WARNING "$warning" %token yD_WRITE "$write" @@ -3696,7 +3698,10 @@ system_f_call_or_t: // 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: // IEEE: elaboration_system_task (1800-2009) diff --git a/test_regress/t/t_process.v b/test_regress/t/t_process.v index 301cb878b..f6ee27bac 100644 --- a/test_regress/t/t_process.v +++ b/test_regress/t/t_process.v @@ -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()); diff --git a/test_regress/t/t_sys_rand_seed.out b/test_regress/t/t_sys_rand_seed.out index c227b2acd..a9ae64425 100644 --- a/test_regress/t/t_sys_rand_seed.out +++ b/test_regress/t/t_sys_rand_seed.out @@ -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 diff --git a/test_regress/t/t_sys_rand_seed.v b/test_regress/t/t_sys_rand_seed.v index 2780345a3..53dc8eee5 100644 --- a/test_regress/t/t_sys_rand_seed.v +++ b/test_regress/t/t_sys_rand_seed.v @@ -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 diff --git a/test_regress/t/t_urandom.pl b/test_regress/t/t_urandom.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_urandom.pl @@ -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; diff --git a/test_regress/t/t_urandom.v b/test_regress/t/t_urandom.v new file mode 100644 index 000000000..3bb04f64f --- /dev/null +++ b/test_regress/t/t_urandom.v @@ -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