Support randc (#4349).

This commit is contained in:
Wilson Snyder 2023-09-18 21:17:21 -04:00
parent 47b3f464a9
commit 24ff3155ae
18 changed files with 220 additions and 61 deletions

View File

@ -13,6 +13,7 @@ Verilator 5.017 devel
**Minor:**
* Support randc (#4349).
* Support resizing function call inout arguments (#4467).

View File

@ -1360,7 +1360,10 @@ List Of Warnings
.. option:: RANDC
Warns that the :code:`randc` keyword is unsupported and being converted
Historical, never issued since version 5.018, when :code:`randc` became
fully supported.
Warned that the :code:`randc` keyword was unsupported and was converted
to :code:`rand`.

View File

@ -74,8 +74,18 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
#define VL_OUTW(name, msb, lsb, words) VlWide<words> name ///< Declare output signal, 65+ bits
//===================================================================
// VlProcess stores metadata of running processes
// Functions needed here
constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE {
if (VL_UNLIKELY(!lhs)) return 0;
--lhs;
int shifts = 0;
for (; lhs != 0; ++shifts) lhs = lhs >> 1ULL;
return shifts;
}
//===================================================================
// VlProcess stores metadata of running processes
class VlProcess final {
// MEMBERS
int m_state; // Current state of the process
@ -210,6 +220,58 @@ public:
size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
};
template <class T_Value, std::size_t T_numValues>
class VlRandC final {
T_Value m_remaining = 0; // Number of values to pull before re-randomize
T_Value m_lfsr = 1; // LFSR state
// Polynomials are first listed at https://users.ece.cmu.edu/~koopman/lfsr/
static constexpr uint64_t s_polynomials[] = {
0x0ULL, // 0 never used (constant, no randomization)
0x0ULL, // 1
0x3ULL, 0x5ULL, 0x9ULL, 0x12ULL, 0x21ULL,
0x41ULL, 0x8eULL, 0x108ULL, 0x204ULL, 0x402ULL,
0x829ULL, 0x100dULL, 0x2015ULL, 0x4001ULL,
0x8016ULL, // 16
0x10004ULL, 0x20040ULL, 0x40013ULL, 0x80004ULL, 0x100002ULL,
0x200001ULL, 0x400010ULL, 0x80000dULL, 0x1000004ULL, 0x2000023ULL,
0x4000013ULL, 0x8000004ULL, 0x10000002ULL, 0x20000029ULL, 0x40000004ULL,
0x80000057ULL, // 32
0x100000029ULL // 33
};
public:
// CONSTRUCTORS
VlRandC() {
static_assert(T_numValues >= 1, "");
static_assert(sizeof(T_Value) == 8 || (T_numValues < (1ULL << (8 * sizeof(T_Value)))), "");
}
// METHODS
T_Value randomize(VlRNG& rngr) {
if (VL_UNLIKELY(!m_remaining)) reseed(rngr);
constexpr uint32_t clogWidth = VL_CLOG2_CE_Q(T_numValues) + 1;
constexpr uint32_t lfsrWidth = (clogWidth < 2) ? 2 : clogWidth;
constexpr T_Value polynomial = static_cast<T_Value>(s_polynomials[lfsrWidth]);
// printf(" numV=%ld w=%d poly=%x\n", T_numValues, lfsrWidth, polynomial);
// Loop until get reasonable value. Because we picked a LFSR of at most one
// extra bit in width, this will only require at most on average 1.5 loops
do {
m_lfsr = (m_lfsr & 1ULL) ? ((m_lfsr >> 1ULL) ^ polynomial) : (m_lfsr >> 1ULL);
} while (m_lfsr > T_numValues); // Note if == then output value 0
--m_remaining;
T_Value result = (m_lfsr == T_numValues) ? 0 : m_lfsr;
// printf(" result=%x (numv=%ld, rem=%d)\n", result, T_numValues, m_remaining);
return result;
}
void reseed(VlRNG& rngr) {
constexpr uint32_t lfsrWidth = VL_CLOG2_CE_Q(T_numValues) + 1;
m_remaining = T_numValues;
do {
m_lfsr = rngr.rand64() & VL_MASK_Q(lfsrWidth);
// printf(" lfsr.reseed=%x\n", m_lfsr);
} while (!m_lfsr); // 0 not a legal seed
}
};
// 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(); }

View File

@ -494,6 +494,41 @@ public:
int widthTotalBytes() const override { V3ERROR_NA_RETURN(0); }
bool isCompound() const override { return true; }
};
class AstCDType final : public AstNodeDType {
// Raw "C" data type passed directly to output
string m_name; // Name of data type, printed when do V3EmitC
public:
AstCDType(FileLine* fl, const string& name)
: ASTGEN_SUPER_CDType(fl)
, m_name{name} {
this->dtypep(this);
}
public:
ASTGEN_MEMBERS_AstCDType;
bool same(const AstNode* samep) const override {
const AstCDType* const asamep = static_cast<const AstCDType*>(samep);
return m_name == asamep->m_name;
}
bool similarDType(const AstNodeDType* samep) const override { return same(samep); }
string name() const override VL_MT_STABLE { return m_name; }
string prettyDTypeName() const override { return m_name; }
const char* broken() const override { return nullptr; }
// METHODS
AstBasicDType* basicp() const override VL_MT_STABLE { return nullptr; }
AstNodeDType* skipRefp() const override VL_MT_STABLE { return (AstNodeDType*)this; }
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
int widthAlignBytes() const override { return 8; } // Assume
int widthTotalBytes() const override { return 8; } // Assume
bool isCompound() const override { return true; }
static string typeToHold(uint64_t maxItem) {
return (maxItem < (1ULL << 8)) ? "CData"
: (maxItem < (1ULL << 16)) ? "SData"
: (maxItem < (1ULL << 32)) ? "IData"
: "QData";
}
};
class AstClassRefDType final : public AstNodeDType {
// Reference to a class
// @astgen op1 := paramsp: List[AstPin]

View File

@ -1684,6 +1684,7 @@ class AstVar final : public AstNode {
bool m_attrSplitVar : 1; // declared with split_var metacomment
bool m_fileDescr : 1; // File descriptor
bool m_isRand : 1; // Random variable
bool m_isRandC : 1; // Random cyclic variable (isRand also set)
bool m_isConst : 1; // Table contains constant data
bool m_isContinuously : 1; // Ever assigned continuously (for force/release)
bool m_hasStrengthAssignment : 1; // Is on LHS of assignment with strength specifier
@ -1691,6 +1692,7 @@ class AstVar final : public AstNode {
bool m_isPulldown : 1; // Tri0
bool m_isPullup : 1; // Tri1
bool m_isIfaceParent : 1; // dtype is reference to interface present in this module
bool m_isInternal : 1; // Internal state, don't add to method pinter
bool m_isDpiOpenArray : 1; // DPI import open array
bool m_isHideLocal : 1; // Verilog local
bool m_isHideProtected : 1; // Verilog protected
@ -1728,6 +1730,7 @@ class AstVar final : public AstNode {
m_attrSplitVar = false;
m_fileDescr = false;
m_isRand = false;
m_isRandC = false;
m_isConst = false;
m_isContinuously = false;
m_hasStrengthAssignment = false;
@ -1735,6 +1738,7 @@ class AstVar final : public AstNode {
m_isPulldown = false;
m_isPullup = false;
m_isIfaceParent = false;
m_isInternal = false;
m_isDpiOpenArray = false;
m_isHideLocal = false;
m_isHideProtected = false;
@ -1880,10 +1884,15 @@ public:
void scSensitive(bool flag) { m_scSensitive = flag; }
void primaryIO(bool flag) { m_primaryIO = flag; }
void isRand(bool flag) { m_isRand = flag; }
void isRandC(bool flag) {
m_isRandC = flag;
if (flag) isRand(true);
}
void isConst(bool flag) { m_isConst = flag; }
void isContinuously(bool flag) { m_isContinuously = flag; }
void isStatic(bool flag) { m_isStatic = flag; }
void isIfaceParent(bool flag) { m_isIfaceParent = flag; }
void isInternal(bool flag) { m_isInternal = flag; }
void funcLocal(bool flag) {
m_funcLocal = flag;
if (flag) m_funcLocalSticky = true;
@ -1929,6 +1938,7 @@ public:
bool isPrimaryInish() const { return isPrimaryIO() && isNonOutput(); }
bool isIfaceRef() const { return (varType() == VVarType::IFACEREF); }
bool isIfaceParent() const { return m_isIfaceParent; }
bool isInternal() const { return m_isInternal; }
bool isSignal() const { return varType().isSignal(); }
bool isNet() const { return varType().isNet(); }
bool isTemp() const { return varType().isTemp(); }
@ -1964,6 +1974,7 @@ public:
bool isSigUserRWPublic() const { return m_sigUserRWPublic; }
bool isTrace() const { return m_trace; }
bool isRand() const { return m_isRand; }
bool isRandC() const { return m_isRandC; }
bool isConst() const VL_MT_SAFE { return m_isConst; }
bool isStatic() const VL_MT_SAFE { return m_isStatic; }
bool isLatched() const { return m_isLatched; }

View File

@ -799,6 +799,8 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
const CTypeRecursed key = adtypep->keyDTypep()->cTypeRecurse(true);
const CTypeRecursed val = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlAssocArray<" + key.m_type + ", " + val.m_type + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, CDType)) {
info.m_type = adtypep->name();
} else if (const auto* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlAssocArray<std::string, " + sub.m_type + ">";
@ -2187,8 +2189,14 @@ void AstVar::dump(std::ostream& str) const {
if (isPulldown()) str << " [PULLDOWN]";
if (isUsedClock()) str << " [CLK]";
if (isSigPublic()) str << " [P]";
if (isInternal()) str << " [INTERNAL]";
if (isLatched()) str << " [LATCHED]";
if (isUsedLoopIdx()) str << " [LOOP]";
if (isRandC()) {
str << " [RANDC]";
} else if (isRand()) {
str << " [RAND]";
}
if (noReset()) str << " [!RST]";
if (attrIsolateAssign()) str << " [aISO]";
if (attrFileDescr()) str << " [aFD]";

View File

@ -116,7 +116,7 @@ static void makeToStringMiddle(AstClass* nodep) {
std::string comma;
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
if (const auto* const varp = VN_CAST(itemp, Var)) {
if (!varp->isParam()) {
if (!varp->isParam() && !varp->isInternal()) {
string stmt = "out += \"";
stmt += comma;
comma = ", ";

View File

@ -667,6 +667,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
suffix + ".atDefault()" + cvtarray);
} else if (VN_IS(dtypep, CDType)) {
return ""; // Constructor does it
} else if (VN_IS(dtypep, ClassRefDType)) {
return ""; // Constructor does it
} else if (VN_IS(dtypep, IfaceRefDType)) {

View File

@ -147,6 +147,11 @@ private:
m_hash += nodep->nrange().right();
});
}
void visit(AstCDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
m_hash += nodep->name();
});
}
void visit(AstConstDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, HASH_DTYPE, HASH_CHILDREN, [=]() { //
iterateConstNull(nodep->virtRefDTypep());

View File

@ -83,11 +83,8 @@ struct VMemberQualifiers {
}
void applyToNodes(AstVar* nodesp) const {
for (AstVar* nodep = nodesp; nodep; nodep = VN_AS(nodep->nextp(), Var)) {
if (m_randc) {
nodep->v3warn(RANDC, "Unsupported: Converting 'randc' to 'rand'");
nodep->isRand(true);
}
if (m_rand) nodep->isRand(true);
if (m_randc) nodep->isRandC(true);
if (m_local) nodep->isHideLocal(true);
if (m_protected) nodep->isHideProtected(true);
if (m_automatic) nodep->lifetime(VLifetime::AUTOMATIC);

View File

@ -144,6 +144,7 @@ private:
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
size_t m_enumValueTabCount = 0; // Number of tables with enum values created
int m_randCaseNum = 0; // Randcase number within a module for var naming
std::map<std::string, AstCDType*> m_randcDtypes; // RandC data type deduplication
// METHODS
AstVar* enumValueTabp(AstEnumDType* nodep) {
@ -172,8 +173,49 @@ private:
nodep->user2p(varp);
return varp;
}
AstNodeStmt* newRandStmtsp(FileLine* fl, AstNodeVarRef* varrefp, int offset = 0,
AstMemberDType* memberp = nullptr) {
AstCDType* findVlRandCDType(FileLine* fl, uint64_t items) {
// For 8 items we need to have a 9 item LFSR so items is max count
const std::string type = AstCDType::typeToHold(items);
const std::string name = "VlRandC<" + type + ", " + cvtToStr(items) + "ULL>";
// Create or reuse (to avoid duplicates) randomization object dtype
auto it = m_randcDtypes.find(name);
if (it != m_randcDtypes.end()) return it->second;
AstCDType* newp = new AstCDType{fl, name};
v3Global.rootp()->typeTablep()->addTypesp(newp);
m_randcDtypes.emplace(std::make_pair(name, newp));
return newp;
}
AstVar* newRandcVarsp(AstVar* varp) {
// If a randc, make a VlRandC object to hold the state
if (!varp->isRandC()) return nullptr;
uint64_t items = 0;
if (AstEnumDType* const enumDtp = VN_CAST(varp->dtypep()->skipRefToEnump(), EnumDType)) {
items = static_cast<uint64_t>(enumDtp->itemCount());
} else {
AstBasicDType* const basicp = varp->dtypep()->skipRefp()->basicp();
UASSERT_OBJ(basicp, varp, "Unexpected randc variable dtype");
if (basicp->width() > 32) {
varp->v3error("Maxiumum implemented width for randc is 32 bits, "
<< varp->prettyNameQ() << " is " << basicp->width() << " bits");
varp->isRandC(false);
varp->isRand(true);
return nullptr;
}
items = 1ULL << basicp->width();
}
AstCDType* newdtp = findVlRandCDType(varp->fileline(), items);
AstVar* newp
= new AstVar{varp->fileline(), VVarType::MEMBER, varp->name() + "__Vrandc", newdtp};
newp->isInternal(true);
varp->addNextHere(newp);
UINFO(9, "created " << varp << endl);
return newp;
}
AstNodeStmt* newRandStmtsp(FileLine* fl, AstNodeVarRef* varrefp, AstVar* randcVarp,
int offset = 0, AstMemberDType* memberp = nullptr) {
if (const auto* const structDtp
= VN_CAST(memberp ? memberp->subDTypep()->skipRefp() : varrefp->dtypep()->skipRefp(),
StructDType)) {
@ -182,7 +224,7 @@ private:
for (AstMemberDType* smemberp = structDtp->membersp(); smemberp;
smemberp = VN_AS(smemberp->nextp(), MemberDType)) {
AstNodeStmt* const randp = newRandStmtsp(
fl, stmtsp ? varrefp->cloneTree(false) : varrefp, offset, smemberp);
fl, stmtsp ? varrefp->cloneTree(false) : varrefp, nullptr, offset, smemberp);
if (stmtsp) {
stmtsp->addNext(randp);
} else {
@ -198,15 +240,15 @@ private:
AstVarRef* const tabRefp
= new AstVarRef{fl, enumValueTabp(enumDtp), VAccess::READ};
tabRefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
AstRandRNG* const randp
= new AstRandRNG{fl, varrefp->findBasicDType(VBasicDTypeKwd::UINT32)};
AstNodeExpr* const randp
= newRandValue(fl, randcVarp, varrefp->findBasicDType(VBasicDTypeKwd::UINT32));
AstNodeExpr* const moddivp = new AstModDiv{
fl, randp, new AstConst{fl, static_cast<uint32_t>(enumDtp->itemCount())}};
moddivp->dtypep(enumDtp);
valp = new AstArraySel{fl, tabRefp, moddivp};
} else {
valp = new AstRandRNG{fl,
(memberp ? memberp->dtypep() : varrefp->varp()->dtypep())};
valp = newRandValue(fl, randcVarp,
(memberp ? memberp->dtypep() : varrefp->varp()->dtypep()));
}
return new AstAssign{fl,
new AstSel{fl, varrefp, offset + (memberp ? memberp->lsb() : 0),
@ -214,6 +256,17 @@ private:
valp};
}
}
AstNodeExpr* newRandValue(FileLine* fl, AstVar* randcVarp, AstNodeDType* dtypep) {
if (randcVarp) {
AstNode* argsp = new AstVarRef{fl, randcVarp, VAccess::READWRITE};
argsp->addNext(new AstText{fl, ".randomize(__Vm_rng)"});
AstCExpr* newp = new AstCExpr{fl, argsp};
newp->dtypep(dtypep);
return newp;
} else {
return new AstRandRNG{fl, dtypep};
}
}
void addPrePostCall(AstClass* classp, AstFunc* funcp, const string& name) {
if (AstTask* userFuncp = VN_CAST(m_memberMap.findMember(classp, name), Task)) {
AstTaskRef* const callp
@ -271,8 +324,9 @@ private:
if (!memberVarp || !memberVarp->isRand()) continue;
const AstNodeDType* const dtypep = memberp->dtypep()->skipRefp();
if (VN_IS(dtypep, BasicDType) || VN_IS(dtypep, StructDType)) {
AstVar* const randcVarp = newRandcVarsp(memberVarp);
AstVarRef* const refp = new AstVarRef{fl, memberVarp, VAccess::WRITE};
AstNodeStmt* const stmtp = newRandStmtsp(fl, refp);
AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp);
funcp->addStmtsp(stmtp);
} else if (const auto* const classRefp = VN_CAST(dtypep, ClassRefDType)) {
if (classRefp->classp() == nodep) {

View File

@ -452,6 +452,18 @@ private:
}
if (debug() >= 9) nodep->dumpTree("- arraysel_old: ");
// If value MODDIV constant, where constant <= declElements, known ok
// V3Random makes these to intentionally prevent exceeding enum array bounds.
if (const AstModDiv* const moddivp = VN_CAST(nodep->bitp(), ModDiv)) {
if (const AstConst* const modconstp = VN_CAST(moddivp->rhsp(), Const)) {
if (modconstp->width() <= 32
&& modconstp->toUInt() <= static_cast<uint32_t>(declElements)) {
UINFO(9, "arraysel mod const " << declElements
<< " >= " << modconstp->toUInt() << endl);
return;
}
}
}
// See if the condition is constant true
AstNodeExpr* condp
= new AstGte{nodep->fileline(),

View File

@ -1,12 +0,0 @@
%Warning-RANDC: t/t_randc.v:8:26: Unsupported: Converting 'randc' to 'rand'
8 | randc bit [WIDTH-1:0] m_var;
| ^~~~~
... For warning description see https://verilator.org/warn/RANDC?v=latest
... Use "/* verilator lint_off RANDC */" and lint_on around source to disable this message.
%Warning-RANDC: t/t_randc.v:46:26: Unsupported: Converting 'randc' to 'rand'
46 | randc bit [WIDTH-1:0] m_var;
| ^~~~~
%Warning-RANDC: t/t_randc.v:76:17: Unsupported: Converting 'randc' to 'rand'
76 | randc enum_t m_var;
| ^~~~~
%Error: Exiting due to

View File

@ -8,11 +8,13 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(linter => 1);
scenarios(simulator => 1);
lint(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
compile(
);
execute(
check_finished => 1,
);
ok(1);

View File

@ -1,24 +0,0 @@
#!/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);
top_filename("t/t_randc.v");
compile(
verilator_flags2 => ['-Wno-RANDC -DTEST_IGNORE_RANDC'],
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -1,6 +1,5 @@
%Warning-RANDC: t/t_randc_oversize_bad.v:8:21: Unsupported: Converting 'randc' to 'rand'
8 | randc bit [33:0] i;
%Error: t/t_randc_oversize_bad.v:8:21: Maxiumum implemented width for randc is 32 bits, 'i' is 38 bits
: ... In instance t
8 | randc bit [37:0] i;
| ^
... For warning description see https://verilator.org/warn/RANDC?v=latest
... Use "/* verilator lint_off RANDC */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -5,8 +5,13 @@
// SPDX-License-Identifier: CC0-1.0
class Cls;
randc bit [33:0] i;
randc bit [37:0] i;
endclass
module t (/*AUTOARG*/);
Cls c;
initial begin
c = new;
c.randomize;
end
endmodule

View File

@ -15,7 +15,6 @@ compile(
"-Wno-PKGNODECL -Wno-IMPLICITSTATIC -Wno-CONSTRAINTIGN -Wno-MISINDENT",
"-Wno-CASEINCOMPLETE -Wno-CASTCONST -Wno-SYMRSVDWORD -Wno-WIDTHEXPAND -Wno-WIDTHTRUNC",
"-Wno-REALCVT", # TODO note mostly related to $realtime - could suppress or fix upstream
"-Wno-RANDC", # TODO issue #4349, add support
"-Wno-ZERODLY", # TODO issue #4494, add support
],
verilator_make_gmake => 0,