diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 666570d1e..1253c8527 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -211,7 +211,7 @@ public: } // HashedDT doesn't recurse, so need to check children bool similarDTypeNode(const AstNodeDType* samep) const override { const AstNodeArrayDType* const asamep = VN_DBG_AS(samep, NodeArrayDType); - return hi() == asamep->hi() && rangenp()->sameTree(asamep->rangenp()) + return elementsConst() == asamep->elementsConst() && subDTypep()->similarDType(asamep->subDTypep()); } AstNodeDType* getChildDTypep() const override { return childDTypep(); } @@ -1452,6 +1452,7 @@ public: const AstUnpackArrayDType* const sp = VN_DBG_AS(samep, UnpackArrayDType); return m_isCompound == sp->m_isCompound; } + bool similarDTypeNode(const AstNodeDType* samep) const override; bool isAggregateType() const override { return true; } // Outer dimension comes first. The first element is this node. std::vector unpackDimensions(); diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index a4119ead5..3cf464d23 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -282,7 +282,9 @@ bool AstBasicDType::similarDTypeNode(const AstNodeDType* samep) const { || (m.m_keyword == VBasicDTypeKwd::LOGIC && sp->m.m_keyword == VBasicDTypeKwd::LOGIC_IMPLICIT))) return false; - if (!(m.m_nrange == sp->m.m_nrange)) return false; + // IEEE 1800-2023 6.22.2: equivalent by bit width, not range direction + if (m.m_nrange.ranged() != sp->m.m_nrange.ranged()) return false; + if (m.m_nrange.elements() != sp->m.m_nrange.elements()) return false; // Squash so NOSIGN == UNSIGNED if (numeric().isSigned() != sp->numeric().isSigned()) return false; if (!rangep() && !sp->rangep()) return true; @@ -3003,6 +3005,11 @@ bool AstWildcardArrayDType::similarDTypeNode(const AstNodeDType* samep) const { const AstWildcardArrayDType* const asamep = VN_DBG_AS(samep, WildcardArrayDType); return asamep->subDTypep() && subDTypep()->similarDType(asamep->subDTypep()); } +bool AstUnpackArrayDType::similarDTypeNode(const AstNodeDType* samep) const { + const AstUnpackArrayDType* const asamep = VN_DBG_AS(samep, UnpackArrayDType); + return hi() == asamep->hi() && rangep()->sameTree(asamep->rangep()) + && subDTypep()->similarDType(asamep->subDTypep()); +} void AstSampleQueueDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "[*]"; diff --git a/test_regress/t/t_ref_arg_array_range_dir.py b/test_regress/t/t_ref_arg_array_range_dir.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_ref_arg_array_range_dir.py @@ -0,0 +1,18 @@ +#!/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('simulator') + +test.compile(verilator_flags2=["--binary"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_ref_arg_array_range_dir.v b/test_regress/t/t_ref_arg_array_range_dir.v new file mode 100644 index 000000000..d0b222d89 --- /dev/null +++ b/test_regress/t/t_ref_arg_array_range_dir.v @@ -0,0 +1,68 @@ +// 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 + +// Ref args with a packed-array range-direction mismatch must be accepted. +// IEEE 1800-2023 6.22.2: packed types are equivalent by bit width, not range +// direction, so [15:0] and [0:15] are compatible for a ref port. + +// verilator lint_off ASCRANGE + +// verilog_format: off +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +module t; + function automatic void fill_desc(ref logic [3:0][7:0] arr); +`ifdef T_NOINLINE + // verilator no_inline_task +`endif + arr = 32'hdead_beef; + endfunction + + function automatic void fill_asc(ref logic [0:3][7:0] arr); +`ifdef T_NOINLINE + // verilator no_inline_task +`endif + arr = 32'hdead_beef; + endfunction + + // Inner (basic-dtype) range-direction mismatch. + function automatic void fill_inner_desc(ref logic [1:0][15:0] arr); +`ifdef T_NOINLINE + // verilator no_inline_task +`endif + arr = 32'hdead_beef; + endfunction + + function automatic void fill_inner_asc(ref logic [1:0][0:15] arr); +`ifdef T_NOINLINE + // verilator no_inline_task +`endif + arr = 32'hdead_beef; + endfunction + + logic [3:0][7:0] a; // descending outer + logic [0:3][7:0] b; // ascending outer + logic [1:0][15:0] c; // descending inner + logic [1:0][0:15] d; // ascending inner + + initial begin + // Outer-dimension direction mismatch + a = '0; fill_desc(a); `checkh(a, 32'hdead_beef); + b = '0; fill_desc(b); `checkh(b, 32'hdead_beef); + a = '0; fill_asc(a); `checkh(a, 32'hdead_beef); + b = '0; fill_asc(b); `checkh(b, 32'hdead_beef); + // Inner-dimension direction mismatch + c = '0; fill_inner_desc(c); `checkh(c, 32'hdead_beef); + d = '0; fill_inner_desc(d); `checkh(d, 32'hdead_beef); + c = '0; fill_inner_asc(c); `checkh(c, 32'hdead_beef); + d = '0; fill_inner_asc(d); `checkh(d, 32'hdead_beef); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_ref_arg_array_range_dir_bad.out b/test_regress/t/t_ref_arg_array_range_dir_bad.out new file mode 100644 index 000000000..69d46e396 --- /dev/null +++ b/test_regress/t/t_ref_arg_array_range_dir_bad.out @@ -0,0 +1,6 @@ +%Error: t/t_ref_arg_array_range_dir_bad.v:20:16: Ref argument requires matching types; port 'arr' requires 'logic[31:0]$[0:3]' but connection is 'logic[31:0]$[3:0]'. + : ... note: In instance 't' + 20 | initial fill(a); + | ^ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: Exiting due to diff --git a/test_regress/t/t_ref_arg_array_range_dir_bad.py b/test_regress/t/t_ref_arg_array_range_dir_bad.py new file mode 100755 index 000000000..38cf36b43 --- /dev/null +++ b/test_regress/t/t_ref_arg_array_range_dir_bad.py @@ -0,0 +1,16 @@ +#!/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('linter') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_ref_arg_array_range_dir_bad.v b/test_regress/t/t_ref_arg_array_range_dir_bad.v new file mode 100644 index 000000000..3e7b3f532 --- /dev/null +++ b/test_regress/t/t_ref_arg_array_range_dir_bad.v @@ -0,0 +1,21 @@ +// 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 + +// Unlike packed arrays, unpacked-array element correspondence is range-direction +// dependent, so a 'ref' port of type logic[31:0]$[0:3] must reject an actual of +// type logic[31:0]$[3:0] (see t_ref_arg_array_range_dir for the packed case). + +// verilator lint_off ASCRANGE + +module t; + function automatic void fill(ref logic [31:0] arr [0:3]); + arr[0] = '0; + endfunction + + logic [31:0] a [3:0]; + + initial fill(a); +endmodule diff --git a/test_regress/t/t_ref_arg_array_range_dir_noinline.py b/test_regress/t/t_ref_arg_array_range_dir_noinline.py new file mode 100755 index 000000000..5f7f075f6 --- /dev/null +++ b/test_regress/t/t_ref_arg_array_range_dir_noinline.py @@ -0,0 +1,19 @@ +#!/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('simulator') +test.top_filename = "t/t_ref_arg_array_range_dir.v" + +test.compile(verilator_flags2=["--binary"], v_flags2=['+define+T_NOINLINE']) + +test.execute() + +test.passes()