Add support for `const` variables
SystemVerilog allows to declare const variables. These variables are read-only and can not be assigned a value after their declaration. It is only possible to assign an initial value as an initializer. E.g. ``` const int x = 10; x = 20; // Error ``` The LRM requires that for variable declarations with static storage the initializer is a constant expression with the extension that other const variables are also allowed. const variables with automatic storage can be initialized by any expression. Checking if an expression contains only const variables requires a bit more work to implement. So for now be more lenient that what the standard requires and allow arbitrary expressions to initialize const variables even for those with static storage. Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
parent
9e4c4d5460
commit
3daa2982ac
12
PExpr.h
12
PExpr.h
|
|
@ -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_;
|
||||
|
|
|
|||
6
PWire.h
6
PWire.h
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
19
elab_lval.cc
19
elab_lval.cc
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
50
parse.y
|
|
@ -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; }
|
||||
|
|
|
|||
16
pform.cc
16
pform.cc
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
9
pform.h
9
pform.h
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue