diff --git a/include/verilated_types.h b/include/verilated_types.h index 469852b98..5cc66b1ea 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -413,6 +413,10 @@ public: return m_deque[index]; } } + // Access with an index counted from end (e.g. q[$]) + T_Value& atBack(int32_t index) { return at(m_deque.size() - 1 - index); } + const T_Value& atBack(int32_t index) const { return at(m_deque.size() - 1 - index); } + // function void q.insert(index, value); void insert(int32_t index, const T_Value& value) { if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) return; @@ -428,6 +432,12 @@ public: for (int32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]); return out; } + VlQueue sliceFrontBack(int32_t lsb, int32_t msb) const { + return slice(lsb, m_deque.size() - 1 - msb); + } + VlQueue sliceBackBack(int32_t lsb, int32_t msb) const { + return slice(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb); + } // For save/restore const_iterator begin() const { return m_deque.begin(); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index bd70ab255..5bec957ad 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1297,15 +1297,15 @@ private: if (const auto* const varp = VN_CAST(nodep->backp(), Var)) { if (varp->isParam()) return; // Ok, leave } - // queue_slice[#:$] - if (const auto* const selp = VN_CAST(nodep->backp(), SelExtract)) { - if (VN_IS(selp->fromp()->dtypep(), QueueDType)) { - nodep->replaceWith( - new AstConst(nodep->fileline(), AstConst::Signed32{}, 0x7FFFFFFF)); - VL_DO_DANGLING(nodep->deleteTree(), nodep); - return; - } + AstNode* backp = nodep->backp(); + if (VN_IS(backp, Sub)) backp = backp->backp(); + if (const auto* const selp = VN_CAST(backp, SelExtract)) { + if (VN_IS(selp->fromp()->dtypep(), QueueDType)) return; } + if (const auto* const selp = VN_CAST(backp, SelBit)) { + if (VN_IS(selp->fromp()->dtypep(), QueueDType)) return; + } + // queue_slice[#:$] and queue_bitsel[$] etc handled in V3WidthSel nodep->v3warn(E_UNSUPPORTED, "Unsupported/illegal unbounded ('$') in this context."); } void visit(AstIsUnbounded* nodep) override { diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index a4a1628b3..c6308416b 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -190,6 +190,18 @@ private: } } + AstNodeExpr* selQueueBackness(AstNode* nodep) { + if (VN_IS(nodep, Unbounded)) { // e.g. "[$]" + return new AstConst{nodep->fileline(), AstConst::Signed32{}, 0}; + } else if (VN_IS(nodep, Sub) && VN_IS(VN_CAST(nodep, Sub)->lhsp(), Unbounded)) { + // e.g. "q[$ - 1]", where 1 is subnodep + AstNodeExpr* subrhsp = VN_CAST(nodep, Sub)->rhsp()->unlinkFrBack(); + return subrhsp; + } else { + return nullptr; + } + } + void warnTri(AstNode* nodep) { if (VN_IS(nodep, Const) && VN_AS(nodep, Const)->num().isFourState()) { nodep->v3error( @@ -250,8 +262,7 @@ private: VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstAssocArrayDType* const adtypep = VN_CAST(ddtypep, AssocArrayDType)) { // SELBIT(array, index) -> ASSOCSEL(array, index) - AstNodeExpr* const subp = rhsp; - AstAssocSel* const newp = new AstAssocSel{nodep->fileline(), fromp, subp}; + AstAssocSel* const newp = new AstAssocSel{nodep->fileline(), fromp, rhsp}; newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference if (debug() >= 9) newp->dumpTree("- SELBTn: "); nodep->replaceWith(newp); @@ -259,24 +270,26 @@ private: } else if (const AstWildcardArrayDType* const adtypep = VN_CAST(ddtypep, WildcardArrayDType)) { // SELBIT(array, index) -> WILDCARDSEL(array, index) - AstNodeExpr* const subp = rhsp; - AstWildcardSel* const newp = new AstWildcardSel{nodep->fileline(), fromp, subp}; + AstWildcardSel* const newp = new AstWildcardSel{nodep->fileline(), fromp, rhsp}; newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off array reference if (debug() >= 9) newp->dumpTree("- 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) - AstNodeExpr* const subp = rhsp; - AstCMethodHard* const newp = new AstCMethodHard{nodep->fileline(), fromp, "at", subp}; + AstCMethodHard* const newp = new AstCMethodHard{nodep->fileline(), fromp, "at", rhsp}; newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off queue reference if (debug() >= 9) newp->dumpTree("- SELBTq: "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (const AstQueueDType* const adtypep = VN_CAST(ddtypep, QueueDType)) { // SELBIT(array, index) -> CMETHODCALL(queue, "at", index) - AstNodeExpr* const subp = rhsp; - AstCMethodHard* const newp = new AstCMethodHard{nodep->fileline(), fromp, "at", subp}; + AstCMethodHard* newp; + if (AstNodeExpr* const backnessp = selQueueBackness(rhsp)) { + newp = new AstCMethodHard{nodep->fileline(), fromp, "atBack", backnessp}; + } else { + newp = new AstCMethodHard{nodep->fileline(), fromp, "at", rhsp}; + } newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off queue reference if (debug() >= 9) newp->dumpTree("- SELBTq: "); nodep->replaceWith(newp); @@ -338,19 +351,43 @@ private: V3Const::constifyParamsEdit(nodep->leftp()); // May relink pointed to node V3Const::constifyParamsEdit(nodep->rightp()); // May relink pointed to node // if (debug() >= 9) nodep->dumpTree("- SELEX3: "); + AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack(); + const FromData fromdata = fromDataForArray(nodep, fromp); + AstNodeDType* const ddtypep = fromdata.m_dtypep; + const VNumRange fromRange = fromdata.m_fromRange; + if (VN_IS(ddtypep, QueueDType)) { + AstNodeExpr* const qleftp = nodep->rhsp()->unlinkFrBack(); + AstNodeExpr* const qrightp = nodep->thsp()->unlinkFrBack(); + AstNodeExpr* const qleftBacknessp = selQueueBackness(qleftp); + AstNodeExpr* const qrightBacknessp = selQueueBackness(qrightp); + // Use special methods to refer to back rather than math using + // queue size, this allows a single queue reference, to support + // for equations in side effects that select the queue to + // operate upon. + std::string name = (qleftBacknessp ? "sliceBackBack" + : qrightBacknessp ? "sliceFrontBack" + : "slice"); + auto* const newp = new AstCMethodHard{nodep->fileline(), fromp, name, + qleftBacknessp ? qleftBacknessp : qleftp}; + newp->addPinsp(qrightBacknessp ? qrightBacknessp : qrightp); + newp->dtypep(ddtypep); + newp->didWidth(true); + newp->protect(false); + UINFO(6, " new " << newp << endl); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + return; + } + // Non-queue checkConstantOrReplace(nodep->leftp(), "First value of [a:b] isn't a constant, maybe you want +: or -:"); checkConstantOrReplace(nodep->rightp(), "Second value of [a:b] isn't a constant, maybe you want +: or -:"); - AstNodeExpr* const fromp = nodep->fromp()->unlinkFrBack(); AstNodeExpr* const msbp = nodep->rhsp()->unlinkFrBack(); AstNodeExpr* const lsbp = nodep->thsp()->unlinkFrBack(); int32_t msb = VN_AS(msbp, Const)->toSInt(); int32_t lsb = VN_AS(lsbp, Const)->toSInt(); const int32_t elem = (msb > lsb) ? (msb - lsb + 1) : (lsb - msb + 1); - const FromData fromdata = fromDataForArray(nodep, fromp); - AstNodeDType* const ddtypep = fromdata.m_dtypep; - const VNumRange fromRange = fromdata.m_fromRange; if (VN_IS(ddtypep, UnpackArrayDType)) { // Slice extraction if (fromRange.elements() == elem @@ -453,15 +490,6 @@ private: // if (debug() >= 9) newp->dumpTree("- SELEXnew: "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); - } else if (VN_IS(ddtypep, QueueDType)) { - auto* const newp = new AstCMethodHard{nodep->fileline(), fromp, "slice", msbp}; - msbp->addNext(lsbp); - newp->dtypep(ddtypep); - newp->didWidth(true); - newp->protect(false); - UINFO(6, " new " << newp << endl); - nodep->replaceWith(newp); - VL_DO_DANGLING(pushDeletep(nodep), nodep); } else { // nullptr=bad extract, or unknown node type nodep->v3error("Illegal range select; type already selected, or bad dimension: " << "data type is " << fromdata.m_errp->prettyDTypeNameQ()); diff --git a/test_regress/t/t_queue_back.pl b/test_regress/t/t_queue_back.pl new file mode 100755 index 000000000..1aa73f80a --- /dev/null +++ b/test_regress/t/t_queue_back.pl @@ -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 2022 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; diff --git a/test_regress/t/t_queue_back.v b/test_regress/t/t_queue_back.v new file mode 100644 index 000000000..1d8d2bb90 --- /dev/null +++ b/test_regress/t/t_queue_back.v @@ -0,0 +1,36 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2023 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/); + + int q[$]; + int r; + + initial begin + q = { 20, 30, 40 }; + + r = q[$]; + if (r != 40) $stop; + + r = q[$-1]; + if (r != 30) $stop; + + q = q[0:$-1]; // void'(q.pop_back()) or q.delete(q.size-1) + if (q.size != 2) $stop; + if (q[0] != 20) $stop; + if (q[1] != 30) $stop; + + q = { 20, 30, 40 }; + q = q[$-1:$]; + if (q.size != 2) $stop; + if (q[0] != 30) $stop; + if (q[1] != 40) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule