diff --git a/Changes b/Changes index cd1c4dee6..cd044ae68 100644 --- a/Changes +++ b/Changes @@ -18,6 +18,7 @@ Verilator 5.011 devel **Minor:** +* Support get_randstate/set_randstate class method function. * Optimize VPI callValueCbs (#4155). [Hennadii Chernyshchyk] * Fix crash on duplicate imported modules (#3231). [Robert Balas] * Fix false WIDTHEXPAND on array declarations (#3959). [JOTEGO] diff --git a/include/verilated.cpp b/include/verilated.cpp index dc23517cc..29fbf89ea 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -315,15 +315,28 @@ void VlRNG::srandom(uint64_t n) VL_MT_UNSAFE { if (VL_COUNTONES_I(m_state[0]) < 10) m_state[0] = ~m_state[0]; if (VL_COUNTONES_I(m_state[1]) < 10) m_state[1] = ~m_state[1]; } -// Unused: void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE { -// Unused: if (VL_LIKELY(state.length() == sizeof(m_state))) { -// Unused: memcpy(m_state, state.data(), sizeof(m_state)); -// Unused: } -// Unused: } -// Unused: std::string VlRNG::get_randstate() const VL_MT_UNSAFE { -// Unused: std::string result{reinterpret_cast(&m_state), sizeof(m_state)}; -// Unused: return result; -// Unused: } +std::string VlRNG::get_randstate() const VL_MT_UNSAFE { + // Though not stated in IEEE, assumption is the string must be printable + const char* const stateCharsp = reinterpret_cast(&m_state); + static_assert(sizeof(m_state) == 16); + std::string result{"R00112233445566770011223344556677"}; + for (int i = 0; i < sizeof(m_state); ++i) { + result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15); + result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15); + } + return result; +} +void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE { + if (VL_UNLIKELY((state.length() != 1 + 2 * sizeof(m_state)) || (state[0] != 'R'))) { + VL_PRINTF_MT("%%Warning: set_randstate ignored as state string not from get_randstate\n"); + return; + } + char* const stateCharsp = reinterpret_cast(&m_state); + for (int i = 0; i < sizeof(m_state); ++i) { + stateCharsp[i] + = (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15); + } +} static uint32_t vl_sys_rand32() VL_MT_SAFE { // Return random 32-bits using system library. diff --git a/include/verilated_types.h b/include/verilated_types.h index 0214e8fb4..ab871ec84 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -161,8 +161,8 @@ public: VlRNG() VL_MT_SAFE; explicit VlRNG(uint64_t seed0) VL_MT_SAFE : m_state{0x12341234UL, seed0} {} void srandom(uint64_t n) VL_MT_UNSAFE; - // Unused: std::string get_randstate() const VL_MT_UNSAFE; - // Unused: void set_randstate(const std::string& state) VL_MT_UNSAFE; + std::string get_randstate() const VL_MT_UNSAFE; + void set_randstate(const std::string& state) VL_MT_UNSAFE; uint64_t rand64() VL_MT_UNSAFE; // Threadsafe, but requires use on vl_thread_rng static uint64_t vl_thread_rng_rand64() VL_MT_SAFE; diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index dabe702a0..ca1965964 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3110,7 +3110,9 @@ private: } } else if (VN_IS(nodep, New) && m_statep->forPrearray()) { // Resolved in V3Width - } else if (nodep->name() == "randomize" || nodep->name() == "srandom") { + } else if (nodep->name() == "randomize" || nodep->name() == "srandom" + || nodep->name() == "get_randstate" + || nodep->name() == "set_randstate") { // Resolved in V3Width } else if (nodep->dotted() == "") { if (nodep->pli()) { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 0d02e36a0..09f34f325 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2812,6 +2812,10 @@ private: return false; } + void visit(AstCExpr* nodep) override { + // Inserted by V3Width only so we know has been resolved + userIterateChildren(nodep, WidthVP{SELF, BOTH}.p()); + } void visit(AstCMethodHard* nodep) override { // Never created before V3Width, so no need to redo it UASSERT_OBJ(nodep->dtypep(), nodep, "CMETHODCALLs should have already been sized"); @@ -2878,7 +2882,7 @@ private: } return nullptr; } - void methodOkArguments(AstMethodCall* nodep, int minArg, int maxArg) { + void methodOkArguments(AstNodeFTaskRef* nodep, int minArg, int maxArg) { int narg = 0; for (AstNode* argp = nodep->pinsp(); argp; argp = argp->nextp()) { if (VN_IS(argp, With)) { @@ -3522,6 +3526,13 @@ private: processFTaskRefArgs(nodep); } return; + } else if (nodep->name() == "get_randstate" || nodep->name() == "set_randstate") { + // See implementations under AstNodeFTaskRef + nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'get_randstate'/'set_randstate' called " + "on object. Suggest call from inside class."); + nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitTrue{}}); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; } classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr; } @@ -5537,7 +5548,9 @@ private: // For arguments, is assignment-like context; see IEEE rules in AstNodeAssign // Function hasn't been widthed, so make it so. UINFO(5, " FTASKREF " << nodep << endl); - if (nodep->name() == "randomize" || nodep->name() == "srandom") { + if (nodep->name() == "randomize" || nodep->name() == "srandom" + || (!nodep->taskp() + && (nodep->name() == "get_randstate" || nodep->name() == "set_randstate"))) { // TODO perhaps this should move to V3LinkDot if (!m_classp) { nodep->v3error("Calling implicit class method " << nodep->prettyNameQ() @@ -5548,8 +5561,35 @@ private: } if (nodep->name() == "randomize") { nodep->taskp(V3Randomize::newRandomizeFunc(m_classp)); - } else { + } else if (nodep->name() == "srandom") { nodep->taskp(V3Randomize::newSRandomFunc(m_classp)); + } else if (nodep->name() == "get_randstate") { + methodOkArguments(nodep, 0, 0); + m_classp->baseMostClassp()->needRNG(true); + v3Global.useRandomizeMethods(true); + AstCExpr* const newp + = new AstCExpr{nodep->fileline(), "__Vm_rng.get_randstate()", 1, true}; + newp->dtypeSetString(); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } else if (nodep->name() == "set_randstate") { + methodOkArguments(nodep, 1, 1); + AstNodeExpr* const expr1p = VN_AS(nodep->pinsp(), Arg)->exprp(); // May edit + iterateCheckString(nodep, "LHS", expr1p, BOTH); + AstNodeExpr* const exprp = VN_AS(nodep->pinsp(), Arg)->exprp(); + m_classp->baseMostClassp()->needRNG(true); + v3Global.useRandomizeMethods(true); + AstCExpr* const newp + = new AstCExpr{nodep->fileline(), "__Vm_rng.set_randstate(", 1, true}; + newp->addExprsp(exprp->unlinkFrBack()); + newp->addExprsp(new AstText{nodep->fileline(), ")", true}); + newp->dtypeSetString(); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } else { + UASSERT_OBJ(false, nodep, "Bad case"); } } UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked"); diff --git a/test_regress/t/t_randstate_func.pl b/test_regress/t/t_randstate_func.pl new file mode 100755 index 000000000..aabcde63e --- /dev/null +++ b/test_regress/t/t_randstate_func.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_randstate_func.v b/test_regress/t/t_randstate_func.v new file mode 100644 index 000000000..87563d1f4 --- /dev/null +++ b/test_regress/t/t_randstate_func.v @@ -0,0 +1,48 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + rand int length; + + function void test; + automatic int rand_result, v1, v2; + automatic string s; + + // UVM 2023 does a print, so check is ascii + $display("get_randstate = '%s'", get_randstate()); + + s = get_randstate(); + + rand_result = randomize(); + if (rand_result != 1) $stop; + v1 = length; + + set_randstate(s); + + rand_result = randomize(); + if (rand_result != 1) $stop; + v2 = length; + +`ifdef VERILATOR // About half of the other simulators fail at this + if (v1 != v2) $stop; +`endif + endfunction +endclass + +module t(/*AUTOARG*/); + + automatic int rand_result, v1, v2; + automatic string s; + + initial begin + Cls c; + c = new; + c.test; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randstate_obj.out b/test_regress/t/t_randstate_obj.out new file mode 100644 index 000000000..74f858877 --- /dev/null +++ b/test_regress/t/t_randstate_obj.out @@ -0,0 +1,10 @@ +%Error-UNSUPPORTED: t/t_randstate_obj.v:20:13: Unsupported: 'get_randstate'/'set_randstate' called on object. Suggest call from inside class. + : ... In instance t + 20 | s = c.get_randstate(); + | ^~~~~~~~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error-UNSUPPORTED: t/t_randstate_obj.v:26:9: Unsupported: 'get_randstate'/'set_randstate' called on object. Suggest call from inside class. + : ... In instance t + 26 | c.set_randstate(s); + | ^~~~~~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randstate_obj.pl b/test_regress/t/t_randstate_obj.pl new file mode 100755 index 000000000..b50a85167 --- /dev/null +++ b/test_regress/t/t_randstate_obj.pl @@ -0,0 +1,19 @@ +#!/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( + expect_filename => $Self->{golden_filename}, + fails => $Self->{vlt_all}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_randstate_obj.v b/test_regress/t/t_randstate_obj.v new file mode 100644 index 000000000..bc5255913 --- /dev/null +++ b/test_regress/t/t_randstate_obj.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + rand int length; +endclass + +module t(/*AUTOARG*/); + + automatic int rand_result, v1, v2; + automatic string s; + + initial begin + Cls c; + c = new; + + s = c.get_randstate(); + + rand_result = c.randomize(); + if (rand_result != 1) $stop; + v1 = c.length; + + c.set_randstate(s); + + rand_result = c.randomize(); + if (rand_result != 1) $stop; + v2 = c.length; + +`ifdef VERILATOR // About half of the other simulators fail at this + if (v1 != v2) $stop; +`endif + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randstate_seed_bad.out b/test_regress/t/t_randstate_seed_bad.out new file mode 100644 index 000000000..75e3250f6 --- /dev/null +++ b/test_regress/t/t_randstate_seed_bad.out @@ -0,0 +1,2 @@ +%Warning: set_randstate ignored as state string not from get_randstate +*-* All Finished *-* diff --git a/test_regress/t/t_randstate_seed_bad.pl b/test_regress/t/t_randstate_seed_bad.pl new file mode 100755 index 000000000..c7c7ef8ca --- /dev/null +++ b/test_regress/t/t_randstate_seed_bad.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(vlt => 1); + +compile( + ); + +execute( + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_randstate_seed_bad.v b/test_regress/t/t_randstate_seed_bad.v new file mode 100644 index 000000000..ebcf2c0a9 --- /dev/null +++ b/test_regress/t/t_randstate_seed_bad.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + function void test; + automatic string s; + + s = get_randstate(); + // Vlt only result check + if (s[0] !== "R") $fatal(2, $sformatf("Bad get_randstate = '%s'", s)); + + set_randstate("000bad"); // Bad + endfunction +endclass + +module t(/*AUTOARG*/); + + initial begin + Cls c; + c = new; + c.test; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule