diff --git a/src/est/README.md b/src/est/README.md index 02b9a4d88f9..a5676f1fdbd 100644 --- a/src/est/README.md +++ b/src/est/README.md @@ -15,6 +15,9 @@ The `set_wire_rc` command sets the resistance and capacitance used to estimate delay of routing wires. Separate values can be specified for clock and data nets with the `-signal` and `-clock` flags. Without either `-signal` or `-clock` the resistance and capacitance for clocks and data nets are set. +In 3D designs the values can be targeted at a technology with the `-tech` and +`-redistribution_layer` selectors; without a selector the values are the +defaults used by chips whose technology has no specific values. ``` # Either run @@ -31,6 +34,8 @@ set_wire_rc [-data] [-corner corner] [-layers layers_list] + [-tech tech] + [-redistribution_layer] or set_wire_rc @@ -38,6 +43,8 @@ set_wire_rc [-h_capacitance cap] [-v_resistance res] [-v_capacitance cap] + [-tech tech] + [-redistribution_layer] or set_wire_rc @@ -46,10 +53,14 @@ set_wire_rc [-data] [-corner corner] [-layer layer_name] + [-tech tech] + [-redistribution_layer] or set_wire_rc [-resistance res] [-capacitance cap] + [-tech tech] + [-redistribution_layer] ``` #### Options @@ -66,6 +77,13 @@ set_wire_rc | `-h_capacitance` | Capacitance per unit length for horizontal wires, units are from the first Liberty file read. | | `-v_resistance` | Resistance per unit length for vertical wires, units are from the first Liberty file read. | | `-v_capacitance` | Capacitance per unit length for vertical wires, units are from the first Liberty file read. | +| `-tech` | Apply the values to the named technology (3D designs). Layers given with `-layer`/`-layers` are looked up in this technology. | +| `-redistribution_layer` | Apply the values to the technology of every RDL chip in the design. Warns and does nothing when the design has none, so shared scripts work across designs. `-layer`/`-layers` require the RDL chips to share one technology; with mixed RDL technologies set each one separately with `-tech`. | + +Without `-tech` or `-redistribution_layer` the values are the defaults used by +every chip whose technology has no specific values. Signal values, clock values +and routing layers fall back to the defaults independently, so a technology with +only specific signal values still uses the default clock values. ### Set Layer RC diff --git a/src/est/include/est/EstimateParasitics.h b/src/est/include/est/EstimateParasitics.h index 997a1b27489..7da2e624c39 100644 --- a/src/est/include/est/EstimateParasitics.h +++ b/src/est/include/est/EstimateParasitics.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -99,25 +100,31 @@ class EstimateParasitics : public sta::dbStaState, public ParasiticsService // Return values. double& res, double& cap) const; - void addClkLayer(odb::dbTechLayer* layer); - void addSignalLayer(odb::dbTechLayer* layer); + // A null tech in the setters below writes the default values used by + // chips whose technology has no specific values. + void addClkLayer(odb::dbTech* tech, odb::dbTechLayer* layer); + void addSignalLayer(odb::dbTech* tech, odb::dbTechLayer* layer); void sortClkAndSignalLayers(); // Set the resistance and capacitance used for horizontal parasitics on signal // nets. - void setHWireSignalRC(const sta::Scene* scene, + void setHWireSignalRC(odb::dbTech* tech, + const sta::Scene* scene, double res, // ohms/meter double cap); // farads/meter // Set the resistance and capacitance used for vertical wires parasitics on // signal nets. - void setVWireSignalRC(const sta::Scene* scene, + void setVWireSignalRC(odb::dbTech* tech, + const sta::Scene* scene, double res, // ohms/meter double cap); // farads/meter // Set the resistance and capacitance used for parasitics on clock nets. - void setHWireClkRC(const sta::Scene* scene, + void setHWireClkRC(odb::dbTech* tech, + const sta::Scene* scene, double res, double cap); // farads/meter // Set the resistance and capacitance used for parasitics on clock nets. - void setVWireClkRC(const sta::Scene* scene, + void setVWireClkRC(odb::dbTech* tech, + const sta::Scene* scene, double res, double cap); // farads/meter // ohms/meter, farads/meter @@ -210,6 +217,23 @@ class EstimateParasitics : public sta::dbStaState, public ParasiticsService utl::Logger* getLogger() { return logger_; } private: + // Wire RC values and layers of one technology, indexed by corner->index() + struct WireRC + { + std::vector signal_layers; + std::vector clk_layers; + std::vector signal_res; // ohms/meter + std::vector signal_cap; // Farads/meter + std::vector clk_res; // ohms/meter + std::vector clk_cap; // Farads/meter + }; + + odb::dbTech* currentTech() const; + WireRC& wireRC(odb::dbTech* tech) { return wire_rc_[tech]; } + // Resolve one WireRC category for the current technology; a category left + // unset for a tech falls back to the defaults (nullptr entry) independently. + template + const std::vector& resolveWireRC(std::vector WireRC::*category) const; void ensureParasitics(); bool isIdealClockPin(const sta::Pin* pin) const; bool isIdealClockNet(const sta::Net* net) const; @@ -258,17 +282,14 @@ class EstimateParasitics : public sta::dbStaState, public ParasiticsService odb::dbBlock* block_ = nullptr; std::unique_ptr db_cbk_; - std::vector signal_layers_; - std::vector clk_layers_; - // Layer RC per wire length indexed by layer->getNumber(), corner->index - std::vector> layer_res_; // ohms/meter - std::vector> layer_cap_; // Farads/meter - // Signal wire RC indexed by corner->index - std::vector wire_signal_res_; // ohms/metre - std::vector wire_signal_cap_; // Farads/meter - // Clock wire RC. - std::vector wire_clk_res_; // ohms/metre - std::vector wire_clk_cap_; // Farads/meter + // Layer RC per wire length keyed by layer, indexed by corner->index() + std::unordered_map> + layer_res_; // ohms/meter + std::unordered_map> + layer_cap_; // Farads/meter + // Wire RC per technology; the nullptr entry holds the defaults used by + // chips whose technology has no specific values + std::unordered_map wire_rc_; ParasiticsSrc parasitics_src_ = ParasiticsSrc::kNone; diff --git a/src/est/src/EstimateParasitics.cpp b/src/est/src/EstimateParasitics.cpp index 06dbb3ae733..97ad69ce80d 100644 --- a/src/est/src/EstimateParasitics.cpp +++ b/src/est/src/EstimateParasitics.cpp @@ -73,12 +73,7 @@ EstimateParasitics::EstimateParasitics(utl::Logger* logger, stt_builder_(stt_builder), global_router_(global_router), db_network_(sta->getDbNetwork()), - db_(db), - db_cbk_(std::make_unique(this, network_, db_network_)), - wire_signal_res_(0.0), - wire_signal_cap_(0.0), - wire_clk_res_(0.0), - wire_clk_cap_(0.0) + db_(db) { dbStaState::init(sta); db_cbk_ = std::make_unique(this, network_, db_network_); @@ -115,19 +110,13 @@ void EstimateParasitics::setLayerRC(odb::dbTechLayer* layer, double res, double cap) { - if (layer_res_.empty()) { - int layer_count = layer->getTech()->getLayerCount(); - int corner_count = sta_->scenes().size(); - layer_res_.resize(layer_count); - layer_cap_.resize(layer_count); - for (int i = 0; i < layer_count; i++) { - layer_res_[i].resize(corner_count); - layer_cap_[i].resize(corner_count); - } - } - - layer_res_[layer->getNumber()][corner->index()] = res; - layer_cap_[layer->getNumber()][corner->index()] = cap; + const size_t corner_count = sta_->scenes().size(); + std::vector& layer_res = layer_res_[layer]; + std::vector& layer_cap = layer_cap_[layer]; + layer_res.resize(corner_count, 0.0); + layer_cap.resize(corner_count, 0.0); + layer_res[corner->index()] = res; + layer_cap[corner->index()] = cap; } void EstimateParasitics::layerRC(odb::dbTechLayer* layer, @@ -136,26 +125,39 @@ void EstimateParasitics::layerRC(odb::dbTechLayer* layer, double& res, double& cap) const { - if (layer_res_.empty()) { - res = 0.0; - cap = 0.0; - } else { - const int layer_level = layer->getNumber(); - res = layer_res_[layer_level][corner->index()]; - cap = layer_cap_[layer_level][corner->index()]; - } + const auto res_it = layer_res_.find(layer); + const auto cap_it = layer_cap_.find(layer); + const size_t corner_idx = corner->index(); + res = res_it != layer_res_.end() && corner_idx < res_it->second.size() + ? res_it->second[corner_idx] + : 0.0; + cap = cap_it != layer_cap_.end() && corner_idx < cap_it->second.size() + ? cap_it->second[corner_idx] + : 0.0; } //////////////////////////////////////////////////////////////// -void EstimateParasitics::addClkLayer(odb::dbTechLayer* layer) +odb::dbTech* EstimateParasitics::currentTech() const +{ + if (block_ != nullptr) { + return block_->getTech(); + } + odb::dbChip* chip = db_->getChip(); + return chip != nullptr ? chip->getTech() : nullptr; +} + +void EstimateParasitics::addClkLayer(odb::dbTech* tech, odb::dbTechLayer* layer) { - clk_layers_.push_back(layer); + WireRC& wire_rc = wireRC(tech); + wire_rc.clk_layers.push_back(layer); } -void EstimateParasitics::addSignalLayer(odb::dbTechLayer* layer) +void EstimateParasitics::addSignalLayer(odb::dbTech* tech, + odb::dbTechLayer* layer) { - signal_layers_.push_back(layer); + WireRC& wire_rc = wireRC(tech); + wire_rc.signal_layers.push_back(layer); } void EstimateParasitics::sortClkAndSignalLayers() @@ -164,81 +166,110 @@ void EstimateParasitics::sortClkAndSignalLayers() return a->getNumber() < b->getNumber(); }; - std::ranges::sort(clk_layers_, sort_layers); - std::ranges::sort(signal_layers_, sort_layers); + for (auto& [tech, wire_rc] : wire_rc_) { + std::ranges::sort(wire_rc.clk_layers, sort_layers); + std::ranges::sort(wire_rc.signal_layers, sort_layers); + } } -void EstimateParasitics::setHWireSignalRC(const sta::Scene* scene, +void EstimateParasitics::setHWireSignalRC(odb::dbTech* tech, + const sta::Scene* scene, double res, double cap) { - wire_signal_res_.resize(sta_->scenes().size()); - wire_signal_cap_.resize(sta_->scenes().size()); - wire_signal_res_[scene->index()].h_res = res; - wire_signal_cap_[scene->index()].h_cap = cap; + WireRC& wire_rc = wireRC(tech); + wire_rc.signal_res.resize(sta_->scenes().size()); + wire_rc.signal_cap.resize(sta_->scenes().size()); + wire_rc.signal_res[scene->index()].h_res = res; + wire_rc.signal_cap[scene->index()].h_cap = cap; } -void EstimateParasitics::setVWireSignalRC(const sta::Scene* scene, + +void EstimateParasitics::setVWireSignalRC(odb::dbTech* tech, + const sta::Scene* scene, double res, double cap) { - wire_signal_res_.resize(sta_->scenes().size()); - wire_signal_cap_.resize(sta_->scenes().size()); - wire_signal_res_[scene->index()].v_res = res; - wire_signal_cap_[scene->index()].v_cap = cap; + WireRC& wire_rc = wireRC(tech); + wire_rc.signal_res.resize(sta_->scenes().size()); + wire_rc.signal_cap.resize(sta_->scenes().size()); + wire_rc.signal_res[scene->index()].v_res = res; + wire_rc.signal_cap[scene->index()].v_cap = cap; +} + +// Defined outside the header; all instantiations live in this file. +template +const std::vector& EstimateParasitics::resolveWireRC( + std::vector WireRC::*category) const +{ + static const std::vector empty; + odb::dbTech* tech = currentTech(); + auto it = wire_rc_.find(tech); + if (it != wire_rc_.end() && !(it->second.*category).empty()) { + return it->second.*category; + } + if (tech != nullptr) { + it = wire_rc_.find(nullptr); + if (it != wire_rc_.end() && !(it->second.*category).empty()) { + return it->second.*category; + } + } + return empty; } double EstimateParasitics::wireSignalResistance(const sta::Scene* scene) const { - if (wire_signal_res_.empty()) { + const auto& signal_res = resolveWireRC(&WireRC::signal_res); + if (signal_res.empty()) { return 0.0; } - - return (wire_signal_res_[scene->index()].h_res - + wire_signal_res_[scene->index()].v_res) + return (signal_res[scene->index()].h_res + signal_res[scene->index()].v_res) / 2; } double EstimateParasitics::wireSignalHResistance(const sta::Scene* scene) const { - if (wire_signal_res_.empty()) { + const auto& signal_res = resolveWireRC(&WireRC::signal_res); + if (signal_res.empty()) { return 0.0; } - return wire_signal_res_[scene->index()].h_res; + return signal_res[scene->index()].h_res; } double EstimateParasitics::wireSignalVResistance(const sta::Scene* scene) const { - if (wire_signal_res_.empty()) { + const auto& signal_res = resolveWireRC(&WireRC::signal_res); + if (signal_res.empty()) { return 0.0; } - return wire_signal_res_[scene->index()].v_res; + return signal_res[scene->index()].v_res; } double EstimateParasitics::wireSignalCapacitance(const sta::Scene* scene) const { - if (wire_signal_cap_.empty()) { + const auto& signal_cap = resolveWireRC(&WireRC::signal_cap); + if (signal_cap.empty()) { return 0.0; } - - return (wire_signal_cap_[scene->index()].h_cap - + wire_signal_cap_[scene->index()].v_cap) + return (signal_cap[scene->index()].h_cap + signal_cap[scene->index()].v_cap) / 2; } double EstimateParasitics::wireSignalHCapacitance(const sta::Scene* scene) const { - if (wire_signal_cap_.empty()) { + const auto& signal_cap = resolveWireRC(&WireRC::signal_cap); + if (signal_cap.empty()) { return 0.0; } - return wire_signal_cap_[scene->index()].h_cap; + return signal_cap[scene->index()].h_cap; } double EstimateParasitics::wireSignalVCapacitance(const sta::Scene* scene) const { - if (wire_signal_cap_.empty()) { + const auto& signal_cap = resolveWireRC(&WireRC::signal_cap); + if (signal_cap.empty()) { return 0.0; } - return wire_signal_cap_[scene->index()].v_cap; + return signal_cap[scene->index()].v_cap; } void EstimateParasitics::wireSignalRC(const sta::Scene* scene, @@ -246,96 +277,94 @@ void EstimateParasitics::wireSignalRC(const sta::Scene* scene, double& res, double& cap) const { - if (wire_signal_res_.empty()) { - res = 0.0; - } else { - auto resistance = wire_signal_res_[scene->index()]; - res = (resistance.h_res + resistance.v_res) / 2; - } - if (wire_signal_cap_.empty()) { - cap = 0.0; - } else { - auto capacitance = wire_signal_cap_[scene->index()]; - cap = (capacitance.h_cap + capacitance.v_cap) / 2; - } + const auto& resistance = resolveWireRC(&WireRC::signal_res); + const auto& capacitance = resolveWireRC(&WireRC::signal_cap); + res = resistance.empty() ? 0.0 + : (resistance[scene->index()].h_res + + resistance[scene->index()].v_res) + / 2; + cap = capacitance.empty() ? 0.0 + : (capacitance[scene->index()].h_cap + + capacitance[scene->index()].v_cap) + / 2; } -void EstimateParasitics::setHWireClkRC(const sta::Scene* scene, +void EstimateParasitics::setHWireClkRC(odb::dbTech* tech, + const sta::Scene* scene, double res, double cap) { - wire_clk_res_.resize(sta_->scenes().size()); - wire_clk_cap_.resize(sta_->scenes().size()); - wire_clk_res_[scene->index()].h_res = res; - wire_clk_cap_[scene->index()].h_cap = cap; + WireRC& wire_rc = wireRC(tech); + wire_rc.clk_res.resize(sta_->scenes().size()); + wire_rc.clk_cap.resize(sta_->scenes().size()); + wire_rc.clk_res[scene->index()].h_res = res; + wire_rc.clk_cap[scene->index()].h_cap = cap; } -void EstimateParasitics::setVWireClkRC(const sta::Scene* scene, +void EstimateParasitics::setVWireClkRC(odb::dbTech* tech, + const sta::Scene* scene, double res, double cap) { - wire_clk_res_.resize(sta_->scenes().size()); - wire_clk_cap_.resize(sta_->scenes().size()); - wire_clk_res_[scene->index()].v_res = res; - wire_clk_cap_[scene->index()].v_cap = cap; + WireRC& wire_rc = wireRC(tech); + wire_rc.clk_res.resize(sta_->scenes().size()); + wire_rc.clk_cap.resize(sta_->scenes().size()); + wire_rc.clk_res[scene->index()].v_res = res; + wire_rc.clk_cap[scene->index()].v_cap = cap; } double EstimateParasitics::wireClkResistance(const sta::Scene* scene) const { - if (wire_clk_res_.empty()) { + const auto& clk_res = resolveWireRC(&WireRC::clk_res); + if (clk_res.empty()) { return 0.0; } - - return (wire_clk_res_[scene->index()].h_res - + wire_clk_res_[scene->index()].v_res) - / 2; + return (clk_res[scene->index()].h_res + clk_res[scene->index()].v_res) / 2; } double EstimateParasitics::wireClkHResistance(const sta::Scene* scene) const { - if (wire_clk_res_.empty()) { + const auto& clk_res = resolveWireRC(&WireRC::clk_res); + if (clk_res.empty()) { return 0.0; } - - return wire_clk_res_[scene->index()].h_res; + return clk_res[scene->index()].h_res; } double EstimateParasitics::wireClkVResistance(const sta::Scene* scene) const { - if (wire_clk_res_.empty()) { + const auto& clk_res = resolveWireRC(&WireRC::clk_res); + if (clk_res.empty()) { return 0.0; } - - return wire_clk_res_[scene->index()].v_res; + return clk_res[scene->index()].v_res; } double EstimateParasitics::wireClkCapacitance(const sta::Scene* scene) const { - if (wire_clk_cap_.empty()) { + const auto& clk_cap = resolveWireRC(&WireRC::clk_cap); + if (clk_cap.empty()) { return 0.0; } - - return (wire_clk_cap_[scene->index()].h_cap - + wire_clk_cap_[scene->index()].v_cap) - / 2; + return (clk_cap[scene->index()].h_cap + clk_cap[scene->index()].v_cap) / 2; } double EstimateParasitics::wireClkHCapacitance(const sta::Scene* scene) const { - if (wire_clk_cap_.empty()) { + const auto& clk_cap = resolveWireRC(&WireRC::clk_cap); + if (clk_cap.empty()) { return 0.0; } - - return wire_clk_cap_[scene->index()].h_cap; + return clk_cap[scene->index()].h_cap; } double EstimateParasitics::wireClkVCapacitance(const sta::Scene* scene) const { - if (wire_clk_cap_.empty()) { + const auto& clk_cap = resolveWireRC(&WireRC::clk_cap); + if (clk_cap.empty()) { return 0.0; } - - return wire_clk_cap_[scene->index()].v_cap; + return clk_cap[scene->index()].v_cap; } //////////////////////////////////////////////////////////////// @@ -624,7 +653,7 @@ void EstimateParasitics::clearParasitics() void EstimateParasitics::estimateWireParasitics(sta::SpefWriter* spef_writer) { initBlock(); - if (!wire_signal_cap_.empty()) { + if (!resolveWireRC(&WireRC::signal_cap).empty()) { for (auto mode : sta_->modes()) { sta_->ensureClkNetwork(mode); } @@ -957,8 +986,9 @@ double EstimateParasitics::computeAverageCutResistance(sta::Scene* scene) layer_idx++) { odb::dbTechLayer* layer = tech->findLayer(layer_idx); if (layer && layer->getType() == odb::dbTechLayerType::CUT) { - const float resistance = layer_res_[layer_idx][scene->index()]; - total_resistance += resistance; + double res, cap; + layerRC(layer, scene, res, cap); + total_resistance += res; count++; } } @@ -981,12 +1011,9 @@ void EstimateParasitics::parasiticNodeConnectPins( { const sta::PinSeq* pins = tree->pins(pt); if (pins) { - odb::dbTechLayer* tree_layer; - if (is_clk) { - tree_layer = clk_layers_.empty() ? nullptr : clk_layers_[0]; - } else { - tree_layer = signal_layers_.empty() ? nullptr : signal_layers_[0]; - } + const auto& layers + = resolveWireRC(is_clk ? &WireRC::clk_layers : &WireRC::signal_layers); + odb::dbTechLayer* tree_layer = layers.empty() ? nullptr : layers[0]; for (const sta::Pin* pin : *pins) { sta::ParasiticNode* pin_node @@ -1029,6 +1056,7 @@ void EstimateParasitics::insertViaResistances(odb::dbTechLayer* pin_layer, { sta::ParasiticNode* prev_node = nullptr; + odb::dbTech* tech = pin_layer->getTech(); const int pin_layer_idx = pin_layer->getNumber(); const int tree_layer_idx = tree_layer->getNumber(); if (std::abs(pin_layer_idx - tree_layer->getNumber()) == 2) { @@ -1036,8 +1064,12 @@ void EstimateParasitics::insertViaResistances(odb::dbTechLayer* pin_layer, const int cut_layer_idx = pin_layer_idx < tree_layer_idx ? pin_layer_idx + 1 : pin_layer_idx - 1; - const double cut_res - = std::max(layer_res_[cut_layer_idx][corner->index()], 1.0e-3); + double res = 0.0; + double cap = 0.0; + if (odb::dbTechLayer* cut_layer = tech->findLayer(cut_layer_idx)) { + layerRC(cut_layer, corner, res, cap); + } + const double cut_res = std::max(res, 1.0e-3); parasitics->makeResistor(parasitic, resistor_id++, cut_res, pin_node, node); } else if (pin_layer_idx == tree_layer_idx) { // Add a small resistor between the pin node and tree node to keep @@ -1047,15 +1079,15 @@ void EstimateParasitics::insertViaResistances(odb::dbTechLayer* pin_layer, const auto [start_idx, end_idx] = std::minmax(pin_layer_idx, tree_layer_idx); const bool pin_is_below = (pin_layer_idx < tree_layer_idx); - odb::dbTech* tech = pin_layer->getTech(); for (int layer_idx = start_idx; layer_idx < end_idx; layer_idx++) { odb::dbTechLayer* cut_layer = tech->findLayer(layer_idx); if (cut_layer->getType() != odb::dbTechLayerType::CUT) { continue; } - const double cut_res - = std::max(layer_res_[layer_idx][corner->index()], 1.0e-3); + double res, cap; + layerRC(cut_layer, corner, res, cap); + const double cut_res = std::max(res, 1.0e-3); // Resolve from/to endpoints first, so we only allocate a new mid_node // when this iteration actually needs one. On the terminal iteration the diff --git a/src/est/src/EstimateParasitics.i b/src/est/src/EstimateParasitics.i index 923be09ce10..bb950491380 100644 --- a/src/est/src/EstimateParasitics.i +++ b/src/est/src/EstimateParasitics.i @@ -130,17 +130,19 @@ set_layer_rc_cmd(odb::dbTechLayer *layer, } void -add_clk_layer_cmd(odb::dbTechLayer *layer) +add_clk_layer_cmd(odb::dbTech *tech, + odb::dbTechLayer *layer) { est::EstimateParasitics *estimate_parasitics = getEstimateParasitics(); - estimate_parasitics->addClkLayer(layer); + estimate_parasitics->addClkLayer(tech, layer); } void -add_signal_layer_cmd(odb::dbTechLayer *layer) +add_signal_layer_cmd(odb::dbTech *tech, + odb::dbTechLayer *layer) { est::EstimateParasitics *estimate_parasitics = getEstimateParasitics(); - estimate_parasitics->addSignalLayer(layer); + estimate_parasitics->addSignalLayer(tech, layer); } double @@ -164,43 +166,47 @@ layer_capacitance(odb::dbTechLayer *layer, } void -set_h_wire_signal_rc_cmd(const Scene *corner, +set_h_wire_signal_rc_cmd(odb::dbTech *tech, + const Scene *corner, float res, float cap) { ensureLinked(); est::EstimateParasitics *estimate_parasitics = getEstimateParasitics(); - estimate_parasitics->setHWireSignalRC(corner, res, cap); + estimate_parasitics->setHWireSignalRC(tech, corner, res, cap); } void -set_v_wire_signal_rc_cmd(const Scene *corner, +set_v_wire_signal_rc_cmd(odb::dbTech *tech, + const Scene *corner, float res, float cap) { ensureLinked(); est::EstimateParasitics *estimate_parasitics = getEstimateParasitics(); - estimate_parasitics->setVWireSignalRC(corner, res, cap); + estimate_parasitics->setVWireSignalRC(tech, corner, res, cap); } void -set_h_wire_clk_rc_cmd(const Scene *corner, +set_h_wire_clk_rc_cmd(odb::dbTech *tech, + const Scene *corner, float res, float cap) { ensureLinked(); est::EstimateParasitics *estimate_parasitics = getEstimateParasitics(); - estimate_parasitics->setHWireClkRC(corner, res, cap); + estimate_parasitics->setHWireClkRC(tech, corner, res, cap); } void -set_v_wire_clk_rc_cmd(const Scene *corner, +set_v_wire_clk_rc_cmd(odb::dbTech *tech, + const Scene *corner, float res, float cap) { ensureLinked(); est::EstimateParasitics *estimate_parasitics = getEstimateParasitics(); - estimate_parasitics->setVWireClkRC(corner, res, cap); + estimate_parasitics->setVWireClkRC(tech, corner, res, cap); } // ohms/meter diff --git a/src/est/src/EstimateParasitics.tcl b/src/est/src/EstimateParasitics.tcl index 0af39a918d2..bc26a136f86 100644 --- a/src/est/src/EstimateParasitics.tcl +++ b/src/est/src/EstimateParasitics.tcl @@ -1,18 +1,6 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) 2019-2025, The OpenROAD Authors -namespace eval est { -proc get_db_tech_checked { } { - set tech [ord::get_db_tech] - if { $tech == "NULL" } { - utl::error EST 210 "No technology loaded." - } - return $tech -} - -# namespace eval est -} - sta::define_cmd_args "estimate_parasitics" { -placement|-global_routing \ [-spef_file filename]} @@ -194,16 +182,28 @@ sta::define_cmd_args "set_wire_rc" {[-clock] [-signal] [-data]\ [-v_capacitance v_cap]\ [-resistance res]\ [-capacitance cap]\ - [-corner corner]} + [-corner corner]\ + [-tech tech]\ + [-redistribution_layer]} proc set_wire_rc { args } { sta::parse_key_args "set_wire_rc" args \ keys {-layer -layers -resistance -capacitance -corner \ - -h_resistance -h_capacitance -v_resistance -v_capacitance} \ - flags {-clock -signal -data} + -h_resistance -h_capacitance -v_resistance -v_capacitance \ + -tech} \ + flags {-clock -signal -data -redistribution_layer} set corner [sta::parse_scene_or_null keys] + set target_techs [est::parse_wire_rc_techs keys flags has_selector] + if { $has_selector && [llength $target_techs] == 0 } { + return + } + set tech_args $target_techs + if { [llength $tech_args] == 0 } { + set tech_args {NULL} + } + set h_wire_res 0.0 set h_wire_cap 0.0 set v_wire_res 0.0 @@ -230,9 +230,10 @@ proc set_wire_rc { args } { set v_layers 0 set layers $keys(-layers) + set tech [est::wire_rc_tech $target_techs] foreach layer_name $layers { - set tec_layer [[est::get_db_tech_checked] findLayer $layer_name] + set tec_layer [$tech findLayer $layer_name] if { $tec_layer == "NULL" } { utl::error EST 2 "layer $layer_name not found." } @@ -260,18 +261,8 @@ proc set_wire_rc { args } { incr v_layers } - if { [info exists flags(-clock)] } { - est::add_clk_layer_cmd $tec_layer - } - - if { [info exists flags(-signal)] } { - est::add_signal_layer_cmd $tec_layer - } - - if { ![info exists flags(-clock)] && ![info exists flags(-signal)] } { - est::add_clk_layer_cmd $tec_layer - est::add_signal_layer_cmd $tec_layer - } + est::add_wire_rc_layers $tech_args [info exists flags(-clock)] \ + [info exists flags(-signal)] $tec_layer } if { $h_layers == 0 } { utl::error EST 16 "No horizontal layer specified." @@ -286,7 +277,7 @@ proc set_wire_rc { args } { set v_wire_cap [expr $total_v_wire_cap / $v_layers] } elseif { [info exists keys(-layer)] } { set layer_name $keys(-layer) - set tec_layer [[est::get_db_tech_checked] findLayer $layer_name] + set tec_layer [[est::wire_rc_tech $target_techs] findLayer $layer_name] if { $tec_layer == "NULL" } { utl::error EST 15 "layer $tec_layer not found." } @@ -301,18 +292,8 @@ proc set_wire_rc { args } { set v_wire_cap [est::layer_capacitance $tec_layer $corner] } - if { [info exists flags(-clock)] } { - est::add_clk_layer_cmd $tec_layer - } - - if { [info exists flags(-signal)] } { - est::add_signal_layer_cmd $tec_layer - } - - if { ![info exists flags(-clock)] && ![info exists flags(-signal)] } { - est::add_clk_layer_cmd $tec_layer - est::add_signal_layer_cmd $tec_layer - } + est::add_wire_rc_layers $tech_args [info exists flags(-clock)] \ + [info exists flags(-signal)] $tec_layer } else { ord::ensure_units_initialized if { [info exists keys(-resistance)] } { @@ -389,18 +370,28 @@ proc set_wire_rc { args } { set corners [sta::scenes] } foreach corner $corners { - if { $signal } { - est::set_h_wire_signal_rc_cmd $corner $h_wire_res $h_wire_cap - est::set_v_wire_signal_rc_cmd $corner $v_wire_res $v_wire_cap - } - if { $clk } { - est::set_h_wire_clk_rc_cmd $corner $h_wire_res $h_wire_cap - est::set_v_wire_clk_rc_cmd $corner $v_wire_res $v_wire_cap + foreach tech $tech_args { + if { $signal } { + est::set_h_wire_signal_rc_cmd $tech $corner $h_wire_res $h_wire_cap + est::set_v_wire_signal_rc_cmd $tech $corner $v_wire_res $v_wire_cap + } + if { $clk } { + est::set_h_wire_clk_rc_cmd $tech $corner $h_wire_res $h_wire_cap + est::set_v_wire_clk_rc_cmd $tech $corner $v_wire_res $v_wire_cap + } } } } namespace eval est { +proc get_db_tech_checked { } { + set tech [ord::get_db_tech] + if { $tech == "NULL" } { + utl::error EST 210 "No technology loaded." + } + return $tech +} + proc check_corner_wire_caps { } { set have_rc 1 foreach corner [sta::scenes] { @@ -421,7 +412,8 @@ proc check_parasitics { } { proc dblayer_wire_rc { layer } { set layer_width_dbu [$layer getWidth] - set layer_width_micron [ord::dbu_to_microns $layer_width_dbu] + set dbu_per_micron [[$layer getTech] getDbUnitsPerMicron] + set layer_width_micron [expr { double($layer_width_dbu) / $dbu_per_micron }] set res_ohm_per_sq [$layer getResistance] set res_ohm_per_micron [expr $res_ohm_per_sq / $layer_width_micron] set cap_area_pf_per_sq_micron [$layer getCapacitance] @@ -439,7 +431,8 @@ proc dblayer_wire_rc { layer } { proc set_dblayer_wire_rc { layer res cap } { # Zero the edge cap and just use the user given value $layer setEdgeCapacitance 0 - set wire_width [ord::dbu_to_microns [$layer getWidth]] + set dbu_per_micron [[$layer getTech] getDbUnitsPerMicron] + set wire_width [expr { double([$layer getWidth]) / $dbu_per_micron }] # Convert wire capacitance/wire_length to capacitance/area (pF/um) set cap_per_square [expr $cap * 1e+6 / $wire_width] $layer setCapacitance $cap_per_square @@ -453,5 +446,70 @@ proc set_dbvia_wire_r { layer res } { $layer setResistance $res } +# Return the technologies selected by -tech/-redistribution_layer, setting +# has_selector in the caller. An empty result means "set the shared defaults" +# when no selector was given and "nothing to do" when one was. +proc parse_wire_rc_techs { keys_var flags_var selector_var } { + upvar 1 $keys_var keys + upvar 1 $flags_var flags + upvar 1 $selector_var has_selector + + set selector_count [expr { + [info exists keys(-tech)] + [info exists flags(-redistribution_layer)] + }] + if { $selector_count > 1 } { + utl::error EST 28 "Use only one of -tech or -redistribution_layer." + } + set has_selector [expr { $selector_count > 0 }] + + set db [ord::get_db] + set target_techs {} + if { [info exists keys(-tech)] } { + set tech [$db findTech $keys(-tech)] + if { $tech == "NULL" } { + utl::error EST 30 "technology $keys(-tech) not found." + } + lappend target_techs $tech + } elseif { [info exists flags(-redistribution_layer)] } { + foreach chip [$db getChips] { + if { [$chip getChipType] != "RDL" } { + continue + } + set tech [$chip getTech] + if { $tech != "NULL" && [lsearch -exact $target_techs $tech] < 0 } { + lappend target_techs $tech + } + } + if { [llength $target_techs] == 0 } { + utl::warn EST 31 "design has no RDL chip; values ignored." + } + } + return $target_techs +} + +# Layer lookups use the targeted technology when one is selected; a layer +# cannot be resolved when the selection spans more than one technology. +proc wire_rc_tech { techs } { + if { [llength $techs] > 1 } { + utl::error EST 32 "-layer and -layers require a single technology; set\ + each technology separately with -tech." + } + if { [llength $techs] == 1 } { + return [lindex $techs 0] + } + return [est::get_db_tech_checked] +} + +proc add_wire_rc_layers { techs clk signal layer } { + foreach tech $techs { + if { $clk || !$signal } { + est::add_clk_layer_cmd $tech $layer + } + if { $signal || !$clk } { + est::add_signal_layer_cmd $tech $layer + } + } +} + # namespace } diff --git a/src/est/test/BUILD b/src/est/test/BUILD index 3e1e5201e62..e73651a5898 100644 --- a/src/est/test/BUILD +++ b/src/est/test/BUILD @@ -14,6 +14,11 @@ TESTS = [ "make_parasitics5", "make_parasitics6", "make_parasitics7", + "set_wire_rc_tech", + "set_wire_rc_tech_errors", + "set_wire_rc_rdl_ignored", + "set_wire_rc_selectors", + "set_wire_rc_rdl_mixed_tech", ] filegroup( diff --git a/src/est/test/CMakeLists.txt b/src/est/test/CMakeLists.txt index ed999bd4d1e..5309e233568 100644 --- a/src/est/test/CMakeLists.txt +++ b/src/est/test/CMakeLists.txt @@ -10,6 +10,11 @@ or_integration_tests( make_parasitics5 make_parasitics6 make_parasitics7 + set_wire_rc_tech + set_wire_rc_tech_errors + set_wire_rc_rdl_ignored + set_wire_rc_selectors + set_wire_rc_rdl_mixed_tech ) add_executable(TestEstimateParasitics cpp/TestEstimateParasitics.cc) diff --git a/src/est/test/cpp/TestEstimateParasitics.cc b/src/est/test/cpp/TestEstimateParasitics.cc index 1110feb644c..74c768c15c7 100644 --- a/src/est/test/cpp/TestEstimateParasitics.cc +++ b/src/est/test/cpp/TestEstimateParasitics.cc @@ -197,4 +197,51 @@ TEST_F(TestEstimateParasitics, ScanClockIdealOnlyInTestMode) ep_.setIncrementalParasiticsEnabled(false); } +// Verifies that wire RC values are stored per chip: chip-specific values take +// precedence over the defaults, and chips without an entry use the defaults. +TEST_F(TestEstimateParasitics, WireRcPerTech) +{ + readVerilogAndSetup("TestEstimateParasitics.v"); + + sta::Scene* scene = sta_->scenes().front(); + odb::dbChip* chip1 = db_->getChip(); + ASSERT_NE(chip1, nullptr); + + // A null tech sets the default values used by techs without an entry. + ep_.initChip(chip1); + ep_.setHWireSignalRC(nullptr, scene, 1.0e3, 1.0e-10); + ep_.setVWireSignalRC(nullptr, scene, 2.0e3, 2.0e-10); + ep_.setHWireClkRC(nullptr, scene, 5.0e3, 5.0e-10); + ep_.setVWireClkRC(nullptr, scene, 5.0e3, 5.0e-10); + EXPECT_DOUBLE_EQ(ep_.wireSignalHResistance(scene), 1.0e3); + EXPECT_DOUBLE_EQ(ep_.wireSignalVCapacitance(scene), 2.0e-10); + + // A second technology with tech-specific values, used by a second chip. + loadTechAndLib( + "tech2", "lib2", getFilePath("_main/test/Nangate45/Nangate45.lef")); + odb::dbTech* tech2 = db_->findTech("tech2"); + ASSERT_NE(tech2, nullptr); + odb::dbChip* chip2 = odb::dbChip::create( + db_.get(), tech2, "chip2", odb::dbChip::ChipType::DIE); + ASSERT_NE(chip2, nullptr); + odb::dbBlock::create(chip2, "chip2_block"); + ep_.setHWireSignalRC(tech2, scene, 3.0e3, 3.0e-10); + ep_.setVWireSignalRC(tech2, scene, 4.0e3, 4.0e-10); + + // The tech-specific values do not leak into the default-valued tech. + EXPECT_DOUBLE_EQ(ep_.wireSignalHResistance(scene), 1.0e3); + + // Rebinding to the second chip resolves tech2's signal values; its unset + // clock values fall back to the defaults independently. + ep_.initChip(chip2); + EXPECT_DOUBLE_EQ(ep_.wireSignalHResistance(scene), 3.0e3); + EXPECT_DOUBLE_EQ(ep_.wireSignalVCapacitance(scene), 4.0e-10); + EXPECT_DOUBLE_EQ(ep_.wireClkHResistance(scene), 5.0e3); + + // Rebinding back to a chip whose tech has no entry falls back to defaults. + ep_.initChip(chip1); + EXPECT_DOUBLE_EQ(ep_.wireSignalHResistance(scene), 1.0e3); + EXPECT_DOUBLE_EQ(ep_.wireSignalVResistance(scene), 2.0e3); +} + } // namespace est diff --git a/src/est/test/set_wire_rc_rdl_ignored.ok b/src/est/test/set_wire_rc_rdl_ignored.ok new file mode 100644 index 00000000000..f58b89b50cf --- /dev/null +++ b/src/est/test/set_wire_rc_rdl_ignored.ok @@ -0,0 +1,7 @@ +[INFO ODB-0227] LEF file: Nangate45/Nangate45.lef, created 22 layers, 27 vias, 135 library cells +[INFO ODB-0128] Design: reg1 +[INFO ODB-0130] Created 4 pins. +[INFO ODB-0131] Created 5 components and 27 component-terminals. +[INFO ODB-0132] Created 2 special nets and 10 connections. +[INFO ODB-0133] Created 8 nets and 14 connections. +[WARNING EST-0031] design has no RDL chip; values ignored. diff --git a/src/est/test/set_wire_rc_rdl_ignored.tcl b/src/est/test/set_wire_rc_rdl_ignored.tcl new file mode 100644 index 00000000000..83a733936ed --- /dev/null +++ b/src/est/test/set_wire_rc_rdl_ignored.tcl @@ -0,0 +1,6 @@ +# set_wire_rc -redistribution_layer with no RDL chip warns and is ignored +source "helpers.tcl" +read_lef Nangate45/Nangate45.lef +read_def reg3.def + +set_wire_rc -redistribution_layer -resistance 1e-3 -capacitance 1e-1 diff --git a/src/est/test/set_wire_rc_rdl_mixed_tech.ok b/src/est/test/set_wire_rc_rdl_mixed_tech.ok new file mode 100644 index 00000000000..a57d8babc86 --- /dev/null +++ b/src/est/test/set_wire_rc_rdl_mixed_tech.ok @@ -0,0 +1,9 @@ +[INFO ODB-0227] LEF file: Nangate45/Nangate45.lef, created 22 layers, 27 vias, 135 library cells +[INFO ODB-0128] Design: reg1 +[INFO ODB-0130] Created 4 pins. +[INFO ODB-0131] Created 5 components and 27 component-terminals. +[INFO ODB-0132] Created 2 special nets and 10 connections. +[INFO ODB-0133] Created 8 nets and 14 connections. +[INFO ODB-0227] LEF file: Nangate45/Nangate45.lef, created 22 layers, 27 vias +[ERROR EST-0032] -layer and -layers require a single technology; set each technology separately with -tech. +EST-0032 diff --git a/src/est/test/set_wire_rc_rdl_mixed_tech.tcl b/src/est/test/set_wire_rc_rdl_mixed_tech.tcl new file mode 100644 index 00000000000..5dc2055aefe --- /dev/null +++ b/src/est/test/set_wire_rc_rdl_mixed_tech.tcl @@ -0,0 +1,16 @@ +# set_wire_rc -redistribution_layer with RDL chips on different technologies +source "helpers.tcl" +read_lef Nangate45/Nangate45.lef +read_liberty Nangate45/Nangate45_typ.lib +read_def reg3.def + +read_lef -tech -tech_name tech2 Nangate45/Nangate45.lef +set db [ord::get_db] +odb::dbChip_create $db [$db findTech tech2] "rdl_a" "RDL" +odb::dbChip_create $db [$db findTech Nangate45] "rdl_b" "RDL" + +# explicit values apply to both RDL chips +set_wire_rc -redistribution_layer -resistance 2e-3 -capacitance 2e-1 +# a shared layer cannot be resolved across different technologies +catch { set_wire_rc -redistribution_layer -layer metal3 } msg +puts $msg diff --git a/src/est/test/set_wire_rc_selectors.ok b/src/est/test/set_wire_rc_selectors.ok new file mode 100644 index 00000000000..5e2c8e61247 --- /dev/null +++ b/src/est/test/set_wire_rc_selectors.ok @@ -0,0 +1,8 @@ +[INFO ODB-0227] LEF file: Nangate45/Nangate45.lef, created 22 layers, 27 vias, 135 library cells +[INFO ODB-0128] Design: reg1 +[INFO ODB-0130] Created 4 pins. +[INFO ODB-0131] Created 5 components and 27 component-terminals. +[INFO ODB-0132] Created 2 special nets and 10 connections. +[INFO ODB-0133] Created 8 nets and 14 connections. +[INFO ODB-0227] LEF file: Nangate45/Nangate45.lef, created 22 layers, 27 vias +selectors applied diff --git a/src/est/test/set_wire_rc_selectors.tcl b/src/est/test/set_wire_rc_selectors.tcl new file mode 100644 index 00000000000..a5ef263b385 --- /dev/null +++ b/src/est/test/set_wire_rc_selectors.tcl @@ -0,0 +1,15 @@ +# set_wire_rc -tech/-redistribution_layer target technologies in a multi-tech db +source "helpers.tcl" +read_lef Nangate45/Nangate45.lef +read_liberty Nangate45/Nangate45_typ.lib +read_def reg3.def + +read_lef -tech -tech_name tech2 Nangate45/Nangate45.lef +set db [ord::get_db] +odb::dbChip_create $db [$db findTech tech2] "rdl" "RDL" + +# a named technology, with layers resolved in that technology +set_wire_rc -tech tech2 -layer metal3 +# every RDL chip's technology +set_wire_rc -redistribution_layer -resistance 2e-3 -capacitance 2e-1 +puts "selectors applied" diff --git a/src/est/test/set_wire_rc_tech.ok b/src/est/test/set_wire_rc_tech.ok new file mode 100644 index 00000000000..1ecd49b941e --- /dev/null +++ b/src/est/test/set_wire_rc_tech.ok @@ -0,0 +1,36 @@ +[INFO ODB-0227] LEF file: Nangate45/Nangate45.lef, created 22 layers, 27 vias, 135 library cells +[INFO ODB-0128] Design: reg1 +[INFO ODB-0130] Created 4 pins. +[INFO ODB-0131] Created 5 components and 27 component-terminals. +[INFO ODB-0132] Created 2 special nets and 10 connections. +[INFO ODB-0133] Created 8 nets and 14 connections. +[INFO ODB-0227] LEF file: Nangate45/Nangate45.lef, created 22 layers, 27 vias +Startpoint: r2 (rising edge-triggered flip-flop clocked by clk) +Endpoint: r3 (rising edge-triggered flip-flop clocked by clk) +Path Group: clk +Path Type: max + + Delay Time Description +--------------------------------------------------------- + 0.00 0.00 clock clk (rise edge) + 0.00 0.00 clock network delay (ideal) + 0.00 0.00 ^ r2/CK (DFF_X1) + 0.14 0.14 ^ r2/Q (DFF_X1) + 0.22 0.36 ^ u1/Z (BUF_X1) + 0.15 0.52 ^ u2/ZN (AND2_X1) + 0.00 0.52 ^ r3/D (DFF_X1) + 0.52 data arrival time + + 10.00 10.00 clock clk (rise edge) + 0.00 10.00 clock network delay (ideal) + 0.00 10.00 clock reconvergence pessimism + 10.00 ^ r3/CK (DFF_X1) + -0.05 9.95 library setup time + 9.95 data required time +--------------------------------------------------------- + 9.95 data required time + -0.52 data arrival time +--------------------------------------------------------- + 9.44 slack (MET) + + diff --git a/src/est/test/set_wire_rc_tech.tcl b/src/est/test/set_wire_rc_tech.tcl new file mode 100644 index 00000000000..425addee135 --- /dev/null +++ b/src/est/test/set_wire_rc_tech.tcl @@ -0,0 +1,27 @@ +# estimate_parasitics with tech-targeted wire RC in a multi-tech database +source "helpers.tcl" +read_lef Nangate45/Nangate45.lef +read_liberty Nangate45/Nangate45_typ.lib +read_def reg3.def + +create_clock -period 10 clk +set_input_delay -clock clk 0 in1 + +# a second tech makes the database multi-tech; tech-targeted values still work +read_lef -tech -tech_name tech2 Nangate45/Nangate45.lef + +# same wire RC values as make_parasitics1 +set lambda .12 +# kohm/square. +set m1_res_sq .08e-3 +# ff/micron^2 +set m1_area_cap 39e-3 +# ff/micron. +set m1_edge_cap 57e-3 +# 4 lambda wide wire +set wire_cap [expr { $m1_area_cap * $lambda * 4 + $m1_edge_cap * 2 }] +set wire_res [expr { $m1_res_sq / ($lambda * 4) }] +set_wire_rc -tech Nangate45 -resistance $wire_res -capacitance $wire_cap +estimate_parasitics -placement + +report_checks diff --git a/src/est/test/set_wire_rc_tech_errors.ok b/src/est/test/set_wire_rc_tech_errors.ok new file mode 100644 index 00000000000..f87f17a925f --- /dev/null +++ b/src/est/test/set_wire_rc_tech_errors.ok @@ -0,0 +1,10 @@ +[INFO ODB-0227] LEF file: Nangate45/Nangate45.lef, created 22 layers, 27 vias, 135 library cells +[INFO ODB-0128] Design: reg1 +[INFO ODB-0130] Created 4 pins. +[INFO ODB-0131] Created 5 components and 27 component-terminals. +[INFO ODB-0132] Created 2 special nets and 10 connections. +[INFO ODB-0133] Created 8 nets and 14 connections. +[ERROR EST-0030] technology no_such_tech not found. +EST-0030 +[ERROR EST-0028] Use only one of -tech or -redistribution_layer. +EST-0028 diff --git a/src/est/test/set_wire_rc_tech_errors.tcl b/src/est/test/set_wire_rc_tech_errors.tcl new file mode 100644 index 00000000000..d03d6235d19 --- /dev/null +++ b/src/est/test/set_wire_rc_tech_errors.tcl @@ -0,0 +1,14 @@ +# set_wire_rc technology selector misuse errors +source "helpers.tcl" +read_lef Nangate45/Nangate45.lef +read_def reg3.def + +# unknown technology name is an error +catch { set_wire_rc -tech no_such_tech -resistance 1e-3 -capacitance 1e-1 } msg +puts $msg +# selectors are mutually exclusive +catch { + set_wire_rc -tech Nangate45 -redistribution_layer \ + -resistance 1e-3 -capacitance 1e-1 +} msg +puts $msg