%{ /* * Copyright (c) 1998-1999 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU * General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #if !defined(WINNT) #ident "$Id: parse.y,v 1.48 1999/07/03 02:12:51 steve Exp $" #endif # include "parse_misc.h" # include "pform.h" extern void lex_start_table(); extern void lex_end_table(); %} %union { char letter; string*text; list*strings; PCase::Item*citem; svector*citems; lgate*gate; svector*gates; portname_t*portname; svector*portnames; PExpr*expr; svector*exprs; svector*event_expr; NetNet::Type nettype; PGBuiltin::Type gatetype; NetNet::PortType porttype; PTask*task; PWire*wire; svector*wires; PEventStatement*event_statement; Statement*statement; svector*statement_list; verinum* number; verireal* realtime; }; %token HIDENTIFIER IDENTIFIER PORTNAME SYSTEM_IDENTIFIER STRING %token NUMBER %token REALTIME %token K_LE K_GE K_EG K_EQ K_NE K_CEQ K_CNE K_LS K_RS K_SG %token K_LOR K_LAND K_NAND K_NOR K_NXOR %token K_always K_and K_assign K_begin K_buf K_bufif0 K_bufif1 K_case %token K_casex K_casez K_cmos K_deassign K_default K_defparam K_disable %token K_edge K_else K_end K_endcase K_endfunction K_endmodule %token K_endprimitive K_endspecify K_endtable K_endtask K_event K_for %token K_force K_forever K_fork K_function K_highz0 K_highz1 K_if %token K_initial K_inout K_input K_integer K_join K_large K_macromodule %token K_medium K_module K_nand K_negedge K_nmos K_nor K_not K_notif0 %token K_notif1 K_or K_output K_parameter K_pmos K_posedge K_primitive %token K_pull0 K_pull1 K_pulldown K_pullup K_rcmos K_real K_realtime %token K_reg K_release K_repeat %token K_rnmos K_rpmos K_rtran K_rtranif0 K_rtranif1 K_scalered %token K_small K_specify %token K_specparam K_strong0 K_strong1 K_supply0 K_supply1 K_table K_task %token K_time K_tran K_tranif0 K_tranif1 K_tri K_tri0 K_tri1 K_triand %token K_trior K_trireg K_vectored K_wait K_wand K_weak0 K_weak1 %token K_while K_wire %token K_wor K_xnor K_xor %token KK_attribute %type udp_input_sym udp_output_sym %type udp_input_list udp_sequ_entry udp_comb_entry %type udp_entry_list udp_comb_entry_list udp_sequ_entry_list %type udp_body udp_port_list %type udp_port_decl udp_port_decls %type udp_initial udp_init_opt %type identifier register_variable %type register_variable_list %type list_of_variables %type net_decl_assign %type net_decl_assigns %type port %type list_of_ports list_of_ports_opt %type port_name %type port_name_list %type case_item %type case_items %type gate_instance %type gate_instance_list %type delay delay_opt delay_value delay_value_list %type expression expr_primary %type lavalue lpvalue %type expression_list %type range range_opt %type net_type %type gatetype %type port_type %type task_body %type event_expression %type event_control %type statement statement_opt %type statement_list %left '?' ':' %left K_LOR %left K_LAND %left '|' %left '^' %left '&' %left K_EQ K_NE K_CEQ K_CNE %left K_GE K_LE '<' '>' %left K_LS K_RS %left '+' '-' %left '*' '/' '%' %left UNARY_PREC %% source_file : description | source_file description ; block_item_decl : K_reg range_opt register_variable_list ';' | K_integer list_of_variables ';' ; block_item_decls : block_item_decl | block_item_decls block_item_decl ; block_item_decls_opt : block_item_decls | ; case_item : expression_list ':' statement_opt { PCase::Item*tmp = new PCase::Item; tmp->expr = *$1; tmp->stat = $3; delete $1; $$ = tmp; } | K_default ':' statement_opt { PCase::Item*tmp = new PCase::Item; tmp->stat = $3; $$ = tmp; } | K_default statement_opt { PCase::Item*tmp = new PCase::Item; tmp->stat = $2; $$ = tmp; } | error ':' statement_opt { yyerror(@1, "Incomprehensible case expression."); yyerrok; } ; case_items : case_items case_item { svector*tmp; tmp = new svector(*$1, $2); delete $1; $$ = tmp; } | case_item { svector*tmp = new svector(1); (*tmp)[0] = $1; $$ = tmp; } ; charge_strength : '(' K_small ')' | '(' K_medium ')' | '(' K_large ')' ; charge_strength_opt : charge_strength | ; defparam_assign : identifier '=' expression { PExpr*tmp = $3; if (!pform_expression_is_constant(tmp)) { yyerror(@3, "parameter value must be constant."); delete tmp; tmp = 0; } yyerror(@1, "Sorry, defparam assignments not supported."); delete $1; delete $3; } ; defparam_assign_list : defparam_assign | range defparam_assign { yyerror(@1, "Ranges in parameter definition " "are not supported."); delete $1; } | defparam_assign_list ',' defparam_assign ; delay : '#' delay_value { $$ = $2; } | '#' '(' delay_value_list ')' { $$ = $3; } ; delay_opt : delay { $$ = $1; } | { $$ = 0; } ; delay_value : NUMBER { verinum*tmp = $1; if (tmp == 0) { yyerror(@1, "XXXX internal error: delay."); $$ = 0; } else { $$ = new PENumber(tmp); } } | IDENTIFIER { PEIdent*tmp = new PEIdent(*$1); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; delete $1; } ; delay_value_list : expression { $$ = $1; } | delay_value_list ',' expression { yyerror(@1, "Sorry, delay value lists not supported."); $$ = $1; delete $3; } ; description : module | udp_primitive | KK_attribute '(' IDENTIFIER ',' STRING ',' STRING ')' { pform_set_type_attrib(*$3, *$5, *$7); delete $3; delete $5; delete $7; } ; event_control : '@' IDENTIFIER { yyerror(@1, "Sorry, event control not supported."); $$ = 0; } | '@' '(' event_expression ')' { PEventStatement*tmp = new PEventStatement(*$3); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $3; $$ = tmp; } | '@' '(' error ')' { yyerror(@1, "Malformed event control expression."); $$ = 0; } ; event_expression : K_posedge expression { PEEvent*tmp = new PEEvent(NetNEvent::POSEDGE, $2); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); svector*tl = new svector(1); (*tl)[0] = tmp; $$ = tl; } | K_negedge expression { PEEvent*tmp = new PEEvent(NetNEvent::NEGEDGE, $2); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); svector*tl = new svector(1); (*tl)[0] = tmp; $$ = tl; } | expression { PEEvent*tmp = new PEEvent(NetNEvent::ANYEDGE, $1); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); svector*tl = new svector(1); (*tl)[0] = tmp; $$ = tl; } | event_expression K_or event_expression { svector*tmp = new svector(*$1, *$3); delete $1; delete $3; $$ = tmp; } ; expression : expr_primary { $$ = $1; } | '+' expr_primary %prec UNARY_PREC { $$ = $2; } | '-' expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('-', $2); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | '~' expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('~', $2); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | '&' expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('&', $2); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | '!' expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('!', $2); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | '|' expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('|', $2); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | '^' expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('^', $2); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | K_NAND expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('A', $2); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | K_NOR expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('N', $2); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | K_NXOR expr_primary %prec UNARY_PREC { PEUnary*tmp = new PEUnary('X', $2); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '^' expression { PEBinary*tmp = new PEBinary('^', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '*' expression { PEBinary*tmp = new PEBinary('*', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '/' expression { PEBinary*tmp = new PEBinary('/', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '%' expression { PEBinary*tmp = new PEBinary('%', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '+' expression { PEBinary*tmp = new PEBinary('+', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '-' expression { PEBinary*tmp = new PEBinary('-', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '&' expression { PEBinary*tmp = new PEBinary('&', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '|' expression { PEBinary*tmp = new PEBinary('|', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '<' expression { PEBinary*tmp = new PEBinary('<', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '>' expression { PEBinary*tmp = new PEBinary('>', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression K_LS expression { PEBinary*tmp = new PEBinary('l', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression K_RS expression { PEBinary*tmp = new PEBinary('r', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression K_EQ expression { PEBinary*tmp = new PEBinary('e', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression K_CEQ expression { PEBinary*tmp = new PEBinary('E', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression K_LE expression { PEBinary*tmp = new PEBinary('L', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression K_GE expression { PEBinary*tmp = new PEBinary('G', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression K_NE expression { PEBinary*tmp = new PEBinary('n', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression K_CNE expression { PEBinary*tmp = new PEBinary('N', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression K_LOR expression { PEBinary*tmp = new PEBinary('o', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression K_LAND expression { PEBinary*tmp = new PEBinary('a', $1, $3); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } | expression '?' expression ':' expression { PETernary*tmp = new PETernary($1, $3, $5); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = tmp; } ; expression_list : expression_list ',' expression { svector*tmp = new svector(*$1, $3); delete $1; $$ = tmp; } | expression { svector*tmp = new svector(1); (*tmp)[0] = $1; $$ = tmp; } | expression_list ',' { svector*tmp = new svector(*$1, 0); delete $1; $$ = tmp; } ; expr_primary : NUMBER { assert($1); PENumber*tmp = new PENumber($1); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | REALTIME { yyerror(@1, "Sorry, real constants not supported."); delete $1; $$ = 0; } | STRING { PEString*tmp = new PEString(*$1); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; delete $1; } | identifier { PEIdent*tmp = new PEIdent(*$1); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; delete $1; } | SYSTEM_IDENTIFIER { PEIdent*tmp = new PEIdent(*$1); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; delete $1; } | identifier '[' expression ']' { PEIdent*tmp = new PEIdent(*$1); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); tmp->msb_ = $3; delete $1; $$ = tmp; } | identifier '[' expression ':' expression ']' { PEIdent*tmp = new PEIdent(*$1); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); tmp->msb_ = $3; tmp->lsb_ = $5; delete $1; $$ = tmp; } | identifier '(' expression_list ')' { yyerror(@2, "Sorry, function calls not supported."); $$ = 0; } | SYSTEM_IDENTIFIER '(' expression_list ')' { yyerror(@2, "Sorry, function calls not supported."); $$ = 0; } | '(' expression ')' { $$ = $2; } | '(' expression ':' expression ':' expression ')' { yyerror(@2, "Sorry, (min:typ:max) not supported."); $$ = $4; delete $2; delete $6; } | '{' expression_list '}' { PEConcat*tmp = new PEConcat(*$2); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); delete $2; $$ = tmp; } | '{' expression '{' expression_list '}' '}' { PExpr*rep = $2; if (!pform_expression_is_constant($2)) { yyerror(@2, "Repeat expression must be constant."); delete rep; delete $2; rep = 0; } PEConcat*tmp = new PEConcat(*$4, rep); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $4; $$ = tmp; } ; func_body : function_item_list statement | function_item_list { yyerror(@1, "function body has no statement."); } ; function_item : K_input range_opt list_of_variables ';' | K_reg range_opt list_of_variables ';' | K_integer list_of_variables ';' ; function_item_list : function_item | function_item_list function_item ; /* A gate_instance is a module instantiation or a built in part type. In any case, the gate has a set of connections to ports. */ gate_instance : IDENTIFIER '(' expression_list ')' { lgate*tmp = new lgate; tmp->name = *$1; tmp->parms = $3; tmp->file = @1.text; tmp->lineno = @1.first_line; delete $1; $$ = tmp; } | IDENTIFIER '(' ')' { lgate*tmp = new lgate; tmp->name = *$1; tmp->parms = 0; tmp->file = @1.text; tmp->lineno = @1.first_line; delete $1; $$ = tmp; } | IDENTIFIER range '(' expression_list ')' { lgate*tmp = new lgate; svector*rng = $2; tmp->name = *$1; tmp->parms = $4; tmp->range[0] = (*rng)[0]; tmp->range[1] = (*rng)[1]; tmp->file = @1.text; tmp->lineno = @1.first_line; delete $1; delete rng; $$ = tmp; } | '(' expression_list ')' { lgate*tmp = new lgate; tmp->name = ""; tmp->parms = $2; tmp->file = @1.text; tmp->lineno = @1.first_line; $$ = tmp; } | IDENTIFIER '(' port_name_list ')' { lgate*tmp = new lgate; tmp->name = *$1; tmp->parms_by_name = $3; tmp->file = @1.text; tmp->lineno = @1.first_line; delete $1; $$ = tmp; } ; gate_instance_list : gate_instance_list ',' gate_instance { svector*tmp1 = $1; lgate*tmp2 = $3; svector*out = new svector (*tmp1, *tmp2); delete tmp1; delete tmp2; $$ = out; } | gate_instance { svector*tmp = new svector(1); (*tmp)[0] = *$1; delete $1; $$ = tmp; } ; gatetype : K_and { $$ = PGBuiltin::AND; } | K_nand { $$ = PGBuiltin::NAND; } | K_or { $$ = PGBuiltin::OR; } | K_nor { $$ = PGBuiltin::NOR; } | K_xor { $$ = PGBuiltin::XOR; } | K_xnor { $$ = PGBuiltin::XNOR; } | K_buf { $$ = PGBuiltin::BUF; } | K_bufif0 { $$ = PGBuiltin::BUFIF0; } | K_bufif1 { $$ = PGBuiltin::BUFIF1; } | K_not { $$ = PGBuiltin::NOT; } | K_notif0 { $$ = PGBuiltin::NOTIF0; } | K_notif1 { $$ = PGBuiltin::NOTIF1; } | K_pulldown { $$ = PGBuiltin::PULLDOWN; } | K_pullup { $$ = PGBuiltin::PULLUP; } | K_nmos { $$ = PGBuiltin::NMOS; } | K_rnmos { $$ = PGBuiltin::RNMOS; } | K_pmos { $$ = PGBuiltin::PMOS; } | K_rpmos { $$ = PGBuiltin::RPMOS; } | K_cmos { $$ = PGBuiltin::CMOS; } | K_rcmos { $$ = PGBuiltin::RCMOS; } | K_tran { $$ = PGBuiltin::TRAN; } | K_rtran { $$ = PGBuiltin::RTRAN; } | K_tranif0 { $$ = PGBuiltin::TRANIF0; } | K_tranif1 { $$ = PGBuiltin::TRANIF1; } | K_rtranif0 { $$ = PGBuiltin::RTRANIF0; } | K_rtranif1 { $$ = PGBuiltin::RTRANIF1; } ; identifier : IDENTIFIER { $$ = $1; } | HIDENTIFIER { yyerror(@1, "Sorry, qualified identifiers not supported."); $$ = $1; } ; list_of_ports : port { svector*tmp = new svector(1); (*tmp)[0] = $1; $$ = tmp; } | list_of_ports ',' port { svector*tmp = new svector(*$1, $3); delete $1; $$ = tmp; } ; list_of_ports_opt : '(' list_of_ports ')' { $$ = $2; } | '(' ')' { $$ = 0; } | { $$ = 0; } ; list_of_variables : IDENTIFIER { list*tmp = new list; tmp->push_back(*$1); delete $1; $$ = tmp; } | list_of_variables ',' IDENTIFIER { list*tmp = $1; tmp->push_back(*$3); delete $3; $$ = tmp; } ; /* An lavalue is the expression that can go on the left side of a continuous assign statement. This checks (where it can) that the expression meets the constraints of continuous assignments. */ lavalue : identifier { PEIdent*tmp = new PEIdent(*$1); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; $$ = tmp; } | identifier '[' expression ']' { PEIdent*tmp = new PEIdent(*$1); PExpr*sel = $3; if (! pform_expression_is_constant(sel)) { yyerror(@2, "Bit select in lvalue must " "contain a constant expression."); delete sel; } else { tmp->msb_ = sel; } tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; $$ = tmp; } | identifier range { PEIdent*tmp = new PEIdent(*$1); assert($2->count() == 2); tmp->msb_ = (*$2)[0]; tmp->lsb_ = (*$2)[1]; tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; delete $2; $$ = tmp; } | '{' expression_list '}' { yyerror(@1, "Sorry, concatenation expressions" " not supported in lvalue."); $$ = 0; delete $2; } ; /* An lpvalue is the expression that can go on the left side of a procedural assignment. This rule handles only procedural assignments. */ lpvalue : identifier { PEIdent*tmp = new PEIdent(*$1); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; $$ = tmp; } | identifier '[' expression ']' { PEIdent*tmp = new PEIdent(*$1); tmp->msb_ = $3; tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; $$ = tmp; } | identifier '[' expression ':' expression ']' { PEIdent*tmp = new PEIdent(*$1); tmp->msb_ = $3; tmp->lsb_ = $5; tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; $$ = tmp; } | '{' expression_list '}' { yyerror(@1, "Sorry, concatenation expressions" " not supported in lvalue."); $$ = 0; delete $2; } ; module : K_module IDENTIFIER list_of_ports_opt ';' { pform_startmodule(*$2, $3); } module_item_list K_endmodule { pform_endmodule(*$2); delete $2; } | K_module IDENTIFIER list_of_ports_opt ';' { pform_startmodule(*$2, $3); } K_endmodule { pform_endmodule(*$2); delete $2; } ; module_item : net_type range_opt list_of_variables ';' { pform_makewire(@1, $3, $1); if ($2) { pform_set_net_range($3, $2); delete $2; } delete $3; } | net_type range_opt net_decl_assigns ';' { pform_makewire(@1, $3, $1); if ($2) { pform_set_net_range($3, $2); delete $2; } delete $3; } | K_trireg charge_strength_opt range_opt delay_opt list_of_variables ';' { yyerror(@1, "Sorry, trireg nets not supported."); delete $3; } | port_type range_opt list_of_variables ';' { pform_set_port_type($3, $1); if ($2) { pform_set_net_range($3, $2); delete $2; } delete $3; } | K_reg range register_variable_list ';' { pform_set_net_range($3, $2); delete $2; delete $3; } | K_reg register_variable_list ';' { delete $2; } | K_integer register_variable_list ';' { pform_set_reg_integer($2); delete $2; } | K_defparam defparam_assign_list ';' | K_event list_of_variables ';' { yyerror(@1, "Sorry, named events not supported."); delete $2; } | K_parameter parameter_assign_list ';' | gatetype delay_opt gate_instance_list ';' { pform_makegates($1, $2, $3); } | IDENTIFIER delay_opt gate_instance_list ';' { pform_make_modgates(*$1, $3); delete $1; if ($2) { yyerror(@2, "Sorry, parameter override not supported."); delete $2; } } | K_assign delay_opt lavalue '=' expression ';' { PGAssign*tmp = pform_make_pgassign($3, $5); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); if ($2) { yyerror(@2, "Sorry, assign delays not supported."); delete $2; } } | K_assign error '=' expression ';' | K_always statement { PProcess*tmp = pform_make_behavior(PProcess::PR_ALWAYS, $2); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); } | K_initial statement { PProcess*tmp = pform_make_behavior(PProcess::PR_INITIAL, $2); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); } | K_task IDENTIFIER ';' { pform_push_scope(*$2); } task_body { pform_pop_scope(); } K_endtask { PTask*tmp = $5; tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); pform_set_task(*$2, $5); delete $2; } | K_function range_or_type_opt IDENTIFIER ';' func_body K_endfunction { yyerror(@1, "Sorry, function declarations not supported."); } | K_specify specify_item_list K_endspecify { } | KK_attribute '(' IDENTIFIER ',' STRING ',' STRING ')' ';' { pform_set_attrib(*$3, *$5, *$7); delete $3; delete $5; delete $7; } | KK_attribute '(' error ')' ';' { yyerror(@1, "Misformed $attribute parameter list."); } ; module_item_list : module_item_list module_item | module_item ; /* A net declaration assignment allows the programmer to combine the net declaration and the continuous assignment into a single statement. */ net_decl_assign : IDENTIFIER '=' expression { PEIdent*id = new PEIdent(*$1); PGAssign*tmp = pform_make_pgassign(id, $3); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = $1; } | delay IDENTIFIER '=' expression { PEIdent*id = new PEIdent(*$2); PGAssign*tmp = pform_make_pgassign(id, $4); tmp->set_file(@2.text); tmp->set_lineno(@2.first_line); $$ = $2; yyerror(@1, "Sorry, net assign delay not supported."); } ; net_decl_assigns : net_decl_assigns ',' net_decl_assign { list*tmp = $1; tmp->push_back(*$3); delete $3; $$ = tmp; } | net_decl_assign { list*tmp = new list; tmp->push_back(*$1); delete $1; $$ = tmp; } ; net_type : K_wire { $$ = NetNet::WIRE; } | K_tri { $$ = NetNet::TRI; } | K_tri1 { $$ = NetNet::TRI1; } | K_supply0 { $$ = NetNet::SUPPLY0; } | K_wand { $$ = NetNet::WAND; } | K_triand { $$ = NetNet::TRIAND; } | K_tri0 { $$ = NetNet::TRI0; } | K_supply1 { $$ = NetNet::SUPPLY1; } | K_wor { $$ = NetNet::WOR; } | K_trior { $$ = NetNet::TRIOR; } ; parameter_assign : IDENTIFIER '=' expression { PExpr*tmp = $3; if (!pform_expression_is_constant(tmp)) { yyerror(@3, "parameter value must be constant."); delete tmp; tmp = 0; } pform_set_parameter(*$1, tmp); delete $1; } ; parameter_assign_list : parameter_assign | range parameter_assign { yyerror(@1, "Ranges in parameter definition " "are not supported."); delete $1; } | parameter_assign_list ',' parameter_assign ; port : IDENTIFIER { $$ = new PWire(*$1, NetNet::IMPLICIT, NetNet::PIMPLICIT); $$->set_file(@1.text); $$->set_lineno(@1.first_line); delete $1; } | IDENTIFIER '[' expression ':' expression ']' { PWire*tmp = new PWire(*$1, NetNet::IMPLICIT, NetNet::PIMPLICIT); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); if (!pform_expression_is_constant($3)) { yyerror(@3, "msb expression of port bit select " "must be constant."); } if (!pform_expression_is_constant($5)) { yyerror(@3, "lsb expression of port bit select " "must be constant."); } tmp->set_range($3, $5); delete $1; $$ = tmp; } | IDENTIFIER '[' error ']' { yyerror(@1, "invalid port bit select"); $$ = new PWire(*$1, NetNet::IMPLICIT, NetNet::PIMPLICIT); $$->set_file(@1.text); $$->set_lineno(@1.first_line); delete $1; } ; port_name : PORTNAME '(' expression ')' { portname_t*tmp = new portname_t; tmp->name = *$1; tmp->parm = $3; delete $1; $$ = tmp; } | PORTNAME '(' error ')' { yyerror(@3, "invalid port connection expression."); portname_t*tmp = new portname_t; tmp->name = *$1; tmp->parm = 0; delete $1; $$ = tmp; } | PORTNAME '(' ')' { portname_t*tmp = new portname_t; tmp->name = *$1; tmp->parm = 0; delete $1; $$ = tmp; } ; port_name_list : port_name_list ',' port_name { svector*tmp; tmp = new svector(*$1, $3); delete $1; $$ = tmp; } | port_name { svector*tmp = new svector(1); (*tmp)[0] = $1; $$ = tmp; } ; port_type : K_input { $$ = NetNet::PINPUT; } | K_output { $$ = NetNet::POUTPUT; } | K_inout { $$ = NetNet::PINOUT; } ; range : '[' expression ':' expression ']' { svector*tmp = new svector (2); if (!pform_expression_is_constant($2)) yyerror(@2, "msb of range must be constant."); (*tmp)[0] = $2; if (!pform_expression_is_constant($4)) yyerror(@4, "msb of range must be constant."); (*tmp)[1] = $4; $$ = tmp; } ; range_opt : range | { $$ = 0; } ; range_or_type_opt : range { } | K_integer | K_real | K_realtime | K_time | ; /* The register_variable rule is matched only when I am parsing variables in a "reg" definition. I therefore know that I am creating registers and I do not need to let the containing rule handle it. The register variable list simply packs them together so that bit ranges can be assigned. */ register_variable : IDENTIFIER { pform_makewire(@1, *$1, NetNet::REG); $$ = $1; } | IDENTIFIER '=' expression { pform_makewire(@1, *$1, NetNet::REG); yyerror(@2, "net declaration assignment to reg/integer not allowed."); delete $3; $$ = $1; } | IDENTIFIER '[' expression ':' expression ']' { pform_makewire(@1, *$1, NetNet::REG); if (! pform_expression_is_constant($3)) yyerror(@3, "msb of register range must be constant."); if (! pform_expression_is_constant($5)) yyerror(@3, "lsb of register range must be constant."); pform_set_reg_idx(*$1, $3, $5); $$ = $1; } ; register_variable_list : register_variable { list*tmp = new list; tmp->push_back(*$1); delete $1; $$ = tmp; } | register_variable_list ',' register_variable { list*tmp = $1; tmp->push_back(*$3); delete $3; $$ = tmp; } ; specify_item : K_specparam specparam_list ';' | specify_simple_path '=' '(' expression_list ')' ';' { yyerror(@1, "Sorry, specify path declarations not supported."); delete $4; } ; specify_item_list : specify_item | specify_item_list specify_item ; specify_simple_path : '(' IDENTIFIER spec_polarity K_EG IDENTIFIER ')' | '(' IDENTIFIER spec_polarity K_SG IDENTIFIER ')' ; specparam : IDENTIFIER '=' expression { yyerror(@1, "Sorry, specparam assignments not supported."); delete $1; delete $3; } ; specparam_list : specparam | specparam_list ',' specparam ; spec_polarity: '+' | '-' | ; statement : K_assign lavalue '=' expression ';' { yyerror(@1, "Sorry, procedural continuous assign not supported."); $$ = 0; } | K_begin statement_list K_end { PBlock*tmp = new PBlock(PBlock::BL_SEQ, *$2); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $2; $$ = tmp; } | K_begin ':' IDENTIFIER { pform_push_scope(*$3); } block_item_decls_opt statement_list K_end { pform_pop_scope(); PBlock*tmp = new PBlock(*$3, PBlock::BL_SEQ, *$6); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $3; delete $6; $$ = tmp; } | K_begin K_end { PBlock*tmp = new PBlock(PBlock::BL_SEQ); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | K_begin ':' IDENTIFIER K_end { PBlock*tmp = new PBlock(PBlock::BL_SEQ); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | K_begin error K_end { yyerrok; } | K_deassign lavalue';' { yyerror(@1, "Sorry, deassign not supported."); $$ = 0; } | K_disable IDENTIFIER ';' { yyerror(@1, "Sorry, disable statements not supported."); delete $2; $$ = 0; } | K_force lavalue '=' expression ';' { yyerror(@1, "Sorry, procedural force assign not supported."); $$ = 0; } | K_forever statement { PForever*tmp = new PForever($2); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | K_fork statement_list K_join { PBlock*tmp = new PBlock(PBlock::BL_PAR, *$2); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $2; $$ = tmp; } | K_release lavalue ';' { yyerror(@1, "Sorry, release not supported."); $$ = 0; } | K_repeat '(' expression ')' statement { PRepeat*tmp = new PRepeat($3, $5); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | K_fork K_join { PBlock*tmp = new PBlock(PBlock::BL_PAR); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | K_case '(' expression ')' case_items K_endcase { PCase*tmp = new PCase($3, $5); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | K_casex '(' expression ')' case_items K_endcase { PCase*tmp = new PCase($3, $5); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); yywarn(@1, "casex not properly supported, using case."); $$ = tmp; } | K_casez '(' expression ')' case_items K_endcase { PCase*tmp = new PCase($3, $5); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); yywarn(@1, "casez not properly supported, using case."); $$ = tmp; } | K_case '(' expression ')' error K_endcase { yyerrok; } | K_casex '(' expression ')' error K_endcase { yyerrok; } | K_casez '(' expression ')' error K_endcase { yyerrok; } | K_if '(' expression ')' statement_opt { PCondit*tmp = new PCondit($3, $5, 0); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | K_if '(' expression ')' statement_opt K_else statement_opt { PCondit*tmp = new PCondit($3, $5, $7); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | K_if '(' error ')' statement_opt { yyerror(@1, "Malformed conditional expression."); $$ = $5; } | K_if '(' error ')' statement_opt K_else statement_opt { yyerror(@1, "Malformed conditional expression."); $$ = $5; } | K_for '(' lpvalue '=' expression ';' expression ';' lpvalue '=' expression ')' statement { PForStatement*tmp = new PForStatement($3, $5, $7, $9, $11, $13); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | K_for '(' lpvalue '=' expression ';' expression ';' error ')' statement { $$ = 0; yyerror(@9, "Error in for loop step assigment."); } | K_for '(' lpvalue '=' expression ';' error ';' lpvalue '=' expression ')' statement { $$ = 0; yyerror(@7, "Error in for loop condition expression."); } | K_for '(' error ')' statement { $$ = 0; yyerror(@3, "Incomprehensible for loop."); } | K_while '(' expression ')' statement { PWhile*tmp = new PWhile($3, $5); $$ = tmp; } | K_while '(' error ')' statement { $$ = 0; yyerror(@3, "Error in while loop condition."); } | delay statement_opt { PDelayStatement*tmp = new PDelayStatement($1, $2); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | event_control statement_opt { PEventStatement*tmp = $1; if (tmp == 0) { yyerror(@1, "Invalid event control."); $$ = 0; } else { tmp->set_statement($2); $$ = tmp; } } | lpvalue '=' expression ';' { PAssign*tmp = new PAssign($1,$3); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | lpvalue K_LE expression ';' { PAssignNB*tmp = new PAssignNB($1,$3); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | lpvalue '=' delay expression ';' { yyerror(@1, "Sorry, assignment timing control not implemented."); PAssign*tmp = new PAssign($1,$3); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); $$ = tmp; } | K_wait '(' expression ')' statement_opt { PEventStatement*tmp; PEEvent*etmp = new PEEvent(NetNEvent::POSITIVE, $3); tmp = new PEventStatement(etmp); tmp->set_statement($5); $$ = tmp; } | SYSTEM_IDENTIFIER '(' expression_list ')' ';' { PCallTask*tmp = new PCallTask(*$1, *$3); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; delete $3; $$ = tmp; } | SYSTEM_IDENTIFIER '(' ')' ';' { svectorpt (0); PCallTask*tmp = new PCallTask(*$1, pt); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; $$ = tmp; } | SYSTEM_IDENTIFIER ';' { svectorpt (0); PCallTask*tmp = new PCallTask(*$1, pt); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; $$ = tmp; } | identifier '(' expression_list ')' ';' { PCallTask*tmp = new PCallTask(*$1, *$3); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; delete $3; $$ = tmp; } | identifier '(' ')' ';' { svectorpt (0); PCallTask*tmp = new PCallTask(*$1, pt); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; $$ = tmp; } | identifier ';' { svectorpt (0); PCallTask*tmp = new PCallTask(*$1, pt); tmp->set_file(@1.text); tmp->set_lineno(@1.first_line); delete $1; $$ = tmp; } | error ';' { yyerror(@1, "malformed statement"); yyerrok; $$ = new PNoop; } ; statement_list : statement_list statement { svector*tmp = new svector(*$1, $2); delete $1; $$ = tmp; } | statement { svector*tmp = new svector(1); (*tmp)[0] = $1; $$ = tmp; } ; statement_opt : statement | ';' { $$ = 0; } ; task_body : task_item_list_opt statement_opt { PTask*tmp = new PTask($2); $$ = tmp; } ; task_item : block_item_decl | K_input range_opt list_of_variables ';' { yyerror(@1, "Sorry, task input ports not implemented."); } | K_output range_opt list_of_variables ';' { yyerror(@1, "Sorry, task output ports not implemented."); } | K_inout range_opt list_of_variables ';' { yyerror(@1, "Sorry, task inout ports not implemented."); } ; task_item_list : task_item_list task_item | task_item ; task_item_list_opt : task_item_list | ; udp_body : K_table { lex_start_table(); } udp_entry_list K_endtable { lex_end_table(); $$ = $3; } ; udp_entry_list : udp_comb_entry_list | udp_sequ_entry_list ; udp_comb_entry : udp_input_list ':' udp_output_sym ';' { string*tmp = $1; *tmp += ':'; *tmp += $3; $$ = tmp; } ; udp_comb_entry_list : udp_comb_entry { list*tmp = new list; tmp->push_back(*$1); delete $1; $$ = tmp; } | udp_comb_entry_list udp_comb_entry { list*tmp = $1; tmp->push_back(*$2); delete $2; $$ = tmp; } ; udp_sequ_entry_list : udp_sequ_entry { list*tmp = new list; tmp->push_back(*$1); delete $1; $$ = tmp; } | udp_sequ_entry_list udp_sequ_entry { list*tmp = $1; tmp->push_back(*$2); delete $2; $$ = tmp; } ; udp_sequ_entry : udp_input_list ':' udp_input_sym ':' udp_output_sym ';' { string*tmp = $1; *tmp += ':'; *tmp += $3; *tmp += ':'; *tmp += $5; $$ = tmp; } ; udp_initial : K_initial IDENTIFIER '=' NUMBER ';' { PExpr*etmp = new PENumber($4); PEIdent*itmp = new PEIdent(*$2); PAssign*atmp = new PAssign(itmp, etmp); atmp->set_file(@2.text); atmp->set_lineno(@2.first_line); delete $2; $$ = atmp; } ; udp_init_opt : udp_initial { $$ = $1; } | { $$ = 0; } ; udp_input_list : udp_input_sym { string*tmp = new string; *tmp += $1; $$ = tmp; } | udp_input_list udp_input_sym { string*tmp = $1; *tmp += $2; $$ = tmp; } ; udp_input_sym : '0' { $$ = '0'; } | '1' { $$ = '1'; } | 'x' { $$ = 'x'; } | '?' { $$ = '?'; } | 'b' { $$ = 'b'; } | '*' { $$ = '*'; } | 'f' { $$ = 'f'; } | 'r' { $$ = 'r'; } | 'n' { $$ = 'n'; } | 'p' { $$ = 'p'; } | '_' { $$ = '_'; } ; udp_output_sym : '0' { $$ = '0'; } | '1' { $$ = '1'; } | 'x' { $$ = 'x'; } | '-' { $$ = '-'; } ; udp_port_decl : K_input list_of_variables ';' { $$ = pform_make_udp_input_ports($2); } | K_output IDENTIFIER ';' { PWire*pp = new PWire(*$2, NetNet::IMPLICIT, NetNet::POUTPUT); svector*tmp = new svector(1); (*tmp)[0] = pp; delete $2; $$ = tmp; } | K_reg IDENTIFIER ';' { PWire*pp = new PWire(*$2, NetNet::REG, NetNet::PIMPLICIT); svector*tmp = new svector(1); (*tmp)[0] = pp; delete $2; $$ = tmp; } ; udp_port_decls : udp_port_decl { $$ = $1; } | udp_port_decls udp_port_decl { svector*tmp = new svector(*$1, *$2); delete $1; delete $2; $$ = tmp; } ; udp_port_list : IDENTIFIER { list*tmp = new list; tmp->push_back(*$1); delete $1; $$ = tmp; } | udp_port_list ',' IDENTIFIER { list*tmp = $1; tmp->push_back(*$3); delete $3; $$ = tmp; } ; udp_primitive : K_primitive IDENTIFIER '(' udp_port_list ')' ';' udp_port_decls udp_init_opt udp_body K_endprimitive { pform_make_udp($2, $4, $7, $9, $8); } ;