From 5547858372baadb8dd912703ade1e6f400d6ec84 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 26 Sep 2022 11:53:49 +0200 Subject: [PATCH] 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; }