Merge pull request #975 from larsclausen/sv_const_var

Add support for `const` variables
This commit is contained in:
Stephen Williams 2023-07-25 19:15:10 -07:00 committed by GitHub
commit 999bcb6935
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 396 additions and 47 deletions

12
PExpr.h
View File

@ -172,7 +172,8 @@ class PExpr : public LineInfo {
virtual NetAssign_* elaborate_lval(Design*des,
NetScope*scope,
bool is_cassign,
bool is_force) const;
bool is_force,
bool is_init = false) const;
// This method returns true if the expression represents a
// structural net that can have multiple drivers. This is
@ -262,7 +263,8 @@ class PEConcat : public PExpr {
virtual NetAssign_* elaborate_lval(Design*des,
NetScope*scope,
bool is_cassign,
bool is_force) const;
bool is_force,
bool is_init = false) const;
virtual bool is_collapsible_net(Design*des, NetScope*scope,
NetNet::PortType port_type) const;
private:
@ -361,7 +363,8 @@ class PEIdent : public PExpr {
virtual NetAssign_* elaborate_lval(Design*des,
NetScope*scope,
bool is_cassign,
bool is_force) const;
bool is_force,
bool is_init = false) const;
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
ivl_type_t type, unsigned flags) const;
@ -647,7 +650,8 @@ class PENumber : public PExpr {
virtual NetAssign_* elaborate_lval(Design*des,
NetScope*scope,
bool is_cassign,
bool is_force) const;
bool is_force,
bool is_init = false) const;
private:
verinum*const value_;

View File

