Wildcard index type support for associative arrays (#3501).

Associative arrays that specify a wildcard index type may be indexed by
integral expressions of any size, with leading zeros removed
automatically.  A natural representation for such expressions is a
string, especially that the standard explicitly specifies automatic
casts from string indices to bit vectors of equivalent size.
The automatic cast part is done implicitly by the existing type system.

A simpler way to just make this work would be to convert wildcard index
type to a string type directly in the parser code, but several new AST
classes are needed to make sure illegal method calls are detected.
The verilated data structure implementation is reused, because there is
no need for differentiating the behavior on C++ side.
This commit is contained in:
Arkadiusz Kozdra 2022-07-20 15:01:36 +02:00 committed by GitHub
parent 1c5e5704f5
commit 542e324869
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 682 additions and 20 deletions

View File

@ -673,6 +673,9 @@ 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, WildcardArrayDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlAssocArray<std::string, " + sub.m_type + ">";
} else if (const auto* const adtypep = VN_CAST(dtypep, DynArrayDType)) {
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
info.m_type = "VlQueue<" + sub.m_type + ">";
@ -1683,6 +1686,10 @@ string AstQueueDType::prettyDTypeName() const {
if (boundConst()) str += ":" + cvtToStr(boundConst());
return str + "]";
}
void AstWildcardArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[*]";
}
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
this->AstNodeDType::dumpSmall(str);
str << "[]";

View File

