forked from github/verilator
Support associative array pattern assignments and defaults.
This commit is contained in:
parent
046c0a7aa1
commit
95d1272269
2
Changes
2
Changes
@ -6,6 +6,8 @@ The contributors that suggested a given feature are shown in []. Thanks!
|
||||
|
||||
**** Support queue slicing (#2326).
|
||||
|
||||
**** Support associative array pattern assignments and defaults.
|
||||
|
||||
**** Fix WIDTH warnings on comparisons with nullptr (#2602). [Rupert Swarbrick]
|
||||
|
||||
|
||||
|
@ -196,6 +196,16 @@ public:
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
// Setting as a chained operation
|
||||
VlAssocArray& set(const T_Key& index, const T_Value& value) {
|
||||
at(index) = value;
|
||||
return *this;
|
||||
}
|
||||
VlAssocArray& setDefault(const T_Value& value) {
|
||||
atDefault() = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// For save/restore
|
||||
const_iterator begin() const { return m_map.begin(); }
|
||||
const_iterator end() const { return m_map.end(); }
|
||||
|
@ -4599,6 +4599,49 @@ public:
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
|
||||
class AstConsAssoc : public AstNodeMath {
|
||||
// Construct an assoc array and return object, '{}
|
||||
// Parents: math
|
||||
// Children: expression (elements or other queues)
|
||||
public:
|
||||
AstConsAssoc(FileLine* fl, AstNode* defaultp)
|
||||
: ASTGEN_SUPER(fl) {
|
||||
setNOp1p(defaultp);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(ConsAssoc)
|
||||
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 V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
class AstSetAssoc : public AstNodeMath {
|
||||
// Set an assoc array element and return object, '{}
|
||||
// Parents: math
|
||||
// Children: expression (elements or other queues)
|
||||
public:
|
||||
AstSetAssoc(FileLine* fl, AstNode* lhsp, AstNode* keyp, AstNode* valuep)
|
||||
: ASTGEN_SUPER(fl) {
|
||||
setOp1p(lhsp);
|
||||
setNOp2p(keyp);
|
||||
setOp3p(valuep);
|
||||
}
|
||||
ASTNODE_NODE_FUNCS(SetAssoc)
|
||||
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 V3Hash sameHash() const override { return V3Hash(); }
|
||||
virtual bool same(const AstNode* samep) const override { return true; }
|
||||
};
|
||||
|
||||
class AstConsQueue : public AstNodeMath {
|
||||
// Construct a queue and return object, '{}. '{lhs}, '{lhs. rhs}
|
||||
// Parents: math
|
||||
|
@ -1575,6 +1575,24 @@ class EmitCImp : EmitCStmts {
|
||||
}
|
||||
}
|
||||
|
||||
virtual void visit(AstConsAssoc* nodep) override {
|
||||
putbs(nodep->dtypep()->cType("", false, false));
|
||||
puts("()");
|
||||
if (nodep->defaultp()) {
|
||||
putbs(".setDefault(");
|
||||
iterateAndNextNull(nodep->defaultp());
|
||||
puts(")");
|
||||
}
|
||||
}
|
||||
virtual void visit(AstSetAssoc* nodep) override {
|
||||
iterateAndNextNull(nodep->lhsp());
|
||||
putbs(".set(");
|
||||
iterateAndNextNull(nodep->keyp());
|
||||
puts(", ");
|
||||
putbs("");
|
||||
iterateAndNextNull(nodep->valuep());
|
||||
puts(")");
|
||||
}
|
||||
virtual void visit(AstConsQueue* nodep) override {
|
||||
putbs(nodep->dtypep()->cType("", false, false));
|
||||
if (!nodep->lhsp()) {
|
||||
|
@ -1960,7 +1960,32 @@ private:
|
||||
}
|
||||
nodep->dtypeFrom(nodep->itemp());
|
||||
}
|
||||
virtual void visit(AstConsQueue* nodep) override { //
|
||||
virtual void visit(AstConsAssoc* nodep) override {
|
||||
// Type computed when constructed here
|
||||
auto* vdtypep = VN_CAST(m_vup->dtypep(), AssocArrayDType);
|
||||
UASSERT_OBJ(vdtypep, nodep, "ConsAssoc requires assoc 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(AstSetAssoc* nodep) override {
|
||||
// Type computed when constructed here
|
||||
auto* vdtypep = VN_CAST(m_vup->dtypep(), AssocArrayDType);
|
||||
UASSERT_OBJ(vdtypep, nodep, "SetsAssoc requires assoc 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->keyDTypep(),
|
||||
EXTEND_EXP);
|
||||
iterateCheck(nodep, "value", nodep->valuep(), CONTEXT, FINAL, vdtypep->subDTypep(),
|
||||
EXTEND_EXP);
|
||||
}
|
||||
}
|
||||
virtual void visit(AstConsQueue* nodep) override {
|
||||
// Type computed when constructed here
|
||||
AstQueueDType* vdtypep = VN_CAST(m_vup->dtypep(), QueueDType);
|
||||
UASSERT_OBJ(vdtypep, nodep, "ConsQueue requires queue upper parent data type");
|
||||
@ -2937,6 +2962,8 @@ private:
|
||||
VL_DO_DANGLING(patternUOrStruct(nodep, vdtypep, defaultp), nodep);
|
||||
} else if (auto* vdtypep = VN_CAST(dtypep, NodeArrayDType)) {
|
||||
VL_DO_DANGLING(patternArray(nodep, vdtypep, defaultp), nodep);
|
||||
} else if (auto* vdtypep = VN_CAST(dtypep, AssocArrayDType)) {
|
||||
VL_DO_DANGLING(patternAssoc(nodep, vdtypep, defaultp), nodep);
|
||||
} else if (auto* vdtypep = VN_CAST(dtypep, QueueDType)) {
|
||||
VL_DO_DANGLING(patternQueue(nodep, vdtypep, defaultp), nodep);
|
||||
} else if (VN_IS(dtypep, BasicDType) && VN_CAST(dtypep, BasicDType)->isRanged()) {
|
||||
@ -3044,9 +3071,8 @@ private:
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
|
||||
}
|
||||
void patternArray(AstPattern* nodep, AstNodeArrayDType* vdtypep, AstPatMember* defaultp) {
|
||||
AstNodeArrayDType* arrayp = VN_CAST(vdtypep, NodeArrayDType);
|
||||
VNumRange range = arrayp->declRange();
|
||||
void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) {
|
||||
VNumRange range = arrayDtp->declRange();
|
||||
PatVecMap patmap = patVectorMap(nodep, range);
|
||||
UINFO(9, "ent " << range.hi() << " to " << range.lo() << endl);
|
||||
AstNode* newp = nullptr;
|
||||
@ -3068,11 +3094,11 @@ private:
|
||||
|
||||
if (patp) {
|
||||
// Don't want the RHS an array
|
||||
patp->dtypep(arrayp->subDTypep());
|
||||
patp->dtypep(arrayDtp->subDTypep());
|
||||
AstNode* valuep = patternMemberValueIterate(patp);
|
||||
if (VN_IS(arrayp, UnpackArrayDType)) {
|
||||
if (VN_IS(arrayDtp, UnpackArrayDType)) {
|
||||
if (!newp) {
|
||||
AstInitArray* newap = new AstInitArray(nodep->fileline(), arrayp, nullptr);
|
||||
AstInitArray* newap = new AstInitArray(nodep->fileline(), arrayDtp, nullptr);
|
||||
newp = newap;
|
||||
}
|
||||
VN_CAST(newp, InitArray)->addIndexValuep(ent - range.lo(), valuep);
|
||||
@ -3099,6 +3125,24 @@ private:
|
||||
// if (debug() >= 9) newp->dumpTree("-apat-out: ");
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep); // Deletes defaultp also, if present
|
||||
}
|
||||
void patternAssoc(AstPattern* nodep, AstAssocArrayDType* arrayDtp, AstPatMember* defaultp) {
|
||||
AstNode* defaultValuep = nullptr;
|
||||
if (defaultp) defaultValuep = defaultp->lhssp()->unlinkFrBack();
|
||||
AstNode* newp = new AstConsAssoc(nodep->fileline(), defaultValuep);
|
||||
newp->dtypeFrom(arrayDtp);
|
||||
for (AstPatMember* patp = VN_CAST(nodep->itemsp(), PatMember); patp;
|
||||
patp = VN_CAST(patp->nextp(), PatMember)) {
|
||||
patp->dtypep(arrayDtp->subDTypep());
|
||||
AstNode* valuep = patternMemberValueIterate(patp);
|
||||
AstNode* keyp = patp->keyp();
|
||||
auto* newap = new AstSetAssoc(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 patternQueue(AstPattern* nodep, AstQueueDType* arrayp, AstPatMember* defaultp) {
|
||||
AstNode* newp = new AstConsQueue(nodep->fileline());
|
||||
newp->dtypeFrom(arrayp);
|
||||
|
@ -82,13 +82,14 @@ module t (/*AUTOARG*/
|
||||
i = a.last(k); `checkh(i, 0);
|
||||
|
||||
// Patterns & default
|
||||
`ifndef VERILATOR // Unsupported: Pattern assignment
|
||||
a = '{ "f": "fooed", "b": "bared", default: "defaulted" };
|
||||
i = a.size(); `checkh(i, 2); // Default doesn't count
|
||||
v = a["f"]; `checks(v, "fooed");
|
||||
v = a["b"]; `checks(v, "bared");
|
||||
v = a["NEXISTS"]; `checks(v, "defaulted");
|
||||
`endif
|
||||
|
||||
a = '{};
|
||||
i = a.size(); `checkh(i, 0);
|
||||
end
|
||||
|
||||
begin
|
||||
|
@ -1,5 +0,0 @@
|
||||
%Error-UNSUPPORTED: t/t_assoc_pattern_unsup.v:19:11: Unsupported: Assignment pattern applies against non struct/union data type: 'string[string]'
|
||||
: ... In instance t
|
||||
19 | a = '{ "f": "fooed", "b": "bared", default: "defaulted" };
|
||||
| ^~
|
||||
%Error: Exiting due to
|
@ -1,19 +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 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(vlt => 1);
|
||||
|
||||
lint(
|
||||
fails => 1,
|
||||
expect_filename => $Self->{golden_filename},
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
@ -1,28 +0,0 @@
|
||||
// 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 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);
|
||||
`define checkg(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='%g' exp='%g'\n", `__FILE__,`__LINE__, (gotv), (expv)); $stop; end while(0);
|
||||
|
||||
module t (/*AUTOARG*/);
|
||||
|
||||
initial begin
|
||||
int i;
|
||||
string a [string];
|
||||
string k;
|
||||
string v;
|
||||
|
||||
a = '{ "f": "fooed", "b": "bared", default: "defaulted" };
|
||||
i = a.size(); `checkh(i, 2); // Default doesn't count
|
||||
v = a["f"]; `checks(v, "fooed");
|
||||
v = a["b"]; `checks(v, "bared");
|
||||
v = a["NEXISTS"]; `checks(v, "defaulted");
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
Loading…
Reference in New Issue
Block a user