// OpenSTA, Static Timing Analyzer // Copyright (c) 2025, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 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, see . // // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. // // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // This notice may not be removed or altered from any source distribution. %{ #include #include "Report.hh" #include "StringUtil.hh" #include "StringSeq.hh" #include "parasitics/SpefReaderPvt.hh" #include "parasitics/SpefScanner.hh" #undef yylex #define yylex scanner->lex // warning: variable 'yynerrs_' set but not used #pragma GCC diagnostic ignored "-Wunused-but-set-variable" void sta::SpefParse::error(const location_type &loc, const std::string &msg) { reader->report()->fileError(164,reader->filename(), loc.begin.line,"%s",msg.c_str()); } %} %require "3.2" %skeleton "lalr1.cc" %debug %define api.namespace {sta} %locations %define api.location.file "SpefLocation.hh" %define parse.assert %parse-param { SpefScanner *scanner } %parse-param { SpefReader *reader } %define api.parser.class {SpefParse} %union { char ch; char *string; int integer; float number; sta::StringSeq *string_seq; sta::PortDirection *port_dir; sta::SpefRspfPi *pi; sta::SpefTriple *triple; sta::Pin *pin; sta::Net *net; } %token SPEF DESIGN DATE VENDOR PROGRAM DESIGN_FLOW %token PVERSION DIVIDER DELIMITER %token BUS_DELIMITER T_UNIT C_UNIT R_UNIT L_UNIT NAME_MAP %token POWER_NETS GROUND_NETS KW_C KW_L KW_S KW_D KW_V %token PORTS PHYSICAL_PORTS DEFINE PDEFINE %token D_NET D_PNET R_NET R_PNET END %token CONN CAP RES INDUC KW_P KW_I KW_N DRIVER CELL C2_R1_C1 LOADS %token RC KW_Q KW_K %token INTEGER FLOAT QSTRING INDEX IDENT NAME %type INTEGER %type FLOAT %type QSTRING INDEX IDENT NAME %type conf cap_id res_id induc_id cap_elem cap_elems %type res_elem res_elems induc_elem induc_elems %type pos_integer %type number pos_number threshold %type real_component imaginary_component pole poles %type residue residues complex_par_value cnumber %type name_or_index net_name net_names inst_name %type name_map_entries name_map_entry mapped_item %type physical_inst port_name pport_name port_entry pport_entry %type port_entries pport_entries pport %type entity external_connection %type cell_type %type driver_cell pnet_ref %type pexternal_connection internal_pdspf_node %type parasitic_node internal_parasitic_node %type hchar suffix_bus_delim prefix_bus_delim %type qstrings %type direction %type par_value total_cap %type pi_model %type pin_name driver_pair internal_connection %type net %start file %{ %} %% file: header_def name_map power_def external_def define_def internal_def ; /****************************************************************/ prefix_bus_delim: '[' { $$ = '['; } | '{' { $$ = '}'; } | '(' { $$ = ')'; } | '<' { $$ = '<'; } ; suffix_bus_delim: ']' { $$ = ']'; } | '}' { $$ = '}'; } | ')' { $$ = ')'; } | '>' { $$ = '>'; } ; hchar: '.' { $$ = '.'; } | '/' { $$ = '/'; } | '|' { $$ = '|'; } | ':' { $$ = ':'; } ; /****************************************************************/ header_def: spef_version design_name date vendor program_name program_version design_flow hierarchy_div_def pin_delim_def bus_delim_def unit_def ; spef_version: SPEF QSTRING { sta::stringDelete($2); } ; design_name: DESIGN QSTRING { sta::stringDelete($2); } ; date: DATE QSTRING { sta::stringDelete($2); } ; program_name: PROGRAM QSTRING { sta::stringDelete($2); } ; program_version: PVERSION QSTRING { sta::stringDelete($2); } ; vendor: VENDOR QSTRING { sta::stringDelete($2); } ; design_flow: DESIGN_FLOW qstrings { reader->setDesignFlow($2); } ; qstrings: QSTRING { $$ = new sta::StringSeq; $$->push_back($1); } | qstrings QSTRING { $$->push_back($2); } ; hierarchy_div_def: DIVIDER hchar { reader->setDivider($2); } ; pin_delim_def: DELIMITER hchar { reader->setDelimiter($2); } ; bus_delim_def: BUS_DELIMITER prefix_bus_delim { reader->setBusBrackets($2, '\0'); } | BUS_DELIMITER prefix_bus_delim suffix_bus_delim { reader->setBusBrackets($2, $3); } ; /****************************************************************/ unit_def: time_scale cap_scale res_scale induc_scale ; time_scale: T_UNIT pos_number IDENT { reader->setTimeScale($2, $3); } ; cap_scale: C_UNIT pos_number IDENT { reader->setCapScale($2, $3); } ; res_scale: R_UNIT pos_number IDENT { reader->setResScale($2, $3); } ; induc_scale: L_UNIT pos_number IDENT { reader->setInductScale($2, $3); } ; /****************************************************************/ name_map: /* empty */ | NAME_MAP name_map_entries ; name_map_entries: name_map_entry | name_map_entries name_map_entry ; name_map_entry: INDEX mapped_item { reader->makeNameMapEntry($1, $2); } ; mapped_item: IDENT | NAME | QSTRING ; /****************************************************************/ power_def: /* empty */ | power_net_def | ground_net_def | power_net_def ground_net_def ; power_net_def: POWER_NETS net_names ; ground_net_def: GROUND_NETS net_names ; net_names: net_name | net_names net_name ; net_name: name_or_index { sta::stringDelete($1); } ; /****************************************************************/ external_def: /* empty */ | port_def | physical_port_def | port_def physical_port_def ; port_def: PORTS port_entries ; port_entries: port_entry | port_entries port_entry ; port_entry: port_name direction conn_attrs { sta::stringDelete($1); } ; direction: IDENT { $$ = reader->portDirection($1); sta::stringDelete($1); } ; port_name: name_or_index ; inst_name: name_or_index ; physical_port_def: PHYSICAL_PORTS pport_entries ; pport_entries: pport_entry | pport_entries pport_entry ; pport_entry: pport_name IDENT conn_attrs ; pport_name: name_or_index { sta::stringDelete($1); } | physical_inst ':' pport { sta::stringDelete($1); sta::stringDelete($3); } ; pport: name_or_index ; physical_inst: name_or_index ; /****************************************************************/ conn_attrs: /* empty */ | conn_attrs conn_attr ; conn_attr: coordinates | cap_load | slews | driving_cell ; coordinates: KW_C number number ; cap_load: KW_L par_value { delete $2; } ; par_value: number { $$ = new sta::SpefTriple($1); } | number ':' number ':' number { $$ = new sta::SpefTriple($1, $3, $5); } ; slews: KW_S par_value par_value { delete $2; delete $3; } | KW_S par_value par_value threshold threshold { delete $2; delete $3; } ; threshold: pos_number | pos_number ':' pos_number ':' pos_number ; driving_cell: KW_D cell_type { sta::stringDelete($2); } ; cell_type: IDENT | INDEX ; /****************************************************************/ define_def: /* empty */ | define_def define_entry ; define_entry: DEFINE inst_name entity { sta::stringDelete($2); sta::stringDelete($3); } | DEFINE inst_name inst_name entity { sta::stringDelete($2); sta::stringDelete($3); sta::stringDelete($4); } | PDEFINE physical_inst entity { sta::stringDelete($2); sta::stringDelete($3); } ; entity: QSTRING ; /****************************************************************/ internal_def: nets | internal_def nets ; nets: d_net | r_net | d_pnet | r_pnet ; /****************************************************************/ d_net: D_NET net total_cap { reader->dspfBegin($2, $3); } routing_conf conn_sec cap_sec res_sec induc_sec END { reader->dspfFinish(); } ; net: name_or_index { $$ = reader->findNet($1); sta::stringDelete($1); } ; total_cap: par_value ; routing_conf: /* empty */ | KW_V conf ; conf: pos_integer ; /****************************************************************/ conn_sec: /* empty */ | CONN conn_defs internal_node_coords ; conn_defs: conn_def | conn_defs conn_def ; conn_def: KW_P external_connection direction conn_attrs | KW_I internal_connection direction conn_attrs ; external_connection: name_or_index { sta::stringDelete($1); } | physical_inst ':' pport { sta::stringDelete($1); sta::stringDelete($3); } ; internal_connection: pin_name ; pin_name: name_or_index { $$ = reader->findPin($1); sta::stringDelete($1); } ; internal_node_coords: /* empty */ | internal_node_coords internal_node_coord ; internal_node_coord: KW_N internal_parasitic_node coordinates ; internal_parasitic_node: name_or_index { sta::stringDelete($1); } ; /****************************************************************/ cap_sec: /* empty */ | CAP cap_elems ; cap_elems: /* empty */ { $$ = 0; } | cap_elems cap_elem ; cap_elem: cap_id parasitic_node par_value { reader->makeCapacitor($1, $2, $3); } | cap_id parasitic_node parasitic_node par_value { reader->makeCapacitor($1, $2, $3, $4); } ; cap_id: pos_integer ; parasitic_node: name_or_index ; /****************************************************************/ res_sec: /* empty */ | RES res_elems ; res_elems: /* empty */ { $$ = 0; } | res_elems res_elem ; res_elem: res_id parasitic_node parasitic_node par_value { reader->makeResistor($1, $2, $3, $4); } ; res_id: pos_integer ; /****************************************************************/ induc_sec: /* empty */ | INDUC induc_elems ; induc_elems: /* empty */ { $$ = 0; } | induc_elems induc_elem ; induc_elem: induc_id parasitic_node parasitic_node par_value { delete $4; } ; induc_id: pos_integer ; /****************************************************************/ r_net: R_NET net total_cap { reader->rspfBegin($2, $3); } routing_conf driver_reducs END { reader->rspfFinish(); } ; driver_reducs: /* empty */ | driver_reducs driver_reduc ; driver_reduc: driver_pair driver_cell pi_model { reader->rspfDrvrBegin($1, $3); sta::stringDelete($2); } load_desc { reader->rspfDrvrFinish(); } ; driver_pair: DRIVER pin_name { $$ = $2; } ; driver_cell: CELL cell_type { $$ = $2; } ; pi_model: C2_R1_C1 par_value par_value par_value { $$ = new sta::SpefRspfPi($2, $3, $4); } ; /****************************************************************/ load_desc: LOADS rc_descs ; rc_descs: rc_desc | rc_descs rc_desc ; rc_desc: RC pin_name par_value { reader->rspfLoad($2, $3); } | RC pin_name par_value pole_residue_desc { reader->rspfLoad($2, $3); } ; pole_residue_desc: pole_desc residue_desc ; pole_desc: KW_Q pos_integer poles ; poles: pole | poles pole ; pole: complex_par_value ; complex_par_value: cnumber | number | cnumber ':' cnumber ':' cnumber | number ':' number ':' number ; cnumber: '(' real_component imaginary_component ')' { $$ = $2; } ; real_component: number ; imaginary_component: number ; residue_desc: KW_K pos_integer residues ; residues: residue | residues residue ; residue: complex_par_value ; /****************************************************************/ d_pnet: D_PNET pnet_ref total_cap routing_conf pconn_sec cap_sec res_sec induc_sec END { sta::stringDelete($2); } ; pnet_ref: name_or_index ; pconn_sec: CONN pconn_defs internal_pnode_coords ; pconn_defs: pconn_def | pconn_defs pconn_def ; pconn_def: KW_P pexternal_connection direction conn_attr | KW_I internal_connection direction conn_attr ; pexternal_connection: pport_name ; internal_pnode_coords: /* empty */ | internal_pnode_coords internal_pnode_coord ; internal_pnode_coord: KW_N internal_pdspf_node coordinates ; internal_pdspf_node: name_or_index { sta::stringDelete($1); $$ = 0; } ; name_or_index: IDENT | NAME | INDEX ; /****************************************************************/ r_pnet: R_PNET pnet_ref total_cap routing_conf END { sta::stringDelete($2); } | R_PNET pnet_ref total_cap routing_conf pdriver_reduc END { sta::stringDelete($2); } ; pdriver_reduc: pdriver_pair driver_cell pi_model load_desc ; pdriver_pair: DRIVER internal_connection ; /****************************************************************/ number: INTEGER { $$ = static_cast($1); } | FLOAT ; pos_integer: INTEGER { int value = $1; if (value < 0) reader->warn(1525, "%d is not positive.", value); $$ = value; } ; pos_number: INTEGER { float value = static_cast($1); if (value < 0) reader->warn(1526, "%.4f is not positive.", value); $$ = value; } | FLOAT { float value = static_cast($1); if (value < 0) reader->warn(1527, "%.4f is not positive.", value); $$ = value; } ; %%