diff --git a/elab_type.cc b/elab_type.cc index 9048e3afc..d52af2d25 100644 --- a/elab_type.cc +++ b/elab_type.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2024 Stephen Williams (steve@icarus.com) + * Copyright (c) 2012-2026 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -220,11 +220,13 @@ ivl_type_t struct_type_t::elaborate_type_raw(Design*des, NetScope*scope) const res->set_line(*this); - res->packed(packed_flag); + bool is_packed = packed_flag || (union_flag && soft_flag); + res->packed(is_packed); res->set_signed(signed_flag); - if (union_flag) - res->union_flag(true); + if (union_flag) { + res->union_flag(true, soft_flag); + } for (list::iterator cur = members->begin() ; cur != members->end() ; ++ cur) { @@ -242,7 +244,7 @@ ivl_type_t struct_type_t::elaborate_type_raw(Design*des, NetScope*scope) const ; cur_name != curp->names->end() ; ++ cur_name) { decl_assignment_t*namep = *cur_name; - if (packed_flag && namep->expr) { + if (is_packed && namep->expr) { cerr << namep->expr->get_fileline() << " error: " << "Packed structs must not have default member values." << endl; diff --git a/ivtest/ivltests/sv_soft_packed_union.v b/ivtest/ivltests/sv_soft_packed_union.v new file mode 100644 index 000000000..6b0e7abc3 --- /dev/null +++ b/ivtest/ivltests/sv_soft_packed_union.v @@ -0,0 +1,81 @@ +// Check that soft packed unions can have members with different widths. + +module test; + + bit failed = 1'b0; + + typedef union soft packed { + logic [31:0] w; + logic [15:0] h; + logic [7:0] b; + } data_t; + + typedef union soft { + logic [31:0] w; + logic [7:0] b; + } implicit_packed_t; + + typedef union soft packed { + logic [15:0] w; + union soft { + logic [7:0] b; + logic [3:0] n; + } part; + } nested_t; + + data_t data; + implicit_packed_t implicit_packed; + nested_t nested; + + `define check(val, exp) do \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0h, got %0h", `__LINE__, \ + `"val`", exp, val); \ + failed = 1'b1; \ + end \ + while(0) + + initial begin + data.w = 32'h12345678; + `check($bits(data), 32); + `check($bits(data_t), 32); + `check(data.w, 32'h12345678); + `check(data.h, 16'h5678); + `check(data.b, 8'h78); + + data.h = 16'habcd; + `check(data.w, 32'h1234abcd); + `check(data.h, 16'habcd); + `check(data.b, 8'hcd); + + data.b = 8'hef; + `check(data.w, 32'h1234abef); + `check(data.h, 16'habef); + `check(data.b, 8'hef); + + implicit_packed.w = 32'hf0e1d2c3; + `check($bits(implicit_packed_t), 32); + `check(implicit_packed.b, 8'hc3); + + implicit_packed.b = 8'h34; + `check(implicit_packed.w, 32'hf0e1d234); + + nested.w = 16'hbeef; + `check($bits(nested_t), 16); + `check($bits(nested.part), 8); + `check(nested.part.b, 8'hef); + `check(nested.part.n, 4'hf); + + nested.part.n = 4'ha; + `check(nested.w, 16'hbeea); + `check(nested.part.b, 8'hea); + + nested.part.b = 8'h55; + `check(nested.w, 16'hbe55); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_soft_packed_union_fail1.v b/ivtest/ivltests/sv_soft_packed_union_fail1.v new file mode 100644 index 000000000..c54b12e76 --- /dev/null +++ b/ivtest/ivltests/sv_soft_packed_union_fail1.v @@ -0,0 +1,12 @@ +// Check that soft packed unions can not have default member values. + +module test; + + typedef union soft { + logic [31:0] w = 32'h1; + logic [7:0] b; + } data_t; + + data_t data; + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 412f724d4..9ffa708db 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -339,6 +339,8 @@ sv_package_lifetime vvp_tests/sv_package_lifetime.json sv_package_lifetime_fail vvp_tests/sv_package_lifetime_fail.json sv_parameter_type vvp_tests/sv_parameter_type.json sv_queue_assign_op vvp_tests/sv_queue_assign_op.json +sv_soft_packed_union vvp_tests/sv_soft_packed_union.json +sv_soft_packed_union_fail1 vvp_tests/sv_soft_packed_union_fail1.json sv_super_member_fail vvp_tests/sv_super_member_fail.json sv_wildcard_import8 vvp_tests/sv_wildcard_import8.json sdf_header vvp_tests/sdf_header.json diff --git a/ivtest/vvp_tests/sv_soft_packed_union.json b/ivtest/vvp_tests/sv_soft_packed_union.json new file mode 100644 index 000000000..073a76414 --- /dev/null +++ b/ivtest/vvp_tests/sv_soft_packed_union.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_soft_packed_union.v", + "iverilog-args" : [ "-g2023" ] +} diff --git a/ivtest/vvp_tests/sv_soft_packed_union_fail1.json b/ivtest/vvp_tests/sv_soft_packed_union_fail1.json new file mode 100644 index 000000000..bd50b1e04 --- /dev/null +++ b/ivtest/vvp_tests/sv_soft_packed_union_fail1.json @@ -0,0 +1,5 @@ +{ + "type" : "CE", + "source" : "sv_soft_packed_union_fail1.v", + "iverilog-args" : [ "-g2023" ] +} diff --git a/netstruct.cc b/netstruct.cc index 3c3dc13f7..0c80bd718 100644 --- a/netstruct.cc +++ b/netstruct.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011-2022 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2026 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -26,22 +26,19 @@ using namespace std; -netstruct_t::netstruct_t() -: union_(false), packed_(false), signed_(false) -{ -} - netstruct_t::~netstruct_t() { } -void netstruct_t::union_flag(bool flag) +void netstruct_t::union_flag(bool flag, bool soft) { // This MUST be called before any members are pushed into the // definition. This is because the append relies on this flag // being accurate. ivl_assert(*this, members_.empty()); + ivl_assert(*this, flag || !soft); union_ = flag; + soft_union_ = soft; } void netstruct_t::packed(bool flag) @@ -64,7 +61,7 @@ void netstruct_t::append_member(Design*des, const netstruct_t::member_t&val) des->errors += 1; } } - if (union_ && packed_ && members_.size() > 1) { + if (union_ && packed_ && !soft_union_ && members_.size() > 1) { unsigned long expect_wid = members_.front().net_type->packed_width(); unsigned long got_wid = members_.back().net_type->packed_width(); if (expect_wid != got_wid) { @@ -104,9 +101,20 @@ long netstruct_t::packed_width(void) const // If this is a packed union, then all the members are the // same width, so it is sufficient to return the width of any // single member. - if (union_) + if (union_ && !soft_union_) return members_.front().net_type->packed_width(); + // A soft packed union uses the largest member width. + if (union_ && soft_union_) { + long res = 0; + for (size_t idx = 0 ; idx < members_.size() ; idx += 1) { + long wid = members_[idx].net_type->packed_width(); + if (wid > res) + res = wid; + } + return res; + } + // The width of a packed struct is the sum of member widths. long res = 0; for (size_t idx = 0 ; idx < members_.size() ; idx += 1) diff --git a/netstruct.h b/netstruct.h index 47fe09e30..a0e66fea9 100644 --- a/netstruct.h +++ b/netstruct.h @@ -1,7 +1,7 @@ #ifndef IVL_netstruct_H #define IVL_netstruct_H /* - * Copyright (c) 2011-2025 Stephen Williams (steve@icarus.com) + * Copyright (c) 2011-2026 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -39,14 +39,15 @@ class netstruct_t : public LineInfo, public ivl_type_s { }; public: - netstruct_t(); + netstruct_t() = default; ~netstruct_t() override; // If this is a union (instead of struct) then this flag is // set. We handle union and struct together because they are // so similar. - void union_flag(bool); + void union_flag(bool, bool soft = false); bool union_flag(void) const; + bool soft_union(void) const; void packed(bool flag); bool packed(void) const override; @@ -81,13 +82,15 @@ class netstruct_t : public LineInfo, public ivl_type_s { bool test_equivalence(ivl_type_t that) const override; private: - bool union_; - bool packed_; - bool signed_; + bool union_ = false; + bool soft_union_ = false; + bool packed_ = false; + bool signed_ = false; std::vectormembers_; }; inline bool netstruct_t::union_flag(void) const { return union_; } +inline bool netstruct_t::soft_union(void) const { return soft_union_; } inline bool netstruct_t::packed(void) const { return packed_; } #endif /* IVL_netstruct_H */ diff --git a/parse.y b/parse.y index 45dcadb37..69187377d 100644 --- a/parse.y +++ b/parse.y @@ -706,7 +706,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type from_exclude block_item_decls_opt %type number pos_neg_number %type signing unsigned_signed_opt signed_unsigned_opt -%type import_export +%type import_export union_soft_opt %type K_genvar_opt K_static_opt K_virtual_opt K_const_opt %type udp_reg_opt edge_operator %type drive_strength drive_strength_opt dr_strength0 dr_strength1 @@ -1356,7 +1356,7 @@ packed_array_data_type /* IEEE1800-2005: A.2.2.1 */ : enum_data_type { $$ = $1; } | struct_data_type - { if (!$1->packed_flag) { + { if (!$1->packed_flag && !($1->union_flag && $1->soft_flag)) { yyerror(@1, "sorry: Unpacked structs not supported."); } $$ = $1; @@ -3013,6 +3013,13 @@ packed_signing /* IEEE 1800-2012 A.2.2.1 */ } ; +union_soft_opt + : K_soft + { $$ = true; } + | + { $$ = false; } + ; + struct_data_type /* IEEE 1800-2012 A.2.2.1 */ : K_struct packed_signing '{' struct_union_member_list '}' { struct_type_t*tmp = new struct_type_t; @@ -3020,16 +3027,20 @@ struct_data_type /* IEEE 1800-2012 A.2.2.1 */ tmp->packed_flag = $2.packed_flag; tmp->signed_flag = $2.signed_flag; tmp->union_flag = false; - tmp->members .reset($4); + tmp->soft_flag = false; + tmp->members.reset($4); $$ = tmp; } - | K_union packed_signing '{' struct_union_member_list '}' - { struct_type_t*tmp = new struct_type_t; + | K_union union_soft_opt packed_signing '{' struct_union_member_list '}' + { if ($2 && generation_flag < GN_VER2023) + yyerror(@1, "error: Soft packed unions require SystemVerilog 2023 or later."); + struct_type_t*tmp = new struct_type_t; FILE_NAME(tmp, @1); - tmp->packed_flag = $2.packed_flag; - tmp->signed_flag = $2.signed_flag; + tmp->packed_flag = $3.packed_flag; + tmp->signed_flag = $3.signed_flag; tmp->union_flag = true; - tmp->members .reset($4); + tmp->soft_flag = $2; + tmp->members.reset($5); $$ = tmp; } | K_struct packed_signing '{' error '}' @@ -3040,16 +3051,20 @@ struct_data_type /* IEEE 1800-2012 A.2.2.1 */ tmp->packed_flag = $2.packed_flag; tmp->signed_flag = $2.signed_flag; tmp->union_flag = false; + tmp->soft_flag = false; $$ = tmp; } - | K_union packed_signing '{' error '}' - { yyerror(@3, "error: Errors in union member list."); + | K_union union_soft_opt packed_signing '{' error '}' + { if ($2 && generation_flag < GN_VER2023) + yyerror(@1, "error: Soft packed unions require SystemVerilog 2023 or later."); + yyerror(@4, "error: Errors in union member list."); yyerrok; struct_type_t*tmp = new struct_type_t; FILE_NAME(tmp, @1); - tmp->packed_flag = $2.packed_flag; - tmp->signed_flag = $2.signed_flag; + tmp->packed_flag = $3.packed_flag; + tmp->signed_flag = $3.signed_flag; tmp->union_flag = true; + tmp->soft_flag = $2; $$ = tmp; } ; diff --git a/pform_types.h b/pform_types.h index 452c3e583..2ed4e7469 100644 --- a/pform_types.h +++ b/pform_types.h @@ -259,6 +259,7 @@ struct struct_type_t : public data_type_t { bool packed_flag; bool union_flag; + bool soft_flag; bool signed_flag; std::unique_ptr< std::list > members; };