diff --git a/Changes b/Changes index 01781f87b..00801af05 100644 --- a/Changes +++ b/Changes @@ -100,6 +100,7 @@ Verilator 5.038 2025-07-08 * Support redeclaring type as non-type; major parsing change (#2412) (#6020) (#6042) (#6044). * Support scoped `new` (#4199). +* Support elaboration-time printing of unpacked array with `%p` (#4732). * Support constrained random for associative arrays (#5985) (#5986). [Yilou Wang] * Support assignments to concatenations with impure RHS (#6002). [Ryszard Rozak, Antmicro Ltd.] * Support SARIF JSON diagnostic output with `--diagnostics-sarif`. (#6017) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 8c851fae5..77a81b6d0 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2521,6 +2521,22 @@ class ConstVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep), nodep); return true; } + bool matchToStringNConst(AstToStringN* nodep) { + iterateChildren(nodep); + if (AstInitArray* const initp = VN_CAST(nodep->lhsp(), InitArray)) { + if (!(m_doExpensive || m_params)) return false; + // At present only support 1D unpacked arrays + const auto initOfConst = [](const AstNode* const nodep) -> bool { // + return VN_IS(nodep, Const) || VN_IS(nodep, InitItem); + }; + if (initp->initsp() && !initp->initsp()->forall(initOfConst)) return false; + if (initp->defaultp() && !initp->defaultp()->forall(initOfConst)) return false; + } else if (!VN_IS(nodep->lhsp(), Const)) { + return false; + } + replaceWithSimulation(nodep); + return true; + } int operandConcatMove(const AstConcat* nodep) { // CONCAT under concat (See moveConcat) // Return value: true indicates to do it; 2 means move to LHS @@ -3892,6 +3908,7 @@ class ConstVisitor final : public VNVisitor { TREEOPA("AstPutcN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)"); TREEOPA("AstSubstrN{$lhsp.castConst, $rhsp.castConst, $thsp.castConst}", "replaceConst(nodep)"); TREEOPA("AstCvtPackString{$lhsp.castConst}", "replaceConstString(nodep, VN_AS(nodep->lhsp(), Const)->num().toString())"); + TREEOP ("AstToStringN{matchToStringNConst(nodep)}", "DONE"); // Custom // Implied by AstIsUnbounded::numberOperate: V("AstIsUnbounded{$lhsp.castConst}", "replaceNum(nodep, 0)"); TREEOPV("AstIsUnbounded{$lhsp.castUnbounded}", "replaceNum(nodep, 1)"); diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 30e3468d9..e3b1d0545 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -410,6 +410,40 @@ private: } } + string toStringRecurse(AstNodeExpr* nodep) { + // Return string representation, or clearOptimizable + const AstNodeExpr* const valuep = fetchValue(nodep); + if (const AstConst* const avaluep = VN_CAST(valuep, Const)) { + return "'h"s + avaluep->num().displayed(nodep, "%0x"); + } + if (const AstInitArray* const avaluep = VN_CAST(valuep, InitArray)) { + string result = "'{"; + string comma; + if (VN_IS(nodep->dtypep(), AssocArrayDType)) { + if (avaluep->defaultp()) { + result += comma + "default:" + toStringRecurse(avaluep->defaultp()); + comma = ", "; + } + const auto& mapr = avaluep->map(); + for (const auto& itr : mapr) { + result += comma + cvtToStr(itr.first) + ":" + + toStringRecurse(itr.second->valuep()); + comma = ", "; + } + } else if (const AstUnpackArrayDType* const dtypep + = VN_CAST(nodep->dtypep(), UnpackArrayDType)) { + for (int n = 0; n < dtypep->elementsConst(); ++n) { + result += comma + toStringRecurse(avaluep->getIndexDefaultedValuep(n)); + comma = ", "; + } + } + result += "}"; + return result; + } + clearOptimizable(nodep, "Cannot convert to string"); // LCOV_EXCL_LINE + return ""; + } + // VISITORS void visit(AstAlways* nodep) override { if (jumpingOver()) return; @@ -1265,7 +1299,12 @@ private: if (!optimizable()) return; // Accelerate checkNodeInfo(nodep); iterateChildrenConst(nodep); - clearOptimizable(nodep, "Cannot convert to string"); // LCOV_EXCL_LINE + if (!optimizable()) return; + std::string result = toStringRecurse(nodep->lhsp()); + if (!optimizable()) return; + AstConst* const resultConstp = new AstConst{nodep->fileline(), AstConst::String{}, result}; + setValue(nodep, resultConstp); + m_reclaimValuesp.push_back(resultConstp); } void visit(AstCoverInc* nodep) override { m_isCoverage = true; } diff --git a/test_regress/t/t_display_p_elab.py b/test_regress/t/t_display_p_elab.py new file mode 100755 index 000000000..2b4837fcc --- /dev/null +++ b/test_regress/t/t_display_p_elab.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_st') + +test.compile(verilator_flags2=['--binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_display_p_elab.v b/test_regress/t/t_display_p_elab.v new file mode 100644 index 000000000..4affaf3b1 --- /dev/null +++ b/test_regress/t/t_display_p_elab.v @@ -0,0 +1,77 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`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); +`ifdef verilator + `define no_optimize(v) $c(v) +`else + `define no_optimize(v) (v) +`endif +// verilog_format: on + + +module t; + + // $sformatf is not supported as parameter by some simulators + + parameter int I = 234; + parameter string IS = $sformatf(">%p<", I); + initial `checks(IS, "> 234<"); + + parameter real R = 1.234; + parameter string RS = $sformatf(">%p<", R); + initial `checks(RS, ">1.234<"); + + int u[2]; + parameter int U[2] = '{5, 6}; + parameter string US = $sformatf(">%p<", U); + initial `checks(US, ">'{'h5, 'h6}<"); + +`ifndef VERILATOR + // Generally not supported by others + parameter int Q[$] = '{1, 2}; + parameter string QS = $sformatf(">%p<", Q); + initial `checks(QS, "x"); + + // Generally not supported by others + parameter int D[] = '{1, 2}; + parameter string DS = $sformatf(">%p<", D); + initial `checks(DS, ">'{1, 2}<"); + + parameter int A[int] = '{1: 10, 2: 20}; + parameter string AS = $sformatf(">%p<", A); + initial `checks(AS, "x"); + + typedef struct {int f, s;} s_t; + parameter s_t S = '{f: 10, s: 20}; + parameter string SS = $sformatf(">%p<", S); + initial `checks(SS, ">'{f:10, s:20}<"); +`endif + + // Original issue + function integer infofunc(); + $info("%p", U); + return 0; + endfunction + localparam integer return_val = infofunc(); + + string s; + + initial begin + #1; + u[0] = `no_optimize(5); + u[1] = `no_optimize(6); + s = $sformatf(">%p<", u); + `checks(s, ">'{'h5, 'h6}<"); + + `checks(US, ">'{'h5, 'h6}<"); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule