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 STATICVAR warning and convert to automatic (#4018) (#4027) (#4030). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support class extends of package::class.
|
||||
* Support class srandom and class random stability.
|
||||
* Support method calls without parenthesis (#4034). [Ryszard Rozak, Antmicro Ltd]
|
||||
* Support complicated IEEE 'for' assignments.
|
||||
* 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.
|
||||
|
||||
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 {
|
||||
// Return random 32-bits using system library.
|
||||
// Used only to construct seed for Verilator's PRNG.
|
||||
@ -292,27 +338,23 @@ static uint32_t vl_sys_rand32() VL_MT_SAFE {
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t vl_rand64() VL_MT_SAFE {
|
||||
static thread_local uint64_t t_state[2];
|
||||
VlRNG& VlRNG::vl_thread_rng() VL_MT_SAFE {
|
||||
static thread_local VlRNG t_rng{0};
|
||||
static thread_local uint32_t t_seedEpoch = 0;
|
||||
// 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
|
||||
if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) {
|
||||
// Set epoch before state, to avoid race case with new seeding
|
||||
t_seedEpoch = VerilatedContextImp::randSeedEpoch();
|
||||
t_state[0] = Verilated::threadContextp()->impp()->randSeedDefault64();
|
||||
t_state[1] = t_state[0];
|
||||
// Same as srandom() but here as needs to be VL_MT_SAFE
|
||||
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
|
||||
// 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_state[1]) < 10) t_state[1] = ~t_state[1];
|
||||
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_rng.m_state[1]) < 10) t_rng.m_state[1] = ~t_rng.m_state[1];
|
||||
}
|
||||
// Xoroshiro128+ algorithm
|
||||
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;
|
||||
return t_rng;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
// $random - seed is a new seed to apply, then we return new seed
|
||||
Verilated::threadContextp()->randSeed(static_cast<int>(seedr));
|
||||
|
@ -136,14 +136,34 @@ public:
|
||||
};
|
||||
|
||||
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 {
|
||||
public:
|
||||
using result_type = size_t;
|
||||
@ -152,6 +172,11 @@ public:
|
||||
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
|
||||
|
||||
|
@ -1620,6 +1620,26 @@ public:
|
||||
bool reset() const { return m_reset; }
|
||||
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 {
|
||||
// Verilog $rose
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
|
@ -2116,6 +2116,7 @@ class AstClass final : public AstNodeModule {
|
||||
AstClassPackage* m_classOrPackagep = nullptr; // Class package this is under
|
||||
bool m_extended = false; // Is extension or extended by other classes
|
||||
bool m_interfaceClass = false; // Interface class
|
||||
bool m_needRNG = false; // Need RNG, uses srandom/randomize
|
||||
bool m_virtual = false; // Virtual class
|
||||
void insertCache(AstNode* nodep);
|
||||
|
||||
@ -2148,9 +2149,13 @@ public:
|
||||
void isInterfaceClass(bool flag) { m_interfaceClass = flag; }
|
||||
bool isVirtual() const { return m_virtual; }
|
||||
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)
|
||||
// Accepts nullptrs
|
||||
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 {
|
||||
// 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) {
|
||||
// TAIL RECURSIVE
|
||||
if (!refClassp || !baseClassp) return false;
|
||||
|
@ -905,6 +905,9 @@ public:
|
||||
void visit(AstRand* nodep) override {
|
||||
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 {
|
||||
puts("VL_TIME_UNITED_Q(");
|
||||
if (nodep->timeunit().isNone()) nodep->v3fatalSrc("$time has no units");
|
||||
|
@ -117,7 +117,12 @@ class EmitCHeader final : public EmitCConstInit {
|
||||
emitCurrentList();
|
||||
}
|
||||
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");
|
||||
puts(symClassName() + "* const vlSymsp;\n");
|
||||
}
|
||||
|
@ -3102,6 +3102,8 @@ private:
|
||||
}
|
||||
} else if (VN_IS(nodep, New) && m_statep->forPrearray()) {
|
||||
// Resolved in V3Width
|
||||
} else if (nodep->name() == "randomize" || nodep->name() == "srandom") {
|
||||
// Resolved in V3Width
|
||||
} else if (nodep->dotted() == "") {
|
||||
if (nodep->pli()) {
|
||||
if (v3Global.opt.bboxSys()) {
|
||||
|
@ -115,7 +115,7 @@ private:
|
||||
if (m_underGenerate) nodep->underGenerate(true);
|
||||
// Remember the existing symbol table scope
|
||||
if (m_classp) {
|
||||
if (nodep->name() == "randomize") {
|
||||
if (nodep->name() == "randomize" || nodep->name() == "srandom") {
|
||||
nodep->v3error(nodep->prettyNameQ()
|
||||
<< " is a predefined class method; redefinition not allowed"
|
||||
" (IEEE 1800-2017 18.6.3)");
|
||||
|
@ -287,6 +287,10 @@ private:
|
||||
iterateChildren(nodep);
|
||||
checkNode(nodep);
|
||||
}
|
||||
void visit(AstRandRNG* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkNode(nodep);
|
||||
}
|
||||
void visit(AstUCFunc* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkNode(nodep);
|
||||
|
@ -188,15 +188,15 @@ private:
|
||||
AstVarRef* const tabRefp
|
||||
= new AstVarRef{fl, enumValueTabp(enumDtp), VAccess::READ};
|
||||
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{
|
||||
fl, randp, new AstConst{fl, static_cast<uint32_t>(enumDtp->itemCount())}};
|
||||
randp->dtypep(varrefp->findBasicDType(VBasicDTypeKwd::UINT32));
|
||||
moddivp->dtypep(enumDtp);
|
||||
valp = new AstArraySel{fl, tabRefp, moddivp};
|
||||
} else {
|
||||
valp = new AstRand{fl, nullptr, false};
|
||||
valp->dtypep(memberp ? memberp->dtypep() : varrefp->varp()->dtypep());
|
||||
valp = new AstRandRNG{fl,
|
||||
(memberp ? memberp->dtypep() : varrefp->varp()->dtypep())};
|
||||
}
|
||||
return new AstAssign{fl,
|
||||
new AstSel{fl, varrefp, offset + (memberp ? memberp->lsb() : 0),
|
||||
@ -374,6 +374,7 @@ void V3Randomize::randomizeNetlist(AstNetlist* nodep) {
|
||||
AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
|
||||
AstFunc* funcp = VN_AS(nodep->findMember("randomize"), Func);
|
||||
if (!funcp) {
|
||||
v3Global.useRandomizeMethods(true);
|
||||
AstNodeDType* const dtypep
|
||||
= nodep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says int return of 0/1
|
||||
AstVar* const fvarp = new AstVar{nodep->fileline(), VVarType::MEMBER, "randomize", dtypep};
|
||||
@ -387,6 +388,31 @@ AstFunc* V3Randomize::newRandomizeFunc(AstClass* nodep) {
|
||||
funcp->isVirtual(nodep->isExtended());
|
||||
nodep->addMembersp(funcp);
|
||||
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;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ public:
|
||||
static void randomizeNetlist(AstNetlist* nodep);
|
||||
|
||||
static AstFunc* newRandomizeFunc(AstClass* nodep);
|
||||
static AstFunc* newSRandomFunc(AstClass* nodep);
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
@ -224,6 +224,7 @@ private:
|
||||
|
||||
// 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 AstEnumItem* m_enumItemp = nullptr; // Current enum item
|
||||
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
@ -2596,6 +2597,8 @@ private:
|
||||
if (nodep->didWidthAndSet()) return;
|
||||
// Must do extends first, as we may in functions under this class
|
||||
// start following a tree of extends that takes us to other classes
|
||||
VL_RESTORER(m_classp);
|
||||
m_classp = nodep;
|
||||
userIterateAndNext(nodep->extendsp(), nullptr);
|
||||
userIterateChildren(nodep, nullptr); // First size all members
|
||||
nodep->repairCache();
|
||||
@ -2611,6 +2614,7 @@ private:
|
||||
// userIterateChildren(nodep->classp(), nullptr);
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
// Visitor does not include AstClass - specialized visitor above
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = nodep;
|
||||
userIterateChildren(nodep, nullptr);
|
||||
@ -3460,8 +3464,9 @@ private:
|
||||
// No need to width-resolve the class, as it was done when we did the child
|
||||
AstClass* const first_classp = adtypep->classp();
|
||||
if (nodep->name() == "randomize") {
|
||||
v3Global.useRandomizeMethods(true);
|
||||
V3Randomize::newRandomizeFunc(first_classp);
|
||||
} else if (nodep->name() == "srandom") {
|
||||
V3Randomize::newSRandomFunc(first_classp);
|
||||
}
|
||||
UASSERT_OBJ(first_classp, nodep, "Unlinked");
|
||||
for (AstClass* classp = first_classp; classp;) {
|
||||
@ -5504,6 +5509,21 @@ 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") {
|
||||
// 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");
|
||||
if (nodep->didWidth()) return;
|
||||
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)
|
||||
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
|
||||
|
@ -13,6 +13,8 @@ endclass
|
||||
class Cls2;
|
||||
function void randomize(int x);
|
||||
endfunction
|
||||
function void srandom(int seed);
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
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