diff --git a/PExpr.h b/PExpr.h index 68bf974a6..c55f0d521 100644 --- a/PExpr.h +++ b/PExpr.h @@ -213,8 +213,18 @@ 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_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; private: std::vectorparms_; diff --git a/elab_expr.cc b/elab_expr.cc index 0dc689e3e..b70f20729 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,50 @@ 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); + } + + 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 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 +294,80 @@ 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_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/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/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 a888672ba..df3af90a2 100644 --- a/ivtest/regress-sv.list +++ b/ivtest/regress-sv.list @@ -520,6 +520,15 @@ 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_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 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; } 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.