From f2e05bc0b7b744e401c0cd1f9c9b59849f6d5f3f Mon Sep 17 00:00:00 2001 From: Jonathan Drolet Date: Thu, 27 Nov 2025 14:17:18 -0500 Subject: [PATCH] Support unpacked struct in localparam (#6053 partial) (#6708) --- src/V3Const.cpp | 32 ++++++++++++ test_regress/t/t_struct_unpacked_param.py | 18 +++++++ test_regress/t/t_struct_unpacked_param.v | 59 +++++++++++++++++++++++ 3 files changed, 109 insertions(+) create mode 100755 test_regress/t/t_struct_unpacked_param.py create mode 100644 test_regress/t/t_struct_unpacked_param.v diff --git a/src/V3Const.cpp b/src/V3Const.cpp index bb0d69434..a058746d1 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2931,6 +2931,31 @@ class ConstVisitor final : public VNVisitor { replaceWithSimulation(nodep); } + void visit(AstStructSel* nodep) override { + iterateChildren(nodep); + + if (VN_IS(nodep->fromp(), ConsPackUOrStruct)) { + const AstConsPackUOrStruct* const consp = VN_AS(nodep->fromp(), ConsPackUOrStruct); + for (AstConsPackMember* memberp = consp->membersp(); memberp; + memberp = VN_AS(memberp->nextp(), ConsPackMember)) { + + if (memberp->dtypep() && memberp->dtypep()->name() == nodep->name()) { + AstNode* const valuep = memberp->rhsp(); + + if (VN_IS(valuep, Const)) { + const V3Number& num = VN_AS(valuep, Const)->num(); + VL_DO_DANGLING(replaceNum(nodep, num), nodep); + } else { + AstNode* const newp = valuep->cloneTree(false); + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } + break; + } + } + } + } + void visit(AstCAwait* nodep) override { m_hasJumpDelay = true; iterateChildren(nodep); @@ -2976,6 +3001,11 @@ class ConstVisitor final : public VNVisitor { nodep->replaceWithKeepDType(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); did = true; + } else if (m_params && VN_IS(valuep, ConsPackUOrStruct)) { + AstNode* const newp = valuep->cloneTree(false); + nodep->replaceWithKeepDType(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + did = true; } else if (nodep->varp()->isParam() && VN_IS(valuep, Unbounded)) { AstNode* const newp = valuep->cloneTree(false); nodep->replaceWithKeepDType(newp); @@ -3654,6 +3684,8 @@ class ConstVisitor final : public VNVisitor { } void visit(AstInitArray* nodep) override { iterateChildren(nodep); } + void visit(AstConsPackUOrStruct* nodep) override { iterateChildren(nodep); } + void visit(AstConsPackMember* nodep) override { iterateChildren(nodep); } void visit(AstInitItem* nodep) override { iterateChildren(nodep); } void visit(AstUnbounded* nodep) override { iterateChildren(nodep); } // These are converted by V3Param. Don't constify as we don't want the diff --git a/test_regress/t/t_struct_unpacked_param.py b/test_regress/t/t_struct_unpacked_param.py new file mode 100755 index 000000000..c39e83d77 --- /dev/null +++ b/test_regress/t/t_struct_unpacked_param.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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("simulator") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_struct_unpacked_param.v b/test_regress/t/t_struct_unpacked_param.v new file mode 100644 index 000000000..634f3bc1b --- /dev/null +++ b/test_regress/t/t_struct_unpacked_param.v @@ -0,0 +1,59 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by Jonathan Drolet. +// 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; + + typedef struct { + int a; + int b; + int c; + } subsubstruct_t; + + typedef struct { + int a; + int b; + subsubstruct_t subsub; + int c; + } substruct_t; + + typedef substruct_t substruct_t_t; + + typedef struct { + int a; + int b; + substruct_t_t sub; + int c; + } struct_t; + + // Constant value unpacked struct support is limited at the moment. + // Only localparams are supported, returning constant unpacked structure + // from function or passing unpacked structure as parameters is not + // (yet) supported + localparam struct_t MY_STRUCT = '{a: 10, b: 5, c: 20, sub: '{a: 100, b: 200, c: 150, subsub: '{default: 10}}}; + + // Make standalone localparam to ensure MY_STRUCT is const + localparam int C = MY_STRUCT.c; + localparam int S_A = MY_STRUCT.sub.a; + localparam int SS_B = MY_STRUCT.sub.subsub.b; + + initial begin + `checkd(MY_STRUCT.a, 10); + `checkd(MY_STRUCT.b, 5); + `checkd(MY_STRUCT.c, 20); + `checkd(MY_STRUCT.sub.c, 150); + `checkd(C, 20); + `checkd(S_A, 100); + `checkd(SS_B, 10); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule