// OpenSTA, Static Timing Analyzer // Copyright (c) 2026, 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 #include "Report.hh" #include "StringUtil.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(1670, reader->filename(), loc.begin.line, "{}", msg); } %} %code requires { #include #include "StringUtil.hh" namespace sta { // Bison's C++ variant skeleton cannot use void as a semantic type. struct SpefParseVoid {}; class SpefReader; class SpefScanner; class Net; class Pin; class PortDirection; class SpefRspfPi; class SpefTriple; } } %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} %define api.value.type variant %token INTEGER %token FLOAT %token QSTRING INDEX IDENT NAME %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 %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 %nterm name_map_entries name_map_entry net_names %nterm net_name port_entries port_entry pport_entries pport_entry %nterm pport_name pexternal_connection %type name_or_index inst_name %type mapped_item %type physical_inst port_name pport %type entity external_connection %type cell_type %type driver_cell pnet_ref %type 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 ; design_name: DESIGN QSTRING ; date: DATE QSTRING ; program_name: PROGRAM QSTRING ; program_version: PVERSION QSTRING ; vendor: VENDOR QSTRING ; design_flow: DESIGN_FLOW qstrings { reader->setDesignFlow($2); } ; qstrings: QSTRING { $$ = new sta::StringSeq; $$->push_back(std::move($1)); } | qstrings QSTRING { $$ = $1; $$->push_back(std::move($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::SpefParseVoid{}; } ; /****************************************************************/ 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::SpefParseVoid{}; } ; direction: IDENT { $$ = reader->portDirection($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 { $$ = sta::SpefParseVoid{}; } ; pport_name: name_or_index { $$ = sta::SpefParseVoid{}; } | physical_inst ':' pport { $$ = sta::SpefParseVoid{}; } ; 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 ; cell_type: IDENT | INDEX ; /****************************************************************/ define_def: /* empty */ | define_def define_entry ; define_entry: DEFINE inst_name entity | DEFINE inst_name inst_name entity | PDEFINE physical_inst entity ; 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); } ; 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 { $$ = std::string(); } | physical_inst ':' pport { $$ = std::string(); } ; internal_connection: pin_name ; pin_name: name_or_index { $$ = reader->findPin($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 { $$ = std::string(); } ; /****************************************************************/ cap_sec: /* empty */ | CAP cap_elems ; cap_elems: /* empty */ { $$ = 0; } | cap_elems cap_elem { $$ = $1; } ; 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 { $$ = $1; } ; 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 { $$ = $1; } ; 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); } 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 { $$ = $2; } ; 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 ; 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 { $$ = std::string(); } ; name_or_index: IDENT | NAME | INDEX ; /****************************************************************/ r_pnet: R_PNET pnet_ref total_cap routing_conf END | R_PNET pnet_ref total_cap routing_conf pdriver_reduc END ; pdriver_reduc: pdriver_pair driver_cell pi_model load_desc ; pdriver_pair: DRIVER internal_connection ; /****************************************************************/ number: INTEGER { $$ = static_cast($1); } | FLOAT { $$ = $1; } ; pos_integer: INTEGER { int value = $1; if (value < 0) reader->warn(1525, "{} 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; } ; %%