@ -68,6 +68,9 @@ class PWire : public PNamedItem {
NetNet::PortType get_port_type() const;
bool set_port_type(NetNet::PortType);
void set_const(bool is_const) { is_const_ = is_const; };
bool get_const() const { return is_const_; };
void set_signed(bool flag);
bool get_signed() const;
@ -100,6 +103,9 @@ class PWire : public PNamedItem {
NetNet::PortType port_type_;
bool signed_;
// Whether the wire is variable declared with the const keyword.
bool is_const_ = false;
// These members hold expressions for the bit width of the
// wire. If they do not exist, the wire is 1 bit wide. If they
// do exist, they represent the packed dimensions of the

View File

@ -29,8 +29,9 @@ Statement::~Statement()
{
}
PAssign_::PAssign_(PExpr*lval__, PExpr*ex, bool is_constant)
: event_(0), count_(0), lval_(lval__), rval_(ex), is_constant_(is_constant)
PAssign_::PAssign_(PExpr*lval__, PExpr*ex, bool is_constant, bool is_init)
: event_(0), count_(0), lval_(lval__), rval_(ex), is_constant_(is_constant),
is_init_(is_init)
{
delay_ = 0;
}
@ -73,8 +74,8 @@ PAssign::PAssign(PExpr*lval__, PExpr*cnt, PEventStatement*d, PExpr*ex)
{
}
PAssign::PAssign(PExpr*lval__, PExpr*ex, bool is_constant)
: PAssign_(lval__, ex, is_constant), op_(0)
PAssign::PAssign(PExpr*lval__, PExpr*ex, bool is_constant, bool is_init)
: PAssign_(lval__, ex, is_constant, is_init), op_(0)
{
}

View File

@ -95,7 +95,8 @@ class Statement : virtual public LineInfo {
*/
class PAssign_ : public Statement {
public:
explicit PAssign_(PExpr*lval, PExpr*ex, bool is_constant);
explicit PAssign_(PExpr*lval, PExpr*ex, bool is_constant,
bool is_init = false);
explicit PAssign_(PExpr*lval, PExpr*de, PExpr*ex);
explicit PAssign_(PExpr*lval, PExpr*cnt, PEventStatement*de, PExpr*ex);
virtual ~PAssign_() =0;
@ -122,6 +123,8 @@ class PAssign_ : public Statement {
PExpr* lval_;
PExpr* rval_;
bool is_constant_;
// Whether the assignment is a variable initializer expression
bool is_init_ = false;
};
class PAssign : public PAssign_ {
@ -135,7 +138,7 @@ class PAssign : public PAssign_ {
explicit PAssign(PExpr*lval, char op, PExpr*ex);
explicit PAssign(PExpr*lval, PExpr*de, PExpr*ex);
explicit PAssign(PExpr*lval, PExpr*cnt, PEventStatement*de, PExpr*ex);
explicit PAssign(PExpr*lval, PExpr*ex, bool is_constant);
explicit PAssign(PExpr*lval, PExpr*ex, bool is_constant, bool is_init);
~PAssign();
virtual void dump(std::ostream&out, unsigned ind) const;

View File

@ -76,7 +76,7 @@ using namespace std;
* is to try to make a net elaboration, and see if the result is
* suitable for assignment.
*/
NetAssign_* PExpr::elaborate_lval(Design*, NetScope*, bool, bool) const
NetAssign_* PExpr::elaborate_lval(Design*, NetScope*, bool, bool, bool) const
{
cerr << get_fileline() << ": Assignment l-value too complex." << endl;
return 0;
@ -98,7 +98,8 @@ NetAssign_* PExpr::elaborate_lval(Design*, NetScope*, bool, bool) const
NetAssign_* PEConcat::elaborate_lval(Design*des,
NetScope*scope,
bool is_cassign,
bool is_force) const
bool is_force,
bool is_init) const
{
if (repeat_) {
cerr << get_fileline() << ": error: Repeat concatenations make "
@ -119,7 +120,7 @@ NetAssign_* PEConcat::elaborate_lval(Design*des,
}
NetAssign_*tmp = parms_[idx]->elaborate_lval(des, scope,
is_cassign, is_force);
is_cassign, is_force, is_init);
/* If the l-value doesn't elaborate, the error was
already detected and printed. We just skip it and let
@ -157,7 +158,8 @@ NetAssign_* PEConcat::elaborate_lval(Design*des,
NetAssign_* PEIdent::elaborate_lval(Design*des,
NetScope*scope,
bool is_cassign,
bool is_force) const
bool is_force,
bool is_init) const
{
if (debug_elaborate) {
@ -213,6 +215,13 @@ NetAssign_* PEIdent::elaborate_lval(Design*des,
<< endl;
}
if (reg->get_const() && !is_init) {
cerr << get_fileline() << ": error: Assignment to const signal `"
<< reg->name() << "` is not allowed." << endl;
des->errors++;
return nullptr;
}
return elaborate_lval_var_(des, scope, is_force, is_cassign, reg,
sr.type, member_path);
}
@ -1535,7 +1544,7 @@ bool PEIdent::elaborate_lval_net_packed_member_(Design*des, NetScope*scope,
return false;
}
NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*, bool, bool) const
NetAssign_* PENumber::elaborate_lval(Design*des, NetScope*, bool, bool, bool) const
{
cerr << get_fileline() << ": error: Constant values not allowed "
<< "in l-value expressions." << endl;

View File

@ -556,6 +556,12 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope,
<< endl;
}
if (sig->get_const()) {
cerr << get_fileline() << ": error: Continuous assignment to const"
<< " signal `" << sig->name() << "` is not allowed." << endl;
des->errors++;
return nullptr;
}
// If this is SystemVerilog and the variable is not yet
// assigned by anything, then convert it to an unresolved

View File

@ -1204,5 +1204,7 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const
for (unsigned idx = 0 ; idx < nattrib ; idx += 1)
sig->attribute(attrib_list[idx].key, attrib_list[idx].val);
sig->set_const(is_const_);
return sig;
}

View File

@ -2378,7 +2378,7 @@ NetAssign_* PAssign_::elaborate_lval(Design*des, NetScope*scope) const
<< "lval_ expr type = " << typeid(*lval_).name() << endl;
}
return lval_->elaborate_lval(des, scope, false, false);
return lval_->elaborate_lval(des, scope, false, false, is_init_);
}
NetExpr* PAssign_::elaborate_rval_(Design*des, NetScope*scope,

View File

@ -0,0 +1,18 @@
// Check that const variables in module scope are supported.
module test;
const integer x = 10;
// The initializer expression is allowed to reference other const variables.
const integer y = 20 + x;
initial begin
if (x === 10 && y === 30) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,29 @@
// Check that const variables are supported in function and task scope.
module test;
function automatic integer f(integer x);
// Automatic const variables can have a non-const initializer epxression
const integer y = 2 * x;
return y;
endfunction
task automatic t(input integer x, output integer y);
// Automatic const variables can have a non-const initializer epxression
const integer z = 2 * x;
y = z;
endtask
initial begin
integer y;
t(15, y);
if (f(10) === 20 && y === 30) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,17 @@
// Check that const variables in block scope are supported.
module test;
initial begin
const static integer x = 10;
// The initializer expression is allowed to reference other const variables.
const static integer y = 20 + x;
if (x === 10 && y === 30) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,18 @@
// Check that const variables are supported in the unit scope.
const integer x = 10;
// The initializer expression is allowed to reference other const variables.
const integer y = 20 + x;
module test;
initial begin
if (x === 10 && y === 30) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,20 @@
// Check that const variables are supported in a package scope.
package P;
const integer x = 10;
// The initializer expression is allowed to reference other const variables.
const integer y = 20 + x;
endpackage
module test;
initial begin
if (P::x === 10 && P::y === 30) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,13 @@
// Check that continuous assignment to a const variable fails.
module test;
const integer x = 10;
assign x = 20; // Error: Assignment to const variable
initial begin
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,12 @@
// Check that blocking assignment to a const variable fails.
module test;
const integer x = 10;
initial begin
x = 20; // Error: Assignment to const variable
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,12 @@
// Check that non-blocking assignment to a const variable fails.
module test;
const integer x = 10;
initial begin
x <= 20; // Error: Assignment to const variable
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,12 @@
// Check that force assignment to a const variable fails.
module test;
const integer x = 10;
initial begin
force x = 20; // Error: Assignment to const variable
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,12 @@
// Check that procedural continuous assignment to a const variable fails.
module test;
const integer x = 10;
initial begin
assign x = 20; // Error: Assignment to const variable
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,16 @@
// Check that binding a const variable to a task output port fails.
module test;
const integer x = 10;
task t(output integer x);
x = 20;
endtask
initial begin
t(x);
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,21 @@
// Check that binding a const variable to a module output port fails.
module M(
output integer x
);
assign x = 20;
endmodule
module test;
const integer x = 10;
M i_m (
.x (x)
);
initial begin
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,21 @@
// Check that binding a const variable to a module inout port fails.
module M(
inout integer x
);
assign x = 20;
endmodule
module test;
const integer x = 10;
M i_m (
.x (x)
);
initial begin
$display("FAILED");
end
endmodule

View File

@ -60,6 +60,19 @@ sv_array_assign_fail1 vvp_tests/sv_array_assign_fail1.json
sv_array_assign_fail2 vvp_tests/sv_array_assign_fail2.json
sv_array_cassign6 vvp_tests/sv_array_cassign6.json
sv_array_cassign7 vvp_tests/sv_array_cassign7.json
sv_const1 vvp_tests/sv_const1.json
sv_const2 vvp_tests/sv_const2.json
sv_const3 vvp_tests/sv_const3.json
sv_const4 vvp_tests/sv_const4.json
sv_const5 vvp_tests/sv_const5.json
sv_const_fail1 vvp_tests/sv_const_fail1.json
sv_const_fail2 vvp_tests/sv_const_fail2.json
sv_const_fail3 vvp_tests/sv_const_fail3.json
sv_const_fail4 vvp_tests/sv_const_fail4.json
sv_const_fail5 vvp_tests/sv_const_fail5.json
sv_const_fail6 vvp_tests/sv_const_fail6.json
sv_const_fail7 vvp_tests/sv_const_fail7.json
sv_const_fail8 vvp_tests/sv_const_fail8.json
sv_foreach9 vvp_tests/sv_foreach9.json
sv_foreach10 vvp_tests/sv_foreach10.json
sv_module_port1 vvp_tests/sv_module_port1.json

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_const1.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_const2.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_const3.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_const4.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_const5.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_const_fail1.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_const_fail2.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_const_fail3.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_const_fail4.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_const_fail5.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_const_fail6.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_const_fail7.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "CE",
"source" : "sv_const_fail8.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -702,6 +702,9 @@ class NetNet : public NetObj, public PortType {
signed. Otherwise, it is unsigned. */
bool get_signed() const;
void set_const(bool is_const) { is_const_ = is_const; }
bool get_const() const { return is_const_; }
bool get_scalar() const;
inline const ivl_type_s* net_type(void) const { return net_type_; }
@ -808,6 +811,9 @@ class NetNet : public NetObj, public PortType {
netuarray_t *array_type_ = nullptr;
ivl_discipline_t discipline_;
// Whether the net is variable declared with the const keyword.
bool is_const_ = false;
std::vector<netrange_t> unpacked_dims_;
// These are the widths of the various slice depths. There is

50
parse.y
View File

@ -639,7 +639,7 @@ Module::port_t *module_declare_port(const YYLTYPE&loc, char *id,
%type <number> number pos_neg_number
%type <flag> signing unsigned_signed_opt signed_unsigned_opt
%type <flag> import_export
%type <flag> K_genvar_opt K_static_opt K_virtual_opt
%type <flag> K_genvar_opt K_static_opt K_virtual_opt K_const_opt
%type <flag> udp_reg_opt edge_operator
%type <drive> drive_strength drive_strength_opt dr_strength0 dr_strength1
%type <letter> udp_input_sym udp_output_sym
@ -1231,21 +1231,22 @@ constraint_set /* IEEE1800-2005 A.1.9 */
;
data_declaration /* IEEE1800-2005: A.2.1.3 */
: attribute_list_opt data_type list_of_variable_decl_assignments ';'
{ data_type_t*data_type = $2;
if (data_type == 0) {
: attribute_list_opt K_const_opt data_type list_of_variable_decl_assignments ';'
{ data_type_t *data_type = $3;
if (!data_type) {
data_type = new vector_type_t(IVL_VT_LOGIC, false, 0);
FILE_NAME(data_type, @2);
FILE_NAME(data_type, @3);
}
pform_makewire(@2, 0, str_strength, $3, NetNet::IMPLICIT_REG, data_type, $1);
pform_makewire(@3, 0, str_strength, $4, NetNet::IMPLICIT_REG, data_type,
$1, $2);
}
| attribute_list_opt K_var data_type_or_implicit list_of_variable_decl_assignments ';'
{ data_type_t*data_type = $3;
if (data_type == 0) {
| attribute_list_opt K_const_opt K_var data_type_or_implicit list_of_variable_decl_assignments ';'
{ data_type_t *data_type = $4;
if (!data_type) {
data_type = new vector_type_t(IVL_VT_LOGIC, false, 0);
FILE_NAME(data_type, @2);
FILE_NAME(data_type, @3);
}
pform_make_var(@2, $4, data_type, $1);
pform_make_var(@3, $5, data_type, $1, $2);
}
| attribute_list_opt K_event event_variable_list ';'
{ if ($3) pform_make_events(@2, $3);
@ -2731,24 +2732,24 @@ block_item_decl
/* variable declarations. Note that data_type can be 0 if we are
recovering from an error. */
: K_var variable_lifetime_opt data_type_or_implicit list_of_variable_decl_assignments ';'
{ data_type_t*data_type = $3;
if (data_type == 0) {
: K_const_opt K_var variable_lifetime_opt data_type_or_implicit list_of_variable_decl_assignments ';'
{ data_type_t *data_type = $4;
if (!data_type) {
data_type = new vector_type_t(IVL_VT_LOGIC, false, 0);
FILE_NAME(data_type, @1);
FILE_NAME(data_type, @2);
}
pform_make_var(@1, $4, data_type, attributes_in_context);
pform_make_var(@2, $5, data_type, attributes_in_context);
var_lifetime = LexicalScope::INHERITED;
}
| variable_lifetime_opt data_type list_of_variable_decl_assignments ';'
{ if ($2) pform_make_var(@2, $3, $2, attributes_in_context);
| K_const_opt variable_lifetime_opt data_type list_of_variable_decl_assignments ';'
{ if ($3) pform_make_var(@3, $4, $3, attributes_in_context, $1);
var_lifetime = LexicalScope::INHERITED;
}
/* The extra `reg` is not valid (System)Verilog, this is a iverilog extension. */
| variable_lifetime_opt K_reg data_type list_of_variable_decl_assignments ';'
{ if ($3) pform_make_var(@3, $4, $3, attributes_in_context);
| K_const_opt variable_lifetime_opt K_reg data_type list_of_variable_decl_assignments ';'
{ if ($4) pform_make_var(@4, $5, $4, attributes_in_context, $1);
var_lifetime = LexicalScope::INHERITED;
}
@ -2769,11 +2770,11 @@ block_item_decl
/* Recover from errors that happen within variable lists. Use the
trailing semi-colon to resync the parser. */
| K_var variable_lifetime_opt data_type_or_implicit error ';'
| K_const_opt K_var variable_lifetime_opt data_type_or_implicit error ';'
{ yyerror(@1, "error: Syntax error in variable list.");
yyerrok;
}
| variable_lifetime_opt data_type error ';'
| K_const_opt variable_lifetime_opt data_type error ';'
{ yyerror(@1, "error: Syntax error in variable list.");
yyerrok;
}
@ -7308,6 +7309,11 @@ unique_priority
presence is significant. This is a fairly common pattern so
collect those rules here. */
K_const_opt
: K_const { $$ = true; }
| { $$ = false; }
;
K_genvar_opt
: K_genvar { $$ = true; }
| { $$ = false; }

View File

@ -951,11 +951,13 @@ PCallTask* pform_make_call_task(const struct vlltype&loc,
void pform_make_var(const struct vlltype&loc,
std::list<decl_assignment_t*>*assign_list,
data_type_t*data_type, std::list<named_pexpr_t>*attr)
data_type_t*data_type, std::list<named_pexpr_t>*attr,
bool is_const)
{
static const struct str_pair_t str = { IVL_DR_STRONG, IVL_DR_STRONG };
pform_makewire(loc, 0, str, assign_list, NetNet::REG, data_type, attr);
pform_makewire(loc, 0, str, assign_list, NetNet::REG, data_type, attr,
is_const);
}
void pform_make_foreach_declarations(const struct vlltype&loc,
@ -2494,7 +2496,7 @@ void pform_make_var_init(const struct vlltype&li,
PEIdent*lval = new PEIdent(name);
FILE_NAME(lval, li);
PAssign*ass = new PAssign(lval, expr, !gn_system_verilog());
PAssign*ass = new PAssign(lval, expr, !gn_system_verilog(), true);
FILE_NAME(ass, li);
lexical_scope->var_inits.push_back(ass);
@ -2646,7 +2648,8 @@ void pform_makewire(const struct vlltype&li,
std::list<decl_assignment_t*>*assign_list,
NetNet::Type type,
data_type_t*data_type,
list<named_pexpr_t>*attr)
list<named_pexpr_t>*attr,
bool is_const)
{
if (is_compilation_unit(lexical_scope) && !gn_system_verilog()) {
VLerror(li, "error: Variable declarations must be contained within a module.");
@ -2662,7 +2665,7 @@ void pform_makewire(const struct vlltype&li,
wires->push_back(wire);
}
pform_set_data_type(li, data_type, wires, type, attr);
pform_set_data_type(li, data_type, wires, type, attr, is_const);
while (! assign_list->empty()) {
decl_assignment_t*first = assign_list->front();
@ -3224,7 +3227,7 @@ void pform_set_port_type(const struct vlltype&li,
*/
void pform_set_data_type(const struct vlltype&li, data_type_t*data_type,
std::vector<PWire*> *wires, NetNet::Type net_type,
list<named_pexpr_t>*attr)
list<named_pexpr_t>*attr, bool is_const)
{
if (data_type == 0) {
VLerror(li, "internal error: data_type==0.");
@ -3245,6 +3248,7 @@ void pform_set_data_type(const struct vlltype&li, data_type_t*data_type,
ivl_assert(li, rc);
wire->set_data_type(data_type);
wire->set_const(is_const);
pform_bind_attributes(wire->attributes, attr, true);
}

View File

@ -350,12 +350,14 @@ extern void pform_makewire(const struct vlltype&li,
std::list<decl_assignment_t*>*assign_list,
NetNet::Type type,
data_type_t*data_type,
std::list<named_pexpr_t>*attr = 0);
std::list<named_pexpr_t>*attr = 0,
bool is_const = false);
extern void pform_make_var(const struct vlltype&loc,
std::list<decl_assignment_t*>*assign_list,
data_type_t*data_type,
std::list<named_pexpr_t>*attr = 0);
std::list<named_pexpr_t>*attr = 0,
bool is_const = false);
extern void pform_make_var_init(const struct vlltype&li,
perm_string name, PExpr*expr);
@ -374,7 +376,8 @@ extern void pform_set_data_type(const struct vlltype&li,
data_type_t *data_type,
std::vector<PWire*> *wires,
NetNet::Type net_type,
std::list<named_pexpr_t>*attr);
std::list<named_pexpr_t>*attr,
bool is_const = false);
extern void pform_set_string_type(const struct vlltype&li, const string_type_t*string_type, std::list<perm_string>*names, NetNet::Type net_type, std::list<named_pexpr_t>*attr);