From 5547858372baadb8dd912703ade1e6f400d6ec84 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 26 Sep 2022 11:53:49 +0200 Subject: [PATCH 1/4] Add initial support for packed arrays/vector assignment pattern SystemVerilog allows to use assignment patterns to assign a value to a packed array. This is similar to using a concatenation, with the difference that for concatenations the values are evaluated in a self-determined context and for assignment patterns they are evaluated in a context defined by the element type of the packed array. This means that the value is for example automatically width expanded or truncated if it does not have the same size as the element type. Automatic type conversion is also done when allowed. E.g. ``` bit [3:0][3:0] x = '{1'b1, 32'h2, 3.0, "TEST"}; $display("%x", x); // -> 1234 ``` Nested assignment patterns are also supported. E.g. ``` bit [1:0][3:0][3:0] x = '{'{1, 2, 3, 4.0}, '{5, 6, 7, 8}}; $display("%x", x); // -> 12345678 ``` Add support for using assignment patterns as the right hand side value. Since the complete type of the target variable is required to correctly evaluate the assignment pattern it is handled as a special case in `elab_rval_expression()`. For other types of expressions for packed values only the total width of the target value is provided to the rvalue elaboration function. SystemVerilog also supports assignment patterns for the left hand side in assignments. This is not yet supported. Also not yet supported is specifying array elements by index, including `default`. Signed-off-by: Lars-Peter Clausen --- PExpr.h | 11 ++++- elab_expr.cc | 112 ++++++++++++++++++++++++++++++++++++++------------ net_design.cc | 12 +++++- netmisc.cc | 5 +++ 4 files changed, 110 insertions(+), 30 deletions(-) diff --git a/PExpr.h b/PExpr.h index 68bf974a6..2ee7b0653 100644 --- a/PExpr.h +++ b/PExpr.h @@ -213,8 +213,15 @@ class PEAssignPattern : public PExpr { unsigned expr_wid, unsigned flags) const; private: - NetExpr* elaborate_expr_darray_(Design*des, NetScope*scope, - ivl_type_t type, unsigned flags) const; + NetExpr* elaborate_expr_packed_(Design *des, NetScope *scope, + ivl_variable_type_t base_type, + unsigned int width, + const std::vector &dims, + unsigned int cur_dim, + bool need_const) const; + NetExpr* elaborate_expr_darray_(Design *des, NetScope *scope, + const netdarray_t *array_type, + bool need_const) const; private: std::vectorparms_; diff --git a/elab_expr.cc b/elab_expr.cc index 0dc689e3e..5db504ae8 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -118,7 +118,8 @@ NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type, NetExpr *rval; int context_wid = -1; - bool fallback = true; + bool typed_elab = false; + switch (lv_type) { case IVL_VT_DARRAY: case IVL_VT_QUEUE: @@ -126,11 +127,7 @@ NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type, // For these types, use a different elab_and_eval that // uses the lv_net_type. We should eventually transition // all the types to this new form. - if (lv_net_type) { - rval = elab_and_eval(des, scope, expr, lv_net_type, need_const); - fallback = false; - } - + typed_elab = true; break; case IVL_VT_REAL: case IVL_VT_STRING: @@ -145,7 +142,14 @@ NetExpr* elaborate_rval_expr(Design*des, NetScope*scope, ivl_type_t lv_net_type, break; } - if (fallback) { + // Special case, PEAssignPattern is context dependend on the type and + // always uses the typed elaboration + if (dynamic_cast(expr)) + typed_elab = true; + + if (lv_net_type && typed_elab) { + rval = elab_and_eval(des, scope, expr, lv_net_type, need_const); + } else { rval = elab_and_eval(des, scope, expr, context_wid, need_const, false, lv_type, force_unsigned); } @@ -229,36 +233,45 @@ unsigned PEAssignPattern::test_width(Design*, NetScope*, width_mode_t&) NetExpr*PEAssignPattern::elaborate_expr(Design*des, NetScope*scope, ivl_type_t ntype, unsigned flags) const { - // Special case: If this is an empty pattern (i.e. '{}) and - // the expected type is a DARRAY or QUEUE, then convert this - // to a null handle. Internally, Icarus Verilog uses this to - // represent nil dynamic arrays. - if (parms_.size() == 0 && (ntype->base_type()==IVL_VT_DARRAY || - ntype->base_type()==IVL_VT_QUEUE)) { - NetENull*tmp = new NetENull; - tmp->set_line(*this); - return tmp; + bool need_const = NEED_CONST & flags; + + if (auto darray_type = dynamic_cast(ntype)) + return elaborate_expr_darray_(des, scope, darray_type, need_const); + + if (auto parray_type = dynamic_cast(ntype)) { + return elaborate_expr_packed_(des, scope, parray_type->base_type(), + parray_type->packed_width(), + parray_type->slice_dimensions(), 0, + need_const); } - if (ntype->base_type()==IVL_VT_DARRAY || - ntype->base_type()==IVL_VT_QUEUE) - return elaborate_expr_darray_(des, scope, ntype, flags); + if (auto vector_type = dynamic_cast(ntype)) { + return elaborate_expr_packed_(des, scope, vector_type->base_type(), + vector_type->packed_width(), + vector_type->slice_dimensions(), 0, + need_const); + } cerr << get_fileline() << ": sorry: I don't know how to elaborate " - << "assignment_pattern expressions yet." << endl; + << "assignment_pattern expressions for " << *ntype << " type yet." << endl; cerr << get_fileline() << ": : Expression is: " << *this << endl; des->errors += 1; return 0; } -NetExpr*PEAssignPattern::elaborate_expr_darray_(Design*des, NetScope*scope, - ivl_type_t ntype, unsigned flags) const +NetExpr* PEAssignPattern::elaborate_expr_darray_(Design *des, NetScope *scope, + const netdarray_t *array_type, + bool need_const) const { - const netdarray_t*array_type = dynamic_cast (ntype); - ivl_assert(*this, array_type); - - bool need_const = NEED_CONST & flags; + // Special case: If this is an empty pattern (i.e. '{}) then convert + // this to a null handle. Internally, Icarus Verilog uses this to + // represent nil dynamic arrays. + if (parms_.empty()) { + NetENull *tmp = new NetENull; + tmp->set_line(*this); + return tmp; + } // This is an array pattern, so run through the elements of // the expression and elaborate each as if they are @@ -276,6 +289,53 @@ NetExpr*PEAssignPattern::elaborate_expr_darray_(Design*des, NetScope*scope, return res; } +NetExpr* PEAssignPattern::elaborate_expr_packed_(Design *des, NetScope *scope, + ivl_variable_type_t base_type, + unsigned int width, + const std::vector &dims, + unsigned int cur_dim, + bool need_const) const +{ + if (dims.size() <= cur_dim) { + cerr << get_fileline() << ": error: scalar type is not a valid" + << " context for assignment pattern." << endl; + des->errors++; + return nullptr; + } + + if (dims[cur_dim].width() != parms_.size()) { + cerr << get_fileline() << ": error: Packed array assignment pattern expects " + << dims[cur_dim].width() << " element(s) in this context.\n" + << get_fileline() << ": : Found " + << parms_.size() << " element(s)." << endl; + des->errors++; + } + + width /= dims[cur_dim].width(); + cur_dim++; + + NetEConcat *concat = new NetEConcat(parms_.size(), 1, base_type); + for (size_t idx = 0; idx < parms_.size(); idx++) { + NetExpr *expr; + // Handle nested assignment patterns as a special case. We do not + // have a good way of passing the inner dimensions through the + // generic elaborate_expr() API and assigment patterns is the only + // place where we need it. + auto ap = dynamic_cast(parms_[idx]); + if (ap) + expr = ap->elaborate_expr_packed_(des, scope, base_type, + width, dims, cur_dim, need_const); + else + expr = elaborate_rval_expr(des, scope, nullptr, + base_type, width, + parms_[idx], need_const); + if (expr) + concat->set(idx, expr); + } + + return concat; +} + NetExpr* PEAssignPattern::elaborate_expr(Design*des, NetScope*, unsigned, unsigned) const { cerr << get_fileline() << ": sorry: I do not know how to" diff --git a/net_design.cc b/net_design.cc index a94582901..7a6b5585d 100644 --- a/net_design.cc +++ b/net_design.cc @@ -550,8 +550,16 @@ void NetScope::evaluate_parameter_logic_(Design*des, param_ref_t cur) << "use_type = " << use_type << endl; } - NetExpr*expr = elab_and_eval(des, val_scope, val_expr, lv_width, true, - cur->second.is_annotatable, use_type); + NetExpr *expr; + + // Handle assignment patterns as a special case as they need the type to + // be evaluated correctly. + if (param_type && dynamic_cast(val_expr)) { + expr = elab_and_eval(des, val_scope, val_expr, param_type, true); + } else { + expr = elab_and_eval(des, val_scope, val_expr, lv_width, true, + cur->second.is_annotatable, use_type); + } if (! expr) return; diff --git a/netmisc.cc b/netmisc.cc index b2548d377..13b79f14b 100644 --- a/netmisc.cc +++ b/netmisc.cc @@ -1019,6 +1019,11 @@ NetExpr* elab_and_eval(Design*des, NetScope*scope, PExpr*pe, return 0; } + if (lv_net_type->packed()) + eval_expr(tmp, lv_net_type->packed_width()); + else + eval_expr(tmp, -1); + return tmp; } From 7f3621d47d4c78cf7780b0687388ad4962eba386 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 5 Oct 2022 00:06:58 +0200 Subject: [PATCH 2/4] Add initial support for struct assignment pattern Structs can be initialized by an assignment pattern. E.g. ``` struct packed { int x; shortint y; } S = '{ 1, 2}; ``` is the same as ``` struct packed { int x; shortint y; } S; s.x = 1; s.y = 2; ``` Add initial support for unnamed struct assignment patterns. Named struct assignment patterns like ``` struct packed { int x; shortint y; } S = '{x: 1, y: 2}; ``` are still unsupported. Signed-off-by: Lars-Peter Clausen --- PExpr.h | 3 +++ elab_expr.cc | 32 ++++++++++++++++++++++++++++++++ netstruct.h | 1 + 3 files changed, 36 insertions(+) diff --git a/PExpr.h b/PExpr.h index 2ee7b0653..c55f0d521 100644 --- a/PExpr.h +++ b/PExpr.h @@ -219,6 +219,9 @@ class PEAssignPattern : public PExpr { const std::vector &dims, unsigned int cur_dim, bool need_const) const; + NetExpr* elaborate_expr_struct_(Design *des, NetScope *scope, + const netstruct_t *struct_type, + bool need_const) const; NetExpr* elaborate_expr_darray_(Design *des, NetScope *scope, const netdarray_t *array_type, bool need_const) const; diff --git a/elab_expr.cc b/elab_expr.cc index 5db504ae8..b70f20729 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -252,6 +252,11 @@ NetExpr*PEAssignPattern::elaborate_expr(Design*des, NetScope*scope, need_const); } + if (auto struct_type = dynamic_cast(ntype)) { + return elaborate_expr_struct_(des, scope, struct_type, + need_const); + } + cerr << get_fileline() << ": sorry: I don't know how to elaborate " << "assignment_pattern expressions for " << *ntype << " type yet." << endl; cerr << get_fileline() << ": : Expression is: " << *this @@ -336,6 +341,33 @@ NetExpr* PEAssignPattern::elaborate_expr_packed_(Design *des, NetScope *scope, return concat; } +NetExpr* PEAssignPattern::elaborate_expr_struct_(Design *des, NetScope *scope, + const netstruct_t *struct_type, + bool need_const) const +{ + auto &members = struct_type->members(); + + if (members.size() != parms_.size()) { + cerr << get_fileline() << ": error: Struct assignment pattern expects " + << members.size() << " element(s) in this context.\n" + << get_fileline() << ": : Found " + << parms_.size() << " element(s)." << endl; + des->errors++; + } + + NetEConcat *concat = new NetEConcat(parms_.size(), 1, + struct_type->base_type()); + for (size_t idx = 0; idx < std::min(parms_.size(), members.size()); idx++) { + auto expr = elaborate_rval_expr(des, scope, + members[idx].net_type, + parms_[idx], need_const); + if (expr) + concat->set(idx, expr); + } + + return concat; +} + NetExpr* PEAssignPattern::elaborate_expr(Design*des, NetScope*, unsigned, unsigned) const { cerr << get_fileline() << ": sorry: I do not know how to" diff --git a/netstruct.h b/netstruct.h index b2ae1f96f..b277a84ac 100644 --- a/netstruct.h +++ b/netstruct.h @@ -65,6 +65,7 @@ class netstruct_t : public LineInfo, public ivl_type_s { // description, and set the off value to be the offset into // the packed value where the member begins. const struct member_t* packed_member(perm_string name, unsigned long&off) const; + const std::vector& members() const { return members_; } // Return the width (in bits) of the packed record, or -1 if // the record is not packed. From 4fca564614304cf56a737b0ff53f2f3015329b37 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 4 Feb 2023 08:43:12 -0800 Subject: [PATCH 3/4] Add regression tests for packed array assignment patterns Check that positional and nested positional assignment patterns are supported for packed arrays. Also check that invalid assignment patterns for packed arrays result in an error. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_ap_parray1.v | 19 +++++++++++++++++++ ivtest/ivltests/sv_ap_parray2.v | 16 ++++++++++++++++ ivtest/ivltests/sv_ap_parray_fail1.v | 13 +++++++++++++ ivtest/ivltests/sv_ap_parray_fail2.v | 13 +++++++++++++ ivtest/ivltests/sv_ap_parray_fail3.v | 13 +++++++++++++ ivtest/regress-sv.list | 5 +++++ 6 files changed, 79 insertions(+) create mode 100644 ivtest/ivltests/sv_ap_parray1.v create mode 100644 ivtest/ivltests/sv_ap_parray2.v create mode 100644 ivtest/ivltests/sv_ap_parray_fail1.v create mode 100644 ivtest/ivltests/sv_ap_parray_fail2.v create mode 100644 ivtest/ivltests/sv_ap_parray_fail3.v diff --git a/ivtest/ivltests/sv_ap_parray1.v b/ivtest/ivltests/sv_ap_parray1.v new file mode 100644 index 000000000..cff387408 --- /dev/null +++ b/ivtest/ivltests/sv_ap_parray1.v @@ -0,0 +1,19 @@ +// Check that positional assigment patterns are supported for packed arrays. + +module test; + + bit [3:0][3:0] x = '{1'b1, 1 + 1, 3.0, "TEST"}; + + // Check nested assignment pattern + bit [1:0][3:0][3:0] y = '{'{1'b1, 1 + 1, 3.0, "TEST"}, + '{5, 6, '{1'b0, 1 * 1, 3, 1.0}, 8}}; + + initial begin + if (x === 16'h1234 && y == 32'h12345678) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_ap_parray2.v b/ivtest/ivltests/sv_ap_parray2.v new file mode 100644 index 000000000..e8dc5e088 --- /dev/null +++ b/ivtest/ivltests/sv_ap_parray2.v @@ -0,0 +1,16 @@ +// Check that positional assigment patterns are supported for packed array +// parameters. + +module test; + + localparam bit [2:0] x = '{1'b1, 2.0, 2 + 1}; + + initial begin + if (x === 3'b101) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_ap_parray_fail1.v b/ivtest/ivltests/sv_ap_parray_fail1.v new file mode 100644 index 000000000..1adc9aafc --- /dev/null +++ b/ivtest/ivltests/sv_ap_parray_fail1.v @@ -0,0 +1,13 @@ +// Check that an error is reported when specifing less elements in a packed +// array assignment pattern than the array size. + +module test; + + bit [2:0][3:0] x = '{1, 2}; // This should fail. Less elements than array + // size. + + initial begin + $display("FAILED"); + end + +endmodule diff --git a/ivtest/ivltests/sv_ap_parray_fail2.v b/ivtest/ivltests/sv_ap_parray_fail2.v new file mode 100644 index 000000000..56104fd73 --- /dev/null +++ b/ivtest/ivltests/sv_ap_parray_fail2.v @@ -0,0 +1,13 @@ +// Check that an error is reported when specifing more elements in a packed +// array assignment pattern than the array size. + +module test; + + bit [2:0][3:0] x = '{1, 2, 3, 4}; // This should fail. More elements than + // array size. + + initial begin + $display("FAILED"); + end + +endmodule diff --git a/ivtest/ivltests/sv_ap_parray_fail3.v b/ivtest/ivltests/sv_ap_parray_fail3.v new file mode 100644 index 000000000..7dd681103 --- /dev/null +++ b/ivtest/ivltests/sv_ap_parray_fail3.v @@ -0,0 +1,13 @@ +// Check that an error is reported when using an assignment pattern on a scalar +// type. + +module test; + + bit x = '{1'b1}; // This should fail. Can't use assignment pattern with + // scalar types + + initial begin + $display("FAILED"); + end + +endmodule diff --git a/ivtest/regress-sv.list b/ivtest/regress-sv.list index a888672ba..d55c39ba7 100644 --- a/ivtest/regress-sv.list +++ b/ivtest/regress-sv.list @@ -520,6 +520,11 @@ struct_packed_write_read2 normal,-g2009 ivltests struct_invalid_member CE,-g2009 ivltests gold=struct_invalid_member.gold struct_signed normal,-g2009 ivltests sv-constants normal,-g2005-sv ivltests +sv_ap_parray1 normal,-g2005-sv ivltests +sv_ap_parray2 normal,-g2005-sv ivltests +sv_ap_parray_fail1 CE,-g2005-sv ivltests +sv_ap_parray_fail2 CE,-g2005-sv ivltests +sv_ap_parray_fail3 CE,-g2005-sv ivltests sv_assign_pattern_cast normal,-g2005-sv ivltests sv_assign_pattern_const normal,-g2005-sv ivltests sv_assign_pattern_concat normal,-g2005-sv ivltests From 6ae085812d7ce1a1dab7f3efdbd5d16e932831aa Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 11 Dec 2022 19:57:43 -0800 Subject: [PATCH 4/4] Add regression tests for struct assignment patterns Check that struct assignment patterns with only positional arguments are supported. Also check that invalid assignment patterns for structs report an error. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_ap_struct1.v | 28 ++++++++++++++++++++++++++++ ivtest/ivltests/sv_ap_struct2.v | 22 ++++++++++++++++++++++ ivtest/ivltests/sv_ap_struct_fail1.v | 16 ++++++++++++++++ ivtest/ivltests/sv_ap_struct_fail2.v | 16 ++++++++++++++++ ivtest/regress-sv.list | 4 ++++ 5 files changed, 86 insertions(+) create mode 100644 ivtest/ivltests/sv_ap_struct1.v create mode 100644 ivtest/ivltests/sv_ap_struct2.v create mode 100644 ivtest/ivltests/sv_ap_struct_fail1.v create mode 100644 ivtest/ivltests/sv_ap_struct_fail2.v diff --git a/ivtest/ivltests/sv_ap_struct1.v b/ivtest/ivltests/sv_ap_struct1.v new file mode 100644 index 000000000..728332e40 --- /dev/null +++ b/ivtest/ivltests/sv_ap_struct1.v @@ -0,0 +1,28 @@ +// Check that positional assigment patterns are supported for structs. + +module test; + + typedef struct packed { + int x; + shortint y; + byte z; + } T; + + T x = '{1'b1, 2.0, 2 + 1}; + + // Check nested assignment patterns + struct packed { + T x; + bit [2:0][3:0] y; + } y = '{'{1'b1, 2.0, 2 + 1}, '{4, 5, 6}}; + + initial begin + if (x === 56'h00000001000203 && + y === 68'h00000001000203456) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_ap_struct2.v b/ivtest/ivltests/sv_ap_struct2.v new file mode 100644 index 000000000..424fb4c22 --- /dev/null +++ b/ivtest/ivltests/sv_ap_struct2.v @@ -0,0 +1,22 @@ +// Check that positional assigment patterns are supported for structs are +// supported for parameters. + +module test; + + typedef struct packed { + int x; + shortint y; + byte z; + } T; + + localparam T x = '{1'b1, 2.0, 2 + 1}; + + initial begin + if (x === 56'h00000001000203) begin + $display("PASSED"); + end else begin + $display("FAILED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_ap_struct_fail1.v b/ivtest/ivltests/sv_ap_struct_fail1.v new file mode 100644 index 000000000..a744343d7 --- /dev/null +++ b/ivtest/ivltests/sv_ap_struct_fail1.v @@ -0,0 +1,16 @@ +// Check that it is an error to provide less elements in a struct assignment +// pattern than there are members in the struct. + +module test; + + struct packed { + int x; + shortint y; + byte z; + } x = '{1, 2}; // This should fail. Less elements than required. + + initial begin + $display("FAILED"); + end + +endmodule diff --git a/ivtest/ivltests/sv_ap_struct_fail2.v b/ivtest/ivltests/sv_ap_struct_fail2.v new file mode 100644 index 000000000..036b7ec2c --- /dev/null +++ b/ivtest/ivltests/sv_ap_struct_fail2.v @@ -0,0 +1,16 @@ +// Check that it is an error to provide more elements in a struct assignment +// pattern than there are members in the struct. + +module test; + + struct packed { + int x; + shortint y; + byte z; + } x = '{1, 2, 3, 4}; // This should fail. More elements than required. + + initial begin + $display("FAILED"); + end + +endmodule diff --git a/ivtest/regress-sv.list b/ivtest/regress-sv.list index d55c39ba7..df3af90a2 100644 --- a/ivtest/regress-sv.list +++ b/ivtest/regress-sv.list @@ -525,6 +525,10 @@ sv_ap_parray2 normal,-g2005-sv ivltests sv_ap_parray_fail1 CE,-g2005-sv ivltests sv_ap_parray_fail2 CE,-g2005-sv ivltests sv_ap_parray_fail3 CE,-g2005-sv ivltests +sv_ap_struct1 normal,-g2005-sv ivltests +sv_ap_struct2 normal,-g2005-sv ivltests +sv_ap_struct_fail1 CE,-g2005-sv ivltests +sv_ap_struct_fail2 CE,-g2005-sv ivltests sv_assign_pattern_cast normal,-g2005-sv ivltests sv_assign_pattern_const normal,-g2005-sv ivltests sv_assign_pattern_concat normal,-g2005-sv ivltests