diff --git a/Changes b/Changes index 2ba870128..e10653ea4 100644 --- a/Changes +++ b/Changes @@ -4,7 +4,9 @@ The contributors that suggested a given feature are shown in []. Thanks! * Verilator 4.023 devel -** Support associative arrays (excluding [*] and pattern assignments). +** Support associative arrays (excluding [*] and pattern assignments), bug544. + +** Support queues (excluding {} notation and pattern assignments), bug545. *** Add +verilator+error+limit to see more assertion errors. [Peter Monsson] diff --git a/include/verilated_heavy.h b/include/verilated_heavy.h index 0b5c9201e..f5d650333 100644 --- a/include/verilated_heavy.h +++ b/include/verilated_heavy.h @@ -30,9 +30,7 @@ #include "verilated.h" -//IFDEF C11 -#include - +#include #include #include @@ -182,6 +180,99 @@ std::string VL_TO_STRING(const VlAssocArray& obj) { return obj.to_string(); } +//=================================================================== +// Verilog queue container +// There are no multithreaded locks on this; the base variable must +// be protected by other means +// +template class VlQueue { +private: + // TYPES + typedef std::deque Deque; +public: + typedef typename Deque::const_iterator const_iterator; + +private: + // MEMBERS + Deque m_deque; // State of the assoc array + T_Value m_defaultValue; // Default value + +public: + // CONSTRUCTORS + VlQueue() { + // m_defaultValue isn't defaulted. Caller's constructor must do it. + } + ~VlQueue() {} + // Standard copy constructor works. Verilog: assoca = assocb + + // METHODS + T_Value& atDefault() { return m_defaultValue; } + + // Size. Verilog: function int size(), or int num() + int size() const { return m_deque.size(); } + // Clear array. Verilog: function void delete([input index]) + void clear() { m_deque.clear(); } + void erase(size_t index) { if (VL_LIKELY(index < m_deque.size())) m_deque.erase(index); } + + // function void q.push_front(value) + void push_front(const T_Value& value) { m_deque.push_front(value); } + // function void q.push_back(value) + void push_back(const T_Value& value) { m_deque.push_back(value); } + // function value_t q.pop_front(); + const T_Value& pop_front() { + if (m_deque.empty()) return m_defaultValue; + const T_Value& v = m_deque.front(); m_deque.pop_front(); return v; + } + // function value_t q.pop_back(); + const T_Value& pop_back() { + if (m_deque.empty()) return m_defaultValue; + const T_Value& v = m_deque.back(); m_deque.pop_back(); return v; + } + + // Setting. Verilog: assoc[index] = v + // Can't just overload operator[] or provide a "at" reference to set, + // because we need to be able to insert only when the value is set + T_Value& at(size_t index) { + static T_Value s_throwAway; + if (VL_UNLIKELY(index >= m_deque.size())) { + s_throwAway = atDefault(); + return s_throwAway; + } + else return m_deque[index]; + } + // Accessing. Verilog: v = assoc[index] + const T_Value& at(size_t index) const { + static T_Value s_throwAway; + if (VL_UNLIKELY(index >= m_deque.size())) return atDefault(); + else return m_deque[index]; + } + // function void q.insert(index, value); + void insert(size_t index, const T_Value& value) { + if (VL_UNLIKELY(index >= m_deque.size())) return; + m_deque[index] = value; + } + + // For save/restore + const_iterator begin() const { return m_deque.begin(); } + const_iterator end() const { return m_deque.end(); } + + // Dumping. Verilog: str = $sformatf("%p", assoc) + std::string to_string() const { + std::string out = "'{"; + std::string comma; + for (typename Deque::const_iterator it = m_deque.begin(); it != m_deque.end(); ++it) { + out += comma + VL_TO_STRING(*it); + comma = ", "; + } + return out + "} "; + } +}; + +template +std::string VL_TO_STRING(const VlQueue& obj) { + return obj.to_string(); +} + //====================================================================== // Conversion functions diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 222d1f059..edcb89ffd 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -278,6 +278,16 @@ AstVar::VlArgTypeRecursed AstVar::vlArgTypeRecurse(bool forFunc, const AstNodeDT VlArgTypeRecursed info; info.m_oprefix = out; return info; + } else if (const AstQueueDType* adtypep = VN_CAST_CONST(dtypep, QueueDType)) { + VlArgTypeRecursed sub = vlArgTypeRecurse(forFunc, adtypep->subDTypep(), true); + VlArgTypeRecursed info; + string out = "VlQueue<" + sub.m_oprefix; + if (!sub.m_osuffix.empty() || !sub.m_oref.empty()) { + out += " " + sub.m_osuffix + sub.m_oref; + } + out += ">"; + info.m_oprefix = out; + return info; } else if (const AstUnpackArrayDType* adtypep = VN_CAST_CONST(dtypep, UnpackArrayDType)) { VlArgTypeRecursed info = vlArgTypeRecurse(forFunc, adtypep->subDTypep(), arrayed); info.m_osuffix = "[" + cvtToStr(adtypep->declRange().elements()) + "]" + info.m_osuffix; @@ -1109,6 +1119,10 @@ void AstAssocArrayDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str<<"[assoc-"<<(void*)keyDTypep()<<"]"; } +void AstQueueDType::dumpSmall(std::ostream& str) const { + this->AstNodeDType::dumpSmall(str); + str<<"[queue]"; +} void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str<<"[]"; diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index e1623f3d5..52b68caf4 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -200,6 +200,19 @@ public: AstNodeDType* keyDTypep() const { return VN_CAST(op1p(), NodeDType); } }; +class AstQueueRange : public AstNodeRange { + // Queue range specification + // Only for early parsing - becomes AstQueueDType +public: + explicit AstQueueRange(FileLine* fl) + : AstNodeRange(fl) {} + ASTNODE_NODE_FUNCS(QueueRange) + virtual string emitC() { V3ERROR_NA; return ""; } + virtual string emitVerilog() { V3ERROR_NA; return ""; } + virtual V3Hash sameHash() const { return V3Hash(); } + virtual bool same(const AstNode* samep) const { return true; } +}; + class AstUnsizedRange : public AstNodeRange { // Unsized range specification, for open arrays public: @@ -717,6 +730,50 @@ public: bool isModport() { return !m_modportName.empty(); } }; +class AstQueueDType : public AstNodeDType { + // Queue array data type, ie "[ $ ]" + // Children: DTYPE (moved to refDTypep() in V3Width) +private: + AstNodeDType* m_refDTypep; // Elements of this type (after widthing) +public: + AstQueueDType(FileLine* fl, VFlagChildDType, AstNodeDType* dtp) + : AstNodeDType(fl) { + childDTypep(dtp); // Only for parser + refDTypep(NULL); + dtypep(NULL); // V3Width will resolve + } + ASTNODE_NODE_FUNCS(QueueDType) + virtual const char* broken() const { + BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists()) + || (!m_refDTypep && childDTypep()))); + return NULL; } + virtual void cloneRelink() { + if (m_refDTypep && m_refDTypep->clonep()) { m_refDTypep = m_refDTypep->clonep(); } } + virtual bool same(const AstNode* samep) const { + const AstQueueDType* asamep = static_cast(samep); + return (subDTypep() == asamep->subDTypep()); } + virtual bool similarDType(AstNodeDType* samep) const { + const AstQueueDType* asamep = static_cast(samep); + return (subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp())); + } + virtual void dumpSmall(std::ostream& str) const; + virtual V3Hash sameHash() const { return V3Hash(m_refDTypep); } + AstNodeDType* getChildDTypep() const { return childDTypep(); } + AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); } // op1 = Range of variable + void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); } + virtual AstNodeDType* subDTypep() const { return m_refDTypep ? m_refDTypep : childDTypep(); } + void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; } + virtual AstNodeDType* virtRefDTypep() const { return m_refDTypep; } + virtual void virtRefDTypep(AstNodeDType* nodep) { refDTypep(nodep); } + // METHODS + virtual AstBasicDType* basicp() const { return NULL; } + virtual AstNodeDType* skipRefp() const { return (AstNodeDType*)this; } + virtual AstNodeDType* skipRefToConstp() const { return (AstNodeDType*)this; } + virtual AstNodeDType* skipRefToEnump() const { return (AstNodeDType*)this; } + virtual int widthAlignBytes() const { return subDTypep()->widthAlignBytes(); } + virtual int widthTotalBytes() const { return subDTypep()->widthTotalBytes(); } +}; + class AstRefDType : public AstNodeDType { private: AstNodeDType* m_refDTypep; // data type pointed to, BELOW the AstTypedef @@ -2247,6 +2304,16 @@ public: AstNode* rhsp() const { return op2p(); } }; +class AstUnbounded : public AstNode { + // A $ in the parser, used for unbounded and queues +public: + AstUnbounded(FileLine* fl) + : AstNode(fl) {} + ASTNODE_NODE_FUNCS(Unbounded) + virtual string emitVerilog() { return "$"; } + virtual string emitC() { V3ERROR_NA; return ""; } +}; + //###################################################################### class AstTask : public AstNodeFTask { diff --git a/src/V3Clean.cpp b/src/V3Clean.cpp index 1261f28c4..c0f377dc6 100644 --- a/src/V3Clean.cpp +++ b/src/V3Clean.cpp @@ -88,6 +88,7 @@ private: if (!nodep->user2() && nodep->hasDType()) { 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(), QueueDType) || VN_IS(nodep->dtypep()->skipRefp(), UnpackArrayDType) || VN_IS(nodep->dtypep()->skipRefp(), VoidDType)) { } else { diff --git a/src/V3EmitC.cpp b/src/V3EmitC.cpp index e6c616f74..21e26aa77 100644 --- a/src/V3EmitC.cpp +++ b/src/V3EmitC.cpp @@ -1348,6 +1348,9 @@ class EmitCImp : EmitCStmts { return emitVarResetRecurse(varp, adtypep->subDTypep(), depth+1, ".atDefault()" + cvtarray); } + else if (AstQueueDType* adtypep = VN_CAST(dtypep, QueueDType)) { + return emitVarResetRecurse(varp, adtypep->subDTypep(), depth+1, ".atDefault()"); + } else if (AstUnpackArrayDType* adtypep = VN_CAST(dtypep, UnpackArrayDType)) { UASSERT_OBJ(adtypep->msb() >= adtypep->lsb(), varp, "Should have swapped msb & lsb earlier."); diff --git a/src/V3EmitCInlines.cpp b/src/V3EmitCInlines.cpp index b78ebf15e..bbea7ebed 100644 --- a/src/V3EmitCInlines.cpp +++ b/src/V3EmitCInlines.cpp @@ -50,6 +50,10 @@ class EmitCInlines : EmitCBaseVisitor { v3Global.needHeavy(true); iterateChildren(nodep); } + virtual void visit(AstQueueDType* nodep) { + v3Global.needHeavy(true); + iterateChildren(nodep); + } virtual void visit(AstValuePlusArgs* nodep) { v3Global.needHeavy(true); iterateChildren(nodep); diff --git a/src/V3ParseGrammar.cpp b/src/V3ParseGrammar.cpp index 871984ebb..52f9345e3 100644 --- a/src/V3ParseGrammar.cpp +++ b/src/V3ParseGrammar.cpp @@ -117,6 +117,10 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, arrayp = new AstPackArrayDType (rangep->fileline(), VFlagChildDType(), arrayp, rangep); } else if (rangep) { + if (VN_IS(rangep->leftp(), Unbounded) + || VN_IS(rangep->rightp(), Unbounded)) { + rangep->v3error("Unsupported: Bounded queues. Suggest use unbounded."); + } arrayp = new AstUnpackArrayDType (rangep->fileline(), VFlagChildDType(), arrayp, rangep); } else if (VN_IS(nrangep, UnsizedRange)) { @@ -127,6 +131,9 @@ AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep, AstNodeDType* keyp = arangep->keyDTypep(); keyp->unlinkFrBack(); arrayp = new AstAssocArrayDType (nrangep->fileline(), VFlagChildDType(), arrayp, keyp); + } else if (VN_IS(nrangep, QueueRange)) { + arrayp = new AstQueueDType + (nrangep->fileline(), VFlagChildDType(), arrayp); } else { UASSERT_OBJ(0, nrangep, "Expected range or unsized range"); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index a0fb7c3a1..584129d44 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -880,6 +880,9 @@ private: nodep->dtypeSetSigned32(); // Says the spec } } + virtual void visit(AstUnbounded* nodep) { + nodep->v3error("Unsupported/illegal unbounded ('$') in this context."); + } virtual void visit(AstUCFunc* nodep) { // Give it the size the user wants. if (m_vup && m_vup->prelim()) { @@ -1076,6 +1079,14 @@ private: nodep->dtypep(nodep); // The array itself, not subDtype UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed + if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); + // Iterate into subDTypep() to resolve that type and update pointer. + nodep->refDTypep(iterateEditDTypep(nodep, nodep->subDTypep())); + nodep->dtypep(nodep); // The array itself, not subDtype + UINFO(4,"dtWidthed "<didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed if (nodep->childDTypep()) nodep->refDTypep(moveChildDTypeEdit(nodep)); @@ -1614,6 +1625,7 @@ private: } else if (VN_IS(fromDtp, EnumDType) || VN_IS(fromDtp, AssocArrayDType) + || VN_IS(fromDtp, QueueDType) || VN_IS(fromDtp, BasicDType)) { // Method call on enum without following parenthesis, e.g. "ENUM.next" // Convert this into a method call, and let that visitor figure out what to do next @@ -1681,6 +1693,9 @@ private: else if (AstAssocArrayDType* adtypep = VN_CAST(fromDtp, AssocArrayDType)) { methodCallAssoc(nodep, adtypep); } + else if (AstQueueDType* adtypep = VN_CAST(fromDtp, QueueDType)) { + methodCallQueue(nodep, adtypep); + } else if (AstUnpackArrayDType* adtypep = VN_CAST(fromDtp, UnpackArrayDType)) { methodCallUnpack(nodep, adtypep); } @@ -1875,6 +1890,93 @@ private: if (lvalue) varrefp->lvalue(true); } } + void methodCallQueue(AstMethodCall* nodep, AstQueueDType* adtypep) { + AstCMethodCall* newp = NULL; + if (nodep->name() == "at") { // Created internally for [] + methodOkArguments(nodep, 1, 1); + methodCallLValue(nodep, nodep->fromp(), true); + newp = new AstCMethodCall(nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + "at", NULL); + newp->dtypeFrom(adtypep->subDTypep()); + newp->protect(false); + newp->didWidth(true); + } else if (nodep->name() == "num" // function int num() + || nodep->name() == "size") { + methodOkArguments(nodep, 0, 0); + newp = new AstCMethodCall(nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + "size", NULL); + newp->dtypeSetSigned32(); + newp->didWidth(true); + newp->protect(false); + } else if (nodep->name() == "delete") { // function void delete([input integer index]) + methodOkArguments(nodep, 0, 1); + methodCallLValue(nodep, nodep->fromp(), true); + if (!nodep->pinsp()) { + newp = new AstCMethodCall(nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + "clear", NULL); + newp->protect(false); + newp->makeStatement(); + } else { + nodep->v3error("Unsupported: Queue .delete(index) method, as is O(n) complexity and slow."); + AstNode* index_exprp = methodCallQueueIndexExpr(nodep); + newp = new AstCMethodCall(nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + "erase", index_exprp->unlinkFrBack()); + newp->protect(false); + newp->makeStatement(); + } + } else if (nodep->name() == "insert") { + nodep->v3error("Unsupported: Queue .insert method, as is O(n) complexity and slow."); + methodOkArguments(nodep, 2, 2); + methodCallLValue(nodep, nodep->fromp(), true); + AstNode* index_exprp = methodCallQueueIndexExpr(nodep); + AstArg* argp = VN_CAST(nodep->pinsp()->nextp(), Arg); + iterateCheckTyped(nodep, "insert value", argp->exprp(), adtypep->subDTypep(), BOTH); + newp = new AstCMethodCall(nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + nodep->name(), + index_exprp->unlinkFrBack()); + newp->addPinsp(argp->exprp()->unlinkFrBack()); + newp->protect(false); + newp->makeStatement(); + } else if (nodep->name() == "pop_front" + || nodep->name() == "pop_back") { + methodOkArguments(nodep, 0, 0); + methodCallLValue(nodep, nodep->fromp(), true); + newp = new AstCMethodCall(nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + nodep->name(), NULL); + newp->dtypeFrom(adtypep->subDTypep()); + newp->protect(false); + newp->didWidth(true); + } else if (nodep->name() == "push_back" + || nodep->name() == "push_front") { + methodOkArguments(nodep, 1, 1); + methodCallLValue(nodep, nodep->fromp(), true); + AstArg* argp = VN_CAST(nodep->pinsp(), Arg); + iterateCheckTyped(nodep, "push value", argp->exprp(), adtypep->subDTypep(), BOTH); + newp = new AstCMethodCall(nodep->fileline(), + nodep->fromp()->unlinkFrBack(), + nodep->name(), argp->exprp()->unlinkFrBack()); + newp->protect(false); + newp->makeStatement(); + } else { + nodep->v3error("Unsupported/unknown built-in associative array method "<prettyNameQ()); + } + if (newp) { + newp->didWidth(true); + nodep->replaceWith(newp); nodep->deleteTree(); VL_DANGLING(nodep); + } + } + AstNode* methodCallQueueIndexExpr(AstMethodCall* nodep) { + AstNode* index_exprp = VN_CAST(nodep->pinsp(), Arg)->exprp(); + iterateCheckSigned32(nodep, "index", index_exprp, BOTH); + VL_DANGLING(index_exprp); // May have been edited + return VN_CAST(nodep->pinsp(), Arg)->exprp(); + } void methodCallUnpack(AstMethodCall* nodep, AstUnpackArrayDType* adtypep) { enum { UNKNOWN = 0, ARRAY_OR, ARRAY_AND, ARRAY_XOR } methodId; @@ -2432,7 +2534,8 @@ private: } else if (basicp && basicp->isDouble()) { added = true; newFormat += "%g"; - } else if (VN_IS(dtypep, AssocArrayDType)) { + } else if (VN_IS(dtypep, AssocArrayDType) + || VN_IS(dtypep, QueueDType)) { added = true; newFormat += "%@"; AstNRelinker handle; @@ -3881,7 +3984,8 @@ private: AstNode* spliceCvtString(AstNode* nodep) { // IEEE-2012 11.8.1: Signed: Type coercion creates signed // 11.8.2: Argument to convert is self-determined - if (nodep && !nodep->dtypep()->basicp()->isString()) { + if (nodep && !(nodep->dtypep()->basicp() + && nodep->dtypep()->basicp()->isString())) { UINFO(6," spliceCvtString: "<unlinkFrBack(&linker); diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index 05700bd84..233c6b27f 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -93,6 +93,8 @@ private: } else if (const AstAssocArrayDType* adtypep = VN_CAST(ddtypep, AssocArrayDType)) { } + else if (const AstQueueDType* adtypep = VN_CAST(ddtypep, QueueDType)) { + } else if (const AstNodeClassDType* adtypep = VN_CAST(ddtypep, NodeClassDType)) { fromRange = adtypep->declRange(); } @@ -257,6 +259,15 @@ private: if (debug()>=9) newp->dumpTree(cout, "--SELBTn: "); nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); } + else if (AstQueueDType* adtypep = VN_CAST(ddtypep, QueueDType)) { + // SELBIT(array, index) -> CMETHODCALL(queue, "at", index) + AstNode* subp = rhsp; + AstCMethodCall* newp = new AstCMethodCall(nodep->fileline(), + fromp, "at", subp); + newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off queue reference + if (debug()>=9) newp->dumpTree(cout, "--SELBTq: "); + nodep->replaceWith(newp); pushDeletep(nodep); VL_DANGLING(nodep); + } else if (VN_IS(ddtypep, BasicDType)) { // SELBIT(range, index) -> SEL(array, index, 1) AstSel* newp = new AstSel(nodep->fileline(), diff --git a/src/verilog.y b/src/verilog.y index 96570107a..84441eb0f 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1625,13 +1625,15 @@ variable_dimension: // ==IEEE: variable_dimension '[' ']' { $$ = new AstUnsizedRange($1); } // // IEEE: unpacked_dimension | anyrange { $$ = $1; } - | '[' constExpr ']' { $$ = new AstRange($1, new AstConst($1, 0), new AstSub($1, $2, new AstConst($1, 1))); } + | '[' constExpr ']' { if (VN_IS($2, Unbounded)) { $2->deleteTree(); $$ = new AstQueueRange($1); } + else { $$ = new AstRange($1, new AstConst($1, 0), + new AstSub($1, $2, new AstConst($1, 1))); } } // // IEEE: associative_dimension | '[' data_type ']' { $$ = new AstAssocRange($1, $2); } | yP_BRASTAR ']' { $$ = NULL; v3error("Unsupported: [*] wildcard associative arrays"); } | '[' '*' ']' { $$ = NULL; v3error("Unsupported: [*] wildcard associative arrays"); } // // IEEE: queue_dimension - // // '[' '$' ']' -- $ is part of expr + // // '[' '$' ']' -- $ is part of expr, see '[' constExpr ']' // // '[' '$' ':' expr ']' -- anyrange:expr:$ ; @@ -3362,8 +3364,7 @@ expr: // IEEE: part of expression/constant_expression/primary // // IEEE: sequence_method_call // // Indistinguishable from function_subroutine_call:method_call // - | '$' { $$ = new AstConst($1, AstConst::LogicFalse()); - BBUNSUP($1, "Unsupported: $ expression"); } + | '$' { $$ = new AstUnbounded($1); } | yNULL { $$ = new AstConst($1, AstConst::LogicFalse()); BBUNSUP($1, "Unsupported: null expression"); } // // IEEE: yTHIS diff --git a/test_regress/t/t_queue.pl b/test_regress/t/t_queue.pl new file mode 100755 index 000000000..6b3b15be5 --- /dev/null +++ b/test_regress/t/t_queue.pl @@ -0,0 +1,20 @@ +#!/usr/bin/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. + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_queue.v b/test_regress/t/t_queue.v new file mode 100644 index 000000000..f21b1522f --- /dev/null +++ b/test_regress/t/t_queue.v @@ -0,0 +1,83 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2019 by Wilson Snyder. + +`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); +`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*/ + // Inputs + clk + ); + input clk; + + integer cyc=0; + + integer i; + + always @ (posedge clk) begin + cyc <= cyc + 1; + begin + // Simple test using integer + typedef bit [3:0] nibble_t; + nibble_t q[$]; + nibble_t v; + + i = q.size(); `checkh(i, 0); + q.push_back(4'd1); // 1 + q.push_front(4'd2); // 2 1 + q.push_back(4'd3); // 2 1 3 + i = q.size; `checkh(i, 3); // Also checks no parens + end + + begin + // Strings + string q[$]; + string v; + + q.push_front("f1"); + q.push_back("b1"); + q.push_front("f2"); + q.push_back("b2"); + i = q.size(); `checkh(i, 4); + + v = q[0]; `checks(v, "f2"); + v = q[1]; `checks(v, "f1"); + v = q[2]; `checks(v, "b1"); + v = q[3]; `checks(v, "b2"); + v = q[4]; `checks(v, ""); + + v = $sformatf("%p", q); `checks(v, "'{\"f2\", \"f1\", \"b1\", \"b2\"} "); + + //Unsup: q.delete(1); + //Unsup: v = q[1]; `checks(v, "b1"); + + //Unsup: q.insert(0, "ins0"); + //Unsup: q.insert(3, "ins3"); + //v = q[0]; `checks(v, "ins0"); + //v = q[3]; `checks(v, "ins3"); + + v = q.pop_front(); `checks(v, "f2"); + v = q.pop_front(); `checks(v, "f1"); + v = q.pop_back(); `checks(v, "b2"); + v = q.pop_back(); `checks(v, "b1"); + i = q.size(); `checkh(i, 0); + + q.push_front("non-empty"); + i = q.size(); `checkh(i, 1); + q.delete(); + i = q.size(); `checkh(i, 0); + v = q.pop_front(); `checks(v, ""); // Was empty, optional warning + v = q.pop_back(); `checks(v, ""); // Was empty, optional warning + end + + // See t_queue_unsup_bad for more unsupported stuff + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_queue_bounded_unsup_bad.out b/test_regress/t/t_queue_bounded_unsup_bad.out new file mode 100644 index 000000000..4f610c1f7 --- /dev/null +++ b/test_regress/t/t_queue_bounded_unsup_bad.out @@ -0,0 +1,4 @@ +%Error: t/t_queue_bounded_unsup_bad.v:7: Unsupported: Bounded queues. Suggest use unbounded. + int q[$ : 3]; + ^ +%Error: Exiting due to diff --git a/test_regress/t/t_queue_bounded_unsup_bad.pl b/test_regress/t/t_queue_bounded_unsup_bad.pl new file mode 100755 index 000000000..b09f43e8b --- /dev/null +++ b/test_regress/t/t_queue_bounded_unsup_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/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. + +scenarios(linter => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_queue_bounded_unsup_bad.v b/test_regress/t/t_queue_bounded_unsup_bad.v new file mode 100644 index 000000000..6a4c94da4 --- /dev/null +++ b/test_regress/t/t_queue_bounded_unsup_bad.v @@ -0,0 +1,8 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2019 by Wilson Snyder. + +module t (/*AUTOARG*/); + int q[$ : 3]; +endmodule diff --git a/test_regress/t/t_queue_unsup_bad.out b/test_regress/t/t_queue_unsup_bad.out new file mode 100644 index 000000000..61d5258c0 --- /dev/null +++ b/test_regress/t/t_queue_unsup_bad.out @@ -0,0 +1,33 @@ +%Error: t/t_queue_unsup_bad.v:23: Unsupported: Queue .delete(index) method, as is O(n) complexity and slow. + : ... In instance t + q.delete(1); + ^~~~~~ +%Error: t/t_queue_unsup_bad.v:26: Unsupported: Queue .insert method, as is O(n) complexity and slow. + : ... In instance t + q.insert(0, "ins0"); + ^~~~~~ +%Error: t/t_queue_unsup_bad.v:27: Unsupported: Queue .insert method, as is O(n) complexity and slow. + : ... In instance t + q.insert(2, "ins2"); + ^~~~~~ +%Error: t/t_queue_unsup_bad.v:37: Unsupported/illegal unbounded ('$') in this context. + : ... In instance t + q = {q[0], q[2:$]}; + ^ +%Error: t/t_queue_unsup_bad.v:37: Expecting expression to be constant, but can't convert a UNBOUNDED to constant. + : ... In instance t + q = {q[0], q[2:$]}; + ^ +%Error: t/t_queue_unsup_bad.v:37: First value of [a:b] isn't a constant, maybe you want +: or -: + : ... In instance t + q = {q[0], q[2:$]}; + ^ +%Error: t/t_queue_unsup_bad.v:37: Illegal range select; type already selected, or bad dimension: type is + : ... In instance t + q = {q[0], q[2:$]}; + ^ +%Error: t/t_queue_unsup_bad.v:46: Unsupported: Assignment pattern applies against non struct/union: QUEUEDTYPE + : ... In instance t + q = '{ "BB", "CC" }; + ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_queue_unsup_bad.pl b/test_regress/t/t_queue_unsup_bad.pl new file mode 100755 index 000000000..adc555a8d --- /dev/null +++ b/test_regress/t/t_queue_unsup_bad.pl @@ -0,0 +1,18 @@ +#!/usr/bin/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. + +scenarios(vlt => 1); + +compile( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_queue_unsup_bad.v b/test_regress/t/t_queue_unsup_bad.v new file mode 100644 index 000000000..2cffaedae --- /dev/null +++ b/test_regress/t/t_queue_unsup_bad.v @@ -0,0 +1,75 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2019 by Wilson Snyder. + +`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); +`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 + string q[$]; + string v; + int i; + + q.push_front("non-empty"); + i = q.size(); `checkh(i, 0); + + q = {"q", "b", "c", "d", "e", "f"}; + v = $sformatf("%p", q); `checks(v, "'{\"q\", \"b\", \"c\", \"d\", \"e\", \"f\"} "); + + q.delete(1); + v = q[1]; `checks(v, "c"); + + q.insert(0, "ins0"); + q.insert(2, "ins2"); + v = q[0]; `checks(v, "ins0"); + v = q[2]; `checks(v, "ins2"); + v = $sformatf("%p", q); `checks(v, "'{need_update, \"q\", \"b\", \"c\", \"d\", \"e\", \"f\"} "); + + // Similar using implied notation + q = {q, "f1"}; // push_front + q = {q, "f2"}; // push_front + q = {"b1", q}; // push_back + q = {"b2", q}; // push_back + q = {q[0], q[2:$]}; // delete element 1 + v = $sformatf("%p", q); `checks(v, "'{need_update, \"q\", \"b\", \"c\", \"d\", \"e\", \"f\"} "); + + begin + string ai[$] = { "Foo", "Bar" }; + q = ai; // Copy + i = q.size(); `checkh(i, 2); + v = q.pop_front(); `checks(v, "Foo"); + v = q.pop_front(); `checks(v, "Bar"); + q = '{ "BB", "CC" }; // Note '{} not {} + v = q.pop_front(); `checks(v, "BB"); + v = q.pop_front(); `checks(v, "CC"); + q = { "BB", "CC" }; // Note {} not '{} + v = q.pop_front(); `checks(v, "BB"); + v = q.pop_front(); `checks(v, "CC"); + end + + // Unpacked methods also allowed. Not supported yet. + // find() + // find_index() + // find_first() + // find_first_index() + // find_last() + // find_last_index() + // min() + // max() + // unique() + // unique_index() + // reverse() + // sort() + // rsort() + // shuffle() + // sum() + // product() + // and() + // or() + // xor() + end +endmodule