// 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 "SpefReader.hh" #include #include #include #include "Zlib.hh" #include "Stats.hh" #include "Report.hh" #include "Debug.hh" #include "StringUtil.hh" #include "Transition.hh" #include "Liberty.hh" #include "Network.hh" #include "PortDirection.hh" #include "Sdc.hh" #include "Parasitics.hh" #include "Scene.hh" #include "ArcDelayCalc.hh" #include "SpefReaderPvt.hh" #include "SpefNamespace.hh" #include "parasitics/SpefScanner.hh" namespace sta { bool readSpefFile(std::string_view filename, Instance *instance, bool pin_cap_included, bool keep_coupling_caps, float coupling_cap_factor, bool reduce, const Scene *scene, const MinMaxAll *min_max, Parasitics *parasitics, StaState *sta) { SpefReader reader(filename, instance, pin_cap_included, keep_coupling_caps, coupling_cap_factor, reduce, scene, min_max, parasitics, sta); bool success = reader.read(); return success; } SpefReader::SpefReader(std::string_view filename, Instance *instance, bool pin_cap_included, bool keep_coupling_caps, float coupling_cap_factor, bool reduce, const Scene *scene, const MinMaxAll *min_max, Parasitics *parasitics, StaState *sta) : StaState(sta), filename_(filename), instance_(instance), pin_cap_included_(pin_cap_included), keep_coupling_caps_(keep_coupling_caps), coupling_cap_factor_(coupling_cap_factor), reduce_(reduce), scene_(scene), min_max_(min_max), // defaults divider_('\0'), delimiter_('\0'), bus_brkt_left_('\0'), bus_brkt_right_('\0'), net_(nullptr), triple_index_(0), time_scale_(1.0), cap_scale_(1.0), res_scale_(1.0), induct_scale_(1.0), parasitics_(parasitics), parasitic_(nullptr) { parasitics->setCouplingCapFactor(coupling_cap_factor); } SpefReader::~SpefReader() {} bool SpefReader::read() { bool success; gzstream::igzstream stream(std::string(filename_).c_str()); if (stream.is_open()) { Stats stats(debug_, report_); SpefScanner scanner(&stream, filename_, this, report_); scanner_ = &scanner; SpefParse parser(&scanner, this); // parser.set_debug_level(1); // yyparse returns 0 on success. success = (parser.parse() == 0); stats.report("Read spef"); } else throw FileNotReadable(filename_); return success; } void SpefReader::setDivider(char divider) { divider_ = divider; } void SpefReader::setDelimiter(char delimiter) { delimiter_ = delimiter; } void SpefReader::setBusBrackets(char left, char right) { if (!((left == '[' && right == ']') || (left == '{' && right == '}') || (left == '(' && right == ')') || (left == '<' && right == '>') || (left == ':' && right == '\0') || (left == '.' && right == '\0'))) warn(1640, "illegal bus delimiters."); bus_brkt_left_ = left; bus_brkt_right_ = right; } Instance * SpefReader::findInstanceRelative(std::string_view name) { return sdc_network_->findInstanceRelative(instance_, name); } Net * SpefReader::findNetRelative(std::string_view name) { Net *net = network_->findNetRelative(instance_, name); // Relax spef escaping requirement because some commercial tools // don't follow the rules. if (net == nullptr) net = sdc_network_->findNetRelative(instance_, name); return net; } Pin * SpefReader::findPinRelative(std::string_view name) { return network_->findPinRelative(instance_, name); } Pin * SpefReader::findPortPinRelative(std::string_view name) { return network_->findPin(instance_, name); } std::string SpefReader::translated(std::string_view spef_name) { return spefToSta(spef_name, divider_, network_->pathDivider(), network_->pathEscape()); } int SpefReader::warnLine() const { return scanner_->line(); } void SpefReader::setTimeScale(float scale, std::string_view units) { if (stringEqual(units, "NS")) time_scale_ = scale * 1E-9F; else if (stringEqual(units, "PS")) time_scale_ = scale * 1E-12F; else warn(1641, "unknown units {}.", units); } void SpefReader::setCapScale(float scale, std::string_view units) { if (stringEqual(units, "PF")) cap_scale_ = scale * 1E-12F; else if (stringEqual(units, "FF")) cap_scale_ = scale * 1E-15F; else warn(1642, "unknown units {}.", units); } void SpefReader::setResScale(float scale, std::string_view units) { if (stringEqual(units, "OHM")) res_scale_ = scale; else if (stringEqual(units, "KOHM")) res_scale_ = scale * 1E+3F; else warn(1643, "unknown units {}.", units); } void SpefReader::setInductScale(float scale, std::string_view units) { if (stringEqual(units, "HENRY")) induct_scale_ = scale; else if (stringEqual(units, "MH")) induct_scale_ = scale * 1E-3F; else if (stringEqual(units, "UH")) induct_scale_ = scale * 1E-6F; else warn(1644, "unknown units {}.", units); } void SpefReader::makeNameMapEntry(std::string_view index, std::string_view name) { int i = std::stoi(std::string(index.substr(1))); name_map_[i] = name; } std::string_view SpefReader::nameMapLookup(std::string_view name) { if (!name.empty() && name[0] == '*') { std::string index_str(name.substr(1)); int index = std::stoi(index_str); const auto &itr = name_map_.find(index); if (itr != name_map_.end()) return itr->second; else { warn(1645, "no name map entry for {}.", index); return ""; } } else return name; } PortDirection * SpefReader::portDirection(std::string_view spef_dir) { PortDirection *direction = PortDirection::unknown(); if (spef_dir == "I") direction = PortDirection::input(); else if (spef_dir == "O") direction = PortDirection::output(); else if (spef_dir == "B") direction = PortDirection::bidirect(); else warn(1646, "unknown port direction {}.", spef_dir); return direction; } void SpefReader::setDesignFlow(StringSeq *flow) { design_flow_ = *flow; delete flow; } Pin * SpefReader::findPin(std::string_view name) { Pin *pin = nullptr; if (!name.empty()) { size_t delim = name.rfind(delimiter_); if (delim != std::string::npos) { std::string inst_name_mapped(name.substr(0, delim)); std::string_view inst_name = nameMapLookup(inst_name_mapped); if (!inst_name.empty()) { Instance *inst = findInstanceRelative(inst_name); std::string port_name(name.substr(delim + 1, std::string::npos)); if (inst) { pin = network_->findPin(inst, port_name); if (pin == nullptr) warn(1647, "pin {}{}{} not found.", inst_name, delimiter_, port_name); } else warn(1648, "instance {}{}{} not found.", inst_name, delimiter_, port_name); } } else { pin = findPortPinRelative(name); if (pin == nullptr) warn(1649, "pin {} not found.", name); } } return pin; } Net * SpefReader::findNet(std::string_view name) { Net *net = nullptr; std::string_view name1 = nameMapLookup(name); if (!name1.empty()) { net = findNetRelative(name1); if (net == nullptr) warn(1650, "net {} not found.", name1); } return net; } void SpefReader::rspfBegin(Net *net, SpefTriple *total_cap) { if (net) parasitics_->deleteParasitics(net); // Net total capacitance is ignored. delete total_cap; } void SpefReader::rspfFinish() { } void SpefReader::rspfDrvrBegin(Pin *drvr_pin, SpefRspfPi *pi) { if (drvr_pin) { float c2 = pi->c2()->value(triple_index_) * cap_scale_; float rpi = pi->r1()->value(triple_index_) * res_scale_; float c1 = pi->c1()->value(triple_index_) * cap_scale_; // Only one parasitic, save it under rise transition. parasitic_ = parasitics_->makePiElmore(drvr_pin, RiseFall::rise(), MinMax::max(), c2, rpi, c1); } delete pi; } void SpefReader::rspfLoad(Pin *load_pin, SpefTriple *rc) { if (parasitic_ && load_pin) { float elmore = rc->value(triple_index_) * time_scale_; parasitics_->setElmore(parasitic_, load_pin, elmore); } delete rc; } void SpefReader::rspfDrvrFinish() { parasitic_ = nullptr; } // Net cap (total_cap) is ignored. void SpefReader::dspfBegin(Net *net, SpefTriple *total_cap) { if (net) { if (network_->isTopInstance(instance_)) { parasitics_->deleteReducedParasitics(net); parasitic_ = parasitics_->makeParasiticNetwork(net, pin_cap_included_); } else { Net *parasitic_owner = net; NetTermIterator *term_iter = network_->termIterator(net); if (term_iter->hasNext()) { Term *term = term_iter->next(); Pin *hpin = network_->pin(term); parasitic_owner = network_->net(hpin); } delete term_iter; parasitic_ = parasitics_->findParasiticNetwork(parasitic_owner); if (parasitic_ == nullptr) parasitic_ = parasitics_->makeParasiticNetwork(parasitic_owner, pin_cap_included_); } net_ = net; } else { parasitic_ = nullptr; net_ = nullptr; } delete total_cap; } void SpefReader::dspfFinish() { if (parasitic_ && reduce_) { arc_delay_calc_->reduceParasitic(parasitic_, net_, scene_, min_max_); parasitics_->deleteParasiticNetwork(net_); } parasitic_ = nullptr; net_ = nullptr; } ParasiticNode * SpefReader::findParasiticNode(std::string_view name, bool local_only) { if (!name.empty() && parasitic_) { size_t delim = name.rfind(delimiter_); if (delim != std::string::npos) { std::string name1_mapped(name.substr(0, delim)); std::string name2(name.substr(delim + 1, std::string::npos)); std::string_view name1 = nameMapLookup(name1_mapped); if (!name1.empty()) { Instance *inst = findInstanceRelative(name1); if (inst) { // : Pin *pin = network_->findPin(inst, name2); if (pin) { if (local_only && !network_->isConnected(net_, pin)) warn(1651, "{} not connected to net {}.", name1, sdc_network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, pin, network_); } else warn(1652, "pin {}{}{} not found.", name1, delimiter_, name2); } else { Net *net = findNet(name1); if (net) { // : if (isDigits(name2)) { int id = std::stoi(name2); if (local_only && !network_->isConnected(net, net_)) warn(1653, "{}{}{} not connected to net {}.", name1, delimiter_, name2, network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, net, id, network_); } else warn(1654, "node {}{}{} not a pin or net:number", name1, delimiter_, name2); } } } } else { // std::string_view name1 = nameMapLookup(name); if (!name1.empty()) { Pin *pin = findPortPinRelative(name1); if (pin) { if (local_only && !network_->isConnected(net_, pin)) warn(1655, "{} not connected to net {}.", name1, network_->pathName(net_)); return parasitics_->ensureParasiticNode(parasitic_, pin, network_); } else warn(1656, "pin {} not found.", name1); } else warn(1657, "pin {} not found.", name); } } return nullptr; } void SpefReader::makeCapacitor(int, std::string_view node_name, SpefTriple *cap) { ParasiticNode *node = findParasiticNode(node_name, true); if (node) { float cap1 = cap->value(triple_index_) * cap_scale_; parasitics_->incrCap(node, cap1); } delete cap; } void SpefReader::makeCapacitor(int id, std::string_view node_name1, std::string_view node_name2, SpefTriple *cap) { ParasiticNode *node1 = findParasiticNode(node_name1, false); ParasiticNode *node2 = findParasiticNode(node_name2, false); float cap1 = cap->value(triple_index_) * cap_scale_; if (cap1 > 0.0) { if (keep_coupling_caps_) parasitics_->makeCapacitor(parasitic_, id, cap1, node1, node2); else { float scaled_cap = cap1 * coupling_cap_factor_; if (node1 && parasitics_->net(node1, network_) == net_) parasitics_->incrCap(node1, scaled_cap); if (node2 && parasitics_->net(node2, network_) == net_) parasitics_->incrCap(node2, scaled_cap); } } delete cap; } void SpefReader::makeResistor(int id, std::string_view node_name1, std::string_view node_name2, SpefTriple *res) { ParasiticNode *node1 = findParasiticNode(node_name1, true); ParasiticNode *node2 = findParasiticNode(node_name2, true); if (node1 && node2) { float res1 = res->value(triple_index_) * res_scale_; parasitics_->makeResistor(parasitic_, id, res1, node1, node2); } delete res; } //////////////////////////////////////////////////////////////// SpefRspfPi::SpefRspfPi(SpefTriple *c2, SpefTriple *r1, SpefTriple *c1) : c2_(c2), r1_(r1), c1_(c1) { } SpefRspfPi::~SpefRspfPi() { delete c2_; delete r1_; delete c1_; } //////////////////////////////////////////////////////////////// SpefTriple::SpefTriple(float value) : is_triple_(false) { values_[0] = value; } SpefTriple::SpefTriple(float value1, float value2, float value3) : is_triple_(true) { values_[0] = value1; values_[1] = value2; values_[2] = value3; } float SpefTriple::value(int index) const { if (is_triple_) return values_[index]; else return values_[0]; } //////////////////////////////////////////////////////////////// SpefScanner::SpefScanner(std::istream *stream, std::string_view filename, SpefReader *reader, Report *report) : yyFlexLexer(stream), filename_(filename), reader_(reader), report_(report) { } void SpefScanner::error(std::string_view msg) { report_->fileError(1658, filename_, lineno(), "{}", msg); } } // namespace sta