Support elaboration-time printing of unpacked array with `%p` (#4732).
This commit is contained in:
parent
e202cb31d8
commit
457fcc267b
1
Changes
1
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)
|
||||
|
|
|
|||
|
|
@ -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)");
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue