Merge pull request #1365 from larsclausen/soft-unions

Support soft packed unions
This commit is contained in:
Cary R. 2026-05-16 17:11:41 -07:00 committed by GitHub
commit 0f75156d37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 166 additions and 32 deletions

View File

@ -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<struct_member_t*>::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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_soft_packed_union.v",
"iverilog-args" : [ "-g2023" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_soft_packed_union_fail1.v",
"iverilog-args" : [ "-g2023" ]
}

View File

@ -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)

View File

@ -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::vector<member_t>members_;
};
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 */

39
parse.y
View File

@ -706,7 +706,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type,
%type <flag> from_exclude block_item_decls_opt
%type <number> number pos_neg_number
%type <flag> signing unsigned_signed_opt signed_unsigned_opt
%type <flag> import_export
%type <flag> import_export union_soft_opt
%type <flag> K_genvar_opt K_static_opt K_virtual_opt K_const_opt
%type <flag> udp_reg_opt edge_operator
%type <drive> 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;
}
;

View File

@ -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<struct_member_t*> > members;
};