Merge pull request #868 from larsclausen/assign-pattern
Add initial support for packed array and packed struct assignment patterns
This commit is contained in:
commit
418bbc14bc
14
PExpr.h
14
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<netrange_t> &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::vector<PExpr*>parms_;
|
||||
|
|
|
|||
144
elab_expr.cc
144
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<PEAssignPattern*>(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<const netdarray_t*>(ntype))
|
||||
return elaborate_expr_darray_(des, scope, darray_type, need_const);
|
||||
|
||||
if (auto parray_type = dynamic_cast<const netparray_t*>(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<const netvector_t*>(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<const netstruct_t*>(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<const netdarray_t*> (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<netrange_t> &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<PEAssignPattern*>(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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<PEAssignPattern*>(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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<member_t>& members() const { return members_; }
|
||||
|
||||
// Return the width (in bits) of the packed record, or -1 if
|
||||
// the record is not packed.
|
||||
|
|
|
|||
Loading…
Reference in New Issue