forked from github/verilator
Support class srandom and class random stability.
This commit is contained in:
parent
28eadded87
commit
69121633cf
1
Changes
1
Changes
@ -17,6 +17,7 @@ Verilator 5.009 devel
|
|||||||
* Add --public-params flag (#3990). [Andrew Nolte]
|
* Add --public-params flag (#3990). [Andrew Nolte]
|
||||||
* Add STATICVAR warning and convert to automatic (#4018) (#4027) (#4030). [Ryszard Rozak, Antmicro Ltd]
|
* Add STATICVAR warning and convert to automatic (#4018) (#4027) (#4030). [Ryszard Rozak, Antmicro Ltd]
|
||||||
* Support class extends of package::class.
|
* Support class extends of package::class.
|
||||||
|
* Support class srandom and class random stability.
|
||||||
* Support method calls without parenthesis (#4034). [Ryszard Rozak, Antmicro Ltd]
|
* Support method calls without parenthesis (#4034). [Ryszard Rozak, Antmicro Ltd]
|
||||||
* Support complicated IEEE 'for' assignments.
|
* Support complicated IEEE 'for' assignments.
|
||||||
* Support $fopen as an expression.
|
* Support $fopen as an expression.
|
||||||
|
@ -279,6 +279,52 @@ void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
|
|||||||
//===========================================================================
|
//===========================================================================
|
||||||
// Random -- Mostly called at init time, so not inline.
|
// Random -- Mostly called at init time, so not inline.
|
||||||
|
|
||||||
|
VlRNG::VlRNG() VL_MT_SAFE {
|
||||||
|
// Starting point for this new class comes from the global RNG
|
||||||
|
VlRNG& fromr = vl_thread_rng();
|
||||||
|
m_state = fromr.m_state;
|
||||||
|
// Advance the *source* so it can later generate a new number
|
||||||
|
// Xoroshiro128+ algorithm
|
||||||
|
fromr.m_state[1] ^= fromr.m_state[0];
|
||||||
|
fromr.m_state[0] = (((fromr.m_state[0] << 55) | (fromr.m_state[0] >> 9)) ^ fromr.m_state[1]
|
||||||
|
^ (fromr.m_state[1] << 14));
|
||||||
|
fromr.m_state[1] = (fromr.m_state[1] << 36) | (fromr.m_state[1] >> 28);
|
||||||
|
}
|
||||||
|
uint64_t VlRNG::rand64() VL_MT_UNSAFE {
|
||||||
|
// Xoroshiro128+ algorithm
|
||||||
|
const uint64_t result = m_state[0] + m_state[1];
|
||||||
|
m_state[1] ^= m_state[0];
|
||||||
|
m_state[0] = (((m_state[0] << 55) | (m_state[0] >> 9)) ^ m_state[1] ^ (m_state[1] << 14));
|
||||||
|
m_state[1] = (m_state[1] << 36) | (m_state[1] >> 28);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
uint64_t VlRNG::vl_thread_rng_rand64() VL_MT_SAFE {
|
||||||
|
VlRNG& fromr = vl_thread_rng();
|
||||||
|
const uint64_t result = fromr.m_state[0] + fromr.m_state[1];
|
||||||
|
fromr.m_state[1] ^= fromr.m_state[0];
|
||||||
|
fromr.m_state[0] = (((fromr.m_state[0] << 55) | (fromr.m_state[0] >> 9)) ^ fromr.m_state[1]
|
||||||
|
^ (fromr.m_state[1] << 14));
|
||||||
|
fromr.m_state[1] = (fromr.m_state[1] << 36) | (fromr.m_state[1] >> 28);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
void VlRNG::srandom(uint64_t n) VL_MT_UNSAFE {
|
||||||
|
m_state[0] = n;
|
||||||
|
m_state[1] = m_state[0];
|
||||||
|
// Fix state as algorithm is slow to randomize if many zeros
|
||||||
|
// This causes a loss of ~ 1 bit of seed entropy, no big deal
|
||||||
|
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 out{reinterpret_cast<const char *>(&m_state), sizeof(m_state)};
|
||||||
|
// Unused: return out;
|
||||||
|
// Unused: }
|
||||||
|
|
||||||
static uint32_t vl_sys_rand32() VL_MT_SAFE {
|
static uint32_t vl_sys_rand32() VL_MT_SAFE {
|
||||||
// Return random 32-bits using system library.
|
// Return random 32-bits using system library.
|
||||||
// Used only to construct seed for Verilator's PRNG.
|
// Used only to construct seed for Verilator's PRNG.
|
||||||
@ -292,27 +338,23 @@ static uint32_t vl_sys_rand32() VL_MT_SAFE {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t vl_rand64() VL_MT_SAFE {
|
VlRNG& VlRNG::vl_thread_rng() VL_MT_SAFE {
|
||||||
static thread_local uint64_t t_state[2];
|
static thread_local VlRNG t_rng{0};
|
||||||
static thread_local uint32_t t_seedEpoch = 0;
|
static thread_local uint32_t t_seedEpoch = 0;
|
||||||
// For speed, we use a thread-local epoch number to know when to reseed
|
// For speed, we use a thread-local epoch number to know when to reseed
|
||||||
// A thread always belongs to a single context, so this works out ok
|
// A thread always belongs to a single context, so this works out ok
|
||||||
if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) {
|
if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) {
|
||||||
// Set epoch before state, to avoid race case with new seeding
|
// Set epoch before state, to avoid race case with new seeding
|
||||||
t_seedEpoch = VerilatedContextImp::randSeedEpoch();
|
t_seedEpoch = VerilatedContextImp::randSeedEpoch();
|
||||||
t_state[0] = Verilated::threadContextp()->impp()->randSeedDefault64();
|
// Same as srandom() but here as needs to be VL_MT_SAFE
|
||||||
t_state[1] = t_state[0];
|
t_rng.m_state[0] = Verilated::threadContextp()->impp()->randSeedDefault64();
|
||||||
|
t_rng.m_state[1] = t_rng.m_state[0];
|
||||||
// Fix state as algorithm is slow to randomize if many zeros
|
// Fix state as algorithm is slow to randomize if many zeros
|
||||||
// This causes a loss of ~ 1 bit of seed entropy, no big deal
|
// This causes a loss of ~ 1 bit of seed entropy, no big deal
|
||||||
if (VL_COUNTONES_I(t_state[0]) < 10) t_state[0] = ~t_state[0];
|
if (VL_COUNTONES_I(t_rng.m_state[0]) < 10) t_rng.m_state[0] = ~t_rng.m_state[0];
|
||||||
if (VL_COUNTONES_I(t_state[1]) < 10) t_state[1] = ~t_state[1];
|
if (VL_COUNTONES_I(t_rng.m_state[1]) < 10) t_rng.m_state[1] = ~t_rng.m_state[1];
|
||||||
}
|
}
|
||||||
// Xoroshiro128+ algorithm
|
return t_rng;
|
||||||
const uint64_t result = t_state[0] + t_state[1];
|
|
||||||
t_state[1] ^= t_state[0];
|
|
||||||
t_state[0] = (((t_state[0] << 55) | (t_state[0] >> 9)) ^ t_state[1] ^ (t_state[1] << 14));
|
|
||||||
t_state[1] = (t_state[1] << 36) | (t_state[1] >> 28);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
||||||
@ -321,6 +363,12 @@ WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
|||||||
return outwp;
|
return outwp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE {
|
||||||
|
for (int i = 0; i < VL_WORDS_I(obits); ++i) outwp[i] = rngr.rand64();
|
||||||
|
// Last word is unclean
|
||||||
|
return outwp;
|
||||||
|
}
|
||||||
|
|
||||||
IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE {
|
IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE {
|
||||||
// $random - seed is a new seed to apply, then we return new seed
|
// $random - seed is a new seed to apply, then we return new seed
|
||||||
Verilated::threadContextp()->randSeed(static_cast<int>(seedr));
|
Verilated::threadContextp()->randSeed(static_cast<int>(seedr));
|
||||||
|
@ -136,14 +136,34 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
inline std::string VL_TO_STRING(const VlEvent& e) {
|
inline std::string VL_TO_STRING(const VlEvent& e) {
|
||||||
return std::string("triggered=") + (e.isTriggered() ? "true" : "false");
|
return std::string{"triggered="} + (e.isTriggered() ? "true" : "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
//===================================================================
|
//===================================================================
|
||||||
// Shuffle RNG
|
// Random
|
||||||
|
|
||||||
extern uint64_t vl_rand64() VL_MT_SAFE;
|
// Random Number Generator with internal state
|
||||||
|
class VlRNG final {
|
||||||
|
std::array<uint64_t, 2> m_state;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// The default constructor simply sets state, to avoid vl_rand64()
|
||||||
|
// having to check for construction at each call
|
||||||
|
// Alternative: seed with zero and check on rand64() call
|
||||||
|
VlRNG() VL_MT_SAFE;
|
||||||
|
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;
|
||||||
|
uint64_t rand64() VL_MT_UNSAFE;
|
||||||
|
// Threadsafe, but requires use on vl_thread_rng
|
||||||
|
static uint64_t vl_thread_rng_rand64() VL_MT_SAFE;
|
||||||
|
static VlRNG& vl_thread_rng() VL_MT_SAFE;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline uint64_t vl_rand64() VL_MT_SAFE { return VlRNG::vl_thread_rng_rand64(); }
|
||||||
|
|
||||||
|
// RNG for shuffle()
|
||||||
class VlURNG final {
|
class VlURNG final {
|
||||||
public:
|
public:
|
||||||
using result_type = size_t;
|
using result_type = size_t;
|
||||||
@ -152,6 +172,11 @@ public:
|
|||||||
size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
|
size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// These require the class object to have the thread safety lock
|
||||||
|
inline IData VL_RANDOM_RNG_I(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); }
|
||||||
|
inline QData VL_RANDOM_RNG_Q(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); }
|
||||||
|
extern WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE;
|
||||||
|
|
||||||
//===================================================================
|
//===================================================================
|
||||||
// Readmem/Writemem operation classes
|
// Readmem/Writemem operation classes
|
||||||
|
|
||||||
|
@ -1620,6 +1620,26 @@ public:
|
|||||||
bool reset() const { return m_reset; }
|
bool reset() const { return m_reset; }
|
||||||
bool urandom() const { return m_urandom; }
|
bool urandom() const { return m_urandom; }
|
||||||
};
|
};
|
||||||
|
class AstRandRNG final : public AstNodeExpr {
|
||||||
|
// Random used in a class using VlRNG
|
||||||
|
// Return a random number, based upon width()
|
||||||
|
public:
|
||||||
|
AstRandRNG(FileLine* fl, AstNodeDType* dtp)
|
||||||
|
: ASTGEN_SUPER_RandRNG(fl) {
|
||||||
|
dtypep(dtp);
|
||||||
|
}
|
||||||
|
ASTGEN_MEMBERS_AstRandRNG;
|
||||||
|
string emitVerilog() override { return "%f$rngrandom()"; }
|
||||||
|
string emitC() override {
|
||||||
|
return isWide() ? "VL_RANDOM_RNG_%nq(__Vm_rng, %nw, %P)" //
|
||||||
|
: "VL_RANDOM_RNG_%nq(__Vm_rng)";
|
||||||
|
}
|
||||||
|
bool cleanOut() const override { return false; }
|
||||||
|
bool isGateOptimizable() const override { return false; }
|
||||||
|
bool isPredictOptimizable() const override { return false; }
|
||||||
|
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||||
|
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||||
|
};
|
||||||
class AstRose final : public AstNodeExpr {
|
class AstRose final : public AstNodeExpr {
|
||||||
// Verilog $rose
|
// Verilog $rose
|
||||||
// @astgen op1 := exprp : AstNodeExpr
|
// @astgen op1 := exprp : AstNodeExpr
|
||||||
|
@ -2116,6 +2116,7 @@ class AstClass final : public AstNodeModule {
|
|||||||
AstClassPackage* m_classOrPackagep = nullptr; // Class package this is under
|
AstClassPackage* m_classOrPackagep = nullptr; // Class package this is under
|
||||||
bool m_extended = false; // Is extension or extended by other classes
|
bool m_extended = false; // Is extension or extended by other classes
|
||||||
bool m_interfaceClass = false; // Interface class
|
bool m_interfaceClass = false; // Interface class
|
||||||
|
bool m_needRNG = false; // Need RNG, uses srandom/randomize
|
||||||
bool m_virtual = false; // Virtual class
|
bool m_virtual = false; // Virtual class
|
||||||
void insertCache(AstNode* nodep);
|
void insertCache(AstNode* nodep);
|
||||||
|
|
||||||
@ -2148,9 +2149,13 @@ public:
|
|||||||
void isInterfaceClass(bool flag) { m_interfaceClass = flag; }
|
void isInterfaceClass(bool flag) { m_interfaceClass = flag; }
|
||||||
bool isVirtual() const { return m_virtual; }
|
bool isVirtual() const { return m_virtual; }
|
||||||
void isVirtual(bool flag) { m_virtual = flag; }
|
void isVirtual(bool flag) { m_virtual = flag; }
|
||||||
|
bool needRNG() const { return m_needRNG; }
|
||||||
|
void needRNG(bool flag) { m_needRNG = flag; }
|
||||||
// Return true if this class is an extension of base class (SLOW)
|
// Return true if this class is an extension of base class (SLOW)
|
||||||
// Accepts nullptrs
|
// Accepts nullptrs
|
||||||
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
|
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
|
||||||
|
// Return the lowest class extended from, or this class
|
||||||
|
AstClass* baseMostClassp();
|
||||||
};
|
};
|
||||||
class AstClassPackage final : public AstNodeModule {
|
class AstClassPackage final : public AstNodeModule {
|
||||||
// The static information portion of a class (treated similarly to a package)
|
// The static information portion of a class (treated similarly to a package)
|
||||||
|
@ -1455,6 +1455,13 @@ void AstClass::repairCache() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
AstClass* AstClass::baseMostClassp() {
|
||||||
|
AstClass* basep = this;
|
||||||
|
while (basep->extendsp() && basep->extendsp()->classp()) {
|
||||||
|
basep = basep->extendsp()->classp();
|
||||||
|
}
|
||||||
|
return basep;
|
||||||
|
}
|
||||||
bool AstClass::isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp) {
|
bool AstClass::isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp) {
|
||||||
// TAIL RECURSIVE
|
// TAIL RECURSIVE
|
||||||
if (!refClassp || !baseClassp) return false;
|
if (!refClassp || !baseClassp) return false;
|
||||||
|
@ -905,6 +905,9 @@ public:
|
|||||||
void visit(AstRand* nodep) override {
|
void visit(AstRand* nodep) override {
|
||||||
emitOpName(nodep, nodep->emitC(), nodep->seedp(), nullptr, nullptr);
|
emitOpName(nodep, nodep->emitC(), nodep->seedp(), nullptr, nullptr);
|
||||||
}
|
}
|
||||||
|
void visit(AstRandRNG* nodep) override {
|
||||||
|
emitOpName(nodep, nodep->emitC(), nullptr, nullptr, nullptr);
|
||||||
|
}
|
||||||
void visit(AstTime* nodep) override {
|
void visit(AstTime* nodep) override {
|
||||||
puts("VL_TIME_UNITED_Q(");
|
puts("VL_TIME_UNITED_Q(");
|
||||||
if (nodep->timeunit().isNone()) nodep->v3fatalSrc("$time has no units");
|
if (nodep->timeunit().isNone()) nodep->v3fatalSrc("$time has no units");
|
||||||
|
@ -117,7 +117,12 @@ class EmitCHeader final : public EmitCConstInit {
|
|||||||
emitCurrentList();
|
emitCurrentList();
|
||||||
}
|
}
|
||||||
void emitInternalVarDecls(const AstNodeModule* modp) {
|
void emitInternalVarDecls(const AstNodeModule* modp) {
|
||||||
if (!VN_IS(modp, Class)) {
|
if (const AstClass* const classp = VN_CAST(modp, Class)) {
|
||||||
|
if (classp->needRNG()) {
|
||||||
|
putsDecoration("\n// INTERNAL VARIABLES\n");
|
||||||
|
puts("VlRNG __Vm_rng;\n");
|
||||||
|
}
|
||||||
|
} else { // not class
|
||||||
putsDecoration("\n// INTERNAL VARIABLES\n");
|
putsDecoration("\n// INTERNAL VARIABLES\n");
|
||||||
puts(symClassName() + "* const vlSymsp;\n");
|
puts(symClassName() + "* const vlSymsp;\n");
|
||||||
}
|
}
|
||||||
|
@ -3102,6 +3102,8 @@ private:
|
|||||||
}
|
}
|
||||||
} else if (VN_IS(nodep, New) && m_statep->forPrearray()) {
|
} else if (VN_IS(nodep, New) && m_statep->forPrearray()) {
|
||||||
// Resolved in V3Width
|
// Resolved in V3Width
|
||||||
|
} else if (nodep->name() == "randomize" || nodep->name() == "srandom") {
|
||||||
|
// Resolved in V3Width
|
||||||
} else if (nodep->dotted() == "") {
|
} else if (nodep->dotted() == "") {
|
||||||
if (nodep->pli()) {
|
if (nodep->pli()) {
|
||||||
if (v3Global.opt.bboxSys()) {
|
if (v3Global.opt.bboxSys()) {
|
||||||
|
@ -115,7 +115,7 @@ private:
|
|||||||
if (m_underGenerate) nodep->underGenerate(true);
|
if (m_underGenerate) nodep->underGenerate(true);
|
||||||
// Remember the existing symbol table scope
|
// Remember the existing symbol table scope
|
||||||
if (m_classp) {
|
if (m_classp) {
|
||||||
if (nodep->name() == "randomize") {
|
if (nodep->name() == "randomize" || nodep->name() == "srandom") {
|
||||||
nodep->v3error(nodep->prettyNameQ()
|
nodep->v3error(nodep->prettyNameQ()
|
||||||
<< " is a predefined class method; redefinition not allowed"
|
<< " is a predefined class method; redefinition not allowed"
|
||||||
" (IEEE 1800-2017 18.6.3)");
|
" (IEEE 1800-2017 18.6.3)");
|
||||||
|
@ -287,6 +287,10 @@ private:
|
|||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
checkNode(nodep);
|
checkNode(nodep);
|
||||||
}
|
}
|
||||||
|
void visit(AstRandRNG* nodep) override {
|
||||||
|
iterateChildren(nodep);
|
||||||
|
checkNode(nodep);
|
||||||
|
}
|
||||||
void visit(AstUCFunc* nodep) override {
|
void visit(AstUCFunc* nodep) override {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
checkNode(nodep);
|
checkNode(nodep);
|
||||||
|
@ -188,15 +188,15 @@ private:
|
|||||||
AstVarRef* const tabRefp
|
AstVarRef* const tabRefp
|
||||||
= new AstVarRef{fl, enumValueTabp(enumDtp), VAccess::READ};
|
= new AstVarRef{fl, enumValueTabp(enumDtp), VAccess::READ};
|
||||||
tabRefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
tabRefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
||||||
AstRand* const randp = new AstRand{fl, nullptr, false};
|
AstRandRNG* const randp
|
||||||
|
= new AstRandRNG{fl, varrefp->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||||
AstNodeExpr* const moddivp = new AstModDiv{
|
AstNodeExpr* const moddivp = new AstModDiv{
|
||||||
fl, randp, new AstConst{fl, static_cast<uint32_t>(enumDtp->itemCount())}};
|
fl, randp, new AstConst{fl, static_cast<uint32_t>(enumDtp->itemCount())}};
|
||||||
randp->dtypep(varrefp->findBasicDType(VBasicDTypeKwd::UINT32));
|
|
||||||
moddivp->dtypep(enumDtp);
|
moddivp->dtypep(enumDtp);
|
||||||
valp = new AstArraySel{fl, tabRefp, moddivp};
|
valp = new AstArraySel{fl, tabRefp, moddivp};
|
||||||
} else {
|
} else {
|
||||||
valp = new AstRand{fl, nullptr, false};
|
valp = new AstRandRNG{fl,
|
||||||
valp->dtypep(memberp ? memberp->dtypep() : varrefp->varp()->dtypep());
|
(memberp ? memberp->dtypep() : varrefp->varp()->dtypep())};
|
||||||
}
|
}
|
||||||
return new AstAssign{fl,
|
return new AstAssign{fl,
|
||||||
new AstSel{fl, varrefp, offset + (memberp ? memberp->lsb() : 0),
|
new AstSel{fl, varrefp, offset + (memberp ? memberp->lsb() : 0),
|
||||||
@ -374,6 +374,7 @@ void V3Randomize::randomizeNetlist(AstNetlist* nodep) {
|
|||||||
AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
|
AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
|
||||||
AstFunc* funcp = VN_AS(nodep->findMember("randomize"), Func);
|
AstFunc* funcp = VN_AS(nodep->findMember("randomize"), Func);
|
||||||
if (!funcp) {
|
if (!funcp) {
|
||||||
|
v3Global.useRandomizeMethods(true);
|
||||||
AstNodeDType* const dtypep
|
AstNodeDType* const dtypep
|
||||||
= nodep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says int return of 0/1
|
= nodep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says int return of 0/1
|
||||||
AstVar* const fvarp = new AstVar{nodep->fileline(), VVarType::MEMBER, "randomize", dtypep};
|
AstVar* const fvarp = new AstVar{nodep->fileline(), VVarType::MEMBER, "randomize", dtypep};
|
||||||
@ -387,6 +388,31 @@ AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
|
|||||||
funcp->isVirtual(nodep->isExtended());
|
funcp->isVirtual(nodep->isExtended());
|
||||||
nodep->addMembersp(funcp);
|
nodep->addMembersp(funcp);
|
||||||
nodep->repairCache();
|
nodep->repairCache();
|
||||||
|
AstClass* const basep = nodep->baseMostClassp();
|
||||||
|
basep->needRNG(true);
|
||||||
|
}
|
||||||
|
return funcp;
|
||||||
|
}
|
||||||
|
|
||||||
|
AstFunc* V3Randomize::newSRandomFunc(AstClass* nodep) {
|
||||||
|
AstClass* const basep = nodep->baseMostClassp();
|
||||||
|
AstFunc* funcp = VN_AS(basep->findMember("srandom"), Func);
|
||||||
|
if (!funcp) {
|
||||||
|
v3Global.useRandomizeMethods(true);
|
||||||
|
AstNodeDType* const dtypep
|
||||||
|
= basep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says argument 0/1
|
||||||
|
AstVar* const ivarp = new AstVar{basep->fileline(), VVarType::MEMBER, "seed", dtypep};
|
||||||
|
ivarp->lifetime(VLifetime::AUTOMATIC);
|
||||||
|
ivarp->funcLocal(true);
|
||||||
|
ivarp->direction(VDirection::INPUT);
|
||||||
|
funcp = new AstFunc{basep->fileline(), "srandom", ivarp, nullptr};
|
||||||
|
funcp->dtypep(basep->findVoidDType());
|
||||||
|
funcp->classMethod(true);
|
||||||
|
funcp->isVirtual(false);
|
||||||
|
basep->addMembersp(funcp);
|
||||||
|
basep->repairCache();
|
||||||
|
funcp->addStmtsp(new AstCStmt{basep->fileline(), "__Vm_rng.srandom(seed);\n"});
|
||||||
|
basep->needRNG(true);
|
||||||
}
|
}
|
||||||
return funcp;
|
return funcp;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ public:
|
|||||||
static void randomizeNetlist(AstNetlist* nodep);
|
static void randomizeNetlist(AstNetlist* nodep);
|
||||||
|
|
||||||
static AstFunc* newRandomizeFunc(AstClass* nodep);
|
static AstFunc* newRandomizeFunc(AstClass* nodep);
|
||||||
|
static AstFunc* newSRandomFunc(AstClass* nodep);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // Guard
|
#endif // Guard
|
||||||
|
@ -224,6 +224,7 @@ private:
|
|||||||
|
|
||||||
// STATE
|
// STATE
|
||||||
WidthVP* m_vup = nullptr; // Current node state
|
WidthVP* m_vup = nullptr; // Current node state
|
||||||
|
AstClass* m_classp = nullptr; // Current class
|
||||||
const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
|
const AstCell* m_cellp = nullptr; // Current cell for arrayed instantiations
|
||||||
const AstEnumItem* m_enumItemp = nullptr; // Current enum item
|
const AstEnumItem* m_enumItemp = nullptr; // Current enum item
|
||||||
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||||
@ -2596,6 +2597,8 @@ private:
|
|||||||
if (nodep->didWidthAndSet()) return;
|
if (nodep->didWidthAndSet()) return;
|
||||||
// Must do extends first, as we may in functions under this class
|
// Must do extends first, as we may in functions under this class
|
||||||
// start following a tree of extends that takes us to other classes
|
// start following a tree of extends that takes us to other classes
|
||||||
|
VL_RESTORER(m_classp);
|
||||||
|
m_classp = nodep;
|
||||||
userIterateAndNext(nodep->extendsp(), nullptr);
|
userIterateAndNext(nodep->extendsp(), nullptr);
|
||||||
userIterateChildren(nodep, nullptr); // First size all members
|
userIterateChildren(nodep, nullptr); // First size all members
|
||||||
nodep->repairCache();
|
nodep->repairCache();
|
||||||
@ -2611,6 +2614,7 @@ private:
|
|||||||
// userIterateChildren(nodep->classp(), nullptr);
|
// userIterateChildren(nodep->classp(), nullptr);
|
||||||
}
|
}
|
||||||
void visit(AstNodeModule* nodep) override {
|
void visit(AstNodeModule* nodep) override {
|
||||||
|
// Visitor does not include AstClass - specialized visitor above
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
m_modp = nodep;
|
m_modp = nodep;
|
||||||
userIterateChildren(nodep, nullptr);
|
userIterateChildren(nodep, nullptr);
|
||||||
@ -3460,8 +3464,9 @@ private:
|
|||||||
// No need to width-resolve the class, as it was done when we did the child
|
// No need to width-resolve the class, as it was done when we did the child
|
||||||
AstClass* const first_classp = adtypep->classp();
|
AstClass* const first_classp = adtypep->classp();
|
||||||
if (nodep->name() == "randomize") {
|
if (nodep->name() == "randomize") {
|
||||||
v3Global.useRandomizeMethods(true);
|
|
||||||
V3Randomize::newRandomizeFunc(first_classp);
|
V3Randomize::newRandomizeFunc(first_classp);
|
||||||
|
} else if (nodep->name() == "srandom") {
|
||||||
|
V3Randomize::newSRandomFunc(first_classp);
|
||||||
}
|
}
|
||||||
UASSERT_OBJ(first_classp, nodep, "Unlinked");
|
UASSERT_OBJ(first_classp, nodep, "Unlinked");
|
||||||
for (AstClass* classp = first_classp; classp;) {
|
for (AstClass* classp = first_classp; classp;) {
|
||||||
@ -5504,6 +5509,21 @@ private:
|
|||||||
// For arguments, is assignment-like context; see IEEE rules in AstNodeAssign
|
// For arguments, is assignment-like context; see IEEE rules in AstNodeAssign
|
||||||
// Function hasn't been widthed, so make it so.
|
// Function hasn't been widthed, so make it so.
|
||||||
UINFO(5, " FTASKREF " << nodep << endl);
|
UINFO(5, " FTASKREF " << nodep << endl);
|
||||||
|
if (nodep->name() == "randomize" || nodep->name() == "srandom") {
|
||||||
|
// TODO perhaps this should move to V3LinkDot
|
||||||
|
if (!m_classp) {
|
||||||
|
nodep->v3error("Calling implicit class method " << nodep->prettyNameQ()
|
||||||
|
<< " without being under class");
|
||||||
|
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
|
||||||
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (nodep->name() == "randomize") {
|
||||||
|
nodep->taskp(V3Randomize::newRandomizeFunc(m_classp));
|
||||||
|
} else {
|
||||||
|
nodep->taskp(V3Randomize::newSRandomFunc(m_classp));
|
||||||
|
}
|
||||||
|
}
|
||||||
UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked");
|
UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked");
|
||||||
if (nodep->didWidth()) return;
|
if (nodep->didWidth()) return;
|
||||||
userIterate(nodep->taskp(), nullptr);
|
userIterate(nodep->taskp(), nullptr);
|
||||||
|
@ -4,4 +4,7 @@
|
|||||||
%Error: t/t_randomize_method_bad.v:14:18: 'randomize' is a predefined class method; redefinition not allowed (IEEE 1800-2017 18.6.3)
|
%Error: t/t_randomize_method_bad.v:14:18: 'randomize' is a predefined class method; redefinition not allowed (IEEE 1800-2017 18.6.3)
|
||||||
14 | function void randomize(int x);
|
14 | function void randomize(int x);
|
||||||
| ^~~~~~~~~
|
| ^~~~~~~~~
|
||||||
|
%Error: t/t_randomize_method_bad.v:16:18: 'srandom' is a predefined class method; redefinition not allowed (IEEE 1800-2017 18.6.3)
|
||||||
|
16 | function void srandom(int seed);
|
||||||
|
| ^~~~~~~
|
||||||
%Error: Exiting due to
|
%Error: Exiting due to
|
||||||
|
@ -13,6 +13,8 @@ endclass
|
|||||||
class Cls2;
|
class Cls2;
|
||||||
function void randomize(int x);
|
function void randomize(int x);
|
||||||
endfunction
|
endfunction
|
||||||
|
function void srandom(int seed);
|
||||||
|
endfunction
|
||||||
endclass
|
endclass
|
||||||
|
|
||||||
module t (/*AUTOARG*/);
|
module t (/*AUTOARG*/);
|
||||||
|
9
test_regress/t/t_randomize_method_nclass_bad.out
Normal file
9
test_regress/t/t_randomize_method_nclass_bad.out
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
%Error: t/t_randomize_method_nclass_bad.v:9:7: Calling implicit class method 'randomize' without being under class
|
||||||
|
: ... In instance t
|
||||||
|
9 | randomize(1);
|
||||||
|
| ^~~~~~~~~
|
||||||
|
%Error: t/t_randomize_method_nclass_bad.v:10:7: Calling implicit class method 'srandom' without being under class
|
||||||
|
: ... In instance t
|
||||||
|
10 | srandom(1);
|
||||||
|
| ^~~~~~~
|
||||||
|
%Error: Exiting due to
|
19
test_regress/t/t_randomize_method_nclass_bad.pl
Executable file
19
test_regress/t/t_randomize_method_nclass_bad.pl
Executable 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(linter => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
fails => $Self->{vlt_all},
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
12
test_regress/t/t_randomize_method_nclass_bad.v
Normal file
12
test_regress/t/t_randomize_method_nclass_bad.v
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/);
|
||||||
|
initial begin
|
||||||
|
randomize(1);
|
||||||
|
srandom(1);
|
||||||
|
end
|
||||||
|
endmodule
|
21
test_regress/t/t_randomize_srandom.pl
Executable file
21
test_regress/t/t_randomize_srandom.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;
|
106
test_regress/t/t_randomize_srandom.v
Normal file
106
test_regress/t/t_randomize_srandom.v
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
`define stop $stop
|
||||||
|
`define checkeq(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
|
||||||
|
`define checkne(gotv,expv) do if ((gotv) === (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
|
||||||
|
|
||||||
|
class Cls;
|
||||||
|
bit [63:0] m_sum;
|
||||||
|
rand int m_r;
|
||||||
|
function void hash_init();
|
||||||
|
m_sum = 64'h5aef0c8d_d70a4497;
|
||||||
|
endfunction
|
||||||
|
function void hash(int res);
|
||||||
|
$display(" res %x", res);
|
||||||
|
m_sum = {32'h0, res} ^ {m_sum[62:0], m_sum[63] ^ m_sum[2] ^ m_sum[0]};
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function bit [63:0] test1();
|
||||||
|
Cls o;
|
||||||
|
// Affected by srandom
|
||||||
|
$display(" init for randomize");
|
||||||
|
hash_init;
|
||||||
|
// TODO: Support this.randomize()
|
||||||
|
o = this;
|
||||||
|
void'(o.randomize());
|
||||||
|
hash(m_r);
|
||||||
|
void'(o.randomize());
|
||||||
|
hash(m_r);
|
||||||
|
return m_sum;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function bit [63:0] test2(int seed);
|
||||||
|
$display(" init for seeded randomize");
|
||||||
|
hash_init;
|
||||||
|
this.srandom(seed);
|
||||||
|
void'(this.randomize());
|
||||||
|
hash(m_r);
|
||||||
|
return m_sum;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function bit [63:0] test3(int seed);
|
||||||
|
$display(" init for seeded randomize");
|
||||||
|
hash_init;
|
||||||
|
srandom(seed);
|
||||||
|
void'(randomize());
|
||||||
|
hash(m_r);
|
||||||
|
return m_sum;
|
||||||
|
endfunction
|
||||||
|
endclass
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/);
|
||||||
|
|
||||||
|
Cls ca;
|
||||||
|
Cls cb;
|
||||||
|
|
||||||
|
bit [63:0] sa;
|
||||||
|
bit [63:0] sb;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
// Each class gets different seed from same thread,
|
||||||
|
// so the randomization should be different
|
||||||
|
$display("New");
|
||||||
|
ca = new;
|
||||||
|
cb = new;
|
||||||
|
|
||||||
|
sa = ca.test1();
|
||||||
|
sb = cb.test1();
|
||||||
|
`checkne(sa, sb); // Could false-fail 2^-32
|
||||||
|
|
||||||
|
// Seed the classes to be synced
|
||||||
|
$display("Seed");
|
||||||
|
ca.srandom(123);
|
||||||
|
cb.srandom(123);
|
||||||
|
|
||||||
|
sa = ca.test1();
|
||||||
|
sb = cb.test1();
|
||||||
|
`checkeq(sa, sb);
|
||||||
|
|
||||||
|
// Check using this
|
||||||
|
$display("this.srandom");
|
||||||
|
sa = ca.test2(1);
|
||||||
|
sb = cb.test2(2);
|
||||||
|
`checkne(sa, sb);
|
||||||
|
|
||||||
|
sa = ca.test2(3);
|
||||||
|
sb = cb.test2(3);
|
||||||
|
`checkeq(sa, sb);
|
||||||
|
|
||||||
|
// Check using direct call
|
||||||
|
$display("srandom");
|
||||||
|
sa = ca.test3(1);
|
||||||
|
sb = cb.test3(2);
|
||||||
|
`checkne(sa, sb);
|
||||||
|
|
||||||
|
sa = ca.test3(3);
|
||||||
|
sb = cb.test3(3);
|
||||||
|
`checkeq(sa, sb);
|
||||||
|
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
Loading…
Reference in New Issue
Block a user