From 10c3320c6b27b55e07b16ee4524f4572bf8e7740 Mon Sep 17 00:00:00 2001 From: Robin Heinemann Date: Sat, 12 Apr 2025 13:35:37 +0200 Subject: [PATCH] Support soft unions (#5912) (#5932) --- docs/CONTRIBUTORS | 1 + src/V3AstNodeDType.h | 11 +++++++-- src/V3Width.cpp | 12 +++++++++- src/verilog.y | 8 +++---- test_regress/t/t_dpi_result_type_bad.v | 30 ++++++++++++------------ test_regress/t/t_export_packed_struct.v | 6 ++--- test_regress/t/t_export_packed_struct2.v | 8 +++---- test_regress/t/t_union_hard_bad.out | 6 +++++ test_regress/t/t_union_hard_bad.py | 16 +++++++++++++ test_regress/t/t_union_hard_bad.v | 25 ++++++++++++++++++++ test_regress/t/t_union_soft.py | 4 +++- test_regress/t/t_union_soft.v | 13 +++++++++- 12 files changed, 109 insertions(+), 31 deletions(-) create mode 100644 test_regress/t/t_union_hard_bad.out create mode 100644 test_regress/t/t_union_hard_bad.py create mode 100644 test_regress/t/t_union_hard_bad.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 7ee2c4306..377e50772 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -196,6 +196,7 @@ Ricardo Barbedo Richard Myers Risto Pejašinović Robert Balas +Robin Heinemann Rupert Swarbrick Ryan Ziegler Ryszard Rozak diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 36e5f05a2..bbcb8fcc3 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -1388,13 +1388,20 @@ public: string verilogKwd() const override { return "struct"; } }; class AstUnionDType final : public AstNodeUOrStructDType { + bool m_isSoft; // Is a "union soft" + public: // UNSUP: bool isTagged; // VSigning below is mispurposed to indicate if packed or not - AstUnionDType(FileLine* fl, VSigning numericUnpack) - : ASTGEN_SUPER_UnionDType(fl, numericUnpack) {} + // isSoft implies packed + AstUnionDType(FileLine* fl, bool isSoft, VSigning numericUnpack) + : ASTGEN_SUPER_UnionDType(fl, numericUnpack) + , m_isSoft(isSoft) { + packed(packed() | m_isSoft); + } ASTGEN_MEMBERS_AstUnionDType; string verilogKwd() const override { return "union"; } + bool isSoft() const { return m_isSoft; } }; #endif // Guard diff --git a/src/V3Width.cpp b/src/V3Width.cpp index c9e32b3f5..ae82bb1da 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2882,10 +2882,14 @@ class WidthVisitor final : public VNVisitor { pushDeletep(itemp->valuep()->unlinkFrBack()); } } + const bool isHardPackedUnion + = nodep->packed() && VN_IS(nodep, UnionDType) && !VN_CAST(nodep, UnionDType)->isSoft(); + // Determine bit assignments and width if (VN_IS(nodep, UnionDType) || nodep->packed()) { int lsb = 0; int width = 0; + bool first = true; // Report errors on first member first AstMemberDType* itemp; // MSB is first, so loop backwards @@ -2895,11 +2899,17 @@ class WidthVisitor final : public VNVisitor { if (itemp->isFourstate()) nodep->isFourstate(true); itemp->lsb(lsb); if (VN_IS(nodep, UnionDType)) { - width = std::max(width, itemp->width()); + const int itemWidth = itemp->width(); + if (!first && isHardPackedUnion && itemWidth != width) { + itemp->v3error("Hard packed union members must have equal size " + "(IEEE 1800-2023 7.3.1)"); + } + width = std::max(width, itemWidth); } else { lsb += itemp->width(); width += itemp->width(); } + first = false; } nodep->widthForce(width, width); // Signing stays as-is, as parsed from declaration } else { diff --git a/src/verilog.y b/src/verilog.y index 22fe5e1cb..47ce7819d 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -2304,7 +2304,7 @@ struct_unionDecl: // IEEE: part of data_type /*cont*/ struct_union_memberListEnd { $$ = $4; $$->addMembersp($5); SYMP->popScope($$); } | yUNION taggedSoftE packedSigningE '{' - /*mid*/ { $$ = new AstUnionDType{$1, $3}; SYMP->pushNew($$); } + /*mid*/ { $$ = new AstUnionDType{$1, $2, $3}; SYMP->pushNew($$); } /*cont*/ struct_union_memberListEnd { $$ = $5; $$->addMembersp($6); SYMP->popScope($$); } ; @@ -2440,9 +2440,9 @@ random_qualifier: // ==IEEE: random_qualifier | yRANDC { $$ = VMemberQualifiers::none(); $$.m_randc = true; } ; -taggedSoftE: - /*empty*/ { } - | ySOFT { BBUNSUP($1, "Unsupported: 'union soft'"); } +taggedSoftE: + /*empty*/ { $$ = false; } + | ySOFT { $$ = true; } //UNSUP yTAGGED { UNSUP } ; diff --git a/test_regress/t/t_dpi_result_type_bad.v b/test_regress/t/t_dpi_result_type_bad.v index 9f5634000..180c2c16d 100644 --- a/test_regress/t/t_dpi_result_type_bad.v +++ b/test_regress/t/t_dpi_result_type_bad.v @@ -27,10 +27,10 @@ module t_dpi_result_type_bad; typedef struct packed { bit [63:0] x; bit [63:0] y; } struct_2_state_128; // 2-state packed unions of width > 32 - typedef union packed { bit [ 32:0] x; bit y; } union_2_state_33; - typedef union packed { bit [ 63:0] x; bit y; } union_2_state_64; - typedef union packed { bit [ 64:0] x; bit y; } union_2_state_65; - typedef union packed { bit [127:0] x; bit y; } union_2_state_128; + typedef union packed { bit [ 32:0] x; bit [ 32:0] y; } union_2_state_33; + typedef union packed { bit [ 63:0] x; bit [ 63:0] y; } union_2_state_64; + typedef union packed { bit [ 64:0] x; bit [ 64:0] y; } union_2_state_65; + typedef union packed { bit [127:0] x; bit [127:0] y; } union_2_state_128; // 4-state packed arrays of any size typedef logic [ 0:0] array_4_state_1_t; @@ -59,17 +59,17 @@ module t_dpi_result_type_bad; typedef struct packed { logic [63:0] x; bit [63:0] y; } struct_4_state_128; // 4-state packed unions of any size - typedef union packed { logic [ 0:0] x; bit y; } union_4_state_1; - typedef union packed { logic [ 1:0] x; bit y; } union_4_state_2; - typedef union packed { logic [ 7:0] x; bit y; } union_4_state_8; - typedef union packed { logic [ 8:0] x; bit y; } union_4_state_9; - typedef union packed { logic [ 15:0] x; bit y; } union_4_state_16; - typedef union packed { logic [ 16:0] x; bit y; } union_4_state_17; - typedef union packed { logic [ 31:0] x; bit y; } union_4_state_32; - typedef union packed { logic [ 32:0] x; bit y; } union_4_state_33; - typedef union packed { logic [ 63:0] x; bit y; } union_4_state_64; - typedef union packed { logic [ 64:0] x; bit y; } union_4_state_65; - typedef union packed { logic [127:0] x; bit y; } union_4_state_128; + typedef union packed { logic [ 0:0] x; bit [ 0:0] y; } union_4_state_1; + typedef union packed { logic [ 1:0] x; bit [ 1:0] y; } union_4_state_2; + typedef union packed { logic [ 7:0] x; bit [ 7:0] y; } union_4_state_8; + typedef union packed { logic [ 8:0] x; bit [ 8:0] y; } union_4_state_9; + typedef union packed { logic [ 15:0] x; bit [ 15:0] y; } union_4_state_16; + typedef union packed { logic [ 16:0] x; bit [ 16:0] y; } union_4_state_17; + typedef union packed { logic [ 31:0] x; bit [ 31:0] y; } union_4_state_32; + typedef union packed { logic [ 32:0] x; bit [ 32:0] y; } union_4_state_33; + typedef union packed { logic [ 63:0] x; bit [ 63:0] y; } union_4_state_64; + typedef union packed { logic [ 64:0] x; bit [ 64:0] y; } union_4_state_65; + typedef union packed { logic [127:0] x; bit [127:0] y; } union_4_state_128; //====================================================================== // Imports diff --git a/test_regress/t/t_export_packed_struct.v b/test_regress/t/t_export_packed_struct.v index 005038a89..a96f92213 100644 --- a/test_regress/t/t_export_packed_struct.v +++ b/test_regress/t/t_export_packed_struct.v @@ -6,9 +6,9 @@ typedef logic [5:0] udata6_t; -typedef union packed { - udata6_t a; - logic [2:0] b; +typedef union soft packed { + udata6_t a; + logic [2 : 0] b; } sub_t; typedef struct packed { diff --git a/test_regress/t/t_export_packed_struct2.v b/test_regress/t/t_export_packed_struct2.v index b63206cbe..885c23313 100644 --- a/test_regress/t/t_export_packed_struct2.v +++ b/test_regress/t/t_export_packed_struct2.v @@ -6,10 +6,10 @@ // Packed struct in package package TEST_TYPES; - typedef union packed { - logic [64:0] a; - logic [2:0] b; - } sub_t; + typedef union soft packed { + logic [64 : 0] a; + logic [2 : 0] b; + } sub_t; typedef struct packed { struct packed { // Anonymous packed struct logic a; diff --git a/test_regress/t/t_union_hard_bad.out b/test_regress/t/t_union_hard_bad.out new file mode 100644 index 000000000..c58460a4c --- /dev/null +++ b/test_regress/t/t_union_hard_bad.out @@ -0,0 +1,6 @@ +%Error: t/t_union_hard_bad.v:11:21: Hard packed union members must have equal size (IEEE 1800-2023 7.3.1) + : ... note: In instance 't' + 11 | bit [7 : 0] val1; + | ^~~~ + ... 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_union_hard_bad.py b/test_regress/t/t_union_hard_bad.py new file mode 100644 index 000000000..e33e10acf --- /dev/null +++ b/test_regress/t/t_union_hard_bad.py @@ -0,0 +1,16 @@ +#!/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('vlt') + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_union_hard_bad.v b/test_regress/t/t_union_hard_bad.v new file mode 100644 index 000000000..b21a31a49 --- /dev/null +++ b/test_regress/t/t_union_hard_bad.v @@ -0,0 +1,25 @@ +// 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 + +module t + (/*AUTOARG*/); + + union packed { + bit [7 : 0] val1; + bit [3 : 0] val2; + } u; + + initial begin + u.val1 = 8'h7c; + if(u.val1 != 8'h7c) $stop; + u.val2 = 4'h6; + if(u.val2 != 4'h6) $stop; + $display("%p", u); + if(u.val1 != 8'h76) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_union_soft.py b/test_regress/t/t_union_soft.py index e33e10acf..fc5a55e3f 100755 --- a/test_regress/t/t_union_soft.py +++ b/test_regress/t/t_union_soft.py @@ -11,6 +11,8 @@ import vltest_bootstrap test.scenarios('vlt') -test.lint(fails=True, expect_filename=test.golden_filename) +test.compile() + +test.execute() test.passes() diff --git a/test_regress/t/t_union_soft.v b/test_regress/t/t_union_soft.v index 7bbec350f..f5f3e5db2 100644 --- a/test_regress/t/t_union_soft.v +++ b/test_regress/t/t_union_soft.v @@ -11,13 +11,24 @@ module t(/*AUTOARG*/); bit [3:0] val2; } u; + union soft packed { + bit [7 : 0] val1; + bit [3 : 0] val2; + } u2; + initial begin u.val1 = 8'h7c; if (u.val1 != 8'h7c) $stop; u.val2 = 4'h6; if (u.val2 != 4'h6) $stop; $display("%p", u); - if (u.ual1 != 8'h76) $stop; + if(u.val1 != 8'h76) $stop; + u2.val1 = 8'h7c; + if(u2.val1 != 8'h7c) $stop; + u2.val2 = 4'h6; + if(u2.val2 != 4'h6) $stop; + $display("%p", u2); + if(u2.val1 != 8'h76) $stop; $write("*-* All Finished *-*\n"); $finish; end