diff --git a/PExpr.h b/PExpr.h index 7691b44d1..03c9e3284 100644 --- a/PExpr.h +++ b/PExpr.h @@ -509,6 +509,10 @@ class PEUnary : public PExpr { unsigned flags) const; virtual verinum* eval_const(Design*des, NetScope*sc) const; + public: + inline char get_op() const { return op_; } + inline PExpr*get_expr() const { return expr_; } + private: NetExpr* elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const; diff --git a/parse.y b/parse.y index 31d8fcd27..38dc2d0bb 100644 --- a/parse.y +++ b/parse.y @@ -496,7 +496,7 @@ static void current_task_set_statement(vector*s) %type hierarchy_identifier %type assignment_pattern expression expr_primary expr_mintypmax -%type lpvalue +%type inc_or_dec_expression lpvalue %type branch_probe_expression %type delay_value delay_value_simple %type delay1 delay3 delay3_opt delay_value_list @@ -665,6 +665,35 @@ implicit_class_handle /* IEEE1800-2005: A.8.4 */ | K_super ; + /* SystemVerilog adds support for the increment/decrement + expressions, which look like a++, --a, etc. These are primaries + but are in their own rules because they can also be + statements. Note that the operator can only take l-value + expressions. */ + +inc_or_dec_expression /* IEEE1800-2005: A.4.3 */ + : K_INCR lpvalue %prec UNARY_PREC + { PEUnary*tmp = new PEUnary('I', $2); + FILE_NAME(tmp, @2); + $$ = tmp; + } + | lpvalue K_INCR %prec UNARY_PREC + { PEUnary*tmp = new PEUnary('i', $1); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | K_DECR lpvalue %prec UNARY_PREC + { PEUnary*tmp = new PEUnary('D', $2); + FILE_NAME(tmp, @2); + $$ = tmp; + } + | lpvalue K_DECR %prec UNARY_PREC + { PEUnary*tmp = new PEUnary('d', $1); + FILE_NAME(tmp, @1); + $$ = tmp; + } + ; + number : BASED_NUMBER { $$ = $1; based_size = 0;} | DEC_NUMBER @@ -1790,32 +1819,10 @@ branch_probe_expression expression : expr_primary { $$ = $1; } + | inc_or_dec_expression + { $$ = $1; } | '+' expr_primary %prec UNARY_PREC { $$ = $2; } - | K_INCR expr_primary %prec UNARY_PREC - { - PEUnary*tmp = new PEUnary('I', $2); - FILE_NAME(tmp, @2); - $$ = tmp; - } - | expr_primary K_INCR %prec UNARY_PREC - { - PEUnary*tmp = new PEUnary('i', $1); - FILE_NAME(tmp, @1); - $$ = tmp; - } - | K_DECR expr_primary %prec UNARY_PREC - { - PEUnary*tmp = new PEUnary('D', $2); - FILE_NAME(tmp, @2); - $$ = tmp; - } - | expr_primary K_DECR %prec UNARY_PREC - { - PEUnary*tmp = new PEUnary('d', $1); - FILE_NAME(tmp, @1); - $$ = tmp; - } | '-' expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('-', $2); FILE_NAME(tmp, @2); @@ -4897,7 +4904,7 @@ spec_notifier ; -statement +statement /* This is roughly statement_item in the LRM */ /* assign and deassign statements are procedural code to do structural assignments, and to turn that structural assignment @@ -5094,16 +5101,29 @@ statement { $$ = 0; yyerror(@1, "error: Error in while loop condition."); } - | compressed_statement ';' - { $$ = $1; } - | delay1 statement_or_null - { PExpr*del = $1->front(); - assert($1->size() == 1); - delete $1; - PDelayStatement*tmp = new PDelayStatement(del, $2); - FILE_NAME(tmp, @1); - $$ = tmp; - } + + /* SytemVerilog adds the compressed_statement */ + + | compressed_statement ';' + { $$ = $1; } + + /* increment/decrement expressions can also be statements. When used + as statements, we can rewrite a++ as a += 1, and so on. */ + + | inc_or_dec_expression ';' + { $$ = pform_compressed_assign_from_inc_dec(@1, $1); } + + /* */ + + | delay1 statement_or_null + { PExpr*del = $1->front(); + assert($1->size() == 1); + delete $1; + PDelayStatement*tmp = new PDelayStatement(del, $2); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | event_control attribute_list_opt statement_or_null { PEventStatement*tmp = $1; if (tmp == 0) { @@ -5129,6 +5149,9 @@ statement tmp->set_statement($6); $$ = tmp; } + + /* Various assignment statements */ + | lpvalue '=' expression ';' { PAssign*tmp = new PAssign($1,$3); FILE_NAME(tmp, @1); @@ -5184,6 +5207,8 @@ statement FILE_NAME(tmp, @1); $$ = tmp; } + + | K_wait '(' expression ')' statement_or_null { PEventStatement*tmp; PEEvent*etmp = new PEEvent(PEEvent::POSITIVE, $3); diff --git a/pform.cc b/pform.cc index 9629be7bc..cd909fb74 100644 --- a/pform.cc +++ b/pform.cc @@ -2208,6 +2208,43 @@ svector*pform_make_task_ports(const struct vlltype&loc, range_tmp, names); } +/* + * The parser calls this in the rule that matches increment/decrement + * statements. The rule that does the matching creates a PEUnary with + * all the information we need, but here we convert that expression to + * a compressed assignment statement. + */ +PAssign* pform_compressed_assign_from_inc_dec(const struct vlltype&loc, PExpr*exp) +{ + PEUnary*expu = dynamic_cast (exp); + ivl_assert(*exp, expu != 0); + + char use_op = 0; + switch (expu->get_op()) { + case 'i': + case 'I': + use_op = '+'; + break; + case 'd': + case 'D': + use_op = '-'; + break; + default: + ivl_assert(*exp, 0); + break; + } + + PExpr*lval = expu->get_expr(); + PExpr*rval = new PENumber(new verinum((uint64_t)1, 1)); + FILE_NAME(rval, loc); + + PAssign*tmp = new PAssign(lval, use_op, rval); + FILE_NAME(tmp, loc); + + delete exp; + return tmp; +} + void pform_set_attrib(perm_string name, perm_string key, char*value) { if (PWire*cur = lexical_scope->wires_find(name)) { diff --git a/pform.h b/pform.h index 19b0f72b5..addf61c94 100644 --- a/pform.h +++ b/pform.h @@ -398,6 +398,13 @@ extern svector*pform_make_task_ports(const struct vlltype&loc, data_type_t*vtype, list*names); +/* + * The parser uses this function to convert a unary + * increment/decrement expression to the equivilent compressed + * assignment statement. + */ +extern PAssign* pform_compressed_assign_from_inc_dec(const struct vlltype&loc, + PExpr*exp); /* * These are functions that the outside-the-parser code uses the do