diff --git a/include/verilated_funcs.h b/include/verilated_funcs.h index 3e3b6af9a..2250070db 100644 --- a/include/verilated_funcs.h +++ b/include/verilated_funcs.h @@ -233,6 +233,15 @@ static inline std::string VL_CVT_N_CSTR(const char* lhsp) VL_PURE { return lhsp ? std::string{lhsp} : ""s; } +// Return queue from an unpacked array +template +static inline VlQueue VL_CVT_UNPACK_TO_Q(const VlUnpacked& q) VL_PURE { + VlQueue ret; + for (size_t i = 0; i < N_Depth; ++i) + ret.push_back(q[i]); + return ret; +} + // Return double from lhs (numeric) unsigned double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE; static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE { diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 899319811..66706636f 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1156,6 +1156,20 @@ public: string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } }; +class AstCvtUnpackedToQueue final : public AstNodeExpr { + // Cast from unpacked array to dynamic/unpacked queue data type + // @astgen op1 := fromp : AstNodeExpr +public: + AstCvtUnpackedToQueue(FileLine* fl, AstNodeExpr* fromp, AstNodeDType* dtp) + : ASTGEN_SUPER_CvtUnpackedToQueue(fl) { + this->fromp(fromp); + dtypeFrom(dtp); + } + ASTGEN_MEMBERS_AstCvtUnpackedToQueue; + string emitVerilog() override { V3ERROR_NA_RETURN(""); } + string emitC() override { return "VL_CVT_UNPACK_TO_Q(%P, %li)"; } + bool cleanOut() const override { return true; } +}; class AstDist final : public AstNodeExpr { // @astgen op1 := exprp : AstNodeExpr // @astgen op2 := itemsp : List[AstDistItem] diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 74d0e85e8..67236a386 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -392,6 +392,11 @@ public: emitOpName(nodep, nodep->emitC(), nodep->fromp(), elemDTypep, nullptr); } + void visit(AstCvtUnpackedToQueue* nodep) override { + AstNodeDType* const elemDTypep = nodep->fromp()->dtypep()->subDTypep(); + emitOpName(nodep, nodep->emitC(), nodep->fromp(), elemDTypep, nullptr); + } + void visit(AstNodeAssign* nodep) override { bool paren = true; bool decind = false; diff --git a/src/V3Premit.cpp b/src/V3Premit.cpp index fb763aa9e..5a2acec92 100644 --- a/src/V3Premit.cpp +++ b/src/V3Premit.cpp @@ -288,6 +288,10 @@ class PremitVisitor final : public VNVisitor { iterateChildren(nodep); checkNode(nodep); } + void visit(AstCvtUnpackedToQueue* nodep) override { + iterateChildren(nodep); + checkNode(nodep); + } void visit(AstSel* nodep) override { iterateAndNextNull(nodep->fromp()); { // Only the 'from' is part of the assignment LHS diff --git a/src/V3Width.cpp b/src/V3Width.cpp index ae82bb1da..36c95a409 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -1605,6 +1605,12 @@ class WidthVisitor final : public VNVisitor { userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); // Type set in constructor } + void visit(AstCvtUnpackedToQueue* nodep) override { + if (nodep->didWidthAndSet()) return; + // Opaque returns, so arbitrary + userIterateAndNext(nodep->fromp(), WidthVP{SELF, BOTH}.p()); + // Type set in constructor + } void visit(AstTimeImport* nodep) override { // LHS is a real number in seconds // Need to round to time units and precision @@ -7069,6 +7075,12 @@ class WidthVisitor final : public VNVisitor { nodep->v3error(side << " expects a " << lhsClassRefp->prettyTypeName() << ", got " << rhsDtypep->prettyTypeName()); } + if (VN_IS(lhsDTypep->skipRefp(), DynArrayDType) && VN_IS(rhsp->dtypep()->skipRefp(), UnpackArrayDType)){ + VNRelinker relinker; + rhsp->unlinkFrBack(&relinker); + relinker.relink(new AstCvtUnpackedToQueue{ + rhsp->fileline(), VN_AS(rhsp, NodeExpr), lhsDTypep}); + } } static bool similarDTypeRecurse(const AstNodeDType* const node1p, const AstNodeDType* const node2p) { diff --git a/test_regress/t/t_unpacked_to_queue.py b/test_regress/t/t_unpacked_to_queue.py new file mode 100644 index 000000000..d4f986441 --- /dev/null +++ b/test_regress/t/t_unpacked_to_queue.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 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 + +import vltest_bootstrap + +test.scenarios('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_unpacked_to_queue.v b/test_regress/t/t_unpacked_to_queue.v new file mode 100644 index 000000000..16191e042 --- /dev/null +++ b/test_regress/t/t_unpacked_to_queue.v @@ -0,0 +1,100 @@ +// DESCRIPTION: Verilator: Casting queues and dynamic arrays +// into queues as function arguments +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 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=%0x exp=%0x (%s !== %s)\n", \ + `__FILE__,`__LINE__, (gotv), (expv), `"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); + + +class check #(parameter WIDTH=8); + static function automatic void check_array (int n, + logic [WIDTH-1:0] array []); + for (int r=0; r