Improve parameterized hierarchical block substitution (#7009) (#8605)

This commit is contained in:
Michael Bedford Taylor
2026-10-04 10:15:58 -04:00
committed by GitHub
parent 19a3d2d6b3
commit 4e194cc3ec
23 changed files with 6843 additions and 5167 deletions
+12
View File
@@ -162,6 +162,18 @@ Hierarchy blocks have some limitations, including:
- Delays are not allowed in hierarchy blocks.
- Setting type parameters of a hierarchy block is unsupported.
- A ``defparam`` setting a parameter inside a hierarchy block is
unsupported.
- String parameter values of a hierarchy block containing a newline, a
double quote, ``/*``, or whitespace followed by ``//`` are unsupported.
- Values that are equal but differ in width or signedness (``5`` and
``8'd5``), including relative to the default value, are unsupported for a
hierarchy block parameter declared without a data type.
But, the following usage is supported:
- Nested hierarchy blocks. A hierarchy block may instantiate other
+4
View File
@@ -1253,6 +1253,10 @@ public:
int instrCount() const override { return widthInstrs(); }
bool isEqAllOnes() const { return num().isEqAllOnes(width()); }
bool isEqAllOnesV() const { return num().isEqAllOnes(widthMinV()); }
// Whether a parameter declared without a data type takes the same type from this value
// as from 'samep': both real, both string, or both integral with the same width and
// signedness. Unlike AstNodeDType::similarDType, ignores whether two-state or four-state.
bool sameValueType(const AstConst* samep) const;
// Parse string and create appropriate type of AstConst.
// May return nullptr on parse failure.
static AstConst* parseParamLiteral(FileLine* fl, const string& literal);
+6
View File
@@ -1183,6 +1183,12 @@ AstConst* AstConst::parseParamLiteral(FileLine* fl, const string& literal) {
}
return nullptr;
}
bool AstConst::sameValueType(const AstConst* samep) const {
if (num().isDouble() != samep->num().isDouble()) return false;
if (num().isString() != samep->num().isString()) return false;
// Constant folding can leave the signedness on the dtype, not the V3Number
return num().isOpaque() || (width() == samep->width() && isSigned() == samep->isSigned());
}
AstConst::~AstConst() {
// Only rare constants carry originating parameter-name metadata. For all other AstConst nodes,
// the V3Number bit keeps this destructor from touching AstNetlist's side table. When the bit
+18 -1
View File
@@ -95,6 +95,7 @@
#include "V3Stats.h"
#include "V3String.h"
#include <cctype>
#include <memory>
#include <sstream>
#include <utility>
@@ -125,6 +126,19 @@ static void V3HierWriteCommonInputs(const V3HierBlock* hblockp, std::ostream* of
//######################################################################
bool V3HierBlock::stringParamPassable(const string& value) {
// stringifyParams writes string values in double quotes into the arguments file, which
// V3Options::parseOptsFile reads by lines, removing '/*' comments, and '//' comments after
// whitespace, before splitting the quoted arguments. AstConst::parseParamLiteral for -G,
// and V3HierarchicalBlockOption, then end each value at the next double quote.
if (value.find_first_of("\n\"") != string::npos) return false;
if (value.find("/*") != string::npos) return false;
for (size_t pos = value.find("//"); pos != string::npos; pos = value.find("//", pos + 1)) {
if (pos > 0 && std::isspace(static_cast<unsigned char>(value[pos - 1]))) return false;
}
return true;
}
V3HierBlock::StrGParams V3HierBlock::stringifyParams(const std::vector<AstVar*>& gparams,
bool forGOption) {
StrGParams strParams;
@@ -148,7 +162,10 @@ V3HierBlock::StrGParams V3HierBlock::stringifyParams(const std::vector<AstVar*>&
if (!forGOption) s = VString::quoteBackslash(s);
s = VString::quoteStringLiteralForShell(s);
} else { // Either signed or unsigned integer.
s = constp->num().ascii(true, true);
// Constant folding can leave the signedness on the dtype, not the V3Number.
V3Number num{constp->num()};
num.isSigned(constp->isSigned());
s = num.ascii(true, true);
s = VString::quoteAny(s, '\'', '\\');
}
strParams.emplace_back(gparam->name(), s);
+3
View File
@@ -85,6 +85,9 @@ public:
const AstModule* modp() const { return m_modp; }
// Whether a string parameter value can be passed to a hierarchical block's Verilation
static bool stringParamPassable(const string& value) VL_PURE;
// For emitting Makefile and build definition JSON
VStringList commandArgs(bool forMkJson) const VL_MT_DISABLED;
VStringList hierBlockArgs() const VL_MT_DISABLED;
+72 -6
View File
@@ -57,6 +57,7 @@
#include "V3Case.h"
#include "V3Const.h"
#include "V3EmitV.h"
#include "V3HierBlock.h"
#include "V3LinkDotIfaceCapture.h"
#include "V3MemberMap.h"
#include "V3Os.h"
@@ -106,14 +107,16 @@ class ParameterizedHierBlocks final {
std::map<const V3HierarchicalBlockOption*, ParamConstMap> m_hierParams;
// Parameter variables of hierarchical blocks
std::map<const std::string, GParamsMap> m_modParams;
// Formals declared without a type, whose type is that of the assigned value
std::set<const AstVar*> m_untypedParams;
// METHODS
public:
ParameterizedHierBlocks(const V3HierBlockOptSet& hierOpts, AstNetlist* nodep)
: m_hierSubRun{(!v3Global.opt.hierBlocks().empty() || v3Global.opt.hierChild())
// Exclude consolidation
&& !v3Global.opt.hierParamFile().empty()} {
// A type parameter file only describes a child's own types; the final consolidation
// run has none, but must still substitute the libraries it consumes.
: m_hierSubRun{!v3Global.opt.hierBlocks().empty() || v3Global.opt.hierChild()} {
for (const auto& hierOpt : hierOpts) {
m_hierBlockOptsByOrigName.emplace(hierOpt.second.origName(), &hierOpt.second);
const V3HierarchicalBlockOption::ParamStrMap& params = hierOpt.second.params();
@@ -144,6 +147,12 @@ public:
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
if (AstVar* const varp = VN_CAST(stmtp, Var)) {
if (varp->isGParam()) defParamIt->second.emplace(varp->name(), varp);
// Recorded before V3Param resolves the type from the assigned value.
const AstBasicDType* const dtypep
= VN_CAST(varp->childDTypep(), BasicDType);
if (varp->isGParam() && dtypep && dtypep->implicit()) {
m_untypedParams.emplace(varp);
}
}
}
}
@@ -161,6 +170,10 @@ public:
= m_hierBlockOptsByOrigName.equal_range(origName);
const auto paramsIt = m_modParams.find(origName);
UASSERT_OBJ(paramsIt != m_modParams.end(), modp, origName << " must be registered");
// An untyped parameter takes the type of its value, but libraries are distinguished
// only by parameter values. This is a pin whose value equals that of a library or the
// default, but whose type differs.
const AstPin* typeMismatchPinp = nullptr;
HierMapIt hierIt;
for (hierIt = candidates.first; hierIt != candidates.second; ++hierIt) {
bool found = true;
@@ -175,10 +188,14 @@ public:
"parameter for a hierarchical block must have been constified");
const auto paramIt = paramsIt->second.find(modvarp->name());
UASSERT_OBJ(paramIt != paramsIt->second.end(), modvarp, "must be registered");
const bool untyped = m_untypedParams.count(paramIt->second);
AstConst* const defValuep = VN_CAST(paramIt->second->valuep(), Const);
if (defValuep && areSame(constp, defValuep)) {
UINFO(5, "Setting default value of " << constp << " to " << modvarp);
continue; // Skip this parameter because setting the same value
if (!untyped || constp->sameValueType(defValuep)) {
UINFO(5, "Setting default value of " << constp << " to " << modvarp);
continue; // Skip this parameter because setting the same value
}
typeMismatchPinp = pinp;
}
const auto pIt = vlstd::as_const(params).find(modvarp->name());
UINFO(5, "Comparing " << modvarp->name() << " " << constp);
@@ -187,6 +204,11 @@ public:
found = false;
break;
}
if (untyped && !constp->sameValueType(pIt->second.get())) {
typeMismatchPinp = pinp;
found = false;
break;
}
UINFO(5, "Matched " << modvarp->name() << " " << constp << " and "
<< pIt->second.get());
++paramIdx;
@@ -194,6 +216,15 @@ public:
}
if (found && paramIdx == hierIt->second->params().size()) break;
}
if (hierIt == candidates.second && typeMismatchPinp) {
typeMismatchPinp->v3warn(
E_UNSUPPORTED, "Unsupported: Untyped parameter "
<< typeMismatchPinp->modVarp()->prettyNameQ()
<< " of hierarchical block " << AstNode::prettyNameQ(origName)
<< " given equal values of different width or"
" signedness");
return nullptr;
}
UASSERT_OBJ(hierIt != candidates.second, firstPinp, "No --lib-create wrapper found");
// parameter settings will be removed in the bottom of caller visitCell().
const HierBlockModMap::const_iterator modIt
@@ -306,6 +337,8 @@ class ParamProcessor final {
using DefaultValueMap = std::map<std::string, AstNode*>;
// Default parameter values of hierarchical blocks
std::map<AstNodeModule*, DefaultValueMap> m_defaultParameterValues;
// Module -> a defparam in it or below it, or nullptr (see nestedDefparamp)
std::map<const AstNodeModule*, const AstPin*> m_nestedDefparamps;
VNDeleter m_deleter; // Used to delay deletion of nodes
// Class default type paramater dependencies
std::vector<std::pair<AstParamTypeDType*, int>> m_classTypeParams;
@@ -628,6 +661,23 @@ class ParamProcessor final {
std::set<const AstNodeModule*> visited;
return hasDescendantDefparams(modp, visited);
}
// Return a defparam in the module or below it. Unlike hasDescendantDefparams, also look
// inside generate constructs. Memoized, so each module is searched once.
const AstPin* nestedDefparamp(const AstNodeModule* modp) {
const auto pair = m_nestedDefparamps.emplace(modp, nullptr);
if (!pair.second) return pair.first->second; // Searched, or being searched
const AstPin* foundp = nullptr;
modp->exists([&](const AstCell* cellp) {
for (const AstPin* pinp = cellp->paramsp(); pinp && !foundp;
pinp = VN_AS(pinp->nextp(), Pin)) {
if (!pinp->paramPath().empty()) foundp = pinp;
}
if (!foundp && cellp->modp()) foundp = nestedDefparamp(cellp->modp());
return foundp != nullptr;
});
pair.first->second = foundp;
return foundp;
}
// Check if parameter setting during instantiation is simple enough for hierarchical Verilation
void checkSupportedParam(AstNodeModule* modp, AstPin* pinp) const {
// InitArray is not supported because that can not be set via -G
@@ -636,6 +686,12 @@ class ParamProcessor final {
bool supported = false;
if (const AstConst* const constp = VN_CAST(pinp->exprp(), Const)) {
supported = !constp->isOpaque();
if (constp->num().isString()
&& !V3HierBlock::stringParamPassable(constp->num().toString())) {
pinp->v3warn(E_UNSUPPORTED, "Unsupported: String value of hierarchical block"
" parameter with newline, double quote, '/*',"
" or whitespace before '//'");
}
}
if (!supported) {
pinp->v3error(
@@ -643,6 +699,11 @@ class ParamProcessor final {
<< " has hier_block metacomment, hierarchical Verilation"
<< " supports only integer/floating point/string and type param parameters");
}
} else if (const AstParamTypeDType* const typep = pinp->modPTypep()) {
// Libraries are found by comparing values only
pinp->v3warn(E_UNSUPPORTED, "Unsupported: Setting type parameter "
<< typep->prettyNameQ() << " of hierarchical block "
<< AstNode::prettyNameQ(modp->origName()));
}
}
bool moduleExists(const string& modName) const {
@@ -675,6 +736,11 @@ class ParamProcessor final {
const auto pair = m_defaultParameterValues.emplace(
std::piecewise_construct, std::forward_as_tuple(modp), std::forward_as_tuple());
if (pair.second) { // Not cached yet, so check parameters
// A separately compiled library cannot receive defparams aimed into its body
if (const AstPin* const defparamp = nestedDefparamp(modp)) {
defparamp->v3warn(E_UNSUPPORTED, "Unsupported: defparam inside hierarchical block "
<< AstNode::prettyNameQ(modp->origName()));
}
// Using map with key=string so that we can scan it in deterministic order
DefaultValueMap params;
for (AstNode* stmtp = modp->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
@@ -2010,7 +2076,7 @@ class ParamProcessor final {
if (m_hierBlocks.hierSubRun() && m_hierBlocks.isHierBlock(srcModp->origName())) {
AstNodeModule* const paramedModp
= m_hierBlocks.findByParams(srcModp->origName(), paramsp, m_modp);
UASSERT_OBJ(paramedModp, nodep, "Failed to find sub-module for hierarchical block");
if (!paramedModp) return nullptr; // Unsupported, already reported
paramedModp->dead(false);
// We need to relink the pins to the new module
relinkPinsByName(pinsp, paramedModp);
+4
View File
@@ -63,10 +63,14 @@ module t #(
`ifdef PROTLIB_TOP
secret i_secred(.clk(clk));
`else
// Outputs of hierarchical blocks depend on all their inputs, so this registered ring
// through the blocks appears combinational
/* verilator lint_off UNOPTFLAT */
wire [7:0] out0;
wire [7:0] out1;
wire [7:0] out2;
wire [7:0] out3;
/* verilator lint_on UNOPTFLAT */
wire [7:0] out3_2;
wire [7:0] out5;
wire [7:0] out6;
+5
View File
@@ -3,6 +3,11 @@
21 | sub0 #(UNPACKED) i_sub0 (
| ^~~~~~~~
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error-UNSUPPORTED: t/t_hier_block0_bad.v:27:8: Unsupported: Setting type parameter 'T' of hierarchical block 'sub1'
: ... note: In instance 't'
27 | .T(logic [7:0])
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: t/t_hier_block0_bad.v:77:39: Cannot access non-port symbols inside hierarchical block
: ... note: In instance 't.i_sub0'
77 | $display("%m: i_sub.x: %d", i_sub.x);
+4 -4
View File
@@ -13,15 +13,15 @@ module t (
input clk
);
logic [7:0] c;
logic [6:0] c_param;
logic [7:0] c_param;
int cycles = 0;
blk u (
.clk(clk),
.cnt_o(c)
);
// Type parameter T keeps its default from my_pkg; setting it is unsupported
blk_param #(
.T(logic [6:0]),
.STEP(3)
) u_param (
.clk(clk),
@@ -31,10 +31,10 @@ module t (
always @(negedge clk) begin
cycles = cycles + 1;
`checkd(c, 8'(cycles));
`checkd(c_param, 7'(cycles * 3));
`checkd(c_param, 8'(cycles * 3));
if (cycles == 10) begin
`checkd(c, 8'd10);
`checkd(c_param, 7'd30);
`checkd(c_param, 8'd30);
$write("*-* All Finished *-*\n");
$finish;
end
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
# Otherwise a rerun skips the unchanged plan, and so the failing final Verilation
test.clean_objs()
# Reported when the final Verilation, run by make, looks up the libraries
test.compile(verilator_flags2=['--hierarchical'], fails='any')
for line in (19, 21):
test.file_grep(
test.compile_log_filename,
r"%Error-UNSUPPORTED: t/t_hier_block_param_kind_unsup.v:" + str(line) +
r":10: Unsupported: Untyped parameter 'P' of hierarchical block 'mid' given equal values of different width or signedness"
)
test.passes()
@@ -0,0 +1,38 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t (
input clk
);
// The untyped parameter is 32 bits wide for 5, but 8 bits for 8'd5, so they need different
// libraries, but libraries are distinguished by parameter values only
int width0, width1, width2;
mid #(.P(5)) m0 (.width(width0));
mid #(.P(8'd5)) m1 (.width(width1));
// Likewise relative to the default value
mid #(.P(8'd0)) m2 (.width(width2));
always @(posedge clk) begin
`checkd(width0, 32);
`checkd(width1, 8);
`checkd(width2, 8);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module mid #(
parameter P = 0
) (
output int width
);
/*verilator hier_block*/
assign width = $bits(P);
endmodule
@@ -4,14 +4,17 @@
# 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-FileCopyrightText: 2024 Wilson Snyder
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.scenarios('vlt_all')
test.compile(verilator_flags2=['--hierarchical'])
threads = 2 if test.vltmt else 1
test.compile(verilator_flags2=['--hierarchical', '-DROOT_THREADS=' + str(threads)],
threads=threads)
test.execute()
+148
View File
@@ -0,0 +1,148 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// Parameterized hierarchical blocks must be replaced by their libraries in the final
// compilation (#7009), each by the library built for its parameter values.
// verilog_format: off
`define stop $stop
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t (
input clk
);
// Library models: a0, a1, a2, m0 and the acc instance inside it, s0, and s1
localparam int LIBRARY_MODELS = 7;
int cyc = 0;
longint data[6], out[6];
longint expected[6] = '{default: 0};
for (genvar i = 0; i < 6; ++i) begin : stim
assign data[i] = longint'(cyc * (i + 3) + i * 7);
end
// K=3 is shared by two instances with independent state
acc #(
.K(3)
) a0 (
.clk,
.data(data[0]),
.out(out[0])
);
acc #(
.K(3)
) a1 (
.clk,
.data(data[1]),
.out(out[1])
);
// The untyped parameter is a signed -1, which must reach the library
acc #(
.K(-1)
) a2 (
.clk,
.data(data[2]),
.out(out[2])
);
// Also the acc instance inside is a library
mid #(
.P(5)
) m0 (
.clk,
.data(data[3]),
.out(out[3])
);
// Strings that the generated child arguments carry
str_blk #(
.S("p q,r")
) s0 (
.clk,
.data(data[4]),
.out(out[4])
);
str_blk #(
.S("a//b\\c */")
) s1 (
.clk,
.data(data[5]),
.out(out[5])
);
function automatic longint str_hash(string s);
str_hash = 0;
for (int i = 0; i < s.len(); ++i) str_hash = str_hash * 31 + longint'(s[i]);
endfunction
always @(posedge clk) begin
// Each library adds its value and the parameter's width, so a wrong library changes the sum
expected[0] <= expected[0] + data[0] + 3 + 32;
expected[1] <= expected[1] + data[1] + 3 + 32;
expected[2] <= expected[2] + data[2] - 1 + 32;
expected[3] <= expected[3] + data[3] + 5 + 32;
expected[4] <= expected[4] + data[4] + str_hash("p q,r");
expected[5] <= expected[5] + data[5] + str_hash("a//b\\c */");
end
always @(negedge clk) begin
foreach (out[i]) `checkd(out[i], expected[i]);
cyc <= cyc + 1;
if (cyc == 60) begin
`ifdef VERILATOR
// Every block instance is a library model, with its own thread
`checkd($c32("Verilated::threadContextp()->threadsInModels()"),
`ROOT_THREADS + LIBRARY_MODELS);
`endif
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module acc #(
parameter K = 0
) (
input clk,
input longint data,
output longint out
);
/*verilator hier_block*/
longint state = 0;
always @(posedge clk) state <= state + data + longint'(K) + longint'($bits(K));
assign out = state;
endmodule
module mid #(
parameter P = 0
) (
input clk,
input longint data,
output longint out
);
/*verilator hier_block*/
acc #(
.K(P)
) inner (
.clk,
.data,
.out
);
endmodule
module str_blk #(
parameter string S = "x"
) (
input clk,
input longint data,
output longint out
);
/*verilator hier_block*/
function automatic longint str_hash();
str_hash = 0;
for (int i = 0; i < S.len(); ++i) str_hash = str_hash * 31 + longint'(S[i]);
endfunction
longint state = 0;
always @(posedge clk) state <= state + data + str_hash();
assign out = state;
endmodule
@@ -0,0 +1,53 @@
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:10:12: Unsupported: Setting type parameter 'T' of hierarchical block 'typed'
: ... note: In instance 't'
10 | typed #(.T(logic signed [6:0])) t0 ();
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:11:12: Unsupported: Setting type parameter 'T' of hierarchical block 'typed'
: ... note: In instance 't'
11 | typed #(.T(word_t)) t1 ();
| ^
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:13:8: Unsupported: Setting type parameter 'TIN' of hierarchical block 'typed2'
: ... note: In instance 't'
13 | .TIN(logic [3:0]),
| ^~~
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:14:8: Unsupported: Setting type parameter 'TOUT' of hierarchical block 'typed2'
: ... note: In instance 't'
14 | .TOUT(logic [7:0])
| ^~~~
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:16:14: Unsupported: Setting type parameter 'T' of hierarchical block 'nonansi'
: ... note: In instance 't'
16 | nonansi #(.T(logic [1:0])) t3 ();
| ^
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:23:14: Unsupported: String value of hierarchical block parameter with newline, double quote, '/*', or whitespace before '//'
: ... note: In instance 't'
23 | str_blk #(.S(S0)) s0 ();
| ^
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:24:14: Unsupported: String value of hierarchical block parameter with newline, double quote, '/*', or whitespace before '//'
: ... note: In instance 't'
24 | str_blk #(.S(S1)) s1 ();
| ^
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:25:14: Unsupported: String value of hierarchical block parameter with newline, double quote, '/*', or whitespace before '//'
: ... note: In instance 't'
25 | str_blk #(.S(S2)) s2 ();
| ^
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:26:14: Unsupported: String value of hierarchical block parameter with newline, double quote, '/*', or whitespace before '//'
: ... note: In instance 't'
26 | str_blk #(.S(S3)) s3 ();
| ^
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:29:23: Unsupported: defparam inside hierarchical block 'fixed'
: ... note: In instance 't.f0'
29 | defparam f0.child.N = 2;
| ^
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:83:20: Unsupported: defparam inside hierarchical block 'nested'
83 | defparam child.N = 3;
| ^
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:91:22: Unsupported: defparam inside hierarchical block 'generated'
: ... note: In instance 't.g0'
91 | defparam child.N = 4;
| ^
%Error-UNSUPPORTED: t/t_hier_block_param_lib_unsup.v:61:12: Unsupported: Setting type parameter 'T' of hierarchical block 'typed'
: ... note: In instance 't.o0'
61 | typed #(.T(T)) inner ();
| ^
%Error: Exiting due to
@@ -4,15 +4,13 @@
# 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-FileCopyrightText: 2024 Wilson Snyder
# SPDX-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.scenarios('vlt')
test.compile(verilator_flags2=['--hierarchical'])
test.execute()
test.lint(verilator_flags2=['--hierarchical'], fails=True, expect_filename=test.golden_filename)
test.passes()
@@ -0,0 +1,98 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
module t;
// Libraries are found by comparing parameter values, not types
typedef logic [5:0] word_t;
typed #(.T(logic signed [6:0])) t0 ();
typed #(.T(word_t)) t1 ();
typed2 #(
.TIN(logic [3:0]),
.TOUT(logic [7:0])
) t2 ();
nonansi #(.T(logic [1:0])) t3 ();
outer o0 ();
// The generated child arguments cannot carry these strings
localparam string S0 = "p/*q*/r";
localparam string S1 = "p //q";
localparam string S2 = "p\"q";
localparam string S3 = "p\nq";
str_blk #(.S(S0)) s0 ();
str_blk #(.S(S1)) s1 ();
str_blk #(.S(S2)) s2 ();
str_blk #(.S(S3)) s3 ();
// A library cannot receive defparams
fixed f0 ();
defparam f0.child.N = 2;
nested n0 ();
generated g0 ();
endmodule
module typed #(
parameter type T = logic [6:0]
);
/*verilator hier_block*/
T x;
endmodule
module typed2 #(
parameter type TIN = logic,
parameter type TOUT = logic
);
/*verilator hier_block*/
TIN i;
TOUT o;
endmodule
module nonansi;
/*verilator hier_block*/
parameter type T = logic;
T x;
endmodule
// Sets a type parameter of the block inside it
module outer #(
parameter type T = logic [4:0]
);
/*verilator hier_block*/
typed #(.T(T)) inner ();
endmodule
module str_blk #(
parameter string S = "x"
);
/*verilator hier_block*/
endmodule
module fixed;
/*verilator hier_block*/
leaf child ();
endmodule
// Defparam in a module below the block
module nested;
/*verilator hier_block*/
mid m ();
endmodule
module mid;
leaf child ();
defparam child.N = 3;
endmodule
// Defparam inside a generate construct
module generated;
/*verilator hier_block*/
if (1) begin : g
leaf child ();
defparam child.N = 4;
end
endmodule
module leaf #(
parameter int N = 7
);
endmodule
@@ -1,41 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2024 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t (
input clk
);
logic [31:0] in1;
logic [31:0] out1;
assign in1 = 0;
Test #(
.TYPE_IN(logic [31:0]),
.TYPE_OUT(logic [31:0])
) test (
.out(out1),
.in(in1)
);
always @(posedge clk) begin
if (out1 !== ~in1) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module Test #(
parameter type TYPE_IN = logic [4:0],
parameter type TYPE_OUT = logic [7:0]
) (
output TYPE_IN out,
input TYPE_OUT in
);
/*verilator hier_block*/
assign out = ~in;
endmodule
@@ -1,18 +0,0 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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-FileCopyrightText: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=['--hierarchical'])
test.execute()
test.passes()
@@ -1,52 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2024 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t (
input clk
);
logic [21:0] in1;
logic [21:0] out1;
assign in1 = 0;
Test #(
.TYPE_t(logic [21:0])
) test (
.out(out1),
.in(in1)
);
logic [63:0] in2;
logic [63:0] out2;
assign in2 = 0;
Test #(
.TYPE_t(logic [63:0])
) test2 (
.out(out2),
.in(in2)
);
always @(posedge clk) begin
if (out1 !== ~in1) $stop;
if (out2 !== ~in2) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module Test #(
parameter type TYPE_t = logic [4:0]
) (
output TYPE_t out,
input TYPE_t in
);
/*verilator hier_block*/
assign out = ~in;
endmodule
@@ -1,18 +0,0 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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-FileCopyrightText: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=['--hierarchical'])
test.execute()
test.passes()
@@ -1,68 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2024 Antmicro
// SPDX-License-Identifier: CC0-1.0
module t (
input clk
);
logic [2:0] in1;
logic [2:0] out1;
assign in1 = 0;
Test #(
.TYPE_t(logic [2:0])
) test (
.out(out1),
.in(in1)
);
logic [3:0] in2;
logic [3:0] out2;
assign in2 = 0;
Test #(
.TYPE_t(logic [3:0])
) test2 (
.out(out2),
.in(in2)
);
always @(posedge clk) begin
if (out1 !== ~in1) $stop;
if (out2 !== ~in2) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
module Test #(
parameter type TYPE_t = logic [5:0]
) (
output TYPE_t out,
input TYPE_t in
);
/*verilator hier_block*/
SubTest #(
.TYPE_t(TYPE_t)
) subTest (
.out(out),
.in(in)
);
endmodule
module SubTest #(
parameter type TYPE_t = logic [8:0]
) (
output TYPE_t out,
input TYPE_t in
);
/*verilator hier_block*/
assign out = ~in;
endmodule
@@ -1,19 +0,0 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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-FileCopyrightText: 2024 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.top_filename = "t/t_typedef_param.v"
test.compile(verilator_flags2=["--hierarchical"])
test.execute()
test.passes()
File diff suppressed because it is too large Load Diff