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/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; };