Support get_randstate/set_randstate class method function.

This commit is contained in:
Wilson Snyder 2023-05-06 19:09:19 -04:00
parent 250c6950cf
commit a3640c1767
13 changed files with 259 additions and 15 deletions

View File

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

View File

@ -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<const char *>(&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<const char*>(&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<char*>(&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.

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,2 @@
%Warning: set_randstate ignored as state string not from get_randstate
*-* All Finished *-*

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(vlt => 1);
compile(
);
execute(
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

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