%{ /* * Copyright (c) 1998-2019 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ extern int sdflex(void); static void yyerror(const char*msg); # include "vpi_user.h" # include "sdf_parse_priv.h" # include "sdf_priv.h" # include # include # include # include "ivl_alloc.h" /* This is the hierarchy separator to use. */ char sdf_use_hchar = '.'; %} %union { unsigned long int_val; double real_val; char* string_val; struct sdf_delay_s delay; struct port_with_edge_s port_with_edge; struct sdf_delval_list_s delval_list; }; %token K_ABSOLUTE K_CELL K_CELLTYPE K_COND K_CONDELSE K_DATE K_DELAYFILE %token K_DELAY K_DESIGN K_DIVIDER K_HOLD K_INCREMENT K_INSTANCE %token K_INTERCONNECT K_IOPATH K_NEGEDGE K_PATHPULSE K_PATHPULSEPERCENT %token K_PERIOD K_POSEDGE K_PROCESS K_PROGRAM %token K_RECREM K_RECOVERY K_REMOVAL K_SDFVERSION K_SETUP K_SETUPHOLD %token K_TEMPERATURE K_TIMESCALE K_TIMINGCHECK K_VENDOR K_VERSION %token K_VOLTAGE K_WIDTH %token K_01 K_10 K_0Z K_Z1 K_1Z K_Z0 %token K_EQ K_NE K_CEQ K_CNE K_LOGICAL_ONE K_LOGICAL_ZERO %token K_LAND K_LOR %token HCHAR %token QSTRING IDENTIFIER %token REAL_NUMBER %token INTEGER %type celltype %type cell_instance %type hierarchical_identifier %type port port_instance port_interconnect %type signed_real_number %type delval rvalue_opt rvalue rtriple signed_real_number_opt %type edge_identifier %type port_edge port_spec %type delval_list %left K_LOR %left K_LAND %left K_EQ K_NE K_CEQ K_CNE %% source_file : '(' K_DELAYFILE sdf_header_list cell_list ')' | '(' K_DELAYFILE error ')' { vpi_printf("%s:%d:SDF ERROR: Invalid DELAYFILE format\n", sdf_parse_path, @2.first_line); } ; sdf_header_list : sdf_header_list sdf_header_item | sdf_header_item ; sdf_header_item : sdfversion | design_name | date | vendor | program_name | program_version | hierarchy_divider | voltage | process | temperature | time_scale ; sdfversion : '(' K_SDFVERSION QSTRING ')' { free($3); } ; design_name : '(' K_DESIGN QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Design: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; date : '(' K_DATE QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Date: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; vendor : '(' K_VENDOR QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Vendor: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; program_name : '(' K_PROGRAM QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Program: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; program_version : '(' K_VERSION QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Program Version: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; hierarchy_divider : '(' K_DIVIDER '.' ')' { sdf_use_hchar = '.'; } | '(' K_DIVIDER '/' ')' { sdf_use_hchar = '/'; } | '(' K_DIVIDER HCHAR ')' { /* sdf_use_hchar no-change */; } ; voltage : '(' K_VOLTAGE rtriple ')' | '(' K_VOLTAGE signed_real_number ')' ; process : '(' K_PROCESS QSTRING ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: Process: %s\n", sdf_parse_path, @2.first_line, $3); free($3); } ; temperature : '(' K_TEMPERATURE rtriple ')' | '(' K_TEMPERATURE signed_real_number ')' ; time_scale : '(' K_TIMESCALE REAL_NUMBER IDENTIFIER ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: TIMESCALE : %f%s\n", sdf_parse_path, @2.first_line, $3, $4); free($4); } | '(' K_TIMESCALE INTEGER IDENTIFIER ')' { if (sdf_flag_inform) vpi_printf("%s:%d:SDF INFO: TIMESCALE : %lu%s\n", sdf_parse_path, @2.first_line, $3, $4); free($4); } ; cell_list : cell_list cell | cell ; cell : '(' K_CELL celltype cell_instance { sdf_select_instance($3, $4); /* find the instance in the design */} timing_spec_list_opt ')' { free($3); if ($4) free($4); } | '(' K_CELL error ')' { vpi_printf("%s:%d: Syntax error in CELL\n", sdf_parse_path, @2.first_line); } ; celltype : '(' K_CELLTYPE QSTRING ')' { $$ = $3; } ; cell_instance : '(' K_INSTANCE hierarchical_identifier ')' { $$ = $3; } | '(' K_INSTANCE ')' { $$ = strdup(""); } | '(' K_INSTANCE '*' ')' { $$ = 0; } | '(' K_INSTANCE error ')' { vpi_printf("%s:%d:SDF ERROR: Invalid/malformed INSTANCE argument\n", sdf_parse_path, @2.first_line); $$ = strdup(""); } ; timing_spec_list_opt : /* Empty */ | timing_spec_list_opt timing_spec ; timing_spec : '(' K_DELAY deltype_list ')' | '(' K_DELAY error ')' { vpi_printf("%s:%d: Syntax error in CELL DELAY SPEC\n", sdf_parse_path, @2.first_line); } | '(' K_TIMINGCHECK tchk_def_list ')' | '(' K_TIMINGCHECK error ')' { vpi_printf("%s:%d: Syntax error in TIMINGCHECK SPEC\n", sdf_parse_path, @2.first_line); } ; deltype_list : deltype_list deltype | deltype ; deltype : '(' K_PATHPULSE input_output_path_opt rvalue rvalue_opt ')' | '(' K_PATHPULSEPERCENT input_output_path_opt rvalue rvalue_opt ')' | '(' K_ABSOLUTE del_def_list ')' | '(' K_INCREMENT del_def_list ')' | '(' error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed delay type\n", sdf_parse_path, @1.first_line); } ; del_def_list : del_def_list del_def | del_def ; del_def : '(' K_IOPATH port_spec port_instance delval_list ')' { sdf_iopath_delays($3.vpi_edge, $3.string_val, $4, &$5); free($3.string_val); free($4); } | '(' K_IOPATH error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed IOPATH\n", sdf_parse_path, @2.first_line); } | '(' K_COND conditional_port_expr '(' K_IOPATH port_spec port_instance delval_list ')' ')' { if (sdf_flag_warning) vpi_printf("%s:%d: SDF WARNING: " "COND not supported.\n", sdf_parse_path, @2.first_line); free($6.string_val); free($7); } | '(' K_COND QSTRING conditional_port_expr '(' K_IOPATH port_spec port_instance delval_list ')' ')' { if (sdf_flag_warning) vpi_printf("%s:%d: SDF WARNING: " "COND not supported.\n", sdf_parse_path, @2.first_line); free($3); free($7.string_val); free($8); } | '(' K_COND error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed COND\n", sdf_parse_path, @2.first_line); } | '(' K_CONDELSE '(' K_IOPATH port_spec port_instance delval_list ')' ')' { if (sdf_flag_warning) vpi_printf("%s:%d: SDF WARNING: " "CONDELSE not supported.\n", sdf_parse_path, @2.first_line); free($5.string_val); free($6); } | '(' K_CONDELSE error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed CONDELSE\n", sdf_parse_path, @2.first_line); } /* | '(' K_INTERCONNECT port_instance port_instance delval_list ')' */ | '(' K_INTERCONNECT port_interconnect port_interconnect delval_list ')' { if (sdf_flag_warning) vpi_printf("%s:%d: SDF WARNING: " "INTERCONNECT not supported.\n", sdf_parse_path, @2.first_line); free($3); free($4); } | '(' K_INTERCONNECT error ')' { vpi_printf("%s:%d: SDF ERROR: Invalid/malformed INTERCONNECT\n", sdf_parse_path, @2.first_line); } ; tchk_def_list : tchk_def_list tchk_def | tchk_def ; /* Timing checks are ignored. */ tchk_def : '(' K_SETUP port_tchk port_tchk rvalue ')' | '(' K_HOLD port_tchk port_tchk rvalue ')' | '(' K_SETUPHOLD port_tchk port_tchk rvalue rvalue ')' | '(' K_RECOVERY port_tchk port_tchk rvalue ')' | '(' K_RECREM port_tchk port_tchk rvalue rvalue ')' | '(' K_REMOVAL port_tchk port_tchk rvalue ')' | '(' K_WIDTH port_tchk rvalue ')' | '(' K_PERIOD port_tchk rvalue ')' ; port_tchk : port_instance { free($1); } /* This must only be an edge. For now we just accept everything. */ | cond_edge_start port_instance ')' { free($2); } /* These must only be a cond. For now we just accept everything. */ | cond_edge_start timing_check_condition port_spec ')' { free($3.string_val); } | cond_edge_start QSTRING timing_check_condition port_spec ')' { free($2); free($4.string_val); } ; cond_edge_start : '(' { start_edge_id(1); } cond_edge_identifier { stop_edge_id(); } ; cond_edge_identifier : K_POSEDGE | K_NEGEDGE | K_01 | K_10 | K_0Z | K_Z1 | K_1Z | K_Z0 | K_COND ; timing_check_condition : port_interconnect { free($1); } | '~' port_interconnect { free($2); } | '!' port_interconnect { free($2); } | port_interconnect equality_operator scalar_constant { free($1); } ; /* This is not complete! */ conditional_port_expr : port { free($1); } | scalar_constant | '(' conditional_port_expr ')' | conditional_port_expr K_LAND conditional_port_expr | conditional_port_expr K_LOR conditional_port_expr | conditional_port_expr K_EQ conditional_port_expr | conditional_port_expr K_NE conditional_port_expr | conditional_port_expr K_CEQ conditional_port_expr | conditional_port_expr K_CNE conditional_port_expr ; equality_operator : K_EQ | K_NE | K_CEQ | K_CNE ; scalar_constant : K_LOGICAL_ONE | K_LOGICAL_ZERO ; input_output_path_opt : input_output_path | ; input_output_path : port_instance port_instance ; port_spec : port_instance { $$.vpi_edge = vpiNoEdge; $$.string_val = $1; } | port_edge { $$ = $1; } ; port_instance : port { $$ = $1; } ; port : hierarchical_identifier { $$ = $1; } /* | hierarchical_identifier '[' INTEGER ']' */ ; /* Since INTERCONNECT is ignored we can also ignore a vector bit. */ port_interconnect : hierarchical_identifier { $$ = $1; } | hierarchical_identifier '[' INTEGER ']' { $$ = $1;} ; port_edge : '(' {start_edge_id(0);} edge_identifier {stop_edge_id();} port_instance ')' { $$.vpi_edge = $3; $$.string_val = $5; } ; edge_identifier : K_POSEDGE { $$ = vpiPosedge; } | K_NEGEDGE { $$ = vpiNegedge; } | K_01 { $$ = vpiEdge01; } | K_10 { $$ = vpiEdge10; } | K_0Z { $$ = vpiEdge0x; } | K_Z1 { $$ = vpiEdgex1; } | K_1Z { $$ = vpiEdge1x; } | K_Z0 { $$ = vpiEdgex0; } ; delval_list : delval_list delval { int idx; $$.count = $1.count; for (idx = 0 ; idx < $$.count ; idx += 1) $$.val[idx] = $1.val[idx]; // Is this correct? if ($$.count < 12) { $$.val[$$.count] = $2; $$.count += 1; } } | delval { $$.count = 1; $$.val[0] = $1; } ; delval : rvalue { $$ = $1; } | '(' rvalue rvalue ')' { $$ = $2; vpi_printf("%s:%d: SDF WARNING: Pulse rejection limits ignored\n", sdf_parse_path, @3.first_line); } | '(' rvalue rvalue rvalue ')' { $$ = $2; vpi_printf("%s:%d: SDF WARNING: Pulse rejection limits ignored\n", sdf_parse_path, @3.first_line); } ; rvalue_opt : /* When missing. */ { $$.value = 0.0; $$.defined = 0; } | rvalue ; rvalue : '(' signed_real_number ')' { $$.defined = 1; $$.value = $2; } | '(' rtriple ')' { $$ = $2; } | '(' ')' { $$.defined = 0; $$.value = 0.0; } ; hierarchical_identifier : IDENTIFIER { $$ = $1; } | hierarchical_identifier HCHAR IDENTIFIER { int len = strlen($1) + strlen($3) + 2; char*tmp = realloc($1, len); strcat(tmp, "."); strcat(tmp, $3); free($3); $$ = tmp; } ; rtriple : signed_real_number_opt ':' signed_real_number_opt ':' signed_real_number_opt { switch(sdf_min_typ_max) { case _vpiDelaySelMinimum: $$ = $1; break; case _vpiDelaySelTypical: $$ = $3; break; case _vpiDelaySelMaximum: $$ = $5; break; } /* At least one of the values must be defined. */ if (! ($1.defined || $3.defined || $5.defined)) { vpi_printf("%s:%d: SDF ERROR: rtriple must have at least one " "defined value.\n", sdf_parse_path, @1.first_line); } } ; signed_real_number_opt : /* When missing. */ { $$.value = 0.0; $$.defined = 0; } | signed_real_number { $$.value = $1; $$.defined = 1; } ; signed_real_number : REAL_NUMBER { $$ = $1; } | '+' REAL_NUMBER { $$ = $2; } | '-' REAL_NUMBER { $$ = -$2; } | INTEGER { $$ = $1; } | '+' INTEGER { $$ = $2; } | '-' INTEGER { $$ = -$2; } ; %% void yyerror(const char*msg) { vpi_printf("%s:SDF ERROR: Too many errors: %s\n", sdf_parse_path, msg); }