Merge pull request #868 from larsclausen/assign-pattern

Add initial support for packed array and packed struct assignment patterns
This commit is contained in:
Stephen Williams 2023-02-09 12:48:46 -08:00 committed by GitHub
commit 418bbc14bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 311 additions and 30 deletions

14
PExpr.h
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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