Support elaboration-time printing of unpacked array with `%p` (#4732).

This commit is contained in:
Wilson Snyder 2025-08-21 21:44:31 -04:00
parent e202cb31d8
commit 457fcc267b
5 changed files with 153 additions and 1 deletions

View File

@ -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)

View File

@ -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)");

View File

@ -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; }

View File

@ -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()

View File

@ -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