@ -278,6 +278,17 @@ public:
virtual bool same(const AstNode* samep) const override { return true; }
};
class AstWildcardRange final : public AstNodeRange {
// Wildcard range specification, for wildcard index type associative arrays
public:
explicit AstWildcardRange(FileLine* fl)
: ASTGEN_SUPER_WildcardRange(fl) {}
ASTNODE_NODE_FUNCS(WildcardRange)
virtual string emitC() { V3ERROR_NA_RETURN(""); }
virtual string emitVerilog() { return "[*]"; }
virtual bool same(const AstNode* samep) const override { return true; }
};
class AstGatePin final : public AstNodeMath {
// Possibly expand a gate primitive input pin value to match the range of the gate primitive
public:
@ -819,6 +830,62 @@ public:
virtual bool isCompound() const override { return true; }
};
class AstWildcardArrayDType final : public AstNodeDType {
// Wildcard index type associative array data type, ie "some_dtype var_name [*]"
// Children: DTYPE (moved to refDTypep() in V3Width)
private:
AstNodeDType* m_refDTypep; // Elements of this type (after widthing)
public:
AstWildcardArrayDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp)
: ASTGEN_SUPER_WildcardArrayDType(fl) {
childDTypep(dtp); // Only for parser
refDTypep(nullptr);
dtypep(nullptr); // V3Width will resolve
}
ASTNODE_NODE_FUNCS(WildcardArrayDType)
virtual const char* broken() const override {
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|| (!m_refDTypep && childDTypep())));
return nullptr;
}
virtual void cloneRelink() override {
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
}
virtual bool same(const AstNode* samep) const override {
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
if (!asamep->subDTypep()) return false;
return (subDTypep() == asamep->subDTypep());
}
virtual bool similarDType(AstNodeDType* samep) const override {
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
return type() == samep->type() && asamep->subDTypep()
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
}
virtual void dumpSmall(std::ostream& str) const override;
virtual AstNodeDType* getChildDTypep() const override { return childDTypep(); }
// op1 = Range of variable
AstNodeDType* childDTypep() const { return VN_AS(op1p(), NodeDType); }
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
virtual AstNodeDType* subDTypep() const override {
return m_refDTypep ? m_refDTypep : childDTypep();
}
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
virtual AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
virtual void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
// METHODS
virtual AstBasicDType* basicp() const override { return subDTypep()->basicp(); }
virtual AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
virtual AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
virtual AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
virtual int widthAlignBytes() const override {
return sizeof(std::map<std::string, std::string>);
}
virtual int widthTotalBytes() const override {
return sizeof(std::map<std::string, std::string>);
}
virtual bool isCompound() const override { return true; }
};
class AstBasicDType final : public AstNodeDType {
// Builtin atomic/vectored data type
// Children: RANGE (converted to constant in V3Width)
@ -1686,6 +1753,44 @@ public:
virtual int instrCount() const override { return widthInstrs(); }
};
class AstWildcardSel final : public AstNodeSel {
// Parents: math|stmt
// Children: varref|arraysel, math
private:
void init(AstNode* fromp) {
if (fromp && VN_IS(fromp->dtypep()->skipRefp(), WildcardArrayDType)) {
// Strip off array to find what array references
dtypeFrom(VN_AS(fromp->dtypep()->skipRefp(), WildcardArrayDType)->subDTypep());
}
}
public:
AstWildcardSel(FileLine* fl, AstNode* fromp, AstNode* bitp)
: ASTGEN_SUPER_WildcardSel(fl, fromp, bitp) {
init(fromp);
}
ASTNODE_NODE_FUNCS(WildcardSel)
virtual AstNode* cloneType(AstNode* lhsp, AstNode* rhsp) override {
return new AstWildcardSel{this->fileline(), lhsp, rhsp};
}
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
V3ERROR_NA;
}
virtual string emitVerilog() override { return "%k(%l%f[%r])"; }
virtual string emitC() override { return "%li%k[%ri]"; }
virtual bool cleanOut() const override { return true; }
virtual bool cleanLhs() const override { return false; }
virtual bool cleanRhs() const override { return true; }
virtual bool sizeMattersLhs() const override { return false; }
virtual bool sizeMattersRhs() const override { return false; }
virtual bool isGateOptimizable() const override {
return true;
} // esp for V3Const::ifSameAssign
virtual bool isPredictOptimizable() const override { return false; }
virtual bool same(const AstNode* samep) const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
};
class AstWordSel final : public AstNodeSel {
// Select a single word from a multi-word wide value
public:
@ -4887,6 +4992,47 @@ public:
virtual bool same(const AstNode* samep) const override { return true; }
};
class AstConsWildcard final : public AstNodeMath {
// Construct a wildcard assoc array and return object, '{}
// Parents: math
// Children: expression (elements or other queues)
public:
AstConsWildcard(FileLine* fl, AstNode* defaultp)
: ASTGEN_SUPER_ConsWildcard(fl) {
setNOp1p(defaultp);
}
ASTNODE_NODE_FUNCS(ConsWildcard)
virtual string emitVerilog() override { return "'{}"; }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
AstNode* defaultp() const { return op1p(); }
virtual bool same(const AstNode* samep) const override { return true; }
};
class AstSetWildcard final : public AstNodeMath {
// Set a wildcard assoc array element and return object, '{}
// Parents: math
// Children: expression (elements or other queues)
public:
AstSetWildcard(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNode* valuep)
: ASTGEN_SUPER_SetWildcard(fl) {
setOp1p(lhsp);
setNOp2p(keyp);
setOp3p(valuep);
}
ASTNODE_NODE_FUNCS(SetWildcard)
virtual string emitVerilog() override { return "'{}"; }
virtual string emitC() override { V3ERROR_NA_RETURN(""); }
virtual string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
virtual bool cleanOut() const override { return true; }
virtual int instrCount() const override { return widthInstrs(); }
AstNode* lhsp() const { return op1p(); }
AstNode* keyp() const { return op2p(); }
AstNode* valuep() const { return op3p(); }
virtual bool same(const AstNode* samep) const override { return true; }
};
class AstConsDynArray final : public AstNodeMath {
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
// Parents: math

View File

@ -88,6 +88,7 @@ private:
if (VN_IS(nodep, Var)
|| VN_IS(nodep, NodeDType) // Don't want to change variable widths!
|| VN_IS(nodep->dtypep()->skipRefp(), AssocArrayDType) // Or arrays
|| VN_IS(nodep->dtypep()->skipRefp(), WildcardArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), DynArrayDType)
|| VN_IS(nodep->dtypep()->skipRefp(), ClassRefDType)
|| VN_IS(nodep->dtypep()->skipRefp(), QueueDType)

View File

@ -611,6 +611,17 @@ void EmitCFunc::emitVarReset(AstVar* varp) {
emitSetVarConstant(varNameProtected + ".at(" + cvtToStr(itr.first) + ")",
VN_AS(valuep, Const));
}
} else if (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
if (initarp->defaultp()) {
emitSetVarConstant(varNameProtected + ".atDefault()",
VN_AS(initarp->defaultp(), Const));
}
const auto& mapr = initarp->map();
for (const auto& itr : mapr) {
AstNode* const valuep = itr.second->valuep();
emitSetVarConstant(varNameProtected + ".at(" + cvtToStr(itr.first) + ")",
VN_AS(valuep, Const));
}
} else if (AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
if (initarp->defaultp()) {
puts("for (int __Vi=0; __Vi<" + cvtToStr(adtypep->elementsConst()));
@ -642,6 +653,11 @@ 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 (AstWildcardArrayDType* const adtypep = VN_CAST(dtypep, WildcardArrayDType)) {
// Access std::array as C array
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
suffix + ".atDefault()" + cvtarray);
} else if (VN_IS(dtypep, ClassRefDType)) {
return ""; // Constructor does it
} else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) {

View File

@ -379,6 +379,14 @@ public:
}
puts(")");
}
virtual void visit(AstWildcardSel* nodep) override {
iterateAndNextNull(nodep->fromp());
putbs(".at(");
AstWildcardArrayDType* const adtypep = VN_AS(nodep->fromp()->dtypep(), WildcardArrayDType);
UASSERT_OBJ(adtypep, nodep, "Wildcard select on non-wildcard-associative type");
iterateAndNextNull(nodep->bitp());
puts(")");
}
virtual void visit(AstCCall* nodep) override {
const AstCFunc* const funcp = nodep->funcp();
const AstNodeModule* const funcModp = EmitCParentModule::get(funcp);
@ -1189,6 +1197,24 @@ public:
iterateAndNextNull(nodep->valuep());
puts(")");
}
virtual void visit(AstConsWildcard* nodep) override {
putbs(nodep->dtypep()->cType("", false, false));
puts("()");
if (nodep->defaultp()) {
putbs(".setDefault(");
iterateAndNextNull(nodep->defaultp());
puts(")");
}
}
virtual void visit(AstSetWildcard* nodep) override {
iterateAndNextNull(nodep->lhsp());
putbs(".set(");
iterateAndNextNull(nodep->keyp());
puts(", ");
putbs("");
iterateAndNextNull(nodep->valuep());
puts(")");
}
virtual void visit(AstConsDynArray* nodep) override {
putbs(nodep->dtypep()->cType("", false, false));
if (!nodep->lhsp()) {

View File

@ -133,6 +133,11 @@ private:
iterateNull(nodep->virtRefDTypep());
});
}
virtual void visit(AstWildcardArrayDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() { //
iterateNull(nodep->virtRefDTypep());
});
}
virtual void visit(AstBasicDType* nodep) override {
m_hash += hashNodeAndIterate(nodep, false, HASH_CHILDREN, [=]() {
m_hash += nodep->keyword();

View File

@ -138,6 +138,8 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstNodeRange* nra
AstNode* const keyp = arangep->elementsp()->unlinkFrBack();
arrayp = new AstBracketArrayDType(nrangep->fileline(), VFlagChildDType(), arrayp,
keyp);
} else if (VN_IS(nrangep, WildcardRange)) {
arrayp = new AstWildcardArrayDType{nrangep->fileline(), VFlagChildDType{}, arrayp};
} else {
UASSERT_OBJ(0, nrangep, "Expected range or unsized range");
}

View File

@ -981,6 +981,27 @@ private:
}
}
virtual void visit(AstWildcardSel* nodep) override {
// Signed/Real: Output type based on array-declared type; binary operator
if (m_vup->prelim()) {
const AstNodeDType* const fromDtp = nodep->fromp()->dtypep()->skipRefp();
const AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType);
if (!adtypep) {
UINFO(1, " Related dtype: " << fromDtp << endl);
nodep->v3fatalSrc("Wildcard array reference is not to wildcard array");
}
const AstBasicDType* const basicp = nodep->bitp()->dtypep()->skipRefp()->basicp();
if (!basicp
|| (basicp->keyword() != VBasicDTypeKwd::STRING
&& !basicp->keyword().isIntNumeric())) {
nodep->v3error("Wildcard index must be integral (IEEE 1800-2017 7.8.1)");
}
iterateCheckTyped(nodep, "Wildcard associative select", nodep->bitp(),
adtypep->findStringDType(), BOTH);
nodep->dtypeFrom(adtypep->subDTypep());
}
}
virtual void visit(AstSliceSel* nodep) override {
// Always creates as output an unpacked array
if (m_vup->prelim()) {
@ -1582,6 +1603,14 @@ private:
nodep->dtypep(nodep); // The array itself, not subDtype
UINFO(4, "dtWidthed " << nodep << endl);
}
virtual void visit(AstWildcardArrayDType* nodep) override {
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
// Iterate into subDTypep() to resolve that type and update pointer.
nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep()));
// Cleanup array size
nodep->dtypep(nodep); // The array itself, not subDtype
UINFO(4, "dtWidthed " << nodep << endl);
}
virtual void visit(AstBasicDType* nodep) override {
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed
if (nodep->generic()) return; // Already perfect
@ -2178,6 +2207,31 @@ private:
EXTEND_EXP);
}
}
virtual void visit(AstConsWildcard* nodep) override {
// Type computed when constructed here
auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType);
UASSERT_OBJ(vdtypep, nodep, "ConsWildcard requires wildcard upper parent data type");
if (m_vup->prelim()) {
nodep->dtypeFrom(vdtypep);
if (nodep->defaultp()) {
iterateCheck(nodep, "default", nodep->defaultp(), CONTEXT, FINAL,
vdtypep->subDTypep(), EXTEND_EXP);
}
}
}
virtual void visit(AstSetWildcard* nodep) override {
// Type computed when constructed here
auto* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), WildcardArrayDType);
UASSERT_OBJ(vdtypep, nodep, "SetWildcard requires wildcard upper parent data type");
if (m_vup->prelim()) {
nodep->dtypeFrom(vdtypep);
userIterateAndNext(nodep->lhsp(), WidthVP{vdtypep, BOTH}.p());
iterateCheck(nodep, "key", nodep->keyp(), CONTEXT, FINAL, vdtypep->findStringDType(),
EXTEND_EXP);
iterateCheck(nodep, "value", nodep->valuep(), CONTEXT, FINAL, vdtypep->subDTypep(),
EXTEND_EXP);
}
}
virtual void visit(AstConsDynArray* nodep) override {
// Type computed when constructed here
AstDynArrayDType* const vdtypep = VN_AS(m_vup->dtypep()->skipRefp(), DynArrayDType);
@ -2426,6 +2480,7 @@ private:
}
} else if (VN_IS(fromDtp, EnumDType) //
|| VN_IS(fromDtp, AssocArrayDType) //
|| VN_IS(fromDtp, WildcardArrayDType) //
|| VN_IS(fromDtp, UnpackArrayDType) //
|| VN_IS(fromDtp, DynArrayDType) //
|| VN_IS(fromDtp, QueueDType) //
@ -2523,6 +2578,8 @@ private:
methodCallEnum(nodep, adtypep);
} else if (AstAssocArrayDType* const adtypep = VN_CAST(fromDtp, AssocArrayDType)) {
methodCallAssoc(nodep, adtypep);
} else if (AstWildcardArrayDType* const adtypep = VN_CAST(fromDtp, WildcardArrayDType)) {
methodCallWildcard(nodep, adtypep);
} else if (AstDynArrayDType* const adtypep = VN_CAST(fromDtp, DynArrayDType)) {
methodCallDyn(nodep, adtypep);
} else if (AstQueueDType* const adtypep = VN_CAST(fromDtp, QueueDType)) {
@ -2684,6 +2741,89 @@ private:
nodep->v3error("Unknown built-in enum method " << nodep->prettyNameQ());
}
}
void methodCallWildcard(AstMethodCall* nodep, AstWildcardArrayDType* adtypep) {
AstCMethodHard* newp = nullptr;
if (nodep->name() == "num" // function int num()
|| nodep->name() == "size") {
methodOkArguments(nodep, 0, 0);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
"size"}; // So don't need num()
newp->dtypeSetSigned32();
} else if (nodep->name() == "first" // function int first(ref index)
|| nodep->name() == "last" //
|| nodep->name() == "next" //
|| nodep->name() == "prev" //
|| nodep->name() == "unique_index" //
|| nodep->name() == "find_index" || nodep->name() == "find_first_index"
|| nodep->name() == "find_last_index") {
nodep->v3error("Array method " << nodep->prettyNameQ()
<< " not legal on wildcard associative arrays");
} else if (nodep->name() == "exists") { // function int exists(input index)
// IEEE really should have made this a "bit" return
methodOkArguments(nodep, 1, 1);
AstNode* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "exists",
index_exprp->unlinkFrBack()};
newp->dtypeSetSigned32();
newp->pure(true);
} else if (nodep->name() == "delete") { // function void delete([input integer index])
methodOkArguments(nodep, 0, 1);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
if (!nodep->pinsp()) {
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
"clear"};
newp->makeStatement();
} else {
AstNode* const index_exprp = methodCallWildcardIndexExpr(nodep, adtypep);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
"erase", index_exprp->unlinkFrBack()};
newp->makeStatement();
}
} else if (nodep->name() == "sort" || nodep->name() == "rsort"
|| nodep->name() == "reverse" || nodep->name() == "shuffle") {
nodep->v3error("Array method " << nodep->prettyNameQ()
<< " not legal on associative arrays");
} else if (nodep->name() == "and" || nodep->name() == "or" || nodep->name() == "xor"
|| nodep->name() == "sum" || nodep->name() == "product") {
// All value return
AstWith* const withp
= methodWithArgument(nodep, false, false, adtypep->subDTypep(),
adtypep->findStringDType(), adtypep->subDTypep());
methodOkArguments(nodep, 0, 0);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
"r_" + nodep->name(), withp};
newp->dtypeFrom(withp ? withp->dtypep() : adtypep->subDTypep());
if (!nodep->firstAbovep()) newp->makeStatement();
} else if (nodep->name() == "min" || nodep->name() == "max" || nodep->name() == "unique") {
methodOkArguments(nodep, 0, 0);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
nodep->name()};
newp->dtypeFrom(adtypep);
if (!nodep->firstAbovep()) newp->makeStatement();
} else if (nodep->name() == "find" || nodep->name() == "find_first"
|| nodep->name() == "find_last") {
AstWith* const withp
= methodWithArgument(nodep, true, false, nodep->findBitDType(),
adtypep->findStringDType(), adtypep->subDTypep());
methodOkArguments(nodep, 0, 0);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::READ);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
nodep->name(), withp};
newp->dtypeFrom(adtypep);
if (!nodep->firstAbovep()) newp->makeStatement();
} else {
nodep->v3error("Unknown wildcard associative array method " << nodep->prettyNameQ());
nodep->dtypeFrom(adtypep->subDTypep()); // Best guess
}
if (newp) {
newp->protect(false);
newp->didWidth(true);
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
}
}
void methodCallAssoc(AstMethodCall* nodep, AstAssocArrayDType* adtypep) {
AstCMethodHard* newp = nullptr;
if (nodep->name() == "num" // function int num()
@ -2789,6 +2929,13 @@ private:
VL_DANGLING(index_exprp); // May have been edited
return VN_AS(nodep->pinsp(), Arg)->exprp();
}
AstNode* methodCallWildcardIndexExpr(AstMethodCall* nodep, AstWildcardArrayDType* adtypep) {
AstNode* const index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp();
iterateCheck(nodep, "index", index_exprp, CONTEXT, FINAL, adtypep->findStringDType(),
EXTEND_EXP);
VL_DANGLING(index_exprp); // May have been edited
return VN_AS(nodep->pinsp(), Arg)->exprp();
}
void methodCallLValueRecurse(AstMethodCall* nodep, AstNode* childp, const VAccess& access) {
if (AstNodeVarRef* const varrefp = VN_CAST(childp, NodeVarRef)) {
varrefp->access(access);
@ -3393,6 +3540,8 @@ private:
VL_DO_DANGLING(patternArray(nodep, vdtypep, defaultp), nodep);
} else if (auto* const vdtypep = VN_CAST(dtypep, AssocArrayDType)) {
VL_DO_DANGLING(patternAssoc(nodep, vdtypep, defaultp), nodep);
} else if (auto* const vdtypep = VN_CAST(dtypep, WildcardArrayDType)) {
VL_DO_DANGLING(patternWildcard(nodep, vdtypep, defaultp), nodep);
} else if (auto* const vdtypep = VN_CAST(dtypep, DynArrayDType)) {
VL_DO_DANGLING(patternDynArray(nodep, vdtypep, defaultp), nodep);
} else if (auto* const vdtypep = VN_CAST(dtypep, QueueDType)) {
@ -3576,6 +3725,26 @@ private:
// if (debug() >= 9) newp->dumpTree("-apat-out: ");
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
}
void patternWildcard(AstPattern* nodep, AstWildcardArrayDType* arrayDtp,
AstPatMember* defaultp) {
AstNode* defaultValuep = nullptr;
if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack();
AstNode* newp = new AstConsWildcard{nodep->fileline(), defaultValuep};
newp->dtypeFrom(arrayDtp);
for (AstPatMember* patp = VN_AS(nodep->itemsp(), PatMember); patp;
patp = VN_AS(patp->nextp(), PatMember)) {
patp->dtypep(arrayDtp->subDTypep());
AstNode* const valuep = patternMemberValueIterate(patp);
AstNode* const keyp = patp->keyp();
auto* const newap
= new AstSetWildcard{nodep->fileline(), newp, keyp->unlinkFrBack(), valuep};
newap->dtypeFrom(arrayDtp);
newp = newap;
}
nodep->replaceWith(newp);
// if (debug() >= 9) newp->dumpTree("-apat-out: ");
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
}
void patternDynArray(AstPattern* nodep, AstDynArrayDType* arrayp, AstPatMember*) {
AstNode* newp = new AstConsDynArray(nodep->fileline());
newp->dtypeFrom(arrayp);
@ -4092,6 +4261,7 @@ private:
added = true;
newFormat += "%g";
} else if (VN_IS(dtypep, AssocArrayDType) //
|| VN_IS(dtypep, WildcardArrayDType) //
|| VN_IS(dtypep, ClassRefDType) //
|| VN_IS(dtypep, DynArrayDType) //
|| VN_IS(dtypep, QueueDType)) {

View File

@ -88,6 +88,7 @@ private:
if (const AstNodeArrayDType* const adtypep = VN_CAST(ddtypep, NodeArrayDType)) {
fromRange = adtypep->declRange();
} else if (VN_IS(ddtypep, AssocArrayDType)) {
} else if (VN_IS(ddtypep, WildcardArrayDType)) {
} else if (VN_IS(ddtypep, DynArrayDType)) {
} else if (VN_IS(ddtypep, QueueDType)) {
} else if (const AstNodeUOrStructDType* const adtypep
@ -257,6 +258,15 @@ private:
if (debug() >= 9) newp->dumpTree(cout, "--SELBTn: ");
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else if (const AstWildcardArrayDType* const adtypep
= VN_CAST(ddtypep, WildcardArrayDType)) {
// SELBIT(array, index) -> WILDCARDSEL(array, index)
AstNode* const subp = rhsp;
AstWildcardSel* const newp = new AstWildcardSel{nodep->fileline(), fromp, subp};
newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference
if (debug() >= 9) newp->dumpTree(cout, "--SELBTn: ");
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else if (const AstDynArrayDType* const adtypep = VN_CAST(ddtypep, DynArrayDType)) {
// SELBIT(array, index) -> CMETHODCALL(queue, "at", index)
AstNode* const subp = rhsp;

View File

@ -2074,10 +2074,8 @@ variable_dimension<nodeRangep>: // ==IEEE: variable_dimension
// // IEEE: associative_dimension (if data_type)
// // Can't tell which until see if expr is data type or not
| '[' exprOrDataType ']' { $$ = new AstBracketRange($1, $2); }
| yP_BRASTAR ']'
{ $$ = nullptr; BBUNSUP($1, "Unsupported: [*] wildcard associative arrays"); }
| '[' '*' ']'
{ $$ = nullptr; BBUNSUP($2, "Unsupported: [*] wildcard associative arrays"); }
| yP_BRASTAR ']' { $$ = new AstWildcardRange{$1}; }
| '[' '*' ']' { $$ = new AstWildcardRange{$1}; }
// // IEEE: queue_dimension
// // '[' '$' ']' -- $ is part of expr, see '[' constExpr ']'
// // '[' '$' ':' expr ']' -- anyrange:expr:$

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

@ -22,26 +22,25 @@ module t (/*AUTOARG*/
cyc <= cyc + 1;
begin
// Wildcard
string a [*];
string a [*] = '{default: "nope", "BBBBB": "fooing", 23'h434343: "baring"};
int k;
string v;
v = a["CCC"]; `checks(v, "baring");
v = a["BBBBB"]; `checks(v, "fooing");
a[32'd1234] = "fooed";
a[4'd3] = "bared";
i = a.num(); `checkh(i, 2);
i = a.size(); `checkh(i, 2);
v = a[32'd1234]; `checks(v, "fooed");
a[79'h4141] = "bazed";
i = a.num(); `checkh(i, 5);
i = a.size(); `checkh(i, 5);
v = a[39'd1234]; `checks(v, "fooed");
v = a["AA"]; `checks(v, "bazed");
v = a[4'd3]; `checks(v, "bared");
i = a.exists("baz"); `checkh(i, 0);
i = a.exists(4'd3); `checkh(i, 1);
i = a.first(k); `checkh(i, 1); `checks(k, 4'd3);
i = a.next(k); `checkh(i, 1); `checks(k, 32'd1234);
i = a.next(k); `checkh(i, 0);
i = a.last(k); `checkh(i, 1); `checks(k, 32'd1234);
i = a.prev(k); `checkh(i, 1); `checks(k, 4'd3);
i = a.prev(k); `checkh(i, 0);
a.delete(4'd3);
i = a.size(); `checkh(i, 1);
i = a.size(); `checkh(i, 4);
end
$write("*-* All Finished *-*\n");

View File

@ -0,0 +1,73 @@
%Error: t/t_assoc_wildcard_bad.v:23:13: The 1 arguments passed to .num method does not match its requiring 0 arguments
: ... In instance t
23 | v = a.num("badarg");
| ^~~
%Error: t/t_assoc_wildcard_bad.v:24:13: The 1 arguments passed to .size method does not match its requiring 0 arguments
: ... In instance t
24 | v = a.size("badarg");
| ^~~~
%Error: t/t_assoc_wildcard_bad.v:25:13: The 0 arguments passed to .exists method does not match its requiring 1 arguments
: ... In instance t
25 | v = a.exists();
| ^~~~~~
%Error: t/t_assoc_wildcard_bad.v:26:13: The 2 arguments passed to .exists method does not match its requiring 1 arguments
: ... In instance t
26 | v = a.exists(k, "bad2");
| ^~~~~~
%Error: t/t_assoc_wildcard_bad.v:27:9: The 2 arguments passed to .delete method does not match its requiring 0 to 1 arguments
: ... In instance t
27 | a.delete(k, "bad2");
| ^~~~~~
%Error: t/t_assoc_wildcard_bad.v:29:9: Array method 'sort' not legal on associative arrays
: ... In instance t
29 | a.sort;
| ^~~~
%Error: t/t_assoc_wildcard_bad.v:30:9: Array method 'rsort' not legal on associative arrays
: ... In instance t
30 | a.rsort;
| ^~~~~
%Error: t/t_assoc_wildcard_bad.v:31:9: Array method 'reverse' not legal on associative arrays
: ... In instance t
31 | a.reverse;
| ^~~~~~~
%Error: t/t_assoc_wildcard_bad.v:32:9: Array method 'shuffle' not legal on associative arrays
: ... In instance t
32 | a.shuffle;
| ^~~~~~~
%Error: t/t_assoc_wildcard_bad.v:34:9: Array method 'first' not legal on wildcard associative arrays
: ... In instance t
34 | a.first;
| ^~~~~
%Error: t/t_assoc_wildcard_bad.v:35:9: Array method 'last' not legal on wildcard associative arrays
: ... In instance t
35 | a.last;
| ^~~~
%Error: t/t_assoc_wildcard_bad.v:36:9: Array method 'next' not legal on wildcard associative arrays
: ... In instance t
36 | a.next;
| ^~~~
%Error: t/t_assoc_wildcard_bad.v:37:9: Array method 'prev' not legal on wildcard associative arrays
: ... In instance t
37 | a.prev;
| ^~~~
%Error: t/t_assoc_wildcard_bad.v:38:9: Array method 'unique_index' not legal on wildcard associative arrays
: ... In instance t
38 | a.unique_index;
| ^~~~~~~~~~~~
%Error: t/t_assoc_wildcard_bad.v:39:9: Array method 'find_index' not legal on wildcard associative arrays
: ... In instance t
39 | a.find_index;
| ^~~~~~~~~~
%Error: t/t_assoc_wildcard_bad.v:40:9: Array method 'find_first_index' not legal on wildcard associative arrays
: ... In instance t
40 | a.find_first_index;
| ^~~~~~~~~~~~~~~~
%Error: t/t_assoc_wildcard_bad.v:41:9: Array method 'find_last_index' not legal on wildcard associative arrays
: ... In instance t
41 | a.find_last_index;
| ^~~~~~~~~~~~~~~
%Error: t/t_assoc_wildcard_bad.v:43:8: Wildcard index must be integral (IEEE 1800-2017 7.8.1)
: ... In instance t
43 | a[x] = "bad";
| ^
%Error: Exiting due to

View File

@ -0,0 +1,45 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2019 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
typedef class Cls;
class Cls;
integer imembera;
integer imemberb;
endclass : Cls
module t (/*AUTOARG*/);
initial begin
string a [*];
string k;
string v;
Cls x;
v = a.num("badarg");
v = a.size("badarg");
v = a.exists(); // Bad
v = a.exists(k, "bad2");
a.delete(k, "bad2");
a.sort; // Not legal on assoc
a.rsort; // Not legal on assoc
a.reverse; // Not legal on assoc
a.shuffle; // Not legal on assoc
a.first; // Not legal on wildcard
a.last; // Not legal on wildcard
a.next; // Not legal on wildcard
a.prev; // Not legal on wildcard
a.unique_index; // Not legal on wildcard
a.find_index; // Not legal on wildcard
a.find_first_index; // Not legal on wildcard
a.find_last_index; // Not legal on wildcard
a[x] = "bad";
end
endmodule

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 2003 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,127 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2019 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`define stop $stop
`define checkh(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 checks(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
module t (/*AUTOARG*/);
initial begin
int q[*];
int qe[*]; // Empty
int qv[$]; // Value returns
int qi[$]; // Index returns
int i;
string v;
q = '{"a":1, "b":2, "c":2, "d":4, "e":3};
v = $sformatf("%p", q); `checks(v, "'{\"a\":'h1, \"b\":'h2, \"c\":'h2, \"d\":'h4, \"e\":'h3} ");
// NOT tested: with ... selectors
//q.sort; // Not legal on assoc - see t_assoc_meth_bad
//q.rsort; // Not legal on assoc - see t_assoc_meth_bad
//q.reverse; // Not legal on assoc - see t_assoc_meth_bad
//q.shuffle; // Not legal on assoc - see t_assoc_meth_bad
v = $sformatf("%p", qe); `checks(v, "'{}");
qv = q.unique;
v = $sformatf("%p", qv); `checks(v, "'{'h1, 'h2, 'h4, 'h3} ");
qv = qe.unique;
v = $sformatf("%p", qv); `checks(v, "'{}");
//q.unique_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad
// These require an with clause or are illegal
qv = q.find with (item == 2);
v = $sformatf("%p", qv); `checks(v, "'{'h2, 'h2} ");
qv = q.find_first with (item == 2);
v = $sformatf("%p", qv); `checks(v, "'{'h2} ");
qv = q.find_last with (item == 2);
v = $sformatf("%p", qv); `checks(v, "'{'h2} ");
qv = q.find with (item == 20);
v = $sformatf("%p", qv); `checks(v, "'{}");
qv = q.find_first with (item == 20);
v = $sformatf("%p", qv); `checks(v, "'{}");
qv = q.find_last with (item == 20);
v = $sformatf("%p", qv); `checks(v, "'{}");
//q.find_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad
//q.find_first_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad
//q.find_last_index; // Not legal on wildcard assoc - see t_assoc_wildcard_bad
qv = q.min;
v = $sformatf("%p", qv); `checks(v, "'{'h1} ");
qv = q.max;
v = $sformatf("%p", qv); `checks(v, "'{'h4} ");
qv = qe.min;
v = $sformatf("%p", qv); `checks(v, "'{}");
qv = qe.max;
v = $sformatf("%p", qv); `checks(v, "'{}");
// Reduction methods
i = q.sum;
`checkh(i, 32'hc);
i = q.sum with (item + 1);
`checkh(i, 32'h11);
i = q.product;
`checkh(i, 32'h30);
i = q.product with (item + 1);
`checkh(i, 32'h168);
i = qe.sum;
`checkh(i, 32'h0);
i = qe.product;
`checkh(i, 32'h0);
q = '{10:32'b1100, 11:32'b1010};
i = q.and;
`checkh(i, 32'b1000);
i = q.and with (item + 1);
`checkh(i, 32'b1001);
i = q.or;
`checkh(i, 32'b1110);
i = q.or with (item + 1);
`checkh(i, 32'b1111);
i = q.xor;
`checkh(i, 32'b0110);
i = q.xor with (item + 1);
`checkh(i, 32'b0110);
i = qe.and;
`checkh(i, 32'b0);
i = qe.or;
`checkh(i, 32'b0);
i = qe.xor;
`checkh(i, 32'b0);
i = q.and();
`checkh(i, 32'b1000);
i = q.and() with (item + 1);
`checkh(i, 32'b1001);
i = q.or();
`checkh(i, 32'b1110);
i = q.or() with (item + 1);
`checkh(i, 32'b1111);
i = q.xor();
`checkh(i, 32'b0110);
i = q.xor() with (item + 1);
`checkh(i, 32'b0110);
i = qe.and();
`checkh(i, 32'b0);
i = qe.or();
`checkh(i, 32'b0);
i = qe.xor();
`checkh(i, 32'b0);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,5 +0,0 @@
%Error-UNSUPPORTED: t/t_assoc_wildcard_unsup.v:25:19: Unsupported: [*] wildcard associative arrays
25 | string a [*];
| ^~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to