Support parameter resolution of 1D unpacked array slices (#6257) (#6268)

This commit is contained in:
Michael Bedford Taylor 2025-08-06 14:29:40 -07:00 committed by GitHub
parent 6c1cfc68cf
commit 218659f4e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 170 additions and 0 deletions

View File

@ -161,6 +161,7 @@ Martin Stadler
Mateusz Gancarz
Matthew Ballance
Max Wipfli
Michael Bedford Taylor
Michael Bikovitsky
Michael Killough
Michal Czyz

View File

@ -2787,6 +2787,18 @@ class ConstVisitor final : public VNVisitor {
}
m_selp = nullptr;
}
// Evaluate a slice of an unpacked array. If constantification is
// required (m_required=true), call replaceWithSimulation() to compute
// the slice via simulation. Otherwise just iterate the children.
void visit(AstSliceSel* nodep) override {
// First constify or width any child nodes
iterateChildren(nodep);
if (!m_required) return; // Do nothing unless we are in parameter mode
// Fallback to simulation: this will invoke SimulateVisitor::visit(AstSliceSel*)
replaceWithSimulation(nodep);
}
void visit(AstCAwait* nodep) override {
m_hasJumpDelay = true;
iterateChildren(nodep);

View File

@ -732,6 +732,14 @@ class ParamProcessor final {
AstNode* const exprp = pinp->exprp();
longnamer += "_" + paramSmallName(srcModp, modvarp) + paramValueNumber(exprp);
any_overridesr = true;
} else if (VN_IS(pinp->exprp(), InitArray)) {
// Array assigned to scalar parameter. Treat the InitArray as a constant
// integer array and include it in the module name. Constantify nested
// expressions before mangling the value number.
V3Const::constifyParamsEdit(pinp->exprp());
longnamer += "_" + paramSmallName(srcModp, modvarp)
+ paramValueNumber(pinp->exprp());
any_overridesr = true;
} else {
V3Const::constifyParamsEdit(pinp->exprp());
// String constants are parsed as logic arrays and converted to strings in V3Const.

View File

@ -894,6 +894,40 @@ private:
clearOptimizable(nodep, "Array select of non-array");
}
}
// Evaluate a slice of an unpacked array. If the base value is a constant
// AstInitArray, build a new AstInitArray representing the slice and assign
// it as this node's value. New index 0 corresponds to the lowest index of
// the slice. Otherwise, mark this node as unoptimizable.
void visit(AstSliceSel* nodep) override {
checkNodeInfo(nodep);
iterateChildrenConst(nodep);
if (m_checkOnly || !optimizable()) return;
// Fetch the base constant array
if (AstInitArray* const initp = VN_CAST(fetchValueNull(nodep->fromp()), InitArray)) {
const VNumRange& sliceRange = nodep->declRange();
const uint32_t sliceElements = sliceRange.elements();
const int sliceLo = sliceRange.lo();
// Use this node's dtype for the slice array
AstNodeDType* const dtypep = nodep->dtypep()->skipRefp();
// Clone the default value from the base array, if present
AstNodeExpr* defaultp = nullptr;
if (initp->defaultp()) defaultp = initp->defaultp()->cloneTree(false);
AstInitArray* const newInitp = new AstInitArray{nodep->fileline(), dtypep, defaultp};
// Copy slice elements in ascending order
for (uint32_t idx = 0; idx < sliceElements; ++idx) {
const uint32_t baseIdx = sliceLo + idx;
AstNodeExpr* const itemp = initp->getIndexDefaultedValuep(baseIdx);
if (itemp) newInitp->addIndexValuep(idx, itemp->cloneTree(false));
}
// Assign the new constant array and track it for later deletion
setValue(nodep, newInitp);
m_reclaimValuesp.push_back(newInitp);
} else {
clearOptimizable(nodep, "Slice select of non-array");
}
}
void visit(AstBegin* nodep) override {
checkNodeInfo(nodep);
iterateChildrenConst(nodep);

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test constant parameter slicing of unpacked arrays (issue #6257)
#
# Copyright 2025 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('vlt')
test.compile(verilator_flags2=['--exe','--main','--timing'])
test.execute()
test.passes()

View File

@ -0,0 +1,98 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
`timescale 1ns/1ps
// Test constant parameter slicing of unpacked arrays with various slice ranges.
module issue_desc #(
parameter int els_p = 1,
parameter int val_p [els_p+1:2],
parameter int orig_els = 1
) ();
// Drop the lowest index (2) in each recursion: slice [high:3]
if (els_p > 1) begin : r
issue_desc #(
.els_p(els_p-1),
.val_p(val_p[els_p+1:3]),
.orig_els(orig_els)
) x();
end
initial begin
int expected = orig_els - els_p + 1;
if (val_p[2] !== expected) begin
$error("DESC wrong value %0d expected %0d in %m", val_p[2], expected);
$finish;
end
$display("%08x (desc %m)", val_p[2]);
end
endmodule
module issue_rev #(
parameter int els_p = 1,
parameter int val_p [2:els_p+1],
parameter int orig_els = 1
) ();
// Drop the lowest index (2) in each recursion: slice [3:high]
if (els_p > 1) begin : r
issue_rev #(
.els_p(els_p-1),
.val_p(val_p[3:els_p+1]),
.orig_els(orig_els)
) x();
end
initial begin
int expected = orig_els - els_p + 1;
if (val_p[2] !== expected) begin
$error("REV wrong value %0d expected %0d in %m", val_p[2], expected);
$finish;
end
$display("%08x (rev %m)", val_p[2]);
end
endmodule
module issue_def #(
parameter int els_p = 1,
// Internal default fill is zero; the test overrides this with DEADBEEF.
parameter int val_p [els_p+1:2] = '{default:0},
parameter int orig_els = 1
) ();
// Recursively slice off the lowest index (2)
if (els_p > 1) begin : r
issue_def #(
.els_p(els_p-1),
.val_p(val_p[els_p+1:3]),
.orig_els(orig_els)
) x();
end
initial begin
// Expect 32'hDEADBEEF when overridden by the top-level test.
if (val_p[2] !== 32'hDEADBEEF) begin
$error("DEF wrong value %0x expected DEADBEEF in %m", val_p[2]);
$finish;
end
$display("%08x (def %m)", val_p[2]);
end
endmodule
module t;
// For els_p=5, the range [els_p+1:2] is [6:2].
// Descending initializer: index 6=5,5=4,4=3,3=2,2=1.
parameter int val_desc [6:2] = '{5,4,3,2,1};
// Reverse slice initializer: ascending values on [2:6].
parameter int val_rev [2:6] = '{1,2,3,4,5};
// Override for default-array test: all elements set to 32'hDEADBEEF on [6:2].
parameter int val_def [6:2] = '{default: 32'hDEADBEEF};
issue_desc #(.els_p(5), .val_p(val_desc), .orig_els(5)) iss_desc();
issue_rev #(.els_p(5), .val_p(val_rev), .orig_els(5)) iss_rev();
issue_def #(.els_p(5), .val_p(val_def), .orig_els(5)) iss_def();
initial begin
#1;
$write("*-* All Finished *-*\\n");
$finish;
end
endmodule