openlane hacks reference
diff --git a/hacks/patch/resizer.patch b/hacks/patch/resizer.patch
new file mode 100644
index 0000000..44e2796
--- /dev/null
+++ b/hacks/patch/resizer.patch
@@ -0,0 +1,29 @@
+diff --git a/src/rsz/src/Resizer.cc b/src/rsz/src/Resizer.cc
+index b57fce674..cac938c44 100644
+--- a/src/rsz/src/Resizer.cc
++++ b/src/rsz/src/Resizer.cc
+@@ -1685,9 +1685,11 @@ Resizer::resizeToTargetSlew(const Pin *drvr_pin,
+ ensureWireParasitic(drvr_pin);
+ // Includes net parasitic capacitance.
+ float load_cap = graph_delay_calc_->loadCap(drvr_pin, tgt_slew_dcalc_ap_);
+- if (load_cap > 0.0) {
++ // DINESH-A: delay cells resize disabled
++ if (load_cap > 0.00 && (strncmp(cell->name(),"sky130_fd_sc_hd__clkdlybuf4s15_2",26) != 0)) {
+ LibertyCell *target_cell = findTargetCell(cell, load_cap, revisiting_inst);
+ if (target_cell != cell) {
++ //printf("Dinesh-A: Resizing : %s => %s %s Load_cap: %f \n",sdc_network_->pathName(drvr_pin),cell->name(),target_cell->name(),load_cap);
+ debugPrint(logger_, RSZ, "resize", 2, "{} {} -> {}",
+ sdc_network_->pathName(drvr_pin),
+ cell->name(),
+@@ -2500,8 +2502,10 @@ Resizer::repairSetup(PathRef &path,
+ prev_drive = 0.0;
+ LibertyCell *upsize = upsizeCell(in_port, drvr_port, load_cap,
+ prev_drive, dcalc_ap);
+- if (upsize) {
++ // DINESH-A: delay cells resize disabled
++ if (upsize && (strncmp(drvr_port->libertyCell()->name(),"sky130_fd_sc_hd__clkdlybuf4s15_2",26) != 0)) {
+ Instance *drvr = network_->instance(drvr_pin);
++ //printf("Dinesh-A: Upsizing the cells: %s %s %s\n",network_->pathName(drvr_pin),drvr_port->libertyCell()->name(),upsize->name());
+ debugPrint(logger_, RSZ, "repair_setup", 2, "resize {} {} -> {}",
+ network_->pathName(drvr_pin),
+ drvr_port->libertyCell()->name(),
diff --git a/hacks/patch/scan_swap.patch b/hacks/patch/scan_swap.patch
new file mode 100644
index 0000000..41d98b7
--- /dev/null
+++ b/hacks/patch/scan_swap.patch
@@ -0,0 +1,60 @@
+diff --git a/src/sta/network/ConcreteNetwork.cc b/src/sta/network/ConcreteNetwork.cc
+index 6f8b842..8096f2e 100644
+--- a/src/sta/network/ConcreteNetwork.cc
++++ b/src/sta/network/ConcreteNetwork.cc
+@@ -1180,11 +1180,14 @@ ConcreteNetwork::replaceCell(Instance *inst,
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ int port_count = ccell->portBitCount();
+ ConcreteInstance *cinst = reinterpret_cast<ConcreteInstance*>(inst);
++ // Port count picked from Instance instead of Target cells-Dinesh A
++ ConcreteCell *instcell = reinterpret_cast<ConcreteCell*>(cinst->cell());
++ int inst_port_count = instcell->portBitCount();
+ ConcretePin **pins = cinst->pins_;
+ ConcretePin **rpins = new ConcretePin*[port_count];
+- for (int i = 0; i < port_count; i++)
+- rpins[i] = nullptr;
+- for (int i = 0; i < port_count; i++) {
++ for (int i = 0; i < port_count; i++)
++ rpins[i] = pins[inst_port_count-1];
++ for (int i = 0; i < inst_port_count; i++) {
+ ConcretePin *cpin = pins[i];
+ if (cpin) {
+ ConcretePort *pin_cport = reinterpret_cast<ConcretePort*>(cpin->port());
+diff --git a/src/sta/tcl/NetworkEdit.tcl b/src/sta/tcl/NetworkEdit.tcl
+index 5ce616b..bdd4057 100644
+--- a/src/sta/tcl/NetworkEdit.tcl
++++ b/src/sta/tcl/NetworkEdit.tcl
+@@ -236,6 +236,21 @@ proc replace_cell { instance lib_cell } {
+ }
+ }
+
++proc replace_to_scan_cell { instance } {
++ set inst [get_instance_error "instance" $instance]
++ set inst_cell [get_full_name [$inst liberty_cell]]
++ #Target scan cell __d to __sd
++ #example sky130_fd_sc_hd__dfrtp_2 to sky130_fd_sc_hd__sdfrtp_2
++ set inst_scell [regsub -all "__d" $inst_cell "__sd"]
++ puts "Info: Scan Swapping => Instance:$instance From:$inst_cell to:$inst_scell";
++ if { $inst_cell == "NULL"} {
++ return 0
++ }
++ set scell [get_lib_cell_warn "lib_cell" $inst_scell]
++ replace_cell_cmd $inst $scell
++ return 1
++}
++
+ proc path_regexp {} {
+ global hierarchy_separator
+ set id_regexp "\[^${hierarchy_separator}\]+"
+diff --git a/src/sta/tcl/Sta.tcl b/src/sta/tcl/Sta.tcl
+index f3de994..a6e8e34 100644
+--- a/src/sta/tcl/Sta.tcl
++++ b/src/sta/tcl/Sta.tcl
+@@ -623,6 +623,7 @@ define_cmd_args "make_instance" {inst_path lib_cell}
+ define_cmd_args "make_net" {}
+
+ define_cmd_args "replace_cell" {instance lib_cell}
++define_cmd_args "replace_to_scan_cell" {instance}
+
+ define_cmd_args "insert_buffer" {buffer_name buffer_cell net load_pins\
+ buffer_out_net_name}
diff --git a/hacks/src/OpenROAD/Resizer.cc b/hacks/src/OpenROAD/Resizer.cc
new file mode 100644
index 0000000..a339071
--- /dev/null
+++ b/hacks/src/OpenROAD/Resizer.cc
@@ -0,0 +1,4095 @@
+/////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (c) 2019, The Regents of the University of California
+// All rights reserved.
+//
+// BSD 3-Clause License
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// * Redistributions of source code must retain the above copyright notice, this
+// list of conditions and the following disclaimer.
+//
+// * Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+//
+// * Neither the name of the copyright holder nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "rsz/Resizer.hh"
+
+#include "rsz/SteinerTree.hh"
+
+#include "ord/OpenRoad.hh"
+#include "gui/gui.h"
+#include "utl/Logger.h"
+
+#include "sta/Report.hh"
+#include "sta/FuncExpr.hh"
+#include "sta/PortDirection.hh"
+#include "sta/TimingRole.hh"
+#include "sta/Units.hh"
+#include "sta/Liberty.hh"
+#include "sta/TimingArc.hh"
+#include "sta/TimingModel.hh"
+#include "sta/Network.hh"
+#include "sta/Graph.hh"
+#include "sta/DcalcAnalysisPt.hh"
+#include "sta/ArcDelayCalc.hh"
+#include "sta/GraphDelayCalc.hh"
+#include "sta/Parasitics.hh"
+#include "sta/Sdc.hh"
+#include "sta/InputDrive.hh"
+#include "sta/Corner.hh"
+#include "sta/PathVertex.hh"
+#include "sta/SearchPred.hh"
+#include "sta/Bfs.hh"
+#include "sta/Search.hh"
+#include "sta/PathRef.hh"
+#include "sta/PathExpanded.hh"
+#include "sta/StaMain.hh"
+#include "sta/Fuzzy.hh"
+
+// http://vlsicad.eecs.umich.edu/BK/Slots/cache/dropzone.tamu.edu/~zhuoli/GSRC/fast_buffer_insertion.html
+
+namespace sta {
+extern const char *rsz_tcl_inits[];
+}
+
+namespace rsz {
+
+using std::abs;
+using std::min;
+using std::max;
+using std::string;
+using std::to_string;
+using std::vector;
+using std::map;
+using std::pair;
+using std::sqrt;
+
+using utl::RSZ;
+using ord::closestPtInRect;
+
+using odb::dbInst;
+using odb::dbPlacementStatus;
+using odb::Rect;
+using odb::dbOrientType;
+using odb::dbMPin;
+using odb::dbBox;
+using odb::dbMasterType;
+
+using sta::evalTclInit;
+using sta::makeBlockSta;
+using sta::Level;
+using sta::stringLess;
+using sta::Network;
+using sta::NetworkEdit;
+using sta::NetPinIterator;
+using sta::NetConnectedPinIterator;
+using sta::InstancePinIterator;
+using sta::LeafInstanceIterator;
+using sta::LibertyLibraryIterator;
+using sta::LibertyCellIterator;
+using sta::LibertyCellTimingArcSetIterator;
+using sta::TimingArcSet;
+using sta::TimingArcSetArcIterator;
+using sta::TimingArcSetSeq;
+using sta::GateTimingModel;
+using sta::TimingRole;
+using sta::FuncExpr;
+using sta::Term;
+using sta::Port;
+using sta::PinSeq;
+using sta::NetIterator;
+using sta::PinConnectedPinIterator;
+using sta::FindNetDrvrLoads;;
+using sta::VertexIterator;
+using sta::VertexOutEdgeIterator;
+using sta::Edge;
+using sta::Search;
+using sta::SearchPredNonReg2;
+using sta::ClkArrivalSearchPred;
+using sta::BfsBkwdIterator;
+using sta::BfsFwdIterator;
+using sta::BfsIndex;
+using sta::Clock;
+using sta::PathExpanded;
+using sta::INF;
+using sta::fuzzyEqual;
+using sta::fuzzyLess;
+using sta::fuzzyLessEqual;
+using sta::fuzzyGreater;
+using sta::fuzzyGreaterEqual;
+using sta::delayInf;
+using sta::stringPrint;
+using sta::Unit;
+using sta::ArcDelayCalc;
+using sta::Corners;
+using sta::InputDrive;
+
+extern "C" {
+extern int Rsz_Init(Tcl_Interp *interp);
+}
+
+Resizer::Resizer() :
+ StaState(),
+ wire_signal_res_(0.0),
+ wire_signal_cap_(0.0),
+ wire_clk_res_(0.0),
+ wire_clk_cap_(0.0),
+ max_area_(0.0),
+ openroad_(nullptr),
+ logger_(nullptr),
+ gui_(nullptr),
+ sta_(nullptr),
+ db_network_(nullptr),
+ db_(nullptr),
+ block_(nullptr),
+ core_exists_(false),
+ parasitics_src_(ParasiticsSrc::none),
+ design_area_(0.0),
+ max_(MinMax::max()),
+ buffer_lowest_drive_(nullptr),
+ buffer_med_drive_(nullptr),
+ buffer_highest_drive_(nullptr),
+ target_load_map_(nullptr),
+ level_drvr_vertices_valid_(false),
+ tgt_slews_{0.0, 0.0},
+ tgt_slew_corner_(nullptr),
+ tgt_slew_dcalc_ap_(nullptr),
+ unique_net_index_(1),
+ unique_inst_index_(1),
+ resize_count_(0),
+ inserted_buffer_count_(0),
+ max_wire_length_(0),
+ steiner_renderer_(nullptr),
+ rebuffer_net_count_(0)
+{
+}
+
+void
+Resizer::init(OpenRoad *openroad,
+ Tcl_Interp *interp,
+ Logger *logger,
+ Gui *gui,
+ dbDatabase *db,
+ dbSta *sta,
+ SteinerTreeBuilder *stt_builder,
+ GlobalRouter *global_router)
+{
+ openroad_ = openroad;
+ logger_ = logger;
+ gui_ = gui;
+ db_ = db;
+ block_ = nullptr;
+ sta_ = sta;
+ stt_builder_ = stt_builder;
+ global_router_ = global_router;
+ incr_groute_ = nullptr;
+ db_network_ = sta->getDbNetwork();
+ copyState(sta);
+ // Define swig TCL commands.
+ Rsz_Init(interp);
+ // Eval encoded sta TCL sources.
+ evalTclInit(interp, sta::rsz_tcl_inits);
+}
+
+////////////////////////////////////////////////////////////////
+
+double
+Resizer::coreArea() const
+{
+ return dbuToMeters(core_.dx()) * dbuToMeters(core_.dy());
+}
+
+double
+Resizer::utilization()
+{
+ ensureBlock();
+ ensureDesignArea();
+ double core_area = coreArea();
+ if (core_area > 0.0)
+ return design_area_ / core_area;
+ else
+ return 1.0;
+}
+
+double
+Resizer::maxArea() const
+{
+ return max_area_;
+}
+
+////////////////////////////////////////////////////////////////
+
+class VertexLevelLess
+{
+public:
+ VertexLevelLess(const Network *network);
+ bool operator()(const Vertex *vertex1,
+ const Vertex *vertex2) const;
+
+protected:
+ const Network *network_;
+};
+
+VertexLevelLess::VertexLevelLess(const Network *network) :
+ network_(network)
+{
+}
+
+bool
+VertexLevelLess::operator()(const Vertex *vertex1,
+ const Vertex *vertex2) const
+{
+ Level level1 = vertex1->level();
+ Level level2 = vertex2->level();
+ return (level1 < level2)
+ || (level1 == level2
+ // Break ties for stable results.
+ && stringLess(network_->pathName(vertex1->pin()),
+ network_->pathName(vertex2->pin())));
+}
+
+
+////////////////////////////////////////////////////////////////
+
+// block_ indicates core_, design_area_, db_network_ etc valid.
+void
+Resizer::ensureBlock()
+{
+ // block_ indicates core_, design_area_
+ if (block_ == nullptr) {
+ block_ = db_->getChip()->getBlock();
+ block_->getCoreArea(core_);
+ core_exists_ = !(core_.xMin() == 0
+ && core_.xMax() == 0
+ && core_.yMin() == 0
+ && core_.yMax() == 0);
+ }
+}
+
+void
+Resizer::init()
+{
+ // Abbreviated copyState
+ db_network_ = sta_->getDbNetwork();
+ sta_->ensureLevelized();
+ graph_ = sta_->graph();
+ ensureBlock();
+ ensureDesignArea();
+ ensureLevelDrvrVertices();
+ sta_->ensureClkNetwork();
+}
+
+void
+Resizer::removeBuffers()
+{
+ ensureBlock();
+ db_network_ = sta_->getDbNetwork();
+ // Disable incremental timing.
+ graph_delay_calc_->delaysInvalid();
+ search_->arrivalsInvalid();
+
+ int remove_count = 0;
+ for (dbInst *inst : block_->getInsts()) {
+ LibertyCell *lib_cell = db_network_->libertyCell(inst);
+ if (lib_cell && lib_cell->isBuffer()) {
+ Instance *buffer = db_network_->dbToSta(inst);
+ // Do not remove buffers connected to input/output ports
+ // because verilog netlists use the net name for the port.
+ if (!bufferBetweenPorts(buffer)) {
+ removeBuffer(buffer);
+ remove_count++;
+ }
+ }
+ }
+ level_drvr_vertices_valid_ = false;
+ logger_->info(RSZ, 26, "Removed {} buffers.", remove_count);
+}
+
+bool
+Resizer::bufferBetweenPorts(Instance *buffer)
+{
+ LibertyCell *lib_cell = network_->libertyCell(buffer);
+ LibertyPort *in_port, *out_port;
+ lib_cell->bufferPorts(in_port, out_port);
+ Pin *in_pin = db_network_->findPin(buffer, in_port);
+ Pin *out_pin = db_network_->findPin(buffer, out_port);
+ Net *in_net = db_network_->net(in_pin);
+ Net *out_net = db_network_->net(out_pin);
+ bool in_net_ports = hasPort(in_net);
+ bool out_net_ports = hasPort(out_net);
+ return in_net_ports && out_net_ports;
+}
+
+void
+Resizer::removeBuffer(Instance *buffer)
+{
+ LibertyCell *lib_cell = network_->libertyCell(buffer);
+ LibertyPort *in_port, *out_port;
+ lib_cell->bufferPorts(in_port, out_port);
+ Pin *in_pin = db_network_->findPin(buffer, in_port);
+ Pin *out_pin = db_network_->findPin(buffer, out_port);
+ Net *in_net = db_network_->net(in_pin);
+ Net *out_net = db_network_->net(out_pin);
+ bool out_net_ports = hasPort(out_net);
+ Net *survivor, *removed;
+ if (out_net_ports) {
+ survivor = out_net;
+ removed = in_net;
+ }
+ else {
+ // default or out_net_ports
+ // Default to in_net surviving so drivers (cached in dbNetwork)
+ // do not change.
+ survivor = in_net;
+ removed = out_net;
+ }
+
+ if (!sdc_->isConstrained(in_pin)
+ && !sdc_->isConstrained(out_pin)
+ && !sdc_->isConstrained(removed)
+ && !sdc_->isConstrained(buffer)) {
+ sta_->disconnectPin(in_pin);
+ sta_->disconnectPin(out_pin);
+ sta_->deleteInstance(buffer);
+
+ NetPinIterator *pin_iter = db_network_->pinIterator(removed);
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ Instance *pin_inst = db_network_->instance(pin);
+ if (pin_inst != buffer) {
+ Port *pin_port = db_network_->port(pin);
+ sta_->disconnectPin(pin);
+ sta_->connectPin(pin_inst, pin_port, survivor);
+ }
+ }
+ delete pin_iter;
+ sta_->deleteNet(removed);
+ parasitics_invalid_.erase(removed);
+ }
+}
+
+void
+Resizer::ensureLevelDrvrVertices()
+{
+ if (!level_drvr_vertices_valid_) {
+ level_drvr_vertices_.clear();
+ VertexIterator vertex_iter(graph_);
+ while (vertex_iter.hasNext()) {
+ Vertex *vertex = vertex_iter.next();
+ if (vertex->isDriver(network_))
+ level_drvr_vertices_.push_back(vertex);
+ }
+ sort(level_drvr_vertices_, VertexLevelLess(network_));
+ level_drvr_vertices_valid_ = true;
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+Resizer::resizePreamble()
+{
+ init();
+ makeEquivCells();
+ findBuffers();
+ findTargetLoads();
+}
+
+static float
+bufferDrive(const LibertyCell *buffer)
+{
+ LibertyPort *input, *output;
+ buffer->bufferPorts(input, output);
+ return output->driveResistance();
+}
+
+void
+Resizer::findBuffers()
+{
+ LibertyLibraryIterator *lib_iter = network_->libertyLibraryIterator();
+ while (lib_iter->hasNext()) {
+ LibertyLibrary *lib = lib_iter->next();
+ for (LibertyCell *buffer : *lib->buffers()) {
+ if (!dontUse(buffer)
+ && isLinkCell(buffer)) {
+ buffer_cells_.push_back(buffer);
+ }
+ }
+ }
+ delete lib_iter;
+
+ if (buffer_cells_.empty())
+ logger_->error(RSZ, 22, "no buffers found.");
+
+ sort(buffer_cells_, [] (const LibertyCell *buffer1,
+ const LibertyCell *buffer2) {
+ return bufferDrive(buffer1) > bufferDrive(buffer2);
+ });
+ buffer_lowest_drive_ = buffer_cells_[0];
+ buffer_med_drive_ = buffer_cells_[buffer_cells_.size() / 2];
+ buffer_highest_drive_ = buffer_cells_[buffer_cells_.size() - 1];
+}
+
+bool
+Resizer::isLinkCell(LibertyCell *cell)
+{
+ return network_->findLibertyCell(cell->name()) == cell;
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+Resizer::bufferInputs()
+{
+ init();
+ inserted_buffer_count_ = 0;
+ incrementalParasiticsBegin();
+ InstancePinIterator *port_iter = network_->pinIterator(network_->topInstance());
+ while (port_iter->hasNext()) {
+ Pin *pin = port_iter->next();
+ Vertex *vertex = graph_->pinDrvrVertex(pin);
+ Net *net = network_->net(network_->term(pin));
+ if (network_->direction(pin)->isInput()
+ && !vertex->isConstant()
+ && !sta_->isClock(pin)
+ // Hands off special nets.
+ && !db_network_->isSpecial(net)
+ && hasPins(net))
+ // repair_design will resize to target slew.
+ bufferInput(pin, buffer_lowest_drive_);
+ }
+ delete port_iter;
+ updateParasitics();
+ incrementalParasiticsEnd();
+
+ if (inserted_buffer_count_ > 0) {
+ logger_->info(RSZ, 27, "Inserted {} input buffers.", inserted_buffer_count_);
+ level_drvr_vertices_valid_ = false;
+ }
+}
+
+bool
+Resizer::hasPins(Net *net)
+{
+ NetPinIterator *pin_iter = db_network_->pinIterator(net);
+ bool has_pins = pin_iter->hasNext();
+ delete pin_iter;
+ return has_pins;
+}
+
+Instance *
+Resizer::bufferInput(const Pin *top_pin,
+ LibertyCell *buffer_cell)
+{
+ Term *term = db_network_->term(top_pin);
+ Net *input_net = db_network_->net(term);
+ LibertyPort *input, *output;
+ buffer_cell->bufferPorts(input, output);
+ string buffer_name = makeUniqueInstName("input");
+ Instance *parent = db_network_->topInstance();
+ Net *buffer_out = makeUniqueNet();
+ Instance *buffer = db_network_->makeInstance(buffer_cell,
+ buffer_name.c_str(),
+ parent);
+ if (buffer) {
+ journalMakeBuffer(buffer);
+ Point pin_loc = db_network_->location(top_pin);
+ Point buf_loc = core_exists_ ? closestPtInRect(core_, pin_loc) : pin_loc;
+ setLocation(buffer, buf_loc);
+ designAreaIncr(area(db_network_->cell(buffer_cell)));
+ inserted_buffer_count_++;
+
+ NetPinIterator *pin_iter = db_network_->pinIterator(input_net);
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ // Leave input port pin connected to input_net.
+ if (pin != top_pin) {
+ sta_->disconnectPin(pin);
+ Port *pin_port = db_network_->port(pin);
+ sta_->connectPin(db_network_->instance(pin), pin_port, buffer_out);
+ }
+ }
+ delete pin_iter;
+ sta_->connectPin(buffer, input, input_net);
+ sta_->connectPin(buffer, output, buffer_out);
+
+ parasiticsInvalid(input_net);
+ parasiticsInvalid(buffer_out);
+ }
+ return buffer;
+}
+
+void
+Resizer::setLocation(Instance *inst,
+ Point pt)
+{
+ int x = pt.getX();
+ int y = pt.getY();
+ // Stay inside the lines.
+ if (core_exists_) {
+ Point in_core = closestPtInRect(core_, x, y);
+ x = in_core.getX();
+ y = in_core.getY();
+ }
+
+ dbInst *dinst = db_network_->staToDb(inst);
+ dinst->setPlacementStatus(dbPlacementStatus::PLACED);
+ dinst->setLocation(x, y);
+}
+
+void
+Resizer::bufferOutputs()
+{
+ init();
+ inserted_buffer_count_ = 0;
+ incrementalParasiticsBegin();
+ InstancePinIterator *port_iter = network_->pinIterator(network_->topInstance());
+ while (port_iter->hasNext()) {
+ Pin *pin = port_iter->next();
+ Vertex *vertex = graph_->pinLoadVertex(pin);
+ Net *net = network_->net(network_->term(pin));
+ if (network_->direction(pin)->isOutput()
+ && net
+ // DEF does not have tristate output types so we have look at the drivers.
+ && !hasTristateDriver(net)
+ && !vertex->isConstant()
+ // Hands off special nets.
+ && !db_network_->isSpecial(net)
+ && hasPins(net))
+ bufferOutput(pin, buffer_lowest_drive_);
+ }
+ delete port_iter;
+ updateParasitics();
+ incrementalParasiticsEnd();
+
+ if (inserted_buffer_count_ > 0) {
+ logger_->info(RSZ, 28, "Inserted {} output buffers.", inserted_buffer_count_);
+ level_drvr_vertices_valid_ = false;
+ }
+}
+
+bool
+Resizer::hasTristateDriver(const Net *net)
+{
+ PinSet *drivers = network_->drivers(net);
+ if (drivers) {
+ for (Pin *pin : *drivers) {
+ if (isTristateDriver(pin))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+Resizer::isTristateDriver(const Pin *pin)
+{
+ // Note LEF macro PINs do not have a clue about tristates.
+ LibertyPort *port = network_->libertyPort(pin);
+ return port && port->direction()->isAnyTristate();
+}
+
+void
+Resizer::bufferOutput(Pin *top_pin,
+ LibertyCell *buffer_cell)
+{
+ NetworkEdit *network = networkEdit();
+ Term *term = network_->term(top_pin);
+ Net *output_net = network_->net(term);
+ LibertyPort *input, *output;
+ buffer_cell->bufferPorts(input, output);
+ string buffer_name = makeUniqueInstName("output");
+ Instance *parent = network->topInstance();
+ Net *buffer_in = makeUniqueNet();
+ Instance *buffer = network->makeInstance(buffer_cell,
+ buffer_name.c_str(),
+ parent);
+ if (buffer) {
+ journalMakeBuffer(buffer);
+ setLocation(buffer, db_network_->location(top_pin));
+ designAreaIncr(area(db_network_->cell(buffer_cell)));
+ inserted_buffer_count_++;
+
+ NetPinIterator *pin_iter = network->pinIterator(output_net);
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ if (pin != top_pin) {
+ // Leave output port pin connected to output_net.
+ sta_->disconnectPin(pin);
+ Port *pin_port = network->port(pin);
+ sta_->connectPin(network->instance(pin), pin_port, buffer_in);
+ }
+ }
+ delete pin_iter;
+ sta_->connectPin(buffer, input, buffer_in);
+ sta_->connectPin(buffer, output, output_net);
+
+ parasiticsInvalid(buffer_in);
+ parasiticsInvalid(output_net);
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+// Repair long wires, max slew, max capacitance, max fanout violations
+// The whole enchilada.
+// max_wire_length zero for none (meters)
+void
+Resizer::repairDesign(double max_wire_length,
+ double slew_margin,
+ double max_cap_margin)
+{
+ int repair_count, slew_violations, cap_violations;
+ int fanout_violations, length_violations;
+ repairDesign(max_wire_length, slew_margin, max_cap_margin,
+ repair_count, slew_violations, cap_violations,
+ fanout_violations, length_violations);
+
+ if (slew_violations > 0)
+ logger_->info(RSZ, 34, "Found {} slew violations.", slew_violations);
+ if (fanout_violations > 0)
+ logger_->info(RSZ, 35, "Found {} fanout violations.", fanout_violations);
+ if (cap_violations > 0)
+ logger_->info(RSZ, 36, "Found {} capacitance violations.", cap_violations);
+ if (length_violations > 0)
+ logger_->info(RSZ, 37, "Found {} long wires.", length_violations);
+ if (inserted_buffer_count_ > 0)
+ logger_->info(RSZ, 38, "Inserted {} buffers in {} nets.",
+ inserted_buffer_count_,
+ repair_count);
+ if (resize_count_ > 0)
+ logger_->info(RSZ, 39, "Resized {} instances.", resize_count_);
+}
+
+void
+Resizer::repairDesign(double max_wire_length, // zero for none (meters)
+ double slew_margin,
+ double max_cap_margin,
+ int &repair_count,
+ int &slew_violations,
+ int &cap_violations,
+ int &fanout_violations,
+ int &length_violations)
+{
+ repair_count = 0;
+ slew_violations = 0;
+ cap_violations = 0;
+ fanout_violations = 0;
+ length_violations = 0;
+ inserted_buffer_count_ = 0;
+ resize_count_ = 0;
+
+ sta_->checkSlewLimitPreamble();
+ sta_->checkCapacitanceLimitPreamble();
+ sta_->checkFanoutLimitPreamble();
+
+ incrementalParasiticsBegin();
+ int max_length = metersToDbu(max_wire_length);
+ for (int i = level_drvr_vertices_.size() - 1; i >= 0; i--) {
+ Vertex *drvr = level_drvr_vertices_[i];
+ Pin *drvr_pin = drvr->pin();
+ Net *net = network_->isTopLevelPort(drvr_pin)
+ ? network_->net(network_->term(drvr_pin))
+ : network_->net(drvr_pin);
+ if (net
+ && !sta_->isClock(drvr_pin)
+ // Exclude tie hi/low cells and supply nets.
+ && !drvr->isConstant())
+ repairNet(net, drvr_pin, drvr, slew_margin, max_cap_margin,
+ true, true, true, max_length, true,
+ repair_count, slew_violations, cap_violations,
+ fanout_violations, length_violations);
+ }
+ updateParasitics();
+ incrementalParasiticsEnd();
+
+ if (inserted_buffer_count_ > 0)
+ level_drvr_vertices_valid_ = false;
+}
+
+// repairDesign but restricted to clock network and
+// no max_fanout/max_cap checks.
+void
+Resizer::repairClkNets(double max_wire_length) // max_wire_length zero for none (meters)
+{
+ init();
+ // Need slews to resize inserted buffers.
+ sta_->findDelays();
+
+ inserted_buffer_count_ = 0;
+ resize_count_ = 0;
+
+ int repair_count = 0;
+ int slew_violations = 0;
+ int cap_violations = 0;
+ int fanout_violations = 0;
+ int length_violations = 0;
+ int max_length = metersToDbu(max_wire_length);
+ incrementalParasiticsBegin();
+ for (Clock *clk : sdc_->clks()) {
+ for (const Pin *clk_pin : *sta_->pins(clk)) {
+ Net *net = network_->isTopLevelPort(clk_pin)
+ ? network_->net(network_->term(clk_pin))
+ : network_->net(clk_pin);
+ if (network_->isDriver(clk_pin)) {
+ Vertex *drvr = graph_->pinDrvrVertex(clk_pin);
+ // Do not resize clock tree gates.
+ repairNet(net, clk_pin, drvr, 0.0, 0.0,
+ false, false, false, max_length, false,
+ repair_count, slew_violations, cap_violations,
+ fanout_violations, length_violations);
+ }
+ }
+ }
+ updateParasitics();
+ incrementalParasiticsEnd();
+
+ if (length_violations > 0)
+ logger_->info(RSZ, 47, "Found {} long wires.", length_violations);
+ if (inserted_buffer_count_ > 0) {
+ logger_->info(RSZ, 48, "Inserted {} buffers in {} nets.",
+ inserted_buffer_count_,
+ repair_count);
+ level_drvr_vertices_valid_ = false;
+ }
+}
+
+// Repair one net (for debugging)
+void
+Resizer::repairNet(Net *net,
+ double max_wire_length, // meters
+ double slew_margin,
+ double max_cap_margin)
+{
+ init();
+
+ sta_->checkSlewLimitPreamble();
+ sta_->checkCapacitanceLimitPreamble();
+ sta_->checkFanoutLimitPreamble();
+
+ inserted_buffer_count_ = 0;
+ resize_count_ = 0;
+ resized_multi_output_insts_.clear();
+ int repair_count = 0;
+ int slew_violations = 0;
+ int cap_violations = 0;
+ int fanout_violations = 0;
+ int length_violations = 0;
+ int max_length = metersToDbu(max_wire_length);
+ PinSet *drivers = network_->drivers(net);
+ if (drivers && !drivers->empty()) {
+ PinSet::Iterator drvr_iter(drivers);
+ Pin *drvr_pin = drvr_iter.next();
+ Vertex *drvr = graph_->pinDrvrVertex(drvr_pin);
+ repairNet(net, drvr_pin, drvr, slew_margin, max_cap_margin,
+ true, true, true, max_length, true,
+ repair_count, slew_violations, cap_violations,
+ fanout_violations, length_violations);
+ }
+
+ if (slew_violations > 0)
+ logger_->info(RSZ, 51, "Found {} slew violations.", slew_violations);
+ if (fanout_violations > 0)
+ logger_->info(RSZ, 52, "Found {} fanout violations.", fanout_violations);
+ if (cap_violations > 0)
+ logger_->info(RSZ, 53, "Found {} capacitance violations.", cap_violations);
+ if (length_violations > 0)
+ logger_->info(RSZ, 54, "Found {} long wires.", length_violations);
+ if (inserted_buffer_count_ > 0) {
+ logger_->info(RSZ, 55, "Inserted {} buffers in {} nets.",
+ inserted_buffer_count_,
+ repair_count);
+ level_drvr_vertices_valid_ = false;
+ }
+ if (resize_count_ > 0)
+ logger_->info(RSZ, 56, "Resized {} instances.", resize_count_);
+ if (resize_count_ > 0)
+ logger_->info(RSZ, 57, "Resized {} instances.", resize_count_);
+}
+
+void
+Resizer::repairNet(Net *net,
+ const Pin *drvr_pin,
+ Vertex *drvr,
+ double slew_margin,
+ double max_cap_margin,
+ bool check_slew,
+ bool check_cap,
+ bool check_fanout,
+ int max_length, // dbu
+ bool resize_drvr,
+ int &repair_count,
+ int &slew_violations,
+ int &cap_violations,
+ int &fanout_violations,
+ int &length_violations)
+{
+ // Hands off special nets.
+ if (!db_network_->isSpecial(net)) {
+ SteinerTree *tree = makeSteinerTree(drvr_pin, true, max_steiner_pin_count_,
+ stt_builder_, db_network_, logger_);
+ if (tree) {
+ debugPrint(logger_, RSZ, "repair_net", 1, "repair net {}",
+ sdc_network_->pathName(drvr_pin));
+ // Resize the driver to normalize slews before repairing limit violations.
+ if (resize_drvr)
+ resizeToTargetSlew(drvr_pin, true);
+ // For tristate nets all we can do is resize the driver.
+ if (!isTristateDriver(drvr_pin)) {
+ ensureWireParasitic(drvr_pin, net);
+ graph_delay_calc_->findDelays(drvr);
+
+ float max_load_slew = INF;
+ float max_cap = INF;
+ float max_fanout = INF;
+ bool repair_slew = false;
+ bool repair_cap = false;
+ bool repair_fanout = false;
+ bool repair_wire = false;
+ const Corner *corner = sta_->cmdCorner();
+ if (check_cap) {
+ float cap1, max_cap1, cap_slack1;
+ const Corner *corner1;
+ const RiseFall *tr1;
+ sta_->checkCapacitance(drvr_pin, nullptr, max_,
+ corner1, tr1, cap1, max_cap1, cap_slack1);
+ if (max_cap1 > 0.0 && corner1) {
+ max_cap1 *= (1.0 - max_cap_margin / 100.0);
+ max_cap = max_cap1;
+ if (cap1 > max_cap1) {
+ corner = corner1;
+ cap_violations++;
+ repair_cap = true;
+ }
+ }
+ }
+ if (check_fanout) {
+ float fanout, fanout_slack;
+ sta_->checkFanout(drvr_pin, max_,
+ fanout, max_fanout, fanout_slack);
+ if (max_fanout > 0.0 && fanout_slack < 0.0) {
+ fanout_violations++;
+ repair_fanout = true;
+ }
+ }
+ int wire_length = findMaxSteinerDist(tree);
+ if (max_length
+ && wire_length > max_length) {
+ length_violations++;
+ repair_wire = true;
+ }
+ if (check_slew) {
+ float slew1, slew_slack1, max_slew1;
+ const Corner *corner1;
+ // Check slew at the driver.
+ checkSlew(drvr_pin, slew_margin, slew1, max_slew1, slew_slack1, corner1);
+ // Max slew violations at the driver pin are repaired by reducing the
+ // load capacitance. Wire resistance may shield capacitance from the
+ // driver but so this is conservative.
+ // Find max load cap that corresponds to max_slew.
+ LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
+ if (corner1
+ && max_slew1 > 0.0 && drvr_port) {
+ float max_cap1 = findSlewLoadCap(drvr_port, max_slew1, corner1);
+ max_cap = min(max_cap, max_cap1);
+ corner = corner1;
+ debugPrint(logger_, RSZ, "repair_net", 2, "drvr_slew={} max_slew={} max_cap={} corner={}",
+ delayAsString(slew1, this, 3),
+ delayAsString(max_slew1, this, 3),
+ units_->capacitanceUnit()->asString(max_cap1, 3),
+ corner1->name());
+ if (slew_slack1 < 0.0)
+ repair_slew = true;
+ }
+ if (slew_slack1 < 0.0)
+ slew_violations++;
+
+ // Check slew at the loads.
+ // Note that many liberty libraries do not have max_transition attributes on
+ // input pins.
+ // Max slew violations at the load pins are repaired by reducing the
+ // wire length.
+ checkLoadSlews(drvr_pin, slew_margin, slew1, max_slew1, slew_slack1, corner1);
+ // Even when there are no load violations we need max_load_slew for
+ // sizing inserted buffers.
+ if (max_slew1 > 0.0) {
+ max_load_slew = max_slew1;
+ debugPrint(logger_, RSZ, "repair_net", 2, "load_slew={} max_load_slew={}",
+ delayAsString(slew1, this, 3),
+ delayAsString(max_load_slew, this, 3));
+ if (slew_slack1 < 0.0) {
+ // Don't double count violations on the same net.
+ if (!repair_slew)
+ slew_violations++;
+ corner = corner1;
+ repair_slew = true;
+ }
+ }
+ }
+
+ if (repair_slew
+ || repair_cap
+ || repair_fanout
+ || repair_wire) {
+ Point drvr_loc = db_network_->location(drvr->pin());
+ debugPrint(logger_, RSZ, "repair_net", 1, "driver {} ({} {}) l={}",
+ sdc_network_->pathName(drvr_pin),
+ units_->distanceUnit()->asString(dbuToMeters(drvr_loc.getX()), 1),
+ units_->distanceUnit()->asString(dbuToMeters(drvr_loc.getY()), 1),
+ units_->distanceUnit()->asString(dbuToMeters(wire_length), 1));
+ SteinerPt drvr_pt = tree->drvrPt(network_);
+ int wire_length;
+ float pin_cap, fanout;
+ PinSeq load_pins;
+ if (drvr_pt != SteinerTree::null_pt)
+ repairNet(tree, drvr_pt, SteinerTree::null_pt, net, drvr_pin,
+ max_load_slew, max_cap, max_fanout, max_length, corner, 0,
+ wire_length, pin_cap, fanout, load_pins);
+ repair_count++;
+
+ if (resize_drvr)
+ resizeToTargetSlew(drvr_pin, true);
+ }
+ }
+ delete tree;
+ }
+ }
+}
+
+bool
+Resizer::checkLimits(const Pin *drvr_pin,
+ double slew_margin,
+ double max_cap_margin,
+ bool check_slew,
+ bool check_cap,
+ bool check_fanout)
+{
+ if (check_cap) {
+ float cap1, max_cap1, cap_slack1;
+ const Corner *corner1;
+ const RiseFall *tr1;
+ sta_->checkCapacitance(drvr_pin, nullptr, max_,
+ corner1, tr1, cap1, max_cap1, cap_slack1);
+ max_cap1 *= (1.0 - max_cap_margin / 100.0);
+ if (cap1 < max_cap1)
+ return true;
+ }
+ if (check_fanout) {
+ float fanout, fanout_slack, max_fanout;
+ sta_->checkFanout(drvr_pin, max_,
+ fanout, max_fanout, fanout_slack);
+ if (fanout_slack < 0.0)
+ return true;
+
+ }
+ if (check_slew) {
+ float slew1, slew_slack1, max_slew1;
+ const Corner *corner1;
+ checkSlew(drvr_pin, slew_margin, slew1, max_slew1, slew_slack1, corner1);
+ if (slew_slack1 < 0.0)
+ return true;
+ checkLoadSlews(drvr_pin, slew_margin, slew1, max_slew1, slew_slack1, corner1);
+ if (slew_slack1 < 0.0)
+ return true;
+ }
+ return false;
+}
+
+void
+Resizer::checkSlew(const Pin *drvr_pin,
+ double slew_margin,
+ // Return values.
+ Slew &slew,
+ float &limit,
+ float &slack,
+ const Corner *&corner)
+{
+ slack = INF;
+ limit = INF;
+ corner = nullptr;
+
+ const Corner *corner1;
+ const RiseFall *tr1;
+ Slew slew1;
+ float limit1, slack1;
+ sta_->checkSlew(drvr_pin, nullptr, max_, false,
+ corner1, tr1, slew1, limit1, slack1);
+ if (corner1) {
+ limit1 *= (1.0 - slew_margin / 100.0);
+ slack1 = limit1 - slew1;
+ if (slack1 < slack) {
+ slew = slew1;
+ limit = limit1;
+ slack = slack1;
+ corner = corner1;
+ }
+ }
+}
+
+void
+Resizer::checkLoadSlews(const Pin *drvr_pin,
+ double slew_margin,
+ // Return values.
+ Slew &slew,
+ float &limit,
+ float &slack,
+ const Corner *&corner)
+{
+ slack = INF;
+ limit = INF;
+ PinConnectedPinIterator *pin_iter = network_->connectedPinIterator(drvr_pin);
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ if (pin != drvr_pin) {
+ const Corner *corner1;
+ const RiseFall *tr1;
+ Slew slew1;
+ float limit1, slack1;
+ sta_->checkSlew(pin, nullptr, max_, false,
+ corner1, tr1, slew1, limit1, slack1);
+ if (corner1) {
+ limit1 *= (1.0 - slew_margin / 100.0);
+ limit = min(limit, limit1);
+ slack1 = limit1 - slew1;
+ if (slack1 < slack) {
+ slew = slew1;
+ slack = slack1;
+ corner = corner1;
+ }
+ }
+ }
+ }
+ delete pin_iter;
+}
+
+// Find the output port load capacitance that results in slew.
+double
+Resizer::findSlewLoadCap(LibertyPort *drvr_port,
+ double slew,
+ const Corner *corner)
+{
+ const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(max_);
+ // cap1 lower bound
+ // cap2 upper bound
+ double cap1 = 0.0;
+ double cap2 = slew / drvr_port->driveResistance() * 2;
+ double tol = .01; // 1%
+ double diff1 = gateSlewDiff(drvr_port, cap2, slew, dcalc_ap);
+ // binary search for diff = 0.
+ while (abs(cap1 - cap2) > max(cap1, cap2) * tol) {
+ if (diff1 < 0.0) {
+ cap1 = cap2;
+ cap2 *= 2;
+ diff1 = gateSlewDiff(drvr_port, cap2, slew, dcalc_ap);
+ }
+ else {
+ double cap3 = (cap1 + cap2) / 2.0;
+ double diff2 = gateSlewDiff(drvr_port, cap3, slew, dcalc_ap);
+ if (diff2 < 0.0) {
+ cap1 = cap3;
+ }
+ else {
+ cap2 = cap3;
+ diff1 = diff2;
+ }
+ }
+ }
+ return cap1;
+}
+
+// objective function
+double
+Resizer::gateSlewDiff(LibertyPort *drvr_port,
+ double load_cap,
+ double slew,
+ const DcalcAnalysisPt *dcalc_ap)
+{
+ ArcDelay delays[RiseFall::index_count];
+ Slew slews[RiseFall::index_count];
+ gateDelays(drvr_port, load_cap, dcalc_ap, delays, slews);
+ Slew gate_slew = max(slews[RiseFall::riseIndex()], slews[RiseFall::fallIndex()]);
+ return gate_slew - slew;
+}
+
+void
+Resizer::repairNet(SteinerTree *tree,
+ SteinerPt pt,
+ SteinerPt prev_pt,
+ Net *net,
+ const Pin *drvr_pin,
+ float max_load_slew,
+ float max_cap,
+ float max_fanout,
+ int max_length, // dbu
+ const Corner *corner,
+ int level,
+ // Return values.
+ // Remaining parasiics after repeater insertion.
+ int &wire_length, // dbu
+ float &pin_cap,
+ float &fanout,
+ PinSeq &load_pins)
+{
+ Point pt_loc = tree->location(pt);
+ int pt_x = pt_loc.getX();
+ int pt_y = pt_loc.getY();
+ debugPrint(logger_, RSZ, "repair_net", 2, "{:{}s}pt ({} {})",
+ "", level,
+ units_->distanceUnit()->asString(dbuToMeters(pt_x), 1),
+ units_->distanceUnit()->asString(dbuToMeters(pt_y), 1));
+ double wire_cap = wireSignalCapacitance(corner);
+ double wire_res = wireSignalResistance(corner);
+ SteinerPt left = tree->left(pt);
+ int wire_length_left = 0;
+ float pin_cap_left = 0.0;
+ float fanout_left = 0.0;
+ PinSeq loads_left;
+ if (left != SteinerTree::null_pt)
+ repairNet(tree, left, pt, net, drvr_pin, max_load_slew, max_cap, max_fanout, max_length,
+ corner, level+1,
+ wire_length_left, pin_cap_left, fanout_left, loads_left);
+ SteinerPt right = tree->right(pt);
+ int wire_length_right = 0;
+ float pin_cap_right = 0.0;
+ float fanout_right = 0.0;
+ PinSeq loads_right;
+ if (right != SteinerTree::null_pt)
+ repairNet(tree, right, pt, net, drvr_pin, max_load_slew, max_cap, max_fanout, max_length,
+ corner, level+1,
+ wire_length_right, pin_cap_right, fanout_right, loads_right);
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}left l={} pin_cap={} fanout={}, right l={} pin_cap={} fanout={}",
+ "", level,
+ units_->distanceUnit()->asString(dbuToMeters(wire_length_left), 1),
+ units_->capacitanceUnit()->asString(pin_cap_left, 3),
+ fanout_left,
+ units_->distanceUnit()->asString(dbuToMeters(wire_length_right), 1),
+ units_->capacitanceUnit()->asString(pin_cap_right, 3),
+ fanout_right);
+ // Add a buffer to left or right branch to stay under the max cap/length/fanout.
+ bool repeater_left = false;
+ bool repeater_right = false;
+ double cap_left = pin_cap_left + dbuToMeters(wire_length_left) * wire_cap;
+ double cap_right = pin_cap_right + dbuToMeters(wire_length_right) * wire_cap;
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}cap_left={}, right_cap={}",
+ "", level,
+ units_->capacitanceUnit()->asString(cap_left, 3),
+ units_->capacitanceUnit()->asString(cap_right, 3));
+
+ double wire_length1 = dbuToMeters(wire_length_left + wire_length_right);
+ float load_cap = cap_left + cap_right;
+
+ LibertyCell *buffer_cell = findTargetCell(buffer_lowest_drive_, load_cap, false);
+ LibertyPort *input, *buffer_output;
+ buffer_cell->bufferPorts(input, buffer_output);
+ float r_buffer = buffer_output->driveResistance();
+ float r_drv = driveResistance(drvr_pin);
+ float r_drvr = max(r_drv, r_buffer);
+ // Elmore factor for 20-80% slew thresholds.
+ float k_threshold = 1.39;
+ Slew load_slew = (r_drvr + wire_length1 * wire_res) * load_cap * k_threshold;
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}load_slew={} r_drvr={} r_buffer={}",
+ "", level,
+ delayAsString(load_slew, this, 3),
+ units_->resistanceUnit()->asString(r_drv, 3),
+ units_->resistanceUnit()->asString(r_buffer, 3));
+
+ bool slew_violation = load_slew > max_load_slew;
+ if (slew_violation) {
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}slew violation", "", level);
+ if (cap_left > cap_right)
+ repeater_left = true;
+ else
+ repeater_right = true;
+ }
+
+ bool cap_violation = (cap_left + cap_right) > max_cap;
+ if (cap_violation) {
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}cap violation", "", level);
+ if (cap_left > cap_right)
+ repeater_left = true;
+ else
+ repeater_right = true;
+ }
+ bool length_violation = max_length > 0
+ && (wire_length_left + wire_length_right) > max_length;
+ if (length_violation) {
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}length violation", "", level);
+ if (wire_length_left > wire_length_right)
+ repeater_left = true;
+ else
+ repeater_right = true;
+ }
+ bool fanout_violation = max_fanout > 0
+ // Note that if both fanout_left==max_fanout and fanout_right==max_fanout
+ // there is no way repair the violation (adding a buffer to either branch
+ // results in max_fanout+1, which is a violation).
+ // Leave room for one buffer on the other branch by using >= to avoid
+ // this situation.
+ && (fanout_left + fanout_right) >= max_fanout;
+ if (fanout_violation) {
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}fanout violation", "", level);
+ if (fanout_left > fanout_right)
+ repeater_left = true;
+ else
+ repeater_right = true;
+ }
+
+ if (repeater_left)
+ makeRepeater("left", tree, pt, buffer_cell, level,
+ wire_length_left, pin_cap_left, fanout_left, loads_left);
+ if (repeater_right)
+ makeRepeater("right", tree, pt, buffer_cell, level,
+ wire_length_right, pin_cap_right, fanout_right, loads_right);
+
+ // Update after left/right repeaters are inserted.
+ wire_length = wire_length_left + wire_length_right;
+ pin_cap = pin_cap_left + pin_cap_right;
+ fanout = fanout_left + fanout_right;
+
+ // Union left/right load pins.
+ for (Pin *load_pin : loads_left)
+ load_pins.push_back(load_pin);
+ for (Pin *load_pin : loads_right)
+ load_pins.push_back(load_pin);
+
+ // Steiner pt pin is the net driver if prev_pt is null.
+ if (prev_pt != SteinerTree::null_pt) {
+ const PinSeq *pt_pins = tree->pins(pt);
+ if (pt_pins) {
+ for (Pin *load_pin : *pt_pins) {
+ Point load_loc = db_network_->location(load_pin);
+ int load_dist = Point::manhattanDistance(load_loc, pt_loc);
+ debugPrint(logger_, RSZ, "repair_net", 2, "{:{}s}load {} ({} {}) dist={}",
+ "", level,
+ sdc_network_->pathName(load_pin),
+ units_->distanceUnit()->asString(dbuToMeters(load_loc.getX()), 1),
+ units_->distanceUnit()->asString(dbuToMeters(load_loc.getY()), 1),
+ units_->distanceUnit()->asString(dbuToMeters(load_dist), 1));
+ LibertyPort *load_port = network_->libertyPort(load_pin);
+ if (load_port) {
+ pin_cap += load_port->capacitance();
+ fanout += portFanoutLoad(load_port);
+ }
+ else
+ fanout += 1;
+ load_pins.push_back(load_pin);
+ }
+ }
+
+ Point prev_loc = tree->location(prev_pt);
+ int length = Point::manhattanDistance(prev_loc, pt_loc);
+ wire_length += length;
+ // Back up from pt to prev_pt adding repeaters every max_length.
+ int prev_x = prev_loc.getX();
+ int prev_y = prev_loc.getY();
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}wl={} l={}",
+ "", level,
+ units_->distanceUnit()->asString(dbuToMeters(wire_length), 1),
+ units_->distanceUnit()->asString(dbuToMeters(length), 1));
+ wire_length1 = dbuToMeters(wire_length);
+ load_cap = pin_cap + wire_length1 * wire_cap;
+
+ buffer_cell = findTargetCell(buffer_lowest_drive_, load_cap, false);
+ buffer_cell->bufferPorts(input, buffer_output);
+ r_buffer = buffer_output->driveResistance();
+ r_drv = driveResistance(drvr_pin);
+ r_drvr = max(r_drv, r_buffer);
+ load_slew = (r_drvr + wire_length1 * wire_res) * load_cap * k_threshold;
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}load_slew={} r_drvr={} r_buffer={}",
+ "", level,
+ delayAsString(load_slew, this, 3),
+ units_->resistanceUnit()->asString(r_drv, 3),
+ units_->resistanceUnit()->asString(r_buffer, 3));
+
+ while ((max_length > 0 && wire_length > max_length)
+ || (wire_cap > 0.0
+ // Cannot fix max cap violations from pin cap by shortening wire.
+ && pin_cap < max_cap
+ && load_cap > max_cap)
+ || load_slew > max_load_slew) {
+ // Make the wire a bit shorter than necessary to allow for
+ // offset from instance origin to pin and detailed placement movement.
+ double length_margin = .05;
+ int stub_length = std::numeric_limits<int>::max();
+ if (max_length > 0 && wire_length > max_length)
+ stub_length = min(stub_length, max_length);
+ if (wire_cap > 0.0
+ && pin_cap < max_cap
+ && load_cap > max_cap)
+ stub_length = min(stub_length, metersToDbu((max_cap - pin_cap) / wire_cap));
+ if (load_slew > max_load_slew) {
+ // Using elmore delay to approximate wire
+ // load_slew = (Rdrvr + L*Rwire) * (L*Cwire + Cpin) * k_threshold
+ // Setting this to max_slew is a quadratic in L
+ // L^2*Rwire*Cwire + L*(Rdrvr*Cwire + Rwire*Cpin) + Rdrvr*Cpin - max_slew/k_threshold
+ // Solve using quadradic eqn for L.
+ float a = wire_res * wire_cap;
+ float b = r_drvr * wire_cap + wire_res * pin_cap;
+ float c = r_drvr * pin_cap - max_load_slew / k_threshold;
+ float l = (-b + sqrt(b*b - 4 * a * c)) / (2 * a);
+ stub_length = min(stub_length, metersToDbu(l));
+ }
+
+ // Distance from pt to repeater backward toward prev_pt.
+ double buf_dist = length - (wire_length - stub_length * (1.0 - length_margin));
+ double dx = prev_x - pt_x;
+ double dy = prev_y - pt_y;
+ double d = (length == 0) ? 0.0 : buf_dist / length;
+ int buf_x = pt_x + d * dx;
+ int buf_y = pt_y + d * dy;
+ makeRepeater("wire", buf_x, buf_y, buffer_lowest_drive_, level,
+ wire_length, pin_cap, fanout, load_pins);
+ // Update for the next round.
+ length -= buf_dist;
+ wire_length = length;
+ pt_x = buf_x;
+ pt_y = buf_y;
+
+ wire_length1 = dbuToMeters(wire_length);
+ load_cap = pin_cap + wire_length1 * wire_cap;
+ load_slew = (r_drvr + wire_length1 * wire_res) * load_cap * k_threshold;
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}load_slew={}",
+ "", level,
+ delayAsString(load_slew, this, 3));
+ debugPrint(logger_, RSZ, "repair_net", 3, "{:{}s}wl={} l={}",
+ "", level,
+ units_->distanceUnit()->asString(dbuToMeters(wire_length), 1),
+ units_->distanceUnit()->asString(dbuToMeters(length), 1));
+ }
+ }
+}
+
+void
+Resizer::makeRepeater(const char *where,
+ SteinerTree *tree,
+ SteinerPt pt,
+ LibertyCell *buffer_cell,
+ int level,
+ int &wire_length,
+ float &pin_cap,
+ float &fanout,
+ PinSeq &load_pins)
+{
+ Point pt_loc = tree->location(pt);
+ makeRepeater(where, pt_loc.getX(), pt_loc.getY(), buffer_cell, level,
+ wire_length, pin_cap, fanout, load_pins);
+}
+
+void
+Resizer::makeRepeater(const char *where,
+ int x,
+ int y,
+ LibertyCell *buffer_cell,
+ int level,
+ int &wire_length,
+ float &pin_cap,
+ float &fanout,
+ PinSeq &load_pins)
+{
+ LibertyPort *buffer_input_port, *buffer_output_port;
+ buffer_cell->bufferPorts(buffer_input_port, buffer_output_port);
+
+ string buffer_name = makeUniqueInstName("repeater");
+ debugPrint(logger_, RSZ, "repair_net", 2, "{:{}s}{} {} ({} {})",
+ "", level,
+ where,
+ buffer_name.c_str(),
+ units_->distanceUnit()->asString(dbuToMeters(x), 1),
+ units_->distanceUnit()->asString(dbuToMeters(y), 1));
+
+ // Inserting a buffer is complicated by the fact that verilog netlists
+ // use the net name for input and output ports. This means the ports
+ // cannot be moved to a different net.
+
+ // This cannot depend on the net in caller because the buffer may be inserted
+ // between the driver and the loads changing the net as the repair works its
+ // way from the loads to the driver.
+
+ Net *net = nullptr, *in_net, *out_net;
+ bool have_output_port_load = false;
+ for (Pin *pin : load_pins) {
+ if (network_->isTopLevelPort(pin)) {
+ net = network_->net(network_->term(pin));
+ if (network_->direction(pin)->isAnyOutput()) {
+ have_output_port_load = true;
+ break;
+ }
+ }
+ else
+ net = network_->net(pin);
+ }
+ Instance *parent = db_network_->topInstance();
+
+ // If the net is driven by an input port,
+ // use the net as the repeater input net so the port stays connected to it.
+ if (hasInputPort(net)
+ || !have_output_port_load) {
+ in_net = net;
+ out_net = makeUniqueNet();
+ // Copy signal type to new net.
+ dbNet *out_net_db = db_network_->staToDb(out_net);
+ dbNet *in_net_db = db_network_->staToDb(in_net);
+ out_net_db->setSigType(in_net_db->getSigType());
+
+ // Move load pins to out_net.
+ for (Pin *pin : load_pins) {
+ Port *port = network_->port(pin);
+ Instance *inst = network_->instance(pin);
+ sta_->disconnectPin(pin);
+ sta_->connectPin(inst, port, out_net);
+ }
+ }
+ else {
+ // One of the loads is an output port.
+ // Use the net as the repeater output net so the port stays connected to it.
+ in_net = makeUniqueNet();
+ out_net = net;
+ // Copy signal type to new net.
+ dbNet *out_net_db = db_network_->staToDb(out_net);
+ dbNet *in_net_db = db_network_->staToDb(in_net);
+ in_net_db->setSigType(out_net_db->getSigType());
+
+ // Move non-repeater load pins to in_net.
+ PinSet load_pins1;
+ for (Pin *pin : load_pins)
+ load_pins1.insert(pin);
+
+ NetPinIterator *pin_iter = network_->pinIterator(out_net);
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ if (!load_pins1.hasKey(pin)) {
+ Port *port = network_->port(pin);
+ Instance *inst = network_->instance(pin);
+ sta_->disconnectPin(pin);
+ sta_->connectPin(inst, port, in_net);
+ }
+ }
+ }
+
+ Instance *buffer = db_network_->makeInstance(buffer_cell,
+ buffer_name.c_str(),
+ parent);
+ journalMakeBuffer(buffer);
+ Point buf_loc(x, y);
+ setLocation(buffer, buf_loc);
+ designAreaIncr(area(db_network_->cell(buffer_cell)));
+ inserted_buffer_count_++;
+
+ sta_->connectPin(buffer, buffer_input_port, in_net);
+ sta_->connectPin(buffer, buffer_output_port, out_net);
+
+ parasiticsInvalid(in_net);
+ parasiticsInvalid(out_net);
+
+ // Resize repeater as we back up by levels.
+ Pin *drvr_pin = network_->findPin(buffer, buffer_output_port);
+ resizeToTargetSlew(drvr_pin, false);
+ buffer_cell = network_->libertyCell(buffer);
+ buffer_cell->bufferPorts(buffer_input_port, buffer_output_port);
+
+ Pin *buf_in_pin = network_->findPin(buffer, buffer_input_port);
+ load_pins.clear();
+ load_pins.push_back(buf_in_pin);
+ wire_length = 0;
+ pin_cap = buffer_input_port->capacitance();
+ fanout = portFanoutLoad(buffer_input_port);
+}
+
+bool
+Resizer::hasInputPort(const Net *net)
+{
+ bool has_top_level_port = false;
+ NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ if (network_->isTopLevelPort(pin)
+ && network_->direction(pin)->isAnyInput()) {
+ has_top_level_port = true;
+ break;
+ }
+ }
+ delete pin_iter;
+ return has_top_level_port;
+}
+
+bool
+Resizer::hasOutputPort(const Net *net)
+{
+ bool has_top_level_port = false;
+ NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ if (network_->isTopLevelPort(pin)
+ && network_->direction(pin)->isAnyOutput()) {
+ has_top_level_port = true;
+ break;
+ }
+ }
+ delete pin_iter;
+ return has_top_level_port;
+}
+
+bool
+Resizer::hasPort(const Net *net)
+{
+ bool has_top_level_port = false;
+ NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ if (network_->isTopLevelPort(pin)) {
+ has_top_level_port = true;
+ break;
+ }
+ }
+ delete pin_iter;
+ return has_top_level_port;
+}
+
+float
+Resizer::driveResistance(const Pin *drvr_pin)
+{
+ if (network_->isTopLevelPort(drvr_pin)) {
+ InputDrive *drive = sdc_->findInputDrive(network_->port(drvr_pin));
+ if (drive) {
+ float max_res = 0;
+ for (auto min_max : MinMax::range()) {
+ for (auto rf : RiseFall::range()) {
+ LibertyCell *cell;
+ LibertyPort *from_port;
+ float *from_slews;
+ LibertyPort *to_port;
+ drive->driveCell(rf, min_max, cell, from_port, from_slews, to_port);
+ if (to_port)
+ max_res = max(max_res, to_port->driveResistance());
+ else {
+ float res;
+ bool exists;
+ drive->driveResistance(rf, min_max, res, exists);
+ max_res = max(max_res, res);
+ }
+ }
+ }
+ return max_res;
+ }
+ }
+ else {
+ LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
+ if (drvr_port)
+ return drvr_port->driveResistance();
+ }
+ return 0.0;
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+Resizer::resizeToTargetSlew()
+{
+ resize_count_ = 0;
+ resized_multi_output_insts_.clear();
+ incrementalParasiticsBegin();
+ // Resize in reverse level order.
+ for (int i = level_drvr_vertices_.size() - 1; i >= 0; i--) {
+ Vertex *drvr = level_drvr_vertices_[i];
+ Pin *drvr_pin = drvr->pin();
+ Net *net = network_->net(drvr_pin);
+ if (net
+ && !drvr->isConstant()
+ && hasFanout(drvr)
+ // Hands off the clock nets.
+ && !sta_->isClock(drvr_pin)
+ // Hands off special nets.
+ && !db_network_->isSpecial(net)) {
+ resizeToTargetSlew(drvr_pin, true);
+ if (overMaxArea()) {
+ logger_->error(RSZ, 24, "Max utilization reached.");
+ break;
+ }
+ }
+ }
+ updateParasitics();
+ incrementalParasiticsEnd();
+
+ if (resize_count_ > 0)
+ logger_->info(RSZ, 29, "Resized {} instances.", resize_count_);
+}
+
+bool
+Resizer::hasFanout(Vertex *drvr)
+{
+ VertexOutEdgeIterator edge_iter(drvr, graph_);
+ return edge_iter.hasNext();
+}
+
+void
+Resizer::makeEquivCells()
+{
+ LibertyLibrarySeq libs;
+ LibertyLibraryIterator *lib_iter = network_->libertyLibraryIterator();
+ while (lib_iter->hasNext()) {
+ LibertyLibrary *lib = lib_iter->next();
+ // massive kludge until makeEquivCells is fixed to only incldue link cells
+ LibertyCellIterator cell_iter(lib);
+ if (cell_iter.hasNext()) {
+ LibertyCell *cell = cell_iter.next();
+ if (isLinkCell(cell))
+ libs.push_back(lib);
+ }
+ }
+ delete lib_iter;
+ sta_->makeEquivCells(&libs, nullptr);
+}
+
+static float
+targetLoadDist(float load_cap,
+ float target_load)
+{
+ return abs(load_cap - target_load);
+}
+
+bool
+Resizer::resizeToTargetSlew(const Pin *drvr_pin,
+ bool update_count)
+{
+ Instance *inst = network_->instance(drvr_pin);
+ LibertyCell *cell = network_->libertyCell(inst);
+ if (cell) {
+ bool revisiting_inst = false;
+ if (hasMultipleOutputs(inst)) {
+ revisiting_inst = resized_multi_output_insts_.hasKey(inst);
+ debugPrint(logger_, RSZ, "resize", 2, "multiple outputs{}",
+ revisiting_inst ? " - revisit" : "");
+ resized_multi_output_insts_.insert(inst);
+ }
+ ensureWireParasitic(drvr_pin);
+ // Includes net parasitic capacitance.
+ float load_cap = graph_delay_calc_->loadCap(drvr_pin, tgt_slew_dcalc_ap_);
+ // DINESH-A: delay cells resize disabled
+ if (load_cap > 0.00 && (strncmp(cell->name(),"sky130_fd_sc_hd__clkdlybuf4s15_2",26) != 0)) {
+ LibertyCell *target_cell = findTargetCell(cell, load_cap, revisiting_inst);
+ if (target_cell != cell) {
+ //printf("Dinesh-A: Resizing : %s => %s %s Load_cap: %f \n",sdc_network_->pathName(drvr_pin),cell->name(),target_cell->name(),load_cap);
+ debugPrint(logger_, RSZ, "resize", 2, "{} {} -> {}",
+ sdc_network_->pathName(drvr_pin),
+ cell->name(),
+ target_cell->name());
+ if (replaceCell(inst, target_cell, true)
+ && !revisiting_inst
+ && update_count)
+ resize_count_++;
+ }
+ }
+ }
+ return false;
+}
+
+LibertyCell *
+Resizer::findTargetCell(LibertyCell *cell,
+ float load_cap,
+ bool revisiting_inst)
+{
+ LibertyCell *best_cell = cell;
+ LibertyCellSeq *equiv_cells = sta_->equivCells(cell);
+ if (equiv_cells) {
+ bool is_buf_inv = cell->isBuffer() || cell->isInverter();
+ float target_load = (*target_load_map_)[cell];
+ float best_load = target_load;
+ float best_dist = targetLoadDist(load_cap, target_load);
+ float best_delay = is_buf_inv
+ ? bufferDelay(cell, load_cap, tgt_slew_dcalc_ap_)
+ : 0.0;
+ debugPrint(logger_, RSZ, "resize", 3, "{} load cap {} dist={:.2e} delay={}",
+ cell->name(),
+ units_->capacitanceUnit()->asString(load_cap),
+ best_dist,
+ delayAsString(best_delay, sta_, 3));
+ for (LibertyCell *target_cell : *equiv_cells) {
+ if (!dontUse(target_cell)
+ && isLinkCell(target_cell)) {
+ float target_load = (*target_load_map_)[target_cell];
+ float delay = is_buf_inv
+ ? bufferDelay(target_cell, load_cap, tgt_slew_dcalc_ap_)
+ : 0.0;
+ float dist = targetLoadDist(load_cap, target_load);
+ debugPrint(logger_, RSZ, "resize", 3, " {} dist={:.2e} delay={}",
+ target_cell->name(),
+ dist,
+ delayAsString(delay, sta_, 3));
+ if (is_buf_inv
+ // Library may have "delay" buffers/inverters that are
+ // functionally buffers/inverters but have additional
+ // intrinsic delay. Accept worse target load matching if
+ // delay is reduced to avoid using them.
+ ? ((delay < best_delay
+ && dist < best_dist * 1.1)
+ || (dist < best_dist
+ && delay < best_delay * 1.1))
+ : dist < best_dist
+ // If the instance has multiple outputs (generally a register Q/QN)
+ // only allow upsizing after the first pin is visited.
+ && (!revisiting_inst
+ || target_load > best_load)) {
+ best_cell = target_cell;
+ best_dist = dist;
+ best_load = target_load;
+ best_delay = delay;
+ }
+ }
+ }
+ }
+ return best_cell;
+}
+
+// Replace LEF with LEF so ports stay aligned in instance.
+bool
+Resizer::replaceCell(Instance *inst,
+ LibertyCell *replacement,
+ bool journal)
+{
+ const char *replacement_name = replacement->name();
+ dbMaster *replacement_master = db_->findMaster(replacement_name);
+ if (replacement_master) {
+ dbInst *dinst = db_network_->staToDb(inst);
+ dbMaster *master = dinst->getMaster();
+ designAreaIncr(-area(master));
+ Cell *replacement_cell1 = db_network_->dbToSta(replacement_master);
+ if (journal)
+ journalInstReplaceCellBefore(inst);
+ sta_->replaceCell(inst, replacement_cell1);
+ designAreaIncr(area(replacement_master));
+
+ // Invalidate estimated parasitics on all instance pins.
+ // Input nets change pin cap, outputs change location (slightly).
+ if (haveEstimatedParasitics()) {
+ InstancePinIterator *pin_iter = network_->pinIterator(inst);
+ while (pin_iter->hasNext()) {
+ const Pin *pin = pin_iter->next();
+ const Net *net = network_->net(pin);
+ if (net)
+ parasiticsInvalid(net);
+ }
+ delete pin_iter;
+ }
+ return true;
+ }
+ return false;
+}
+
+bool
+Resizer::hasMultipleOutputs(const Instance *inst)
+{
+ int output_count = 0;
+ InstancePinIterator *pin_iter = network_->pinIterator(inst);
+ while (pin_iter->hasNext()) {
+ const Pin *pin = pin_iter->next();
+ if (network_->direction(pin)->isAnyOutput()
+ && network_->net(pin)) {
+ output_count++;
+ if (output_count > 1)
+ return true;
+ }
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+Resizer::resizeSlackPreamble()
+{
+ resizePreamble();
+ // Save max_wire_length for multiple repairDesign calls.
+ max_wire_length_ = findMaxWireLength();
+ net_slack_map_.clear();
+}
+
+// Run repair design to find the slacks but save/restore all changes to the netlist.
+void
+Resizer::findResizeSlacks()
+{
+ journalBegin();
+ estimateWireParasitics();
+ int repair_count, slew_violations, cap_violations;
+ int fanout_violations, length_violations;
+ repairDesign(max_wire_length_, 0.0, 0.0,
+ repair_count, slew_violations, cap_violations,
+ fanout_violations, length_violations);
+ findResizeSlacks1();
+ journalRestore();
+}
+
+void
+Resizer::findResizeSlacks1()
+{
+ // Use driver pin slacks rather than Sta::netSlack to save visiting
+ // the net pins and min'ing the slack.
+ NetSeq nets;
+ for (int i = level_drvr_vertices_.size() - 1; i >= 0; i--) {
+ Vertex *drvr = level_drvr_vertices_[i];
+ Pin *drvr_pin = drvr->pin();
+ Net *net = network_->isTopLevelPort(drvr_pin)
+ ? network_->net(network_->term(drvr_pin))
+ : network_->net(drvr_pin);
+ if (net
+ && !drvr->isConstant()
+ // Hands off special nets.
+ && !db_network_->isSpecial(net)
+ && !sta_->isClock(drvr_pin)) {
+ net_slack_map_[net] = sta_->vertexSlack(drvr, max_);
+ nets.push_back(net);
+ }
+ }
+
+ // Find the nets with the worst slack.
+ double worst_percent = .1;
+ // sort(nets.begin(), nets.end(). [&](const Net *net1,
+ sort(nets, [this](const Net *net1,
+ const Net *net2)
+ { return resizeNetSlack(net1) < resizeNetSlack(net2); });
+ worst_slack_nets_.clear();
+ for (int i = 0; i < nets.size() * worst_percent; i++)
+ worst_slack_nets_.push_back(nets[i]);
+}
+
+NetSeq &
+Resizer::resizeWorstSlackNets()
+{
+ return worst_slack_nets_;
+}
+
+vector<dbNet*>
+Resizer::resizeWorstSlackDbNets()
+{
+ vector<dbNet*> nets;
+ for (Net* net : worst_slack_nets_)
+ nets.push_back(db_network_->staToDb(net));
+ return nets;
+}
+
+Slack
+Resizer::resizeNetSlack(const Net *net)
+{
+ return net_slack_map_[net];
+}
+
+Slack
+Resizer::resizeNetSlack(const dbNet *db_net)
+{
+ const Net *net = db_network_->dbToSta(db_net);
+ return net_slack_map_[net];
+}
+
+////////////////////////////////////////////////////////////////
+
+double
+Resizer::area(Cell *cell)
+{
+ return area(db_network_->staToDb(cell));
+}
+
+double
+Resizer::area(dbMaster *master)
+{
+ if (!master->isCoreAutoPlaceable()) {
+ return 0;
+ }
+ return dbuToMeters(master->getWidth()) * dbuToMeters(master->getHeight());
+}
+
+double
+Resizer::dbuToMeters(int dist) const
+{
+ int dbu = db_->getTech()->getDbUnitsPerMicron();
+ return dist / (dbu * 1e+6);
+}
+
+int
+Resizer::metersToDbu(double dist) const
+{
+ int dbu = db_->getTech()->getDbUnitsPerMicron();
+ return dist * dbu * 1e+6;
+}
+
+void
+Resizer::setMaxUtilization(double max_utilization)
+{
+ max_area_ = coreArea() * max_utilization;
+}
+
+bool
+Resizer::overMaxArea()
+{
+ return max_area_
+ && fuzzyGreaterEqual(design_area_, max_area_);
+}
+
+void
+Resizer::setDontUse(LibertyCellSeq *dont_use)
+{
+ if (dont_use) {
+ for (LibertyCell *cell : *dont_use)
+ dont_use_.insert(cell);
+ }
+}
+
+bool
+Resizer::dontUse(LibertyCell *cell)
+{
+ return cell->dontUse()
+ || dont_use_.hasKey(cell);
+}
+
+////////////////////////////////////////////////////////////////
+
+// Find a target slew for the libraries and then
+// a target load for each cell that gives the target slew.
+void
+Resizer::findTargetLoads()
+{
+ // Find target slew across all buffers in the libraries.
+ findBufferTargetSlews();
+ if (target_load_map_ == nullptr)
+ target_load_map_ = new CellTargetLoadMap;
+ target_load_map_->clear();
+
+ // Find target loads at the tgt_slew_corner.
+ int lib_ap_index = tgt_slew_corner_->libertyIndex(max_);
+ LibertyLibraryIterator *lib_iter = network_->libertyLibraryIterator();
+ while (lib_iter->hasNext()) {
+ LibertyLibrary *lib = lib_iter->next();
+ LibertyCellIterator cell_iter(lib);
+ while (cell_iter.hasNext()) {
+ LibertyCell *cell = cell_iter.next();
+ if (isLinkCell(cell)) {
+ LibertyCell *corner_cell = cell->cornerCell(lib_ap_index);
+ float tgt_load;
+ bool exists;
+ target_load_map_->findKey(corner_cell, tgt_load, exists);
+ if (!exists) {
+ tgt_load = findTargetLoad(corner_cell);
+ (*target_load_map_)[corner_cell] = tgt_load;
+ }
+ // Map link cell to corner cell target load.
+ if (cell != corner_cell)
+ (*target_load_map_)[cell] = tgt_load;
+ }
+ }
+ }
+ delete lib_iter;
+}
+
+float
+Resizer::targetLoadCap(LibertyCell *cell)
+{
+ float load_cap = 0.0;
+ bool exists;
+ target_load_map_->findKey(cell, load_cap, exists);
+ if (!exists)
+ logger_->error(RSZ, 68, "missing target load cap.");
+ return load_cap;
+}
+
+float
+Resizer::findTargetLoad(LibertyCell *cell)
+{
+ LibertyCellTimingArcSetIterator arc_set_iter(cell);
+ float target_load_sum = 0.0;
+ int arc_count = 0;
+ while (arc_set_iter.hasNext()) {
+ TimingArcSet *arc_set = arc_set_iter.next();
+ TimingRole *role = arc_set->role();
+ if (!role->isTimingCheck()
+ && role != TimingRole::tristateDisable()
+ && role != TimingRole::tristateEnable()) {
+ TimingArcSetArcIterator arc_iter(arc_set);
+ while (arc_iter.hasNext()) {
+ TimingArc *arc = arc_iter.next();
+ int in_rf_index = arc->fromTrans()->asRiseFall()->index();
+ int out_rf_index = arc->toTrans()->asRiseFall()->index();
+ float arc_target_load = findTargetLoad(cell, arc,
+ tgt_slews_[in_rf_index],
+ tgt_slews_[out_rf_index]);
+ debugPrint(logger_, RSZ, "target_load", 3, "{} {} -> {} {} target_load = {:.2e}",
+ cell->name(),
+ arc->from()->name(),
+ arc->to()->name(),
+ arc->toTrans()->asString(),
+ arc_target_load);
+ target_load_sum += arc_target_load;
+ arc_count++;
+ }
+ }
+ }
+ float target_load = arc_count ? target_load_sum / arc_count : 0.0;
+ debugPrint(logger_, RSZ, "target_load", 2, "{} target_load = {:.2e}",
+ cell->name(),
+ target_load);
+ return target_load;
+}
+
+// Find the load capacitance that will cause the output slew
+// to be equal to out_slew.
+float
+Resizer::findTargetLoad(LibertyCell *cell,
+ TimingArc *arc,
+ Slew in_slew,
+ Slew out_slew)
+{
+ GateTimingModel *model = dynamic_cast<GateTimingModel*>(arc->model());
+ if (model) {
+ // load_cap1 lower bound
+ // load_cap2 upper bound
+ double load_cap1 = 0.0;
+ double load_cap2 = 1.0e-12; // 1pF
+ double tol = .01; // 1%
+ double diff1 = gateSlewDiff(cell, arc, model, in_slew, load_cap1, out_slew);
+ if (diff1 > 0.0)
+ // Zero load cap out_slew is higher than the target.
+ return 0.0;
+ double diff2 = gateSlewDiff(cell, arc, model, in_slew, load_cap2, out_slew);
+ // binary search for diff = 0.
+ while (abs(load_cap1 - load_cap2) > max(load_cap1, load_cap2) * tol) {
+ if (diff2 < 0.0) {
+ load_cap1 = load_cap2;
+ diff1 = diff2;
+ load_cap2 *= 2;
+ diff2 = gateSlewDiff(cell, arc, model, in_slew, load_cap2, out_slew);
+ }
+ else {
+ double load_cap3 = (load_cap1 + load_cap2) / 2.0;
+ double diff3 = gateSlewDiff(cell, arc, model, in_slew, load_cap3, out_slew);
+ if (diff3 < 0.0) {
+ load_cap1 = load_cap3;
+ diff1 = diff3;
+ }
+ else {
+ load_cap2 = load_cap3;
+ diff2 = diff3;
+ }
+ }
+ }
+ return load_cap1;
+ }
+ return 0.0;
+}
+
+// objective function
+Slew
+Resizer::gateSlewDiff(LibertyCell *cell,
+ TimingArc *arc,
+ GateTimingModel *model,
+ Slew in_slew,
+ float load_cap,
+ Slew out_slew)
+
+{
+ const Pvt *pvt = tgt_slew_dcalc_ap_->operatingConditions();
+ ArcDelay arc_delay;
+ Slew arc_slew;
+ model->gateDelay(cell, pvt, in_slew, load_cap, 0.0, false,
+ arc_delay, arc_slew);
+ return arc_slew - out_slew;
+}
+
+////////////////////////////////////////////////////////////////
+
+Slew
+Resizer::targetSlew(const RiseFall *rf)
+{
+ return tgt_slews_[rf->index()];
+}
+
+// Find target slew across all buffers in the libraries.
+void
+Resizer::findBufferTargetSlews()
+{
+ tgt_slews_ = {0.0};
+ tgt_slew_corner_ = nullptr;
+
+ for (Corner *corner : *sta_->corners()) {
+ int lib_ap_index = corner->libertyIndex(max_);
+ const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(max_);
+ const Pvt *pvt = dcalc_ap->operatingConditions();
+ // Average slews across buffers at corner.
+ Slew slews[RiseFall::index_count]{0.0};
+ int counts[RiseFall::index_count]{0};
+ for (LibertyCell *buffer : buffer_cells_) {
+ LibertyCell *corner_buffer = buffer->cornerCell(lib_ap_index);
+ findBufferTargetSlews(corner_buffer, pvt, slews, counts);
+ }
+ Slew slew_rise = slews[RiseFall::riseIndex()] / counts[RiseFall::riseIndex()];
+ Slew slew_fall = slews[RiseFall::fallIndex()] / counts[RiseFall::fallIndex()];
+ // Use the target slews from the slowest corner,
+ // and resize using that corner.
+ if (slew_rise > tgt_slews_[RiseFall::riseIndex()]) {
+ tgt_slews_[RiseFall::riseIndex()] = slew_rise;
+ tgt_slews_[RiseFall::fallIndex()] = slew_fall;
+ tgt_slew_corner_ = corner;
+ tgt_slew_dcalc_ap_ = corner->findDcalcAnalysisPt(max_);
+ }
+ }
+
+ debugPrint(logger_, RSZ, "target_load", 1, "target slew corner {} = {}/{}",
+ tgt_slew_corner_->name(),
+ delayAsString(tgt_slews_[RiseFall::riseIndex()], sta_, 3),
+ delayAsString(tgt_slews_[RiseFall::fallIndex()], sta_, 3));
+}
+
+void
+Resizer::findBufferTargetSlews(LibertyCell *buffer,
+ const Pvt *pvt,
+ // Return values.
+ Slew slews[],
+ int counts[])
+{
+ LibertyPort *input, *output;
+ buffer->bufferPorts(input, output);
+ TimingArcSetSeq *arc_sets = buffer->timingArcSets(input, output);
+ if (arc_sets) {
+ for (TimingArcSet *arc_set : *arc_sets) {
+ TimingArcSetArcIterator arc_iter(arc_set);
+ while (arc_iter.hasNext()) {
+ TimingArc *arc = arc_iter.next();
+ GateTimingModel *model = dynamic_cast<GateTimingModel*>(arc->model());
+ RiseFall *in_rf = arc->fromTrans()->asRiseFall();
+ RiseFall *out_rf = arc->toTrans()->asRiseFall();
+ float in_cap = input->capacitance(in_rf, max_);
+ float load_cap = in_cap * tgt_slew_load_cap_factor;
+ ArcDelay arc_delay;
+ Slew arc_slew;
+ model->gateDelay(buffer, pvt, 0.0, load_cap, 0.0, false,
+ arc_delay, arc_slew);
+ model->gateDelay(buffer, pvt, arc_slew, load_cap, 0.0, false,
+ arc_delay, arc_slew);
+ slews[out_rf->index()] += arc_slew;
+ counts[out_rf->index()]++;
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+// Repair tie hi/low net driver fanout by duplicating the
+// tie hi/low instances for every pin connected to tie hi/low instances.
+void
+Resizer::repairTieFanout(LibertyPort *tie_port,
+ double separation, // meters
+ bool verbose)
+{
+ ensureBlock();
+ ensureDesignArea();
+ Instance *top_inst = network_->topInstance();
+ LibertyCell *tie_cell = tie_port->libertyCell();
+ InstanceSeq insts;
+ findCellInstances(tie_cell, insts);
+ int tie_count = 0;
+ int separation_dbu = metersToDbu(separation);
+ for (Instance *inst : insts) {
+ Pin *drvr_pin = network_->findPin(inst, tie_port);
+ if (drvr_pin) {
+ const char *inst_name = network_->name(inst);
+ Net *net = network_->net(drvr_pin);
+ if (net) {
+ NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
+ while (pin_iter->hasNext()) {
+ Pin *load = pin_iter->next();
+ if (load != drvr_pin) {
+ // Make tie inst.
+ Point tie_loc = tieLocation(load, separation_dbu);
+ Instance *load_inst = network_->instance(load);
+ string tie_name = makeUniqueInstName(inst_name, true);
+ Instance *tie = sta_->makeInstance(tie_name.c_str(),
+ tie_cell, top_inst);
+ setLocation(tie, tie_loc);
+
+ // Make tie output net.
+ Net *load_net = makeUniqueNet();
+
+ // Connect tie inst output.
+ sta_->connectPin(tie, tie_port, load_net);
+
+ // Connect load to tie output net.
+ sta_->disconnectPin(load);
+ Port *load_port = network_->port(load);
+ sta_->connectPin(load_inst, load_port, load_net);
+
+ designAreaIncr(area(db_network_->cell(tie_cell)));
+ tie_count++;
+ }
+ }
+ delete pin_iter;
+
+ // Delete inst output net.
+ Pin *tie_pin = network_->findPin(inst, tie_port);
+ Net *tie_net = network_->net(tie_pin);
+ sta_->deleteNet(tie_net);
+ parasitics_invalid_.erase(tie_net);
+ // Delete the tie instance.
+ sta_->deleteInstance(inst);
+ }
+ }
+ }
+
+ if (tie_count > 0) {
+ logger_->info(RSZ, 42, "Inserted {} tie {} instances.",
+ tie_count,
+ tie_cell->name());
+ level_drvr_vertices_valid_ = false;
+ }
+}
+
+void
+Resizer::findCellInstances(LibertyCell *cell,
+ // Return value.
+ InstanceSeq &insts)
+{
+ LeafInstanceIterator *inst_iter = network_->leafInstanceIterator();
+ while (inst_iter->hasNext()) {
+ Instance *inst = inst_iter->next();
+ if (network_->libertyCell(inst) == cell)
+ insts.push_back(inst);
+ }
+ delete inst_iter;
+}
+
+// Place the tie instance on the side of the load pin.
+Point
+Resizer::tieLocation(Pin *load,
+ int separation)
+{
+ Point load_loc = db_network_->location(load);
+ int load_x = load_loc.getX();
+ int load_y = load_loc.getY();
+ int tie_x = load_x;
+ int tie_y = load_y;
+ if (!network_->isTopLevelPort(load)) {
+ dbInst *db_inst = db_network_->staToDb(network_->instance(load));
+ dbBox *bbox = db_inst->getBBox();
+ int left_dist = abs(load_x - bbox->xMin());
+ int right_dist = abs(load_x - bbox->xMax());
+ int bot_dist = abs(load_y - bbox->yMin());
+ int top_dist = abs(load_y - bbox->yMax());
+ if (left_dist < right_dist
+ && left_dist < bot_dist
+ && left_dist < top_dist)
+ // left
+ tie_x -= separation;
+ if (right_dist < left_dist
+ && right_dist < bot_dist
+ && right_dist < top_dist)
+ // right
+ tie_x += separation;
+ if (bot_dist < left_dist
+ && bot_dist < right_dist
+ && bot_dist < top_dist)
+ // bot
+ tie_y -= separation;
+ if (top_dist < left_dist
+ && top_dist < right_dist
+ && top_dist < bot_dist)
+ // top
+ tie_y += separation;
+ }
+ return Point(tie_x, tie_y);
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+Resizer::repairSetup(float slack_margin)
+{
+ inserted_buffer_count_ = 0;
+ resize_count_ = 0;
+ Slack worst_slack;
+ Vertex *worst_vertex;
+ sta_->worstSlack(max_, worst_slack, worst_vertex);
+ debugPrint(logger_, RSZ, "repair_setup", 1, "worst_slack = {}",
+ delayAsString(worst_slack, sta_, 3));
+ Slack prev_worst_slack = -INF;
+ int pass = 1;
+ int decreasing_slack_passes = 0;
+ incrementalParasiticsBegin();
+ while (fuzzyLess(worst_slack, slack_margin)) {
+ PathRef worst_path = sta_->vertexWorstSlackPath(worst_vertex, max_);
+ bool changed = repairSetup(worst_path, worst_slack);
+ updateParasitics();
+ sta_->findRequireds();
+ sta_->worstSlack(max_, worst_slack, worst_vertex);
+ bool decreasing_slack = fuzzyLessEqual(worst_slack, prev_worst_slack);
+ debugPrint(logger_, RSZ, "repair_setup", 1, "pass {} worst_slack = {} {}",
+ pass,
+ delayAsString(worst_slack, sta_, 3),
+ decreasing_slack ? "v" : "^");
+ if (decreasing_slack) {
+ // Allow slack to increase to get out of local minima.
+ // Do not update prev_worst_slack so it saves the high water mark.
+ decreasing_slack_passes++;
+ if (!changed
+ || decreasing_slack_passes > repair_setup_decreasing_slack_passes_allowed_) {
+ // Undo changes that reduced slack.
+ journalRestore();
+ debugPrint(logger_, RSZ, "repair_setup", 1,
+ "decreasing slack for {} passes. Restoring best slack {}",
+ decreasing_slack_passes,
+ delayAsString(prev_worst_slack, sta_, 3));
+ break;
+ }
+ }
+ else {
+ prev_worst_slack = worst_slack;
+ decreasing_slack_passes = 0;
+ // Progress, start journal so we can back up to here.
+ journalBegin();
+ }
+ if (overMaxArea())
+ break;
+ pass++;
+ }
+ // Leave the parasitics up to date.
+ updateParasitics();
+ incrementalParasiticsEnd();
+
+ if (inserted_buffer_count_ > 0)
+ logger_->info(RSZ, 40, "Inserted {} buffers.", inserted_buffer_count_);
+ if (resize_count_ > 0)
+ logger_->info(RSZ, 41, "Resized {} instances.", resize_count_);
+ if (fuzzyLess(worst_slack, slack_margin))
+ logger_->warn(RSZ, 62, "Unable to repair all setup violations.");
+ if (overMaxArea())
+ logger_->error(RSZ, 25, "max utilization reached.");
+}
+
+// For testing.
+void
+Resizer::repairSetup(Pin *end_pin)
+{
+ inserted_buffer_count_ = 0;
+ resize_count_ = 0;
+ Vertex *vertex = graph_->pinLoadVertex(end_pin);
+ Slack slack = sta_->vertexSlack(vertex, max_);
+ PathRef path = sta_->vertexWorstSlackPath(vertex, max_);
+ incrementalParasiticsBegin();
+ repairSetup(path, slack);
+ // Leave the parasitices up to date.
+ updateParasitics();
+ incrementalParasiticsEnd();
+
+ if (inserted_buffer_count_ > 0)
+ logger_->info(RSZ, 30, "Inserted {} buffers.", inserted_buffer_count_);
+ if (resize_count_ > 0)
+ logger_->info(RSZ, 31, "Resized {} instances.", resize_count_);
+}
+
+bool
+Resizer::repairSetup(PathRef &path,
+ Slack path_slack)
+{
+ PathExpanded expanded(&path, sta_);
+ bool changed = false;
+ if (expanded.size() > 1) {
+ int path_length = expanded.size();
+ vector<pair<int, Delay>> load_delays;
+ int start_index = expanded.startIndex();
+ const DcalcAnalysisPt *dcalc_ap = path.dcalcAnalysisPt(sta_);
+ int lib_ap = dcalc_ap->libertyIndex();
+ // Find load delay for each gate in the path.
+ for (int i = start_index; i < path_length; i++) {
+ PathRef *path = expanded.path(i);
+ Vertex *path_vertex = path->vertex(sta_);
+ const Pin *path_pin = path->pin(sta_);
+ if (network_->isDriver(path_pin)
+ && !network_->isTopLevelPort(path_pin)) {
+ TimingArc *prev_arc = expanded.prevArc(i);
+ TimingArc *corner_arc = prev_arc->cornerArc(lib_ap);
+ Edge *prev_edge = path->prevEdge(prev_arc, sta_);
+ Delay load_delay = graph_->arcDelay(prev_edge, prev_arc, dcalc_ap->index())
+ // Remove intrinsic delay to find load dependent delay.
+ - corner_arc->intrinsicDelay();
+ load_delays.push_back(pair(i, load_delay));
+ debugPrint(logger_, RSZ, "repair_setup", 3, "{} load_delay = {}",
+ path_vertex->name(network_),
+ delayAsString(load_delay, sta_, 3));
+ }
+ }
+
+ sort(load_delays.begin(), load_delays.end(),
+ [](pair<int, Delay> pair1,
+ pair<int, Delay> pair2) {
+ return pair1.second > pair2.second;
+ });
+ // Attack gates with largest load delays first.
+ for (auto index_delay : load_delays) {
+ int drvr_index = index_delay.first;
+ PathRef *drvr_path = expanded.path(drvr_index);
+ Vertex *drvr_vertex = drvr_path->vertex(sta_);
+ Pin *drvr_pin = drvr_vertex->pin();
+ PathRef *load_path = expanded.path(drvr_index + 1);
+ Vertex *load_vertex = load_path->vertex(sta_);
+ Pin *load_pin = load_vertex->pin();
+ int fanout = this->fanout(drvr_vertex);
+ debugPrint(logger_, RSZ, "repair_setup", 2, "{} fanout = {}",
+ network_->pathName(drvr_pin),
+ fanout);
+ // For tristate nets all we can do is resize the driver.
+ bool tristate_drvr = isTristateDriver(drvr_pin);
+ if (fanout > 1
+ // Rebuffer blows up on large fanout nets.
+ && fanout < rebuffer_max_fanout_
+ && !tristate_drvr) {
+ int count_before = inserted_buffer_count_;
+ rebuffer(drvr_pin);
+ int insert_count = inserted_buffer_count_ - count_before;
+ if (insert_count > 0) {
+ debugPrint(logger_, RSZ, "repair_setup", 2, "rebuffer {} inserted {}",
+ network_->pathName(drvr_pin),
+ insert_count);
+ changed = true;
+ break;
+ }
+ }
+ // Don't split loads on low fanout nets.
+ if (fanout > split_load_min_fanout_
+ && !tristate_drvr) {
+ // Divide and conquer.
+ debugPrint(logger_, RSZ, "repair_setup", 2, "split loads {} -> {}",
+ network_->pathName(drvr_pin),
+ network_->pathName(load_pin));
+ splitLoads(drvr_path, path_slack);
+ changed = true;
+ break;
+ }
+ LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
+ float load_cap = graph_delay_calc_->loadCap(drvr_pin, dcalc_ap);
+ int in_index = drvr_index - 1;
+ PathRef *in_path = expanded.path(in_index);
+ Pin *in_pin = in_path->pin(sta_);
+ LibertyPort *in_port = network_->libertyPort(in_pin);
+
+ float prev_drive;
+ if (drvr_index >= 2) {
+ int prev_drvr_index = drvr_index - 2;
+ PathRef *prev_drvr_path = expanded.path(prev_drvr_index);
+ Pin *prev_drvr_pin = prev_drvr_path->pin(sta_);
+ prev_drive = 0.0;
+ LibertyPort *prev_drvr_port = network_->libertyPort(prev_drvr_pin);
+ if (prev_drvr_port) {
+ prev_drive = prev_drvr_port->driveResistance();
+ }
+ }
+ else
+ prev_drive = 0.0;
+ LibertyCell *upsize = upsizeCell(in_port, drvr_port, load_cap,
+ prev_drive, dcalc_ap);
+ // DINESH-A: delay cells resize disabled
+ if (upsize && (strncmp(drvr_port->libertyCell()->name(),"sky130_fd_sc_hd__clkdlybuf4s15_2",26) != 0)) {
+ Instance *drvr = network_->instance(drvr_pin);
+ //printf("Dinesh-A: Upsizing the cells: %s %s %s\n",network_->pathName(drvr_pin),drvr_port->libertyCell()->name(),upsize->name());
+ debugPrint(logger_, RSZ, "repair_setup", 2, "resize {} {} -> {}",
+ network_->pathName(drvr_pin),
+ drvr_port->libertyCell()->name(),
+ upsize->name());
+ if (replaceCell(drvr, upsize, true)) {
+ resize_count_++;
+ changed = true;
+ break;
+ }
+ }
+ }
+ }
+ return changed;
+}
+
+void
+Resizer::splitLoads(PathRef *drvr_path,
+ Slack drvr_slack)
+{
+ Vertex *drvr_vertex = drvr_path->vertex(sta_);
+ const RiseFall *rf = drvr_path->transition(sta_);
+ // Sort fanouts of the drvr on the critical path by slack margin
+ // wrt the critical path slack.
+ vector<pair<Vertex*, Slack>> fanout_slacks;
+ VertexOutEdgeIterator edge_iter(drvr_vertex, graph_);
+ while (edge_iter.hasNext()) {
+ Edge *edge = edge_iter.next();
+ Vertex *fanout_vertex = edge->to(graph_);
+ Slack fanout_slack = sta_->vertexSlack(fanout_vertex, rf, max_);
+ Slack slack_margin = fanout_slack - drvr_slack;
+ debugPrint(logger_, RSZ, "repair_setup", 3, " fanin {} slack_margin = {}",
+ network_->pathName(fanout_vertex->pin()),
+ delayAsString(slack_margin, sta_, 3));
+ fanout_slacks.push_back(pair<Vertex*, Slack>(fanout_vertex, slack_margin));
+ }
+
+ sort(fanout_slacks.begin(), fanout_slacks.end(),
+ [](pair<Vertex*, Slack> pair1,
+ pair<Vertex*, Slack> pair2) {
+ return pair1.second > pair2.second;
+ });
+
+ Pin *drvr_pin = drvr_vertex->pin();
+ Net *net = network_->net(drvr_pin);
+
+ string buffer_name = makeUniqueInstName("split");
+ Instance *parent = db_network_->topInstance();
+ LibertyCell *buffer_cell = buffer_lowest_drive_;
+ Instance *buffer = db_network_->makeInstance(buffer_cell,
+ buffer_name.c_str(),
+ parent);
+ journalMakeBuffer(buffer);
+ inserted_buffer_count_++;
+ designAreaIncr(area(db_network_->cell(buffer_cell)));
+
+ Net *out_net = makeUniqueNet();
+ LibertyPort *input, *output;
+ buffer_cell->bufferPorts(input, output);
+ Point drvr_loc = db_network_->location(drvr_pin);
+ setLocation(buffer, drvr_loc);
+
+ // Split the loads with extra slack to an inserted buffer.
+ // before
+ // drvr_pin -> net -> load_pins
+ // after
+ // drvr_pin -> net -> load_pins with low slack
+ // -> buffer_in -> net -> rest of loads
+ sta_->connectPin(buffer, input, net);
+ parasiticsInvalid(net);
+ sta_->connectPin(buffer, output, out_net);
+ int split_index = fanout_slacks.size() / 2;
+ for (int i = 0; i < split_index; i++) {
+ pair<Vertex*, Slack> fanout_slack = fanout_slacks[i];
+ Vertex *load_vertex = fanout_slack.first;
+ Pin *load_pin = load_vertex->pin();
+ // Leave ports connected to original net so verilog port names are preserved.
+ if (!network_->isTopLevelPort(load_pin)) {
+ LibertyPort *load_port = network_->libertyPort(load_pin);
+ Instance *load = network_->instance(load_pin);
+
+ sta_->disconnectPin(load_pin);
+ sta_->connectPin(load, load_port, out_net);
+ }
+ }
+ Pin *buffer_out_pin = network_->findPin(buffer, output);
+ resizeToTargetSlew(buffer_out_pin, false);
+}
+
+LibertyCell *
+Resizer::upsizeCell(LibertyPort *in_port,
+ LibertyPort *drvr_port,
+ float load_cap,
+ float prev_drive,
+ const DcalcAnalysisPt *dcalc_ap)
+{
+ int lib_ap = dcalc_ap->libertyIndex();
+ LibertyCell *cell = drvr_port->libertyCell();
+ LibertyCellSeq *equiv_cells = sta_->equivCells(cell);
+ if (equiv_cells) {
+ const char *in_port_name = in_port->name();
+ const char *drvr_port_name = drvr_port->name();
+ sort(equiv_cells,
+ [=] (const LibertyCell *cell1,
+ const LibertyCell *cell2) {
+ LibertyPort *port1=cell1->findLibertyPort(drvr_port_name)->cornerPort(lib_ap);
+ LibertyPort *port2=cell2->findLibertyPort(drvr_port_name)->cornerPort(lib_ap);
+ return port1->driveResistance() > port2->driveResistance();
+ });
+ float drive = drvr_port->cornerPort(lib_ap)->driveResistance();
+ float delay = gateDelay(drvr_port, load_cap, tgt_slew_dcalc_ap_)
+ + prev_drive * in_port->cornerPort(lib_ap)->capacitance();
+ for (LibertyCell *equiv : *equiv_cells) {
+ LibertyCell *equiv_corner = equiv->cornerCell(lib_ap);
+ LibertyPort *equiv_drvr = equiv_corner->findLibertyPort(drvr_port_name);
+ LibertyPort *equiv_input = equiv_corner->findLibertyPort(in_port_name);
+ float equiv_drive = equiv_drvr->driveResistance();
+ // Include delay of previous driver into equiv gate.
+ float equiv_delay = gateDelay(equiv_drvr, load_cap, dcalc_ap)
+ + prev_drive * equiv_input->capacitance();
+ if (!dontUse(equiv)
+ && equiv_drive < drive
+ && equiv_delay < delay)
+ return equiv;
+ }
+ }
+ return nullptr;
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+Resizer::repairHold(float slack_margin,
+ bool allow_setup_violations,
+ // Max buffer count as percent of design instance count.
+ float max_buffer_percent)
+{
+ init();
+ LibertyCell *buffer_cell = findHoldBuffer();
+ sta_->findRequireds();
+ VertexSet *ends = sta_->search()->endpoints();
+ int max_buffer_count = max_buffer_percent * network_->instanceCount();
+ incrementalParasiticsBegin();
+ repairHold(ends, buffer_cell, slack_margin,
+ allow_setup_violations, max_buffer_count);
+
+ // Leave the parasitices up to date.
+ updateParasitics();
+ incrementalParasiticsEnd();
+}
+
+// For testing/debug.
+void
+Resizer::repairHold(Pin *end_pin,
+ LibertyCell *buffer_cell,
+ float slack_margin,
+ bool allow_setup_violations,
+ float max_buffer_percent)
+{
+ Vertex *end = graph_->pinLoadVertex(end_pin);
+ VertexSet ends;
+ ends.insert(end);
+
+ init();
+ sta_->findRequireds();
+ int max_buffer_count = max_buffer_percent * network_->instanceCount();
+ incrementalParasiticsBegin();
+ repairHold(&ends, buffer_cell, slack_margin, allow_setup_violations,
+ max_buffer_count);
+ // Leave the parasitices up to date.
+ updateParasitics();
+ incrementalParasiticsEnd();
+}
+
+// Find the buffer with the most delay in the fastest corner.
+LibertyCell *
+Resizer::findHoldBuffer()
+{
+ LibertyCell *max_buffer = nullptr;
+ float max_delay = 0.0;
+ for (LibertyCell *buffer : buffer_cells_) {
+ float buffer_min_delay = bufferHoldDelay(buffer);
+ if (max_buffer == nullptr
+ || buffer_min_delay > max_delay) {
+ max_buffer = buffer;
+ max_delay = buffer_min_delay;
+ }
+ }
+ return max_buffer;
+}
+
+float
+Resizer::bufferHoldDelay(LibertyCell *buffer)
+{
+ Delay delays[RiseFall::index_count];
+ bufferHoldDelays(buffer, delays);
+ return min(delays[RiseFall::riseIndex()],
+ delays[RiseFall::fallIndex()]);
+}
+
+// Min self delay across corners; buffer -> buffer
+void
+Resizer::bufferHoldDelays(LibertyCell *buffer,
+ // Return values.
+ Delay delays[RiseFall::index_count])
+{
+ LibertyPort *input, *output;
+ buffer->bufferPorts(input, output);
+
+ for (int rf_index : RiseFall::rangeIndex())
+ delays[rf_index] = MinMax::min()->initValue();
+ for (Corner *corner : *sta_->corners()) {
+ LibertyPort *corner_port = input->cornerPort(corner->libertyIndex(max_));
+ const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(max_);
+ float load_cap = corner_port->capacitance();
+ ArcDelay gate_delays[RiseFall::index_count];
+ Slew slews[RiseFall::index_count];
+ gateDelays(output, load_cap, dcalc_ap, gate_delays, slews);
+ for (int rf_index : RiseFall::rangeIndex())
+ delays[rf_index] = min(delays[rf_index], gate_delays[rf_index]);
+ }
+}
+
+void
+Resizer::repairHold(VertexSet *ends,
+ LibertyCell *buffer_cell,
+ float slack_margin,
+ bool allow_setup_violations,
+ int max_buffer_count)
+{
+ // Find endpoints with hold violation.
+ VertexSet hold_failures;
+ Slack worst_slack;
+ findHoldViolations(ends, slack_margin, worst_slack, hold_failures);
+ if (!hold_failures.empty()) {
+ logger_->info(RSZ, 46, "Found {} endpoints with hold violations.",
+ hold_failures.size());
+ inserted_buffer_count_ = 0;
+ int repair_count = 1;
+ int pass = 1;
+ while (!hold_failures.empty()
+ // Make sure we are making progress.
+ && repair_count > 0
+ && !overMaxArea()
+ && inserted_buffer_count_ <= max_buffer_count) {
+ repair_count = repairHoldPass(hold_failures, buffer_cell, slack_margin,
+ allow_setup_violations, max_buffer_count);
+ debugPrint(logger_, RSZ, "repair_hold", 1,
+ "pass {} worst slack {} failures {} inserted {}",
+ pass,
+ delayAsString(worst_slack, sta_, 3),
+ hold_failures .size(),
+ repair_count);
+ sta_->findRequireds();
+ findHoldViolations(ends, slack_margin, worst_slack, hold_failures);
+ pass++;
+ }
+ if (slack_margin == 0.0 && fuzzyLess(worst_slack, 0.0))
+ logger_->warn(RSZ, 66, "Unable to repair all hold violations.");
+ else if (fuzzyLess(worst_slack, slack_margin))
+ logger_->warn(RSZ, 64, "Unable to repair all hold checks within margin.");
+
+ if (inserted_buffer_count_ > 0) {
+ logger_->info(RSZ, 32, "Inserted {} hold buffers.", inserted_buffer_count_);
+ level_drvr_vertices_valid_ = false;
+ }
+ if (inserted_buffer_count_ > max_buffer_count)
+ logger_->error(RSZ, 60, "Max buffer count reached.");
+ if (overMaxArea())
+ logger_->error(RSZ, 50, "Max utilization reached.");
+ }
+ else
+ logger_->info(RSZ, 33, "No hold violations found.");
+}
+
+void
+Resizer::findHoldViolations(VertexSet *ends,
+ float slack_margin,
+ // Return values.
+ Slack &worst_slack,
+ VertexSet &hold_violations)
+{
+ worst_slack = INF;
+ hold_violations.clear();
+ debugPrint(logger_, RSZ, "repair_hold", 3, "Hold violations");
+ for (Vertex *end : *ends) {
+ Slack slack = sta_->vertexSlack(end, MinMax::min());
+ if (!sta_->isClock(end->pin())
+ && fuzzyLess(slack, slack_margin)) {
+ debugPrint(logger_, RSZ, "repair_hold", 3, " {}",
+ end->name(sdc_network_));
+ if (slack < worst_slack)
+ worst_slack = slack;
+ hold_violations.insert(end);
+ }
+ }
+}
+
+int
+Resizer::repairHoldPass(VertexSet &hold_failures,
+ LibertyCell *buffer_cell,
+ float slack_margin,
+ bool allow_setup_violations,
+ int max_buffer_count)
+{
+ VertexSet fanins = findHoldFanins(hold_failures);
+ VertexSeq sorted_fanins = sortHoldFanins(fanins);
+
+ int repair_count = 0;
+ int max_repair_count = max(static_cast<int>(hold_failures.size() * .2), 10);
+ for(int i = 0; i < sorted_fanins.size() && repair_count < max_repair_count ; i++) {
+ Vertex *vertex = sorted_fanins[i];
+ Pin *drvr_pin = vertex->pin();
+ Net *net = network_->isTopLevelPort(drvr_pin)
+ ? network_->net(network_->term(drvr_pin))
+ : network_->net(drvr_pin);
+ updateParasitics();
+ Slack drvr_slacks[RiseFall::index_count][MinMax::index_count];
+ sta_->vertexSlacks(vertex, drvr_slacks);
+ int min_index = MinMax::minIndex();
+ int max_index = MinMax::maxIndex();
+ const RiseFall *drvr_rf = (drvr_slacks[RiseFall::riseIndex()][min_index] <
+ drvr_slacks[RiseFall::fallIndex()][min_index])
+ ? RiseFall::rise()
+ : RiseFall::fall();
+ Slack drvr_hold_slack = drvr_slacks[drvr_rf->index()][min_index] - slack_margin;
+ Slack drvr_setup_slack = drvr_slacks[drvr_rf->index()][max_index];
+ if (!vertex->isConstant()
+ && fuzzyLess(drvr_hold_slack, 0.0)
+ // Hands off special nets.
+ && !db_network_->isSpecial(net)
+ && !isTristateDriver(drvr_pin)
+ // Have to have enough setup slack to add delay to repair the hold violation.
+ && (allow_setup_violations
+ || fuzzyLess(drvr_hold_slack, drvr_setup_slack))) {
+ debugPrint(logger_, RSZ, "repair_hold", 2, "driver {}",
+ vertex->name(sdc_network_));
+ // Only add delay to loads with hold violations.
+ PinSeq load_pins;
+ float load_cap = 0.0;
+ bool loads_have_out_port = false;
+ VertexOutEdgeIterator edge_iter(vertex, graph_);
+ while (edge_iter.hasNext()) {
+ Edge *edge = edge_iter.next();
+ Vertex *fanout = edge->to(graph_);
+ Slack fanout_slack = sta_->vertexSlack(fanout, MinMax::min());
+ if (fanout_slack < slack_margin) {
+ Pin *fanout_pin = fanout->pin();
+ load_pins.push_back(fanout_pin);
+ if (network_->direction(fanout_pin)->isAnyOutput()
+ && network_->isTopLevelPort(fanout_pin))
+ loads_have_out_port = true;
+ else {
+ LibertyPort *fanout_port = network_->libertyPort(fanout_pin);
+ if (fanout_port)
+ load_cap += fanout_port->capacitance();
+ }
+ }
+ }
+ if (!load_pins.empty()) {
+ // multi-corner support MIA
+ const DcalcAnalysisPt *dcalc_ap = sta_->cmdCorner()->findDcalcAnalysisPt(max_);
+ Delay buffer_delay1 = bufferDelay(buffer_cell, drvr_rf, load_cap, dcalc_ap);
+ // Need enough slack to at least insert the last buffer with loads.
+ if (allow_setup_violations
+ || (buffer_delay1 < drvr_slacks[RiseFall::riseIndex()][max_index]
+ && buffer_delay1 < drvr_slacks[RiseFall::fallIndex()][max_index])) {
+ Delay buffer_delay = bufferHoldDelay(buffer_cell);
+ Delay max_insert = min(drvr_slacks[RiseFall::riseIndex()][max_index],
+ drvr_slacks[RiseFall::fallIndex()][max_index]);
+ int max_insert_count = delayInf(max_insert)
+ ? 1
+ : (max_insert - buffer_delay1) / buffer_delay + 1;
+ int hold_buffer_count = (-drvr_hold_slack > buffer_delay1)
+ ? std::ceil((-drvr_hold_slack-buffer_delay1)/buffer_delay)+1
+ : 1;
+ int buffer_count = allow_setup_violations
+ ? hold_buffer_count
+ : min(max_insert_count, hold_buffer_count);
+ debugPrint(logger_, RSZ, "repair_hold", 2,
+ " {} hold={} inserted {} for {}/{} loads",
+ vertex->name(sdc_network_),
+ delayAsString(drvr_hold_slack, this, 3),
+ buffer_count,
+ load_pins.size(),
+ fanout(vertex));
+ makeHoldDelay(vertex, buffer_count, load_pins,
+ loads_have_out_port, buffer_cell);
+ repair_count += buffer_count;
+ if (logger_->debugCheck(RSZ, "repair_hold", 4)) {
+ // Check that no setup violations are introduced.
+ updateParasitics();
+ Slack drvr_setup_slack1 = sta_->vertexSlack(vertex, max_);
+ if (drvr_setup_slack1 < 0
+ && drvr_setup_slack1 < drvr_setup_slack)
+ printf("%s %s -> %s\n", vertex->name(network_),
+ delayAsString(drvr_setup_slack, sta_, 3),
+ delayAsString(drvr_setup_slack1, sta_, 3));
+ }
+ if (inserted_buffer_count_ > max_buffer_count
+ || overMaxArea())
+ return repair_count;
+ }
+ }
+ }
+ }
+ return repair_count;
+}
+
+VertexSet
+Resizer::findHoldFanins(VertexSet &ends)
+{
+ SearchPredNonReg2 pred(sta_);
+ BfsBkwdIterator iter(BfsIndex::other, &pred, this);
+ for (Vertex *vertex : ends)
+ iter.enqueueAdjacentVertices(vertex);
+
+ VertexSet fanins;
+ while (iter.hasNext()) {
+ Vertex *fanin = iter.next();
+ if (!sta_->isClock(fanin->pin())) {
+ if (fanin->isDriver(network_))
+ fanins.insert(fanin);
+ iter.enqueueAdjacentVertices(fanin);
+ }
+ }
+ return fanins;
+}
+
+VertexSeq
+Resizer::sortHoldFanins(VertexSet &fanins)
+{
+ VertexSeq sorted_fanins;
+ for(Vertex *vertex : fanins)
+ sorted_fanins.push_back(vertex);
+
+ sort(sorted_fanins, [&](Vertex *v1, Vertex *v2)
+ { float s1 = sta_->vertexSlack(v1, MinMax::min());
+ float s2 = sta_->vertexSlack(v2, MinMax::min());
+ if (fuzzyEqual(s1, s2)) {
+ float gap1 = slackGap(v1);
+ float gap2 = slackGap(v2);
+ // Break ties based on the hold/setup gap.
+ if (fuzzyEqual(gap1, gap2))
+ return v1->level() > v2->level();
+ else
+ return gap1 > gap2;
+ }
+ else
+ return s1 < s2;});
+ if (logger_->debugCheck(RSZ, "repair_hold", 4)) {
+ printf("Sorted fanins");
+ printf(" hold_slack slack_gap level");
+ for(Vertex *vertex : sorted_fanins)
+ printf("%s %s %s %d",
+ vertex->name(network_),
+ delayAsString(sta_->vertexSlack(vertex, MinMax::min()), sta_, 3),
+ delayAsString(slackGap(vertex), sta_, 3),
+ vertex->level());
+ }
+ return sorted_fanins;
+}
+
+void
+Resizer::makeHoldDelay(Vertex *drvr,
+ int buffer_count,
+ PinSeq &load_pins,
+ bool loads_have_out_port,
+ LibertyCell *buffer_cell)
+{
+ Pin *drvr_pin = drvr->pin();
+ Instance *parent = db_network_->topInstance();
+ Net *drvr_net = network_->isTopLevelPort(drvr_pin)
+ ? db_network_->net(db_network_->term(drvr_pin))
+ : db_network_->net(drvr_pin);
+ Net *in_net, *out_net;
+ if (loads_have_out_port) {
+ // Verilog uses nets as ports, so the net connected to an output port has
+ // to be preserved.
+ // Move the driver pin over to gensym'd net.
+ in_net = makeUniqueNet();
+ Port *drvr_port = network_->port(drvr_pin);
+ Instance *drvr_inst = network_->instance(drvr_pin);
+ sta_->disconnectPin(drvr_pin);
+ sta_->connectPin(drvr_inst, drvr_port, in_net);
+ out_net = drvr_net;
+ }
+ else {
+ in_net = drvr_net;
+ out_net = makeUniqueNet();
+ }
+
+ parasiticsInvalid(in_net);
+ // Spread buffers between driver and load center.
+ Point drvr_loc = db_network_->location(drvr_pin);
+ Point load_center = findCenter(load_pins);
+ int dx = (load_center.x() - drvr_loc.x()) / (buffer_count + 1);
+ int dy = (load_center.y() - drvr_loc.y()) / (buffer_count + 1);
+
+ Net *buf_in_net = in_net;
+ Instance *buffer = nullptr;
+ LibertyPort *input, *output;
+ buffer_cell->bufferPorts(input, output);
+ // drvr_pin->in_net->hold_buffer1->net2->hold_buffer2->out_net...->load_pins
+ for (int i = 0; i < buffer_count; i++) {
+ Net *buf_out_net = (i == buffer_count - 1) ? out_net : makeUniqueNet();
+ // drvr_pin->drvr_net->hold_buffer->net2->load_pins
+ string buffer_name = makeUniqueInstName("hold");
+ buffer = db_network_->makeInstance(buffer_cell, buffer_name.c_str(),
+ parent);
+ journalMakeBuffer(buffer);
+ inserted_buffer_count_++;
+ designAreaIncr(area(db_network_->cell(buffer_cell)));
+
+ sta_->connectPin(buffer, input, buf_in_net);
+ sta_->connectPin(buffer, output, buf_out_net);
+ Point buffer_loc(drvr_loc.x() + dx * i,
+ drvr_loc.y() + dy * i);
+ setLocation(buffer, buffer_loc);
+ buf_in_net = buf_out_net;
+ parasiticsInvalid(buf_out_net);
+ }
+
+ for (Pin *load_pin : load_pins) {
+ Net *load_net = network_->isTopLevelPort(load_pin)
+ ? network_->net(network_->term(load_pin))
+ : network_->net(load_pin);
+ if (load_net != out_net) {
+ Instance *load = db_network_->instance(load_pin);
+ Port *load_port = db_network_->port(load_pin);
+ sta_->disconnectPin(load_pin);
+ sta_->connectPin(load, load_port, out_net);
+ }
+ }
+ Pin *buffer_out_pin = network_->findPin(buffer, output);
+ resizeToTargetSlew(buffer_out_pin, false);
+}
+
+Point
+Resizer::findCenter(PinSeq &pins)
+{
+ long sum_x = 0;
+ long sum_y = 0;
+ for (Pin *pin : pins) {
+ Point loc = db_network_->location(pin);
+ sum_x += loc.x();
+ sum_y += loc.y();
+ }
+ return Point(sum_x / pins.size(), sum_y / pins.size());
+}
+
+// Gap between min setup and hold slacks.
+// This says how much head room there is for adding delay to fix a
+// hold violation before violating a setup check.
+Slack
+Resizer::slackGap(Slacks &slacks)
+{
+ return min(slacks[RiseFall::riseIndex()][MinMax::maxIndex()]
+ - slacks[RiseFall::riseIndex()][MinMax::minIndex()],
+ slacks[RiseFall::fallIndex()][MinMax::maxIndex()]
+ - slacks[RiseFall::fallIndex()][MinMax::minIndex()]);
+}
+
+Slack
+Resizer::slackGap(Vertex *vertex)
+{
+ Slacks slacks;
+ sta_->vertexSlacks(vertex, slacks);
+ return slackGap(slacks);
+}
+
+int
+Resizer::fanout(Vertex *vertex)
+{
+ int fanout = 0;
+ VertexOutEdgeIterator edge_iter(vertex, graph_);
+ while (edge_iter.hasNext()) {
+ edge_iter.next();
+ fanout++;
+ }
+ return fanout;
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+Resizer::reportLongWires(int count,
+ int digits)
+{
+ graph_ = sta_->ensureGraph();
+ sta_->ensureClkNetwork();
+ VertexSeq drvrs;
+ findLongWires(drvrs);
+ report_->reportLine("Driver length delay");
+ const Corner *corner = sta_->cmdCorner();
+ double wire_res = wireSignalResistance(corner);
+ double wire_cap = wireSignalCapacitance(corner);
+ int i = 0;
+ for (Vertex *drvr : drvrs) {
+ Pin *drvr_pin = drvr->pin();
+ double wire_length = dbuToMeters(maxLoadManhattenDistance(drvr));
+ double steiner_length = dbuToMeters(findMaxSteinerDist(drvr));
+ double delay = (wire_length * wire_res) * (wire_length * wire_cap) * 0.5;
+ report_->reportLine("%s manhtn %s steiner %s %s",
+ sdc_network_->pathName(drvr_pin),
+ units_->distanceUnit()->asString(wire_length, 1),
+ units_->distanceUnit()->asString(steiner_length, 1),
+ delayAsString(delay, sta_, digits));
+ if (i == count)
+ break;
+ i++;
+ }
+}
+
+typedef std::pair<Vertex*, int> DrvrDist;
+
+void
+Resizer::findLongWires(VertexSeq &drvrs)
+{
+ Vector<DrvrDist> drvr_dists;
+ VertexIterator vertex_iter(graph_);
+ while (vertex_iter.hasNext()) {
+ Vertex *vertex = vertex_iter.next();
+ if (vertex->isDriver(network_)) {
+ Pin *pin = vertex->pin();
+ // Hands off the clock nets.
+ if (!sta_->isClock(pin)
+ && !vertex->isConstant()
+ && !vertex->isDisabledConstraint())
+ drvr_dists.push_back(DrvrDist(vertex, maxLoadManhattenDistance(vertex)));
+ }
+ }
+ sort(drvr_dists, [](const DrvrDist &drvr_dist1,
+ const DrvrDist &drvr_dist2) {
+ return drvr_dist1.second > drvr_dist2.second;
+ });
+ drvrs.reserve(drvr_dists.size());
+ for (DrvrDist &drvr_dist : drvr_dists)
+ drvrs.push_back(drvr_dist.first);
+}
+
+void
+Resizer::findLongWiresSteiner(VertexSeq &drvrs)
+{
+ Vector<DrvrDist> drvr_dists;
+ VertexIterator vertex_iter(graph_);
+ while (vertex_iter.hasNext()) {
+ Vertex *vertex = vertex_iter.next();
+ if (vertex->isDriver(network_)) {
+ Pin *pin = vertex->pin();
+ // Hands off the clock nets.
+ if (!sta_->isClock(pin)
+ && !vertex->isConstant())
+ drvr_dists.push_back(DrvrDist(vertex, findMaxSteinerDist(vertex)));
+ }
+ }
+ sort(drvr_dists, [](const DrvrDist &drvr_dist1,
+ const DrvrDist &drvr_dist2) {
+ return drvr_dist1.second > drvr_dist2.second;
+ });
+ drvrs.reserve(drvr_dists.size());
+ for (DrvrDist &drvr_dist : drvr_dists)
+ drvrs.push_back(drvr_dist.first);
+}
+
+// Find the maximum distance along steiner tree branches from
+// the driver to loads (in dbu).
+int
+Resizer::findMaxSteinerDist(Vertex *drvr)
+{
+ Pin *drvr_pin = drvr->pin();
+ SteinerTree *tree = makeSteinerTree(drvr_pin, true, max_steiner_pin_count_,
+ stt_builder_, db_network_, logger_);
+ if (tree) {
+ int dist = findMaxSteinerDist(tree);
+ delete tree;
+ return dist;
+ }
+ return 0;
+}
+
+int
+Resizer::findMaxSteinerDist(SteinerTree *tree)
+{
+ SteinerPt drvr_pt = tree->drvrPt(network_);
+ if (drvr_pt == SteinerTree::null_pt)
+ return 0;
+ else
+ return findMaxSteinerDist(tree, drvr_pt, 0);
+}
+
+// DFS of steiner tree.
+int
+Resizer::findMaxSteinerDist(SteinerTree *tree,
+ SteinerPt pt,
+ int dist_from_drvr)
+{
+ const PinSeq *pins = tree->pins(pt);
+ if (pins) {
+ for (const Pin *pin : *pins) {
+ if (db_network_->isLoad(pin))
+ return dist_from_drvr;
+ }
+ }
+ Point loc = tree->location(pt);
+ SteinerPt left = tree->left(pt);
+ int left_max = 0;
+ if (left != SteinerTree::null_pt) {
+ int left_dist = Point::manhattanDistance(loc, tree->location(left));
+ left_max = findMaxSteinerDist(tree, left, dist_from_drvr + left_dist);
+ }
+ SteinerPt right = tree->right(pt);
+ int right_max = 0;
+ if (right != SteinerTree::null_pt) {
+ int right_dist = Point::manhattanDistance(loc, tree->location(right));
+ right_max = findMaxSteinerDist(tree, right, dist_from_drvr + right_dist);
+ }
+ return max(left_max, right_max);
+}
+
+double
+Resizer::maxLoadManhattenDistance(const Net *net)
+{
+ NetPinIterator *pin_iter = network_->pinIterator(net);
+ int max_dist = 0;
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ if (network_->isDriver(pin)) {
+ Vertex *drvr = graph_->pinDrvrVertex(pin);
+ if (drvr) {
+ int dist = maxLoadManhattenDistance(drvr);
+ max_dist = max(max_dist, dist);
+ }
+ }
+ }
+ delete pin_iter;
+ return dbuToMeters(max_dist);
+}
+
+int
+Resizer::maxLoadManhattenDistance(Vertex *drvr)
+{
+ int max_dist = 0;
+ Point drvr_loc = db_network_->location(drvr->pin());
+ VertexOutEdgeIterator edge_iter(drvr, graph_);
+ while (edge_iter.hasNext()) {
+ Edge *edge = edge_iter.next();
+ Vertex *load = edge->to(graph_);
+ Point load_loc = db_network_->location(load->pin());
+ int dist = Point::manhattanDistance(drvr_loc, load_loc);
+ max_dist = max(max_dist, dist);
+ }
+ return max_dist;
+}
+
+////////////////////////////////////////////////////////////////
+
+NetSeq *
+Resizer::findFloatingNets()
+{
+ NetSeq *floating_nets = new NetSeq;
+ NetIterator *net_iter = network_->netIterator(network_->topInstance());
+ while (net_iter->hasNext()) {
+ Net *net = net_iter->next();
+ PinSeq loads;
+ PinSeq drvrs;
+ PinSet visited_drvrs;
+ FindNetDrvrLoads visitor(nullptr, visited_drvrs, loads, drvrs, network_);
+ network_->visitConnectedPins(net, visitor);
+ if (drvrs.size() == 0 && loads.size() > 0)
+ floating_nets->push_back(net);
+ }
+ delete net_iter;
+ sort(floating_nets, sta::NetPathNameLess(network_));
+ return floating_nets;
+}
+
+////////////////////////////////////////////////////////////////
+
+string
+Resizer::makeUniqueNetName()
+{
+ string node_name;
+ Instance *top_inst = network_->topInstance();
+ do
+ stringPrint(node_name, "net%d", unique_net_index_++);
+ while (network_->findNet(top_inst, node_name.c_str()));
+ return node_name;
+}
+
+Net *
+Resizer::makeUniqueNet()
+{
+ string net_name = makeUniqueNetName();
+ Instance *parent = db_network_->topInstance();
+ return db_network_->makeNet(net_name.c_str(), parent);
+}
+
+string
+Resizer::makeUniqueInstName(const char *base_name)
+{
+ return makeUniqueInstName(base_name, false);
+}
+
+string
+Resizer::makeUniqueInstName(const char *base_name,
+ bool underscore)
+{
+ string inst_name;
+ do
+ stringPrint(inst_name, underscore ? "%s_%d" : "%s%d",
+ base_name, unique_inst_index_++);
+ while (network_->findInstance(inst_name.c_str()));
+ return inst_name;
+}
+
+float
+Resizer::portFanoutLoad(LibertyPort *port)
+{
+ float fanout_load;
+ bool exists;
+ port->fanoutLoad(fanout_load, exists);
+ if (!exists) {
+ LibertyLibrary *lib = port->libertyLibrary();
+ lib->defaultFanoutLoad(fanout_load, exists);
+ }
+ if (exists)
+ return fanout_load;
+ else
+ return 0.0;
+}
+
+float
+Resizer::bufferDelay(LibertyCell *buffer_cell,
+ const RiseFall *rf,
+ float load_cap,
+ const DcalcAnalysisPt *dcalc_ap)
+{
+ LibertyPort *input, *output;
+ buffer_cell->bufferPorts(input, output);
+ ArcDelay gate_delays[RiseFall::index_count];
+ Slew slews[RiseFall::index_count];
+ gateDelays(output, load_cap, dcalc_ap, gate_delays, slews);
+ return gate_delays[rf->index()];
+}
+
+float
+Resizer::bufferDelay(LibertyCell *buffer_cell,
+ float load_cap,
+ const DcalcAnalysisPt *dcalc_ap)
+{
+ LibertyPort *input, *output;
+ buffer_cell->bufferPorts(input, output);
+ ArcDelay gate_delays[RiseFall::index_count];
+ Slew slews[RiseFall::index_count];
+ gateDelays(output, load_cap, dcalc_ap, gate_delays, slews);
+ return max(gate_delays[RiseFall::riseIndex()],
+ gate_delays[RiseFall::fallIndex()]);
+}
+
+// Self delay; buffer -> buffer
+float
+Resizer::bufferSelfDelay(LibertyCell *buffer_cell)
+{
+ const DcalcAnalysisPt *dcalc_ap = sta_->cmdCorner()->findDcalcAnalysisPt(max_);
+ LibertyPort *input, *output;
+ buffer_cell->bufferPorts(input, output);
+ ArcDelay gate_delays[RiseFall::index_count];
+ Slew slews[RiseFall::index_count];
+ float load_cap = input->capacitance();
+ gateDelays(output, load_cap, dcalc_ap, gate_delays, slews);
+ return max(gate_delays[RiseFall::riseIndex()],
+ gate_delays[RiseFall::fallIndex()]);
+}
+
+float
+Resizer::bufferSelfDelay(LibertyCell *buffer_cell,
+ const RiseFall *rf)
+{
+ const DcalcAnalysisPt *dcalc_ap = sta_->cmdCorner()->findDcalcAnalysisPt(max_);
+ LibertyPort *input, *output;
+ buffer_cell->bufferPorts(input, output);
+ ArcDelay gate_delays[RiseFall::index_count];
+ Slew slews[RiseFall::index_count];
+ float load_cap = input->capacitance();
+ gateDelays(output, load_cap, dcalc_ap, gate_delays, slews);
+ return gate_delays[rf->index()];
+}
+
+// Rise/fall delays across all timing arcs into drvr_port.
+// Uses target slew for input slew.
+void
+Resizer::gateDelays(LibertyPort *drvr_port,
+ float load_cap,
+ const DcalcAnalysisPt *dcalc_ap,
+ // Return values.
+ ArcDelay delays[RiseFall::index_count],
+ Slew slews[RiseFall::index_count])
+{
+ for (int rf_index : RiseFall::rangeIndex()) {
+ delays[rf_index] = -INF;
+ slews[rf_index] = -INF;
+ }
+ const Pvt *pvt = dcalc_ap->operatingConditions();
+ LibertyCell *cell = drvr_port->libertyCell();
+ LibertyCellTimingArcSetIterator set_iter(cell);
+ while (set_iter.hasNext()) {
+ TimingArcSet *arc_set = set_iter.next();
+ if (arc_set->to() == drvr_port
+ && !arc_set->role()->isTimingCheck()) {
+ TimingArcSetArcIterator arc_iter(arc_set);
+ while (arc_iter.hasNext()) {
+ TimingArc *arc = arc_iter.next();
+ RiseFall *in_rf = arc->fromTrans()->asRiseFall();
+ int out_rf_index = arc->toTrans()->asRiseFall()->index();
+ float in_slew = tgt_slews_[in_rf->index()];
+ ArcDelay gate_delay;
+ Slew drvr_slew;
+ arc_delay_calc_->gateDelay(cell, arc, in_slew, load_cap,
+ nullptr, 0.0, pvt, dcalc_ap,
+ gate_delay,
+ drvr_slew);
+ delays[out_rf_index] = max(delays[out_rf_index], gate_delay);
+ slews[out_rf_index] = max(slews[out_rf_index], drvr_slew);
+ }
+ }
+ }
+}
+
+ArcDelay
+Resizer::gateDelay(LibertyPort *drvr_port,
+ const RiseFall *rf,
+ float load_cap,
+ const DcalcAnalysisPt *dcalc_ap)
+{
+ ArcDelay delays[RiseFall::index_count];
+ Slew slews[RiseFall::index_count];
+ gateDelays(drvr_port, load_cap, dcalc_ap, delays, slews);
+ return delays[rf->index()];
+}
+
+ArcDelay
+Resizer::gateDelay(LibertyPort *drvr_port,
+ float load_cap,
+ const DcalcAnalysisPt *dcalc_ap)
+{
+ ArcDelay delays[RiseFall::index_count];
+ Slew slews[RiseFall::index_count];
+ gateDelays(drvr_port, load_cap, dcalc_ap, delays, slews);
+ return max(delays[RiseFall::riseIndex()], delays[RiseFall::fallIndex()]);
+}
+
+////////////////////////////////////////////////////////////////
+
+double
+Resizer::findMaxWireLength()
+{
+ double max_length = INF;
+ for (const Corner *corner : *sta_->corners()) {
+ if (wireSignalResistance(corner) > 0.0) {
+ for (LibertyCell *buffer_cell : buffer_cells_) {
+ double buffer_length = findMaxWireLength(buffer_cell, corner);
+ max_length = min(max_length, buffer_length);
+ }
+ }
+ }
+ return max_length;
+}
+
+// Find the max wire length before it is faster to split the wire
+// in half with a buffer (in meters).
+double
+Resizer::findMaxWireLength(LibertyCell *buffer_cell,
+ const Corner *corner)
+{
+ ensureBlock();
+ LibertyPort *load_port, *drvr_port;
+ buffer_cell->bufferPorts(load_port, drvr_port);
+ return findMaxWireLength(drvr_port, corner);
+}
+
+double
+Resizer::findMaxWireLength(LibertyPort *drvr_port,
+ const Corner *corner)
+{
+ LibertyCell *cell = drvr_port->libertyCell();
+ double drvr_r = drvr_port->driveResistance();
+ // wire_length1 lower bound
+ // wire_length2 upper bound
+ double wire_length1 = 0.0;
+ // Initial guess with wire resistance same as driver resistance.
+ double wire_length2 = drvr_r / wireSignalResistance(corner);
+ double tol = .01; // 1%
+ double diff1 = splitWireDelayDiff(wire_length2, cell);
+ // binary search for diff = 0.
+ while (abs(wire_length1 - wire_length2) > max(wire_length1, wire_length2) * tol) {
+ if (diff1 < 0.0) {
+ wire_length1 = wire_length2;
+ wire_length2 *= 2;
+ diff1 = splitWireDelayDiff(wire_length2, cell);
+ }
+ else {
+ double wire_length3 = (wire_length1 + wire_length2) / 2.0;
+ double diff2 = splitWireDelayDiff(wire_length3, cell);
+ if (diff2 < 0.0) {
+ wire_length1 = wire_length3;
+ }
+ else {
+ wire_length2 = wire_length3;
+ diff1 = diff2;
+ }
+ }
+ }
+ return wire_length1;
+}
+
+// objective function
+double
+Resizer::splitWireDelayDiff(double wire_length,
+ LibertyCell *buffer_cell)
+{
+ Delay delay1, delay2;
+ Slew slew1, slew2;
+ bufferWireDelay(buffer_cell, wire_length, delay1, slew1);
+ bufferWireDelay(buffer_cell, wire_length / 2, delay2, slew2);
+ return delay1 - delay2 * 2;
+}
+
+void
+Resizer::bufferWireDelay(LibertyCell *buffer_cell,
+ double wire_length, // meters
+ // Return values.
+ Delay &delay,
+ Slew &slew)
+{
+ LibertyPort *load_port, *drvr_port;
+ buffer_cell->bufferPorts(load_port, drvr_port);
+ return cellWireDelay(drvr_port, load_port, wire_length, delay, slew);
+}
+
+// Cell delay plus wire delay.
+// Use target slew for input slew.
+// drvr_port and load_port do not have to be the same liberty cell.
+void
+Resizer::cellWireDelay(LibertyPort *drvr_port,
+ LibertyPort *load_port,
+ double wire_length, // meters
+ // Return values.
+ Delay &delay,
+ Slew &slew)
+{
+ // Make a (hierarchical) block to use as a scratchpad.
+ dbBlock *block = dbBlock::create(block_, "wire_delay", '/');
+ dbSta *sta = makeBlockSta(openroad_, block);
+ Parasitics *parasitics = sta->parasitics();
+ Network *network = sta->network();
+ ArcDelayCalc *arc_delay_calc = sta->arcDelayCalc();
+ Corners *corners = sta->corners();
+ corners->copy(sta_->corners());
+
+ Instance *top_inst = network->topInstance();
+ // Tmp net for parasitics to live on.
+ Net *net = sta->makeNet("wire", top_inst);
+ LibertyCell *drvr_cell = drvr_port->libertyCell();
+ LibertyCell *load_cell = load_port->libertyCell();
+ Instance *drvr = sta->makeInstance("drvr", drvr_cell, top_inst);
+ Instance *load = sta->makeInstance("load", load_cell, top_inst);
+ sta->connectPin(drvr, drvr_port, net);
+ sta->connectPin(load, load_port, net);
+ Pin *drvr_pin = network->findPin(drvr, drvr_port);
+ Pin *load_pin = network->findPin(load, load_port);
+
+ // Max rise/fall delays.
+ delay = -INF;
+ slew = -INF;
+
+ for (Corner *corner : *corners) {
+ const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(max_);
+ const Pvt *pvt = dcalc_ap->operatingConditions();
+
+ makeWireParasitic(net, drvr_pin, load_pin, wire_length, corner, parasitics);
+ // Let delay calc reduce parasitic network as it sees fit.
+ Parasitic *drvr_parasitic = arc_delay_calc->findParasitic(drvr_pin,
+ RiseFall::rise(),
+ dcalc_ap);
+ LibertyCellTimingArcSetIterator set_iter(drvr_cell);
+ while (set_iter.hasNext()) {
+ TimingArcSet *arc_set = set_iter.next();
+ if (arc_set->to() == drvr_port) {
+ TimingArcSetArcIterator arc_iter(arc_set);
+ while (arc_iter.hasNext()) {
+ TimingArc *arc = arc_iter.next();
+ RiseFall *in_rf = arc->fromTrans()->asRiseFall();
+ double in_slew = tgt_slews_[in_rf->index()];
+ ArcDelay gate_delay;
+ Slew drvr_slew;
+ arc_delay_calc->gateDelay(drvr_cell, arc, in_slew, 0.0,
+ drvr_parasitic, 0.0, pvt, dcalc_ap,
+ gate_delay,
+ drvr_slew);
+ ArcDelay wire_delay;
+ Slew load_slew;
+ arc_delay_calc->loadDelay(load_pin, wire_delay, load_slew);
+ delay = max(delay, gate_delay + wire_delay);
+ slew = max(slew, load_slew);
+ }
+ }
+ }
+ arc_delay_calc->finishDrvrPin();
+ parasitics->deleteParasitics(net, dcalc_ap->parasiticAnalysisPt());
+ }
+
+ // Cleanup the turds.
+ sta->deleteInstance(drvr);
+ sta->deleteInstance(load);
+ sta->deleteNet(net);
+ delete sta;
+ dbBlock::destroy(block);
+}
+
+void
+Resizer::makeWireParasitic(Net *net,
+ Pin *drvr_pin,
+ Pin *load_pin,
+ double wire_length, // meters
+ const Corner *corner,
+ Parasitics *parasitics)
+{
+ const ParasiticAnalysisPt *parasitics_ap =
+ corner->findParasiticAnalysisPt(max_);
+ Parasitic *parasitic = parasitics->makeParasiticNetwork(net, false,
+ parasitics_ap);
+ ParasiticNode *n1 = parasitics->ensureParasiticNode(parasitic, drvr_pin);
+ ParasiticNode *n2 = parasitics->ensureParasiticNode(parasitic, load_pin);
+ double wire_cap = wire_length * wireSignalCapacitance(corner);
+ double wire_res = wire_length * wireSignalResistance(corner);
+ parasitics->incrCap(n1, wire_cap / 2.0, parasitics_ap);
+ parasitics->makeResistor(nullptr, n1, n2, wire_res, parasitics_ap);
+ parasitics->incrCap(n2, wire_cap / 2.0, parasitics_ap);
+}
+
+////////////////////////////////////////////////////////////////
+
+double
+Resizer::findMaxSlewWireLength(LibertyPort *drvr_port,
+ LibertyPort *load_port,
+ double max_slew,
+ const Corner *corner)
+{
+ ensureBlock();
+ // wire_length1 lower bound
+ // wire_length2 upper bound
+ double wire_length1 = 0.0;
+ double wire_length2 = std::sqrt(max_slew /(wireSignalResistance(corner)
+ * wireSignalCapacitance(corner)));
+ double tol = .01; // 1%
+ double diff1 = maxSlewWireDiff(drvr_port, load_port, wire_length2, max_slew);
+ // binary search for diff = 0.
+ while (abs(wire_length1 - wire_length2) > max(wire_length1, wire_length2) * tol) {
+ if (diff1 < 0.0) {
+ wire_length1 = wire_length2;
+ wire_length2 *= 2;
+ diff1 = maxSlewWireDiff(drvr_port, load_port, wire_length2, max_slew);
+ }
+ else {
+ double wire_length3 = (wire_length1 + wire_length2) / 2.0;
+ double diff2 = maxSlewWireDiff(drvr_port, load_port, wire_length3, max_slew);
+ if (diff2 < 0.0) {
+ wire_length1 = wire_length3;
+ }
+ else {
+ wire_length2 = wire_length3;
+ diff1 = diff2;
+ }
+ }
+ }
+ return wire_length1;
+}
+
+// objective function
+double
+Resizer::maxSlewWireDiff(LibertyPort *drvr_port,
+ LibertyPort *load_port,
+ double wire_length,
+ double max_slew)
+{
+ Delay delay;
+ Slew slew;
+ cellWireDelay(drvr_port, load_port, wire_length, delay, slew);
+ return slew - max_slew;
+}
+
+////////////////////////////////////////////////////////////////
+
+double
+Resizer::designArea()
+{
+ ensureBlock();
+ ensureDesignArea();
+ return design_area_;
+}
+
+void
+Resizer::designAreaIncr(float delta)
+{
+ design_area_ += delta;
+}
+
+void
+Resizer::ensureDesignArea()
+{
+ if (block_) {
+ design_area_ = 0.0;
+ for (dbInst *inst : block_->getInsts()) {
+ dbMaster *master = inst->getMaster();
+ // Don't count fillers otherwise you'll always get 100% utilization
+ if (!master->isFiller()) {
+ design_area_ += area(master);
+ }
+ }
+ }
+}
+
+int
+Resizer::fanout(Pin *drvr_pin)
+{
+ int fanout = 0;
+ NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(drvr_pin);
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ if (pin != drvr_pin)
+ fanout++;
+ }
+ delete pin_iter;
+ return fanout;
+}
+
+bool
+Resizer::isFuncOneZero(const Pin *drvr_pin)
+{
+ LibertyPort *port = network_->libertyPort(drvr_pin);
+ if (port) {
+ FuncExpr *func = port->function();
+ return func && (func->op() == FuncExpr::op_zero
+ || func->op() == FuncExpr::op_one);
+ }
+ return false;
+}
+
+////////////////////////////////////////////////////////////////
+
+void
+Resizer::repairClkInverters()
+{
+ // Abbreviated copyState
+ db_network_ = sta_->getDbNetwork();
+ sta_->ensureLevelized();
+ graph_ = sta_->graph();
+ ensureBlock();
+ ensureDesignArea();
+ for (Instance *inv : findClkInverters())
+ cloneClkInverter(inv);
+}
+
+InstanceSeq
+Resizer::findClkInverters()
+{
+ InstanceSeq clk_inverters;
+ ClkArrivalSearchPred srch_pred(this);
+ BfsFwdIterator bfs(BfsIndex::other, &srch_pred, this);
+ for (Clock *clk : sdc_->clks()) {
+ for (Pin *pin : clk->leafPins()) {
+ Vertex *vertex = graph_->pinDrvrVertex(pin);
+ bfs.enqueue(vertex);
+ }
+ }
+ while (bfs.hasNext()) {
+ Vertex *vertex = bfs.next();
+ const Pin *pin = vertex->pin();
+ Instance *inst = network_->instance(pin);
+ LibertyCell *lib_cell = network_->libertyCell(inst);
+ if (vertex->isDriver(network_)
+ && lib_cell
+ && lib_cell->isInverter()) {
+ clk_inverters.push_back(inst);
+ debugPrint(logger_, RSZ, "repair_clk_inverters", 2, "inverter {}",
+ network_->pathName(inst));
+ }
+ if (!vertex->isRegClk())
+ bfs.enqueueAdjacentVertices(vertex);
+ }
+ return clk_inverters;
+}
+
+void
+Resizer::cloneClkInverter(Instance *inv)
+{
+ LibertyCell *inv_cell = network_->libertyCell(inv);
+ LibertyPort *in_port, *out_port;
+ inv_cell->bufferPorts(in_port, out_port);
+ Pin *in_pin = network_->findPin(inv, in_port);
+ Pin *out_pin = network_->findPin(inv, out_port);
+ Net *in_net = network_->net(in_pin);
+ dbNet *in_net_db = db_network_->staToDb(in_net);
+ Net *out_net = network_->isTopLevelPort(out_pin)
+ ? network_->net(network_->term(out_pin))
+ : network_->net(out_pin);
+ if (out_net) {
+ const char *inv_name = network_->name(inv);
+ Instance *top_inst = network_->topInstance();
+ NetConnectedPinIterator *load_iter = network_->pinIterator(out_net);
+ while (load_iter->hasNext()) {
+ Pin *load_pin = load_iter->next();
+ if (load_pin != out_pin) {
+ string clone_name = makeUniqueInstName(inv_name, true);
+ Instance *clone = sta_->makeInstance(clone_name.c_str(),
+ inv_cell, top_inst);
+ Point clone_loc = db_network_->location(load_pin);
+ journalMakeBuffer(clone);
+ setLocation(clone, clone_loc);
+
+ Net *clone_out_net = makeUniqueNet();
+ dbNet *clone_out_net_db = db_network_->staToDb(clone_out_net);
+ clone_out_net_db->setSigType(in_net_db->getSigType());
+
+ Instance *load = network_->instance(load_pin);
+ sta_->connectPin(clone, in_port, in_net);
+ sta_->connectPin(clone, out_port, clone_out_net);
+
+ // Connect load to clone
+ sta_->disconnectPin(load_pin);
+ Port *load_port = network_->port(load_pin);
+ sta_->connectPin(load, load_port, clone_out_net);
+ }
+ }
+ delete load_iter;
+
+ // Delete inv
+ sta_->disconnectPin(in_pin);
+ sta_->disconnectPin(out_pin);
+ sta_->deleteNet(out_net);
+ parasitics_invalid_.erase(out_net);
+ sta_->deleteInstance(inv);
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+// Journal to roll back changes (OpenDB not up to the task).
+void
+Resizer::journalBegin()
+{
+ debugPrint(logger_, RSZ, "journal", 1, "journal begin");
+ resized_inst_map_.clear();
+ inserted_buffers_.clear();
+}
+
+void
+Resizer::journalInstReplaceCellBefore(Instance *inst)
+{
+ LibertyCell *lib_cell = network_->libertyCell(inst);
+ debugPrint(logger_, RSZ, "journal", 1, "journal replace {} ({})",
+ network_->pathName(inst),
+ lib_cell->name());
+ // Do not clobber an existing checkpoint cell.
+ if (!resized_inst_map_.hasKey(inst))
+ resized_inst_map_[inst] = lib_cell;
+}
+
+void
+Resizer::journalMakeBuffer(Instance *buffer)
+{
+ debugPrint(logger_, RSZ, "journal", 1, "journal make_buffer {}",
+ network_->pathName(buffer));
+ inserted_buffers_.insert(buffer);
+}
+
+void
+Resizer::journalRestore()
+{
+ for (auto inst_cell : resized_inst_map_) {
+ Instance *inst = inst_cell.first;
+ if (!inserted_buffers_.hasKey(inst)) {
+ LibertyCell *lib_cell = inst_cell.second;
+ debugPrint(logger_, RSZ, "journal", 1, "journal restore {} ({})",
+ network_->pathName(inst),
+ lib_cell->name());
+ replaceCell(inst, lib_cell, false);
+ resize_count_--;
+ }
+ }
+ for (Instance *buffer : inserted_buffers_) {
+ debugPrint(logger_, RSZ, "journal", 1, "journal remove {}",
+ network_->pathName(buffer));
+ removeBuffer(buffer);
+ inserted_buffer_count_--;
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+class SteinerRenderer : public gui::Renderer
+{
+public:
+ SteinerRenderer();
+ void highlight(SteinerTree *tree);
+ virtual void drawObjects(gui::Painter& /* painter */) override;
+
+private:
+ SteinerTree *tree_;
+};
+
+void
+Resizer::highlightSteiner(const Pin *drvr)
+{
+ if (gui::Gui::enabled()) {
+ if (steiner_renderer_ == nullptr) {
+ steiner_renderer_ = new SteinerRenderer();
+ gui_->registerRenderer(steiner_renderer_);
+ }
+ SteinerTree *tree = nullptr;
+ if (drvr)
+ tree = makeSteinerTree(drvr, false, max_steiner_pin_count_,
+ stt_builder_, db_network_, logger_);
+ steiner_renderer_->highlight(tree);
+ }
+}
+
+SteinerRenderer::SteinerRenderer() :
+ tree_(nullptr)
+{
+}
+
+void
+SteinerRenderer::highlight(SteinerTree *tree)
+{
+ tree_ = tree;
+}
+
+void
+SteinerRenderer::drawObjects(gui::Painter &painter)
+{
+ if (tree_) {
+ painter.setPen(gui::Painter::red, true);
+ for (int i = 0 ; i < tree_->branchCount(); ++i) {
+ Point pt1, pt2;
+ int steiner_pt1, steiner_pt2;
+ int wire_length;
+ tree_->branch(i, pt1, steiner_pt1, pt2, steiner_pt2, wire_length);
+ painter.drawLine(pt1, pt2);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+PinSet
+Resizer::findFaninFanouts(PinSet *end_pins)
+{
+ // Abbreviated copyState
+ db_network_ = sta_->getDbNetwork();
+ sta_->ensureLevelized();
+ graph_ = sta_->graph();
+
+ VertexSet ends;
+ for (Pin *pin : *end_pins) {
+ Vertex *end = graph_->pinLoadVertex(pin);
+ ends.insert(end);
+ }
+ PinSet fanin_fanout_pins;
+ VertexSet fanin_fanouts = findFaninFanouts(ends);
+ for (Vertex *vertex : fanin_fanouts)
+ fanin_fanout_pins.insert(vertex->pin());
+ return fanin_fanout_pins;
+}
+
+VertexSet
+Resizer::findFaninFanouts(VertexSet &ends)
+{
+ // Search backwards from ends to fanin register outputs and input ports.
+ VertexSet fanin_roots = findFaninRoots(ends);
+ // Search forward from register outputs.
+ VertexSet fanouts = findFanouts(fanin_roots);
+ return fanouts;
+}
+
+// Find source pins for logic fanin of ends.
+PinSet
+Resizer::findFanins(PinSet *end_pins)
+{
+ // Abbreviated copyState
+ db_network_ = sta_->getDbNetwork();
+ sta_->ensureLevelized();
+ graph_ = sta_->graph();
+
+ VertexSet ends;
+ for (Pin *pin : *end_pins) {
+ Vertex *end = graph_->pinLoadVertex(pin);
+ ends.insert(end);
+ }
+
+ SearchPredNonReg2 pred(sta_);
+ BfsBkwdIterator iter(BfsIndex::other, &pred, this);
+ for (Vertex *vertex : ends)
+ iter.enqueueAdjacentVertices(vertex);
+
+ PinSet fanins;
+ while (iter.hasNext()) {
+ Vertex *vertex = iter.next();
+ if (isRegOutput(vertex)
+ || network_->isTopLevelPort(vertex->pin()))
+ continue;
+ else {
+ iter.enqueueAdjacentVertices(vertex);
+ fanins.insert(vertex->pin());
+ }
+ }
+ return fanins;
+}
+
+// Find roots for logic fanin of ends.
+VertexSet
+Resizer::findFaninRoots(VertexSet &ends)
+{
+ SearchPredNonReg2 pred(sta_);
+ BfsBkwdIterator iter(BfsIndex::other, &pred, this);
+ for (Vertex *vertex : ends)
+ iter.enqueueAdjacentVertices(vertex);
+
+ VertexSet roots;
+ while (iter.hasNext()) {
+ Vertex *vertex = iter.next();
+ if (isRegOutput(vertex)
+ || network_->isTopLevelPort(vertex->pin()))
+ roots.insert(vertex);
+ else
+ iter.enqueueAdjacentVertices(vertex);
+ }
+ return roots;
+}
+
+bool
+Resizer::isRegOutput(Vertex *vertex)
+{
+ LibertyPort *port = network_->libertyPort(vertex->pin());
+ if (port) {
+ LibertyCell *cell = port->libertyCell();
+ LibertyCellTimingArcSetIterator arc_set_iter(cell, nullptr, port);
+ while (arc_set_iter.hasNext()) {
+ TimingArcSet *arc_set = arc_set_iter.next();
+ if (arc_set->role()->genericRole() == TimingRole::regClkToQ())
+ return true;
+ }
+ }
+ return false;
+}
+
+VertexSet
+Resizer::findFanouts(VertexSet ®_outs)
+{
+ VertexSet fanouts;
+ sta::SearchPredNonLatch2 pred(sta_);
+ BfsFwdIterator iter(BfsIndex::other, &pred, this);
+ for (Vertex *reg_out : reg_outs)
+ iter.enqueueAdjacentVertices(reg_out);
+
+ while (iter.hasNext()) {
+ Vertex *vertex = iter.next();
+ if (!isRegister(vertex)) {
+ fanouts.insert(vertex);
+ iter.enqueueAdjacentVertices(vertex);
+ }
+ }
+ return fanouts;
+}
+
+bool
+Resizer::isRegister(Vertex *vertex)
+{
+ LibertyPort *port = network_->libertyPort(vertex->pin());
+ if (port) {
+ LibertyCell *cell = port->libertyCell();
+ return cell && cell->hasSequentials();
+ }
+ return false;
+}
+
+} // namespace
diff --git a/hacks/src/OpenSTA/network/ConcreteNetwork.cc b/hacks/src/OpenSTA/network/ConcreteNetwork.cc
new file mode 100644
index 0000000..8096f2e
--- /dev/null
+++ b/hacks/src/OpenSTA/network/ConcreteNetwork.cc
@@ -0,0 +1,1996 @@
+// OpenSTA, Static Timing Analyzer
+// Copyright (c) 2021, 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 <https://www.gnu.org/licenses/>.
+
+#include "ConcreteNetwork.hh"
+
+#include "DisallowCopyAssign.hh"
+#include "PatternMatch.hh"
+#include "Report.hh"
+#include "Liberty.hh"
+#include "PortDirection.hh"
+#include "ConcreteLibrary.hh"
+#include "Network.hh"
+
+namespace sta {
+
+static void
+makeChildNetwork(Instance *proto,
+ Instance *parent,
+ ConcreteBindingTbl *parent_bindings,
+ NetworkReader *network);
+static void
+makeClonePins(Instance *proto,
+ Instance *clone,
+ Instance *clone_view,
+ ConcreteBindingTbl *bindings,
+ Instance *parent,
+ ConcreteBindingTbl *parent_bindings,
+ NetworkReader *network);
+
+NetworkReader *
+makeConcreteNetwork()
+{
+ return new ConcreteNetwork;
+}
+
+class ConcreteInstanceChildIterator : public InstanceChildIterator
+{
+public:
+ explicit ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map);
+ bool hasNext();
+ Instance *next();
+
+private:
+ ConcreteInstanceChildMap::ConstIterator iter_;
+};
+
+ConcreteInstanceChildIterator::
+ConcreteInstanceChildIterator(ConcreteInstanceChildMap *map) :
+ iter_(map)
+{
+}
+
+bool
+ConcreteInstanceChildIterator::hasNext()
+{
+ return iter_.hasNext();
+}
+
+Instance *
+ConcreteInstanceChildIterator::next()
+{
+ return reinterpret_cast<Instance*>(iter_.next());
+}
+
+class ConcreteInstanceNetIterator : public InstanceNetIterator
+{
+public:
+ explicit ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets);
+ bool hasNext();
+ Net *next();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ConcreteInstanceNetIterator);
+ void findNext();
+
+ ConcreteInstanceNetMap::Iterator iter_;
+ ConcreteNet *next_;
+};
+
+ConcreteInstanceNetIterator::
+ConcreteInstanceNetIterator(ConcreteInstanceNetMap *nets):
+ iter_(nets),
+ next_(nullptr)
+{
+ findNext();
+}
+
+bool
+ConcreteInstanceNetIterator::hasNext()
+{
+ return next_ != nullptr;
+}
+
+// Skip nets that have been merged.
+void
+ConcreteInstanceNetIterator::findNext()
+{
+ while (iter_.hasNext()) {
+ next_ = iter_.next();
+ if (next_->mergedInto() == nullptr)
+ return;
+ }
+ next_ = nullptr;
+}
+
+Net *
+ConcreteInstanceNetIterator::next()
+{
+ ConcreteNet *next = next_;
+ findNext();
+ return reinterpret_cast<Net*>(next);
+}
+
+////////////////////////////////////////////////////////////////
+
+class ConcreteInstancePinIterator : public InstancePinIterator
+{
+public:
+ ConcreteInstancePinIterator(const ConcreteInstance *inst,
+ int pin_count);
+ bool hasNext();
+ Pin *next();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ConcreteInstancePinIterator);
+ void findNext();
+
+ ConcretePin **pins_;
+ int pin_count_;
+ int pin_index_;
+ ConcretePin *next_;
+};
+
+ConcreteInstancePinIterator::
+ConcreteInstancePinIterator(const ConcreteInstance *inst,
+ int pin_count) :
+ pins_(inst->pins_),
+ pin_count_(pin_count),
+ pin_index_(0)
+{
+ findNext();
+}
+
+bool
+ConcreteInstancePinIterator::hasNext()
+{
+ return next_ != nullptr;
+}
+
+Pin *
+ConcreteInstancePinIterator::next()
+{
+ ConcretePin *next = next_;
+ findNext();
+ return reinterpret_cast<Pin*>(next);
+}
+
+// Skip over missing pins.
+void
+ConcreteInstancePinIterator::findNext()
+{
+ while (pin_index_ < pin_count_) {
+ next_ = pins_[pin_index_++];
+ if (next_)
+ return;
+ }
+ next_ = nullptr;
+}
+
+////////////////////////////////////////////////////////////////
+
+class ConcreteNetPinIterator : public NetPinIterator
+{
+public:
+ explicit ConcreteNetPinIterator(const ConcreteNet *net);
+ bool hasNext();
+ Pin *next();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ConcreteNetPinIterator);
+
+ ConcretePin *next_;
+};
+
+ConcreteNetPinIterator::ConcreteNetPinIterator(const ConcreteNet *net) :
+ next_(net->pins_)
+{
+}
+
+bool
+ConcreteNetPinIterator::hasNext()
+{
+ return next_ != nullptr;
+}
+
+Pin *
+ConcreteNetPinIterator::next()
+{
+ ConcretePin *next = next_;
+ next_ = next_->net_next_;
+ return reinterpret_cast<Pin*>(next);
+}
+
+////////////////////////////////////////////////////////////////
+
+class ConcreteNetTermIterator : public NetTermIterator
+{
+public:
+ explicit ConcreteNetTermIterator(const ConcreteNet *net);
+ bool hasNext();
+ Term *next();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ConcreteNetTermIterator);
+
+ ConcreteTerm *next_;
+};
+
+ConcreteNetTermIterator::ConcreteNetTermIterator(const ConcreteNet *net) :
+ next_(net->terms_)
+{
+}
+
+bool
+ConcreteNetTermIterator::hasNext()
+{
+ return next_ != nullptr;
+}
+
+Term *
+ConcreteNetTermIterator::next()
+{
+ ConcreteTerm *next = next_;
+ next_ = next_->net_next_;
+ return reinterpret_cast<Term*>(next);
+}
+
+////////////////////////////////////////////////////////////////
+
+ConcreteNetwork::ConcreteNetwork() :
+ NetworkReader(),
+ top_instance_(nullptr),
+ link_func_(nullptr)
+{
+}
+
+ConcreteNetwork::~ConcreteNetwork()
+{
+ clear();
+}
+
+void
+ConcreteNetwork::clear()
+{
+ deleteTopInstance();
+ deleteCellNetworkViews();
+ library_seq_.deleteContentsClear();
+ library_map_.clear();
+ Network::clear();
+}
+
+void
+ConcreteNetwork::deleteTopInstance()
+{
+ if (top_instance_) {
+ deleteInstance(top_instance_);
+ top_instance_ = nullptr;
+ }
+}
+
+void
+ConcreteNetwork::deleteCellNetworkViews()
+{
+ CellNetworkViewMap::Iterator view_iter(cell_network_view_map_);
+ while (view_iter.hasNext()) {
+ Instance *view = view_iter.next();
+ if (view)
+ deleteInstance(view);
+ }
+ cell_network_view_map_.clear();
+}
+
+Instance *
+ConcreteNetwork::topInstance() const
+{
+ return top_instance_;
+}
+
+////////////////////////////////////////////////////////////////
+
+class ConcreteLibraryIterator1 : public Iterator<Library*>
+{
+public:
+ explicit ConcreteLibraryIterator1(const ConcreteLibrarySeq &lib_seq_);
+ virtual bool hasNext();
+ virtual Library *next();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ConcreteLibraryIterator1);
+
+ ConcreteLibraryIterator iter_;
+};
+
+ConcreteLibraryIterator1::ConcreteLibraryIterator1(const ConcreteLibrarySeq &lib_seq_):
+ iter_(lib_seq_)
+{
+}
+
+bool
+ConcreteLibraryIterator1::hasNext()
+{
+ return iter_.hasNext();
+}
+
+Library *
+ConcreteLibraryIterator1::next()
+{
+ return reinterpret_cast<Library*>(iter_.next());
+}
+
+LibraryIterator *
+ConcreteNetwork::libraryIterator() const
+{
+ return new ConcreteLibraryIterator1(library_seq_);
+}
+
+////////////////////////////////////////////////////////////////
+
+class ConcreteLibertyLibraryIterator : public Iterator<LibertyLibrary*>
+{
+public:
+ explicit ConcreteLibertyLibraryIterator(const ConcreteNetwork *network);
+ virtual ~ConcreteLibertyLibraryIterator();
+ virtual bool hasNext();
+ virtual LibertyLibrary *next();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ConcreteLibertyLibraryIterator);
+ void findNext();
+
+ ConcreteLibrarySeq::ConstIterator iter_;
+ LibertyLibrary *next_;
+};
+
+ConcreteLibertyLibraryIterator::
+ConcreteLibertyLibraryIterator(const ConcreteNetwork *network):
+ iter_(network->library_seq_),
+ next_(nullptr)
+{
+ findNext();
+}
+
+ConcreteLibertyLibraryIterator::~ConcreteLibertyLibraryIterator()
+{
+}
+
+bool
+ConcreteLibertyLibraryIterator::hasNext()
+{
+ return next_ != nullptr;
+}
+
+LibertyLibrary *
+ConcreteLibertyLibraryIterator::next()
+{
+ LibertyLibrary *next = next_;
+ findNext();
+ return next;
+}
+
+void
+ConcreteLibertyLibraryIterator::findNext()
+{
+ next_ = nullptr;
+ while (iter_.hasNext()) {
+ ConcreteLibrary *lib = iter_.next();
+ if (lib->isLiberty()) {
+ LibertyLibrary *liberty = static_cast<LibertyLibrary*>(lib);
+ if (liberty) {
+ next_ = liberty;
+ break;
+ }
+ }
+ }
+}
+
+LibertyLibraryIterator *
+ConcreteNetwork::libertyLibraryIterator() const
+{
+ return new ConcreteLibertyLibraryIterator(this);
+}
+
+////////////////////////////////////////////////////////////////
+
+Library *
+ConcreteNetwork::makeLibrary(const char *name,
+ const char *filename)
+{
+ ConcreteLibrary *library = new ConcreteLibrary(name, filename, false);
+ addLibrary(library);
+ return reinterpret_cast<Library*>(library);
+}
+
+LibertyLibrary *
+ConcreteNetwork::makeLibertyLibrary(const char *name,
+ const char *filename)
+{
+ LibertyLibrary *library = new LibertyLibrary(name, filename);
+ addLibrary(library);
+ return library;
+}
+
+void
+ConcreteNetwork::addLibrary(ConcreteLibrary *library)
+{
+ library_seq_.push_back(library);
+ library_map_[library->name()] = library;
+}
+
+Library *
+ConcreteNetwork::findLibrary(const char *name)
+{
+ return reinterpret_cast<Library*>(library_map_.findKey(name));
+}
+
+void
+ConcreteNetwork::deleteLibrary(Library *library)
+{
+ ConcreteLibrary *clib = reinterpret_cast<ConcreteLibrary*>(library);
+ library_map_.erase(clib->name());
+ library_seq_.eraseObject(clib);
+ delete clib;
+}
+
+const char *
+ConcreteNetwork::name(const Library *library) const
+{
+ const ConcreteLibrary *clib =
+ reinterpret_cast<const ConcreteLibrary*>(library);
+ return clib->name();
+}
+
+LibertyLibrary *
+ConcreteNetwork::findLiberty(const char *name)
+{
+ ConcreteLibrary *lib = library_map_.findKey(name);
+ if (lib) {
+ if (lib->isLiberty())
+ return static_cast<LibertyLibrary*>(lib);
+ // Potential name conflict
+ else {
+ for (ConcreteLibrary *lib : library_seq_) {
+ if (stringEq(lib->name(), name)
+ && lib->isLiberty())
+ return static_cast<LibertyLibrary*>(lib);
+ }
+ }
+ }
+ return nullptr;
+}
+
+LibertyLibrary *
+ConcreteNetwork::libertyLibrary(Library *library) const
+{
+ ConcreteLibrary *lib = reinterpret_cast<ConcreteLibrary*>(library);
+ return static_cast<LibertyLibrary*>(lib);
+}
+
+Cell *
+ConcreteNetwork::makeCell(Library *library,
+ const char *name,
+ bool is_leaf,
+ const char *filename)
+{
+ ConcreteLibrary *clib = reinterpret_cast<ConcreteLibrary*>(library);
+ return reinterpret_cast<Cell*>(clib->makeCell(name, is_leaf, filename));
+}
+
+Cell *
+ConcreteNetwork::findCell(const Library *library,
+ const char *name) const
+{
+ const ConcreteLibrary *clib =
+ reinterpret_cast<const ConcreteLibrary*>(library);
+ return reinterpret_cast<Cell*>(clib->findCell(name));
+}
+
+Cell *
+ConcreteNetwork::findAnyCell(const char *name)
+{
+ ConcreteLibrarySeq::Iterator lib_iter(library_seq_);
+ while (lib_iter.hasNext()) {
+ ConcreteLibrary *lib = lib_iter.next();
+ ConcreteCell *cell = lib->findCell(name);
+ if (cell)
+ return reinterpret_cast<Cell*>(cell);
+ }
+ return nullptr;
+}
+
+void
+ConcreteNetwork::findCellsMatching(const Library *library,
+ const PatternMatch *pattern,
+ CellSeq *cells) const
+{
+ const ConcreteLibrary *clib =
+ reinterpret_cast<const ConcreteLibrary*>(library);
+ clib->findCellsMatching(pattern, cells);
+}
+
+void
+ConcreteNetwork::deleteCell(Cell *cell)
+{
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ ConcreteLibrary *clib = ccell->library();
+ clib->deleteCell(ccell);
+}
+
+////////////////////////////////////////////////////////////////
+
+const char *
+ConcreteNetwork::name(const Cell *cell) const
+{
+ const ConcreteCell *ccell = reinterpret_cast<const ConcreteCell*>(cell);
+ return ccell->name();
+}
+
+void
+ConcreteNetwork::setName(Cell *cell,
+ const char *name)
+{
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ ccell->setName(name);
+}
+
+void
+ConcreteNetwork::setIsLeaf(Cell *cell,
+ bool is_leaf)
+{
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ ccell->setIsLeaf(is_leaf);
+}
+
+Library *
+ConcreteNetwork::library(const Cell *cell) const
+{
+ const ConcreteCell *ccell = reinterpret_cast<const ConcreteCell*>(cell);
+ return reinterpret_cast<Library*>(ccell->library());
+}
+
+LibertyCell *
+ConcreteNetwork::libertyCell(Cell *cell) const
+{
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ return ccell->libertyCell();
+}
+
+const LibertyCell *
+ConcreteNetwork::libertyCell(const Cell *cell) const
+{
+ const ConcreteCell *ccell = reinterpret_cast<const ConcreteCell*>(cell);
+ return ccell->libertyCell();
+}
+
+Cell *
+ConcreteNetwork::cell(LibertyCell *cell) const
+{
+ return reinterpret_cast<Cell*>(cell);
+}
+
+const Cell *
+ConcreteNetwork::cell(const LibertyCell *cell) const
+{
+ return reinterpret_cast<const Cell*>(cell);
+}
+
+const char *
+ConcreteNetwork::filename(const Cell *cell)
+{
+ const ConcreteCell *ccell = reinterpret_cast<const ConcreteCell*>(cell);
+ return ccell->filename();
+}
+
+Port *
+ConcreteNetwork::findPort(const Cell *cell,
+ const char *name) const
+{
+ const ConcreteCell *ccell = reinterpret_cast<const ConcreteCell*>(cell);
+ return reinterpret_cast<Port*>(ccell->findPort(name));
+}
+
+void
+ConcreteNetwork::findPortsMatching(const Cell *cell,
+ const PatternMatch *pattern,
+ PortSeq *ports) const
+{
+ const ConcreteCell *ccell = reinterpret_cast<const ConcreteCell*>(cell);
+ ccell->findPortsMatching(pattern, ports);
+}
+
+bool
+ConcreteNetwork::isLeaf(const Cell *cell) const
+{
+ const ConcreteCell *ccell = reinterpret_cast<const ConcreteCell*>(cell);
+ return ccell->isLeaf();
+}
+
+Port *
+ConcreteNetwork::makePort(Cell *cell,
+ const char *name)
+{
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ ConcretePort *port = ccell->makePort(name);
+ return reinterpret_cast<Port*>(port);
+}
+
+Port *
+ConcreteNetwork::makeBusPort(Cell *cell,
+ const char *name,
+ int from_index,
+ int to_index)
+{
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ ConcretePort *port = ccell->makeBusPort(name, from_index, to_index);
+ return reinterpret_cast<Port*>(port);
+}
+
+void
+ConcreteNetwork::groupBusPorts(Cell *cell,
+ std::function<bool(const char*)> port_msb_first)
+{
+ Library *lib = library(cell);
+ ConcreteLibrary *clib = reinterpret_cast<ConcreteLibrary*>(lib);
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ ccell->groupBusPorts(clib->busBrktLeft(), clib->busBrktRight(), port_msb_first);
+}
+
+Port *
+ConcreteNetwork::makeBundlePort(Cell *cell,
+ const char *name,
+ PortSeq *members)
+{
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ ConcretePortSeq *cmembers = reinterpret_cast<ConcretePortSeq*>(members);
+ ConcretePort *port = ccell->makeBundlePort(name, cmembers);
+ return reinterpret_cast<Port*>(port);
+}
+
+void
+ConcreteNetwork::setDirection(Port *port,
+ PortDirection *dir)
+{
+ ConcretePort *cport = reinterpret_cast<ConcretePort*>(port);
+ cport->setDirection(dir);
+}
+
+////////////////////////////////////////////////////////////////
+
+class ConcreteCellPortIterator1 : public CellPortIterator
+{
+public:
+ explicit ConcreteCellPortIterator1(const ConcreteCell *cell);
+ ~ConcreteCellPortIterator1();
+ virtual bool hasNext() { return iter_->hasNext(); }
+ virtual Port *next();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ConcreteCellPortIterator1);
+
+ ConcreteCellPortIterator *iter_;
+};
+
+ConcreteCellPortIterator1::ConcreteCellPortIterator1(const ConcreteCell *cell):
+ iter_(cell->portIterator())
+{
+}
+
+ConcreteCellPortIterator1::~ConcreteCellPortIterator1()
+{
+ delete iter_;
+}
+
+Port *
+ConcreteCellPortIterator1::next()
+{
+ return reinterpret_cast<Port*>(iter_->next());
+}
+
+CellPortIterator *
+ConcreteNetwork::portIterator(const Cell *cell) const
+{
+ const ConcreteCell *ccell = reinterpret_cast<const ConcreteCell*>(cell);
+ return new ConcreteCellPortIterator1(ccell);
+}
+
+////////////////////////////////////////////////////////////////
+
+class ConcreteCellPortBitIterator1 : public CellPortIterator
+{
+public:
+ explicit ConcreteCellPortBitIterator1(const ConcreteCell *cell);
+ ~ConcreteCellPortBitIterator1();
+ virtual bool hasNext() { return iter_->hasNext(); }
+ virtual Port *next();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ConcreteCellPortBitIterator1);
+
+ ConcreteCellPortBitIterator *iter_;
+};
+
+ConcreteCellPortBitIterator1::ConcreteCellPortBitIterator1(const ConcreteCell *cell):
+ iter_(cell->portBitIterator())
+{
+}
+
+ConcreteCellPortBitIterator1::~ConcreteCellPortBitIterator1()
+{
+ delete iter_;
+}
+
+Port *
+ConcreteCellPortBitIterator1::next()
+{
+ return reinterpret_cast<Port*>(iter_->next());
+}
+
+CellPortBitIterator *
+ConcreteNetwork::portBitIterator(const Cell *cell) const
+{
+ const ConcreteCell *ccell = reinterpret_cast<const ConcreteCell*>(cell);
+ return new ConcreteCellPortBitIterator1(ccell);
+}
+
+int
+ConcreteNetwork::portBitCount(const Cell *cell) const
+{
+ const ConcreteCell *ccell = reinterpret_cast<const ConcreteCell*>(cell);
+ return ccell->portBitCount();
+}
+
+////////////////////////////////////////////////////////////////
+
+const char *
+ConcreteNetwork::name(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->name();
+}
+
+Cell *
+ConcreteNetwork::cell(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->cell();
+}
+
+LibertyPort *
+ConcreteNetwork::libertyPort(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->libertyPort();
+}
+
+PortDirection *
+ConcreteNetwork::direction(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->direction();
+}
+
+bool
+ConcreteNetwork::isBundle(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->isBundle();
+}
+
+bool
+ConcreteNetwork::isBus(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->isBus();
+}
+
+const char *
+ConcreteNetwork::busName(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->busName();
+}
+
+int
+ConcreteNetwork::size(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->size();
+}
+
+int
+ConcreteNetwork::fromIndex(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->fromIndex();
+}
+
+int
+ConcreteNetwork::toIndex(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->toIndex();
+}
+
+Port *
+ConcreteNetwork::findBusBit(const Port *port,
+ int index) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return reinterpret_cast<Port*>(cport->findBusBit(index));
+}
+
+Port *
+ConcreteNetwork::findMember(const Port *port,
+ int index) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return reinterpret_cast<Port*>(cport->findMember(index));
+}
+
+bool
+ConcreteNetwork::hasMembers(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return cport->hasMembers();
+}
+
+////////////////////////////////////////////////////////////////
+
+class ConcretePortMemberIterator1 : public PortMemberIterator
+{
+public:
+ explicit ConcretePortMemberIterator1(const ConcretePort *port);
+ ~ConcretePortMemberIterator1();
+ virtual bool hasNext() { return iter_->hasNext(); }
+ virtual Port *next();
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ConcretePortMemberIterator1);
+
+ ConcretePortMemberIterator *iter_;
+};
+
+ConcretePortMemberIterator1::ConcretePortMemberIterator1(const ConcretePort *
+ port) :
+ iter_(port->memberIterator())
+{
+}
+
+ConcretePortMemberIterator1::~ConcretePortMemberIterator1()
+{
+ delete iter_;
+}
+
+Port *
+ConcretePortMemberIterator1::next()
+{
+ return reinterpret_cast<Port*>(iter_->next());
+}
+
+PortMemberIterator *
+ConcreteNetwork::memberIterator(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return new ConcretePortMemberIterator1(cport);
+}
+
+////////////////////////////////////////////////////////////////
+
+const char *
+ConcreteNetwork::name(const Instance *instance) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ return inst->name();
+}
+
+Cell *
+ConcreteNetwork::cell(const Instance *instance) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ return inst->cell();
+}
+
+Instance *
+ConcreteNetwork::parent(const Instance *instance) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ return reinterpret_cast<Instance*>(inst->parent());
+}
+
+bool
+ConcreteNetwork::isLeaf(const Instance *instance) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(inst->cell());
+ return ccell->isLeaf();
+}
+
+Instance *
+ConcreteNetwork::findChild(const Instance *parent,
+ const char *name) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(parent);
+ return inst->findChild(name);
+}
+
+Pin *
+ConcreteNetwork::findPin(const Instance *instance,
+ const char *port_name) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ return reinterpret_cast<Pin*>(inst->findPin(port_name));
+}
+
+Pin *
+ConcreteNetwork::findPin(const Instance *instance,
+ const Port *port) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ return reinterpret_cast<Pin*>(inst->findPin(port));
+}
+
+Net *
+ConcreteNetwork::findNet(const Instance *instance,
+ const char *net_name) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ return reinterpret_cast<Net*>(inst->findNet(net_name));
+}
+
+void
+ConcreteNetwork::findInstNetsMatching(const Instance *instance,
+ const PatternMatch *pattern,
+ NetSeq *nets) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ inst->findNetsMatching(pattern, nets);
+}
+
+////////////////////////////////////////////////////////////////
+
+InstanceChildIterator *
+ConcreteNetwork::childIterator(const Instance *instance) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ return inst->childIterator();
+}
+
+InstancePinIterator *
+ConcreteNetwork::pinIterator(const Instance *instance) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ ConcreteCell *cell = reinterpret_cast<ConcreteCell*>(inst->cell());
+ int pin_count = cell->portBitCount();
+ return new ConcreteInstancePinIterator(inst, pin_count);
+}
+
+InstanceNetIterator *
+ConcreteNetwork::netIterator(const Instance *instance) const
+{
+ const ConcreteInstance *inst =
+ reinterpret_cast<const ConcreteInstance*>(instance);
+ return inst->netIterator();
+}
+
+////////////////////////////////////////////////////////////////
+
+Instance *
+ConcreteNetwork::instance(const Pin *pin) const
+{
+ const ConcretePin *cpin = reinterpret_cast<const ConcretePin*>(pin);
+ return reinterpret_cast<Instance*>(cpin->instance());
+}
+
+Net *
+ConcreteNetwork::net(const Pin *pin) const
+{
+ const ConcretePin *cpin = reinterpret_cast<const ConcretePin*>(pin);
+ return reinterpret_cast<Net*>(cpin->net());
+}
+
+Term *
+ConcreteNetwork::term(const Pin *pin) const
+{
+ const ConcretePin *cpin = reinterpret_cast<const ConcretePin*>(pin);
+ return reinterpret_cast<Term*>(cpin->term());
+}
+
+Port *
+ConcreteNetwork::port(const Pin *pin) const
+{
+ const ConcretePin *cpin = reinterpret_cast<const ConcretePin*>(pin);
+ return reinterpret_cast<Port*>(cpin->port());
+}
+
+PortDirection *
+ConcreteNetwork::direction(const Pin *pin) const
+{
+ const ConcretePin *cpin = reinterpret_cast<const ConcretePin*>(pin);
+ const ConcretePort *cport = cpin->port();
+ return cport->direction();
+}
+
+VertexId
+ConcreteNetwork::vertexId(const Pin *pin) const
+{
+ const ConcretePin *cpin = reinterpret_cast<const ConcretePin*>(pin);
+ return cpin->vertexId();
+}
+
+void
+ConcreteNetwork::setVertexId(Pin *pin,
+ VertexId id)
+{
+ ConcretePin *cpin = reinterpret_cast<ConcretePin*>(pin);
+ cpin->setVertexId(id);
+}
+
+////////////////////////////////////////////////////////////////
+
+Net *
+ConcreteNetwork::net(const Term *term) const
+{
+ const ConcreteTerm *cterm = reinterpret_cast<const ConcreteTerm*>(term);
+ return reinterpret_cast<Net*>(cterm->net());
+}
+
+Pin *
+ConcreteNetwork::pin(const Term *term) const
+{
+ const ConcreteTerm *cterm = reinterpret_cast<const ConcreteTerm*>(term);
+ return reinterpret_cast<Pin*>(cterm->pin());
+}
+
+////////////////////////////////////////////////////////////////
+
+const char *
+ConcreteNetwork::name(const Net *net) const
+{
+ const ConcreteNet *cnet = reinterpret_cast<const ConcreteNet*>(net);
+ return cnet->name();
+}
+
+Instance *
+ConcreteNetwork::instance(const Net *net) const
+{
+ const ConcreteNet *cnet = reinterpret_cast<const ConcreteNet*>(net);
+ return reinterpret_cast<Instance*>(cnet->instance());
+}
+
+bool
+ConcreteNetwork::isPower(const Net *net) const
+{
+ return constant_nets_[int(LogicValue::one)].hasKey(const_cast<Net*>(net));
+}
+
+bool
+ConcreteNetwork::isGround(const Net *net) const
+{
+ return constant_nets_[int(LogicValue::zero)].hasKey(const_cast<Net*>(net));
+}
+
+NetPinIterator *
+ConcreteNetwork::pinIterator(const Net *net) const
+{
+ const ConcreteNet *cnet = reinterpret_cast<const ConcreteNet*>(net);
+ return new ConcreteNetPinIterator(cnet);
+}
+
+NetTermIterator *
+ConcreteNetwork::termIterator(const Net *net) const
+{
+ const ConcreteNet *cnet = reinterpret_cast<const ConcreteNet*>(net);
+ return new ConcreteNetTermIterator(cnet);
+}
+
+void
+ConcreteNetwork::mergeInto(Net *net,
+ Net *into_net)
+{
+ ConcreteNet *cnet = reinterpret_cast<ConcreteNet*>(net);
+ ConcreteNet *cinto_net = reinterpret_cast<ConcreteNet*>(into_net);
+ cnet->mergeInto(cinto_net);
+ clearNetDrvrPinMap();
+}
+
+Net *
+ConcreteNetwork::mergedInto(Net *net)
+{
+ ConcreteNet *cnet = reinterpret_cast<ConcreteNet*>(net);
+ return reinterpret_cast<Net*>(cnet->mergedInto());
+}
+
+////////////////////////////////////////////////////////////////
+
+Cell *
+ConcreteInstance::cell() const
+{
+ return reinterpret_cast<Cell*>(cell_);
+}
+
+Instance *
+ConcreteNetwork::makeInstance(Cell *cell,
+ const char *name,
+ Instance *parent)
+{
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ return makeConcreteInstance(ccell, name, parent);
+}
+
+Instance *
+ConcreteNetwork::makeInstance(LibertyCell *cell,
+ const char *name,
+ Instance *parent)
+{
+ return makeConcreteInstance(cell, name, parent);
+}
+
+Instance *
+ConcreteNetwork::makeConcreteInstance(ConcreteCell *cell,
+ const char *name,
+ Instance *parent)
+{
+ ConcreteInstance *cparent =
+ reinterpret_cast<ConcreteInstance*>(parent);
+ ConcreteInstance *inst = new ConcreteInstance(cell, name, cparent);
+ if (parent)
+ cparent->addChild(inst);
+ return reinterpret_cast<Instance*>(inst);
+}
+
+void
+ConcreteNetwork::makePins(Instance *inst)
+{
+ CellPortBitIterator *port_iterator = portBitIterator(cell(inst));
+ while (port_iterator->hasNext()) {
+ Port *port = port_iterator->next();
+ makePin(inst, port, nullptr);
+ }
+ delete port_iterator;
+}
+
+void
+ConcreteNetwork::replaceCell(Instance *inst,
+ Cell *cell)
+{
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell);
+ int port_count = ccell->portBitCount();
+ ConcreteInstance *cinst = reinterpret_cast<ConcreteInstance*>(inst);
+ // Port count picked from Instance instead of Target cells-Dinesh A
+ ConcreteCell *instcell = reinterpret_cast<ConcreteCell*>(cinst->cell());
+ int inst_port_count = instcell->portBitCount();
+ ConcretePin **pins = cinst->pins_;
+ ConcretePin **rpins = new ConcretePin*[port_count];
+ for (int i = 0; i < port_count; i++)
+ rpins[i] = pins[inst_port_count-1];
+ for (int i = 0; i < inst_port_count; i++) {
+ ConcretePin *cpin = pins[i];
+ if (cpin) {
+ ConcretePort *pin_cport = reinterpret_cast<ConcretePort*>(cpin->port());
+ ConcretePort *cport = ccell->findPort(pin_cport->name());
+ rpins[cport->pinIndex()] = cpin;
+ cpin->port_ = cport;
+ }
+ }
+ delete [] pins;
+ cinst->pins_ = rpins;
+ cinst->setCell(ccell);
+}
+
+void
+ConcreteNetwork::deleteInstance(Instance *inst)
+{
+ ConcreteInstance *cinst = reinterpret_cast<ConcreteInstance*>(inst);
+
+ // Delete nets first (so children pin deletes are not required).
+ ConcreteInstanceNetMap::Iterator net_iter(cinst->nets_);
+ while (net_iter.hasNext()) {
+ ConcreteNet *cnet = net_iter.next();
+ Net *net = reinterpret_cast<Net*>(cnet);
+ // Delete terminals connected to net.
+ NetTermIterator *term_iter = termIterator(net);
+ while (term_iter->hasNext()) {
+ ConcreteTerm *term = reinterpret_cast<ConcreteTerm*>(term_iter->next());
+ delete term;
+ }
+ delete term_iter;
+ deleteNet(net);
+ }
+
+ // Delete children.
+ InstanceChildIterator *child_iter = childIterator(inst);
+ while (child_iter->hasNext()) {
+ Instance *child = child_iter->next();
+ deleteInstance(child);
+ }
+ delete child_iter;
+
+ InstancePinIterator *pin_iter = pinIterator(inst);
+ while (pin_iter->hasNext()) {
+ Pin *pin = pin_iter->next();
+ deletePin(pin);
+ }
+ delete pin_iter;
+
+ Instance *parent_inst = parent(inst);
+ if (parent_inst) {
+ ConcreteInstance *cparent =
+ reinterpret_cast<ConcreteInstance*>(parent_inst);
+ cparent->deleteChild(cinst);
+ }
+ delete cinst;
+}
+
+Pin *
+ConcreteNetwork::makePin(Instance *inst,
+ Port *port,
+ Net *net)
+{
+ ConcreteInstance *cinst = reinterpret_cast<ConcreteInstance*>(inst);
+ ConcretePort *cport = reinterpret_cast<ConcretePort*>(port);
+ ConcreteNet *cnet = reinterpret_cast<ConcreteNet*>(net);
+ ConcretePin *cpin = new ConcretePin(cinst, cport, cnet);
+ cinst->addPin(cpin);
+ if (cnet)
+ connectNetPin(cnet, cpin);
+ return reinterpret_cast<Pin*>(cpin);
+}
+
+Term *
+ConcreteNetwork::makeTerm(Pin *pin,
+ Net *net)
+{
+ ConcretePin *cpin = reinterpret_cast<ConcretePin*>(pin);
+ ConcreteNet *cnet = reinterpret_cast<ConcreteNet*>(net);
+ ConcreteTerm *cterm = new ConcreteTerm(cpin, cnet);
+ if (cnet)
+ cnet->addTerm(cterm);
+ cpin->term_ = cterm;
+ return reinterpret_cast<Term*>(cterm);
+}
+
+Pin *
+ConcreteNetwork::connect(Instance *inst,
+ LibertyPort *port,
+ Net *net)
+{
+ return connect(inst, reinterpret_cast<Port*>(port), net);
+}
+
+Pin *
+ConcreteNetwork::connect(Instance *inst,
+ Port *port,
+ Net *net)
+{
+ ConcreteNet *cnet = reinterpret_cast<ConcreteNet*>(net);
+ ConcreteInstance *cinst = reinterpret_cast<ConcreteInstance*>(inst);
+ ConcretePort *cport = reinterpret_cast<ConcretePort*>(port);
+ ConcretePin *cpin = cinst->findPin(port);
+ if (cpin) {
+ ConcreteNet *prev_net = cpin->net_;
+ if (prev_net)
+ disconnectNetPin(prev_net, cpin);
+ }
+ else {
+ cpin = new ConcretePin(cinst, cport, cnet);
+ cinst->addPin(cpin);
+ }
+ if (inst == top_instance_) {
+ // makeTerm
+ ConcreteTerm *cterm = new ConcreteTerm(cpin, cnet);
+ cnet->addTerm(cterm);
+ cpin->term_ = cterm;
+ cpin->net_ = nullptr;
+ }
+ else {
+ cpin->net_ = cnet;
+ connectNetPin(cnet, cpin);
+ }
+ return reinterpret_cast<Pin*>(cpin);
+}
+
+void
+ConcreteNetwork::connectNetPin(ConcreteNet *cnet,
+ ConcretePin *cpin)
+{
+ cnet->addPin(cpin);
+
+ // If there are no terminals the net does not span hierarchy levels
+ // and it is safe to incrementally update the drivers.
+ Pin *pin = reinterpret_cast<Pin*>(cpin);
+ if (isDriver(pin)) {
+ if (cnet->terms_ == nullptr) {
+ Net *net = reinterpret_cast<Net*>(cnet);
+ PinSet *drvrs = net_drvr_pin_map_.findKey(net);
+ if (drvrs)
+ drvrs->insert(pin);
+ }
+ else
+ clearNetDrvrPinMap();
+ }
+}
+
+void
+ConcreteNetwork::disconnectPin(Pin *pin)
+{
+ ConcretePin *cpin = reinterpret_cast<ConcretePin*>(pin);
+ if (reinterpret_cast<Instance*>(cpin->instance()) == top_instance_) {
+ ConcreteTerm *cterm = cpin->term_;
+ if (cterm) {
+ ConcreteNet *cnet = cterm->net_;
+ if (cnet) {
+ cnet->deleteTerm(cterm);
+ clearNetDrvrPinMap();
+ }
+ cpin->term_ = nullptr;
+ delete cterm;
+ }
+ }
+ else {
+ ConcreteNet *cnet = cpin->net();
+ if (cnet)
+ disconnectNetPin(cnet, cpin);
+ cpin->net_ = nullptr;
+ }
+}
+
+void
+ConcreteNetwork::disconnectNetPin(ConcreteNet *cnet,
+ ConcretePin *cpin)
+{
+ cnet->deletePin(cpin);
+
+ Pin *pin = reinterpret_cast<Pin*>(cpin);
+ if (isDriver(pin)) {
+ ConcreteNet *cnet = cpin->net();
+ // If there are no terminals the net does not span hierarchy levels
+ // and it is safe to incrementally update the drivers.
+ if (cnet->terms_ == nullptr) {
+ Net *net = reinterpret_cast<Net*>(cnet);
+ PinSet *drvrs = net_drvr_pin_map_.findKey(net);
+ if (drvrs)
+ drvrs->erase(pin);
+ }
+ else
+ clearNetDrvrPinMap();
+ }
+}
+
+void
+ConcreteNetwork::deletePin(Pin *pin)
+{
+ ConcretePin *cpin = reinterpret_cast<ConcretePin*>(pin);
+ ConcreteNet *cnet = cpin->net();
+ if (cnet)
+ disconnectNetPin(cnet, cpin);
+ ConcreteInstance *cinst =
+ reinterpret_cast<ConcreteInstance*>(cpin->instance());
+ if (cinst)
+ cinst->deletePin(cpin);
+ delete cpin;
+}
+
+Net *
+ConcreteNetwork::makeNet(const char *name,
+ Instance *parent)
+{
+ ConcreteInstance *cparent = reinterpret_cast<ConcreteInstance*>(parent);
+ ConcreteNet *net = new ConcreteNet(name, cparent);
+ cparent->addNet(net);
+ return reinterpret_cast<Net*>(net);
+}
+
+void
+ConcreteNetwork::deleteNet(Net *net)
+{
+ ConcreteNet *cnet = reinterpret_cast<ConcreteNet*>(net);
+ ConcreteNetPinIterator pin_iter(cnet);
+ while (pin_iter.hasNext()) {
+ ConcretePin *pin = reinterpret_cast<ConcretePin*>(pin_iter.next());
+ // Do NOT use net->disconnectPin because it would be N^2
+ // to delete all of the pins from the net.
+ pin->net_ = nullptr;
+ }
+
+ constant_nets_[int(LogicValue::zero)].erase(net);
+ constant_nets_[int(LogicValue::one)].erase(net);
+ PinSet *drvrs = net_drvr_pin_map_.findKey(net);
+ if (drvrs) {
+ delete drvrs;
+ net_drvr_pin_map_.erase(net);
+ }
+
+ ConcreteInstance *cinst =
+ reinterpret_cast<ConcreteInstance*>(cnet->instance());
+ cinst->deleteNet(cnet);
+ delete cnet;
+}
+
+void
+ConcreteNetwork::clearConstantNets()
+{
+ constant_nets_[int(LogicValue::zero)].clear();
+ constant_nets_[int(LogicValue::one)].clear();
+}
+
+void
+ConcreteNetwork::addConstantNet(Net *net,
+ LogicValue value)
+{
+ constant_nets_[int(value)].insert(net);
+}
+
+ConstantPinIterator *
+ConcreteNetwork::constantPinIterator()
+{
+ return new NetworkConstantPinIterator(this,
+ constant_nets_[int(LogicValue::zero)],
+ constant_nets_[int(LogicValue::one)]);
+}
+
+////////////////////////////////////////////////////////////////
+
+// Optimized version of Network::visitConnectedPins.
+void
+ConcreteNetwork::visitConnectedPins(const Net *net,
+ PinVisitor &visitor,
+ ConstNetSet &visited_nets) const
+{
+ if (!visited_nets.hasKey(net)) {
+ visited_nets.insert(net);
+ // Search up from net terminals.
+ const ConcreteNet *cnet = reinterpret_cast<const ConcreteNet*>(net);
+ for (ConcreteTerm *term = cnet->terms_; term; term = term->net_next_) {
+ ConcretePin *above_pin = term->pin_;
+ if (above_pin) {
+ ConcreteNet *above_net = above_pin->net_;
+ if (above_net)
+ visitConnectedPins(reinterpret_cast<Net*>(above_net),
+ visitor, visited_nets);
+ else
+ visitor(reinterpret_cast<Pin*>(above_pin));
+ }
+ }
+
+ // Search down from net pins.
+ for (ConcretePin *pin = cnet->pins_; pin; pin = pin->net_next_) {
+ visitor(reinterpret_cast<Pin*>(pin));
+ ConcreteTerm *below_term = pin->term_;
+ if (below_term) {
+ ConcreteNet *below_net = below_term->net_;
+ if (below_net)
+ visitConnectedPins(reinterpret_cast<Net*>(below_net),
+ visitor, visited_nets);
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+ConcreteInstance::ConcreteInstance(ConcreteCell *cell,
+ const char *name,
+ ConcreteInstance *parent) :
+ cell_(cell),
+ name_(stringCopy(name)),
+ parent_(parent),
+ children_(nullptr),
+ nets_(nullptr)
+{
+ initPins();
+}
+
+void
+ConcreteInstance::initPins()
+{
+ int pin_count = reinterpret_cast<ConcreteCell*>(cell_)->portBitCount();
+ if (pin_count) {
+ pins_ = new ConcretePin*[pin_count];
+ for (int i = 0; i < pin_count; i++)
+ pins_[i] = nullptr;
+ }
+ else
+ pins_ = nullptr;
+}
+
+ConcreteInstance::~ConcreteInstance()
+{
+ stringDelete(name_);
+ delete [] pins_;
+ delete children_;
+ delete nets_;
+}
+
+Instance *
+ConcreteInstance::findChild(const char *name) const
+{
+ if (children_)
+ return reinterpret_cast<Instance*>(children_->findKey(name));
+ else
+ return nullptr;
+}
+
+ConcretePin *
+ConcreteInstance::findPin(const char *port_name) const
+{
+ ConcreteCell *ccell = reinterpret_cast<ConcreteCell*>(cell_);
+ const ConcretePort *cport =
+ reinterpret_cast<const ConcretePort*>(ccell->findPort(port_name));
+ if (cport
+ && !cport->isBus())
+ return pins_[cport->pinIndex()];
+ else
+ return nullptr;
+}
+
+ConcretePin *
+ConcreteInstance::findPin(const Port *port) const
+{
+ const ConcretePort *cport = reinterpret_cast<const ConcretePort*>(port);
+ return pins_[cport->pinIndex()];
+}
+
+ConcreteNet *
+ConcreteInstance::findNet(const char *net_name) const
+{
+ ConcreteNet *net = nullptr;
+ if (nets_) {
+ net = nets_->findKey(net_name);
+ // Follow merge pointer to surviving net.
+ if (net) {
+ while (net->mergedInto())
+ net = net->mergedInto();
+ }
+ }
+ return net;
+}
+
+void
+ConcreteInstance::findNetsMatching(const PatternMatch *pattern,
+ NetSeq *nets) const
+{
+ if (pattern->hasWildcards()) {
+ ConcreteInstanceNetMap::Iterator net_iter(nets_);
+ while (net_iter.hasNext()) {
+ const char *net_name;
+ ConcreteNet *cnet;
+ net_iter.next(net_name, cnet);
+ if (pattern->match(net_name))
+ nets->push_back(reinterpret_cast<Net*>(cnet));
+ }
+ }
+ else {
+ ConcreteNet *cnet = findNet(pattern->pattern());
+ if (cnet)
+ nets->push_back(reinterpret_cast<Net*>(cnet));
+ }
+}
+
+InstanceNetIterator *
+ConcreteInstance::netIterator() const
+{
+ return reinterpret_cast<InstanceNetIterator*>
+ (new ConcreteInstanceNetIterator(nets_));
+}
+
+InstanceChildIterator *
+ConcreteInstance::childIterator() const
+{
+ return new ConcreteInstanceChildIterator(children_);
+}
+
+void
+ConcreteInstance::addChild(ConcreteInstance *child)
+{
+ if (children_ == nullptr)
+ children_ = new ConcreteInstanceChildMap;
+ (*children_)[child->name()] = child;
+}
+
+void
+ConcreteInstance::deleteChild(ConcreteInstance *child)
+{
+ children_->erase(child->name());
+}
+
+void
+ConcreteInstance::addPin(ConcretePin *pin)
+{
+ ConcretePort *cport = reinterpret_cast<ConcretePort *>(pin->port());
+ pins_[cport->pinIndex()] = pin;
+}
+
+void
+ConcreteInstance::deletePin(ConcretePin *pin)
+{
+ ConcretePort *cport = reinterpret_cast<ConcretePort *>(pin->port());
+ pins_[cport->pinIndex()] = nullptr;
+}
+
+void
+ConcreteInstance::addNet(ConcreteNet *net)
+{
+ if (nets_ == nullptr)
+ nets_ = new ConcreteInstanceNetMap;
+ (*nets_)[net->name()] = net;
+}
+
+void
+ConcreteInstance::addNet(const char *name,
+ ConcreteNet *net)
+{
+ if (nets_ == nullptr)
+ nets_ = new ConcreteInstanceNetMap;
+ (*nets_)[name] = net;
+}
+
+void
+ConcreteInstance::deleteNet(ConcreteNet *net)
+{
+ nets_->erase(net->name());
+}
+
+void
+ConcreteInstance::setCell(ConcreteCell *cell)
+{
+ cell_ = cell;
+}
+
+////////////////////////////////////////////////////////////////
+
+ConcretePin::ConcretePin(ConcreteInstance *instance,
+ ConcretePort *port,
+ ConcreteNet *net) :
+ instance_(instance),
+ port_(port),
+ net_(net),
+ term_(nullptr),
+ net_next_(nullptr),
+ net_prev_(nullptr),
+ vertex_id_(vertex_id_null)
+{
+}
+
+const char *
+ConcretePin::name() const
+{
+ return port_->name();
+}
+
+void
+ConcretePin::setVertexId(VertexId id)
+{
+ vertex_id_ = id;
+}
+
+////////////////////////////////////////////////////////////////
+
+const char *
+ConcreteTerm::name() const
+{
+ ConcretePin *cpin = reinterpret_cast<ConcretePin*>(pin_);
+ const ConcretePort *cport =
+ reinterpret_cast<const ConcretePort*>(cpin->port());
+ return cport->name();
+}
+
+ConcreteTerm::ConcreteTerm(ConcretePin *pin,
+ ConcreteNet *net) :
+ pin_(pin),
+ net_(net),
+ net_next_(nullptr)
+{
+}
+
+////////////////////////////////////////////////////////////////
+
+ConcreteNet::ConcreteNet(const char *name,
+ ConcreteInstance *instance) :
+ name_(stringCopy(name)),
+ instance_(instance),
+ pins_(nullptr),
+ terms_(nullptr),
+ merged_into_(nullptr)
+{
+}
+
+ConcreteNet::~ConcreteNet()
+{
+ stringDelete(name_);
+}
+
+// Merged nets are kept around to serve as name aliases.
+// Only Instance::findNet and InstanceNetIterator need to know
+// the net has been merged.
+void
+ConcreteNet::mergeInto(ConcreteNet *net)
+{
+ ConcreteNetPinIterator pin_iter(this);
+ while (pin_iter.hasNext()) {
+ Pin *pin = pin_iter.next();
+ ConcretePin *cpin = reinterpret_cast<ConcretePin*>(pin);
+ net->addPin(cpin);
+ cpin->net_ = net;
+ }
+ pins_ = nullptr;
+ ConcreteNetTermIterator term_iter(this);
+ while (term_iter.hasNext()) {
+ Term *term = term_iter.next();
+ ConcreteTerm *cterm = reinterpret_cast<ConcreteTerm*>(term);
+ net->addTerm(cterm);
+ cterm->net_ = net;
+ }
+ terms_ = nullptr;
+ // Leave name map pointing to merged net because otherwise a top
+ // level merged net has no pointer to it and it is leaked.
+ merged_into_ = net;
+}
+
+void
+ConcreteNet::addPin(ConcretePin *pin)
+{
+ if (pins_)
+ pins_->net_prev_ = pin;
+ pin->net_next_ = pins_;
+ pin->net_prev_ = nullptr;
+ pins_ = pin;
+}
+
+void
+ConcreteNet::deletePin(ConcretePin *pin)
+{
+ ConcretePin *prev = pin->net_prev_;
+ ConcretePin *next = pin->net_next_;
+ if (prev)
+ prev->net_next_ = next;
+ if (next)
+ next->net_prev_ = prev;
+ if (pins_ == pin)
+ pins_ = next;
+}
+
+void
+ConcreteNet::addTerm(ConcreteTerm *term)
+{
+ ConcreteTerm *next = terms_;
+ terms_ = term;
+ term->net_next_ = next;
+}
+
+void
+ConcreteNet::deleteTerm(ConcreteTerm *term)
+{
+ ConcreteTerm *net_prev_term = nullptr;
+ for (ConcreteTerm *net_term=terms_;net_term;net_term=net_term->net_next_) {
+ if (net_term == term) {
+ if (net_prev_term)
+ net_prev_term->net_next_ = term->net_next_;
+ else
+ terms_ = term->net_next_;
+ break;
+ }
+ net_prev_term = net_term;
+ }
+}
+
+////////////////////////////////////////////////////////////////
+
+typedef Map<Net*, Net*> BindingMap;
+
+// Binding table used for linking/expanding network.
+class ConcreteBindingTbl
+{
+public:
+ explicit ConcreteBindingTbl(NetworkEdit *network);
+ Net *ensureBinding(Net *proto_net,
+ Instance *parent);
+ Net *find(Net *net);
+ void bind(Net *proto_net,
+ Net *clone_net);
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(ConcreteBindingTbl);
+
+ BindingMap map_;
+ NetworkEdit *network_;
+};
+
+void
+ConcreteNetwork::setCellNetworkView(Cell *cell,
+ Instance *inst)
+{
+ cell_network_view_map_[cell] = inst;
+}
+
+Instance *
+ConcreteNetwork::cellNetworkView(Cell *cell)
+{
+ return cell_network_view_map_.findKey(cell);
+}
+
+void
+ConcreteNetwork::readNetlistBefore()
+{
+ clearConstantNets();
+ deleteTopInstance();
+ clearNetDrvrPinMap();
+}
+
+void
+ConcreteNetwork::setTopInstance(Instance *top_inst)
+{
+ if (top_instance_) {
+ deleteInstance(top_instance_);
+ clearConstantNets();
+ clearNetDrvrPinMap();
+ }
+ top_instance_ = top_inst;
+}
+
+void
+ConcreteNetwork::setLinkFunc(LinkNetworkFunc *link)
+{
+ link_func_ = link;
+}
+
+bool
+ConcreteNetwork::linkNetwork(const char *top_cell_name,
+ bool make_black_boxes,
+ Report *report)
+{
+ if (link_func_) {
+ clearConstantNets();
+ deleteTopInstance();
+ top_instance_ = link_func_(top_cell_name, make_black_boxes, report, this);
+ return top_instance_ != nullptr;
+ }
+ else {
+ report->error(8, "cell type %s can not be linked.", top_cell_name);
+ return false;
+ }
+}
+
+Instance *
+linkReaderNetwork(Cell *top_cell,
+ bool, Report *,
+ NetworkReader *network)
+{
+ Instance *view = network->cellNetworkView(top_cell);
+ if (view) {
+ // Seed the recursion for expansion with the top level instance.
+ Instance *top_instance = network->makeInstance(top_cell, "", nullptr);
+ ConcreteBindingTbl bindings(network);
+ makeClonePins(view, top_instance, view, &bindings, nullptr, nullptr, network);
+ InstanceChildIterator *child_iter = network->childIterator(view);
+ while (child_iter->hasNext()) {
+ Instance *child = child_iter->next();
+ makeChildNetwork(child, top_instance, &bindings, network);
+ }
+ delete child_iter;
+ network->deleteCellNetworkViews();
+ return top_instance;
+ }
+ return nullptr;
+}
+
+static void
+makeChildNetwork(Instance *proto,
+ Instance *parent,
+ ConcreteBindingTbl *parent_bindings,
+ NetworkReader *network)
+{
+ Cell *proto_cell = network->cell(proto);
+ Instance *clone = network->makeInstance(proto_cell, network->name(proto),
+ parent);
+ if (network->isLeaf(proto_cell))
+ makeClonePins(proto, clone, nullptr, nullptr, parent, parent_bindings, network);
+ else {
+ // Recurse if this isn't a leaf cell.
+ ConcreteBindingTbl bindings(network);
+ Instance *clone_view = network->cellNetworkView(proto_cell);
+ makeClonePins(proto, clone, clone_view, &bindings, parent,
+ parent_bindings, network);
+ if (clone_view) {
+ InstanceChildIterator *child_iter = network->childIterator(clone_view);
+ while (child_iter->hasNext()) {
+ Instance *child = child_iter->next();
+ makeChildNetwork(child, clone, &bindings, network);
+ }
+ delete child_iter;
+ }
+ }
+}
+
+static void
+makeClonePins(Instance *proto,
+ Instance *clone,
+ Instance *clone_view,
+ ConcreteBindingTbl *bindings,
+ Instance *parent,
+ ConcreteBindingTbl *parent_bindings,
+ NetworkReader *network)
+{
+ InstancePinIterator *proto_pin_iter = network->pinIterator(proto);
+ while (proto_pin_iter->hasNext()) {
+ Pin *proto_pin = proto_pin_iter->next();
+ Net *proto_net = network->net(proto_pin);
+ Port *proto_port = network->port(proto_pin);
+ Net *clone_net = nullptr;
+ if (proto_net && parent_bindings)
+ clone_net = parent_bindings->ensureBinding(proto_net, parent);
+ Pin *clone_pin = network->connect(clone, proto_port, clone_net);
+ if (clone_view) {
+ Pin *clone_proto_pin = network->findPin(clone_view, proto_port);
+ Net *clone_proto_net = network->net(clone_proto_pin);
+ Net *clone_child_net = nullptr;
+ if (clone_proto_net)
+ clone_child_net = bindings->ensureBinding(clone_proto_net, clone);
+ network->makeTerm(clone_pin, clone_child_net);
+ }
+ }
+ delete proto_pin_iter;
+}
+
+////////////////////////////////////////////////////////////////
+
+ConcreteBindingTbl::ConcreteBindingTbl(NetworkEdit *network) :
+ network_(network)
+{
+}
+
+// Follow the merged_into pointers rather than update the
+// binding tables up the call tree when nodes are merged
+// because the name changes up the hierarchy.
+Net *
+ConcreteBindingTbl::find(Net *proto_net)
+{
+ ConcreteNet *net = reinterpret_cast<ConcreteNet*>(map_.findKey(proto_net));
+ while (net && net->mergedInto())
+ net = net->mergedInto();
+ return reinterpret_cast<Net*>(net);
+}
+
+void
+ConcreteBindingTbl::bind(Net *proto_net,
+ Net *net)
+{
+ map_[proto_net] = net;
+}
+
+Net *
+ConcreteBindingTbl::ensureBinding(Net *proto_net,
+ Instance *parent)
+{
+ Net *net = find(proto_net);
+ if (net == nullptr) {
+ net = network_->makeNet(network_->name(proto_net), parent);
+ map_[proto_net] = net;
+ }
+ return net;
+}
+
+} // namespace
diff --git a/hacks/src/OpenSTA/tcl/NetworkEdit.tcl b/hacks/src/OpenSTA/tcl/NetworkEdit.tcl
new file mode 100644
index 0000000..bdd4057
--- /dev/null
+++ b/hacks/src/OpenSTA/tcl/NetworkEdit.tcl
@@ -0,0 +1,262 @@
+# OpenSTA, Static Timing Analyzer
+# Copyright (c) 2021, 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 <https://www.gnu.org/licenses/>.
+
+# Network editing commands.
+
+namespace eval sta {
+
+proc connect_pin { net pin } {
+ set insts_port [parse_connect_pin $pin]
+ if { $insts_port == 0 } {
+ return 0
+ }
+ set net [get_net_warn "net" $net]
+ if { $net == "NULL" } {
+ return 0
+ }
+ lassign $insts_port inst port
+ connect_pin_cmd $inst $port $net
+ return 1
+}
+
+proc parse_connect_pin { arg } {
+ set path_regexp [path_regexp]
+ set insts_port {}
+ if { [is_object $arg] } {
+ set object_type [object_type $arg]
+ if { $object_type == "Pin" } {
+ set pin $arg
+ set inst [$pin instance]
+ set port [$pin port]
+ } elseif { $object_type == "Port" } {
+ # Explicit port arg - convert to pin.
+ set pin [find_pin [get_name $arg]]
+ set inst [$pin instance]
+ set port [$pin port]
+ } else {
+ sta_error 586 "unsupported object type $object_type."
+ }
+ } else {
+ if {[regexp $path_regexp $arg ignore path_name port_name]} {
+ set inst [find_instance $path_name]
+ if { $inst == "NULL" } {
+ return 0
+ }
+ } else {
+ set inst [top_instance]
+ set port_name $arg
+ }
+ set cell [$inst cell]
+ set port [$cell find_port $port_name]
+ if { $port == "NULL" } {
+ return 0
+ }
+ set pin [$inst find_pin $port_name]
+ }
+
+ # Make sure the pin is not currently connected to a net.
+ if { $pin != "NULL" \
+ && ![$pin is_hierarchical] \
+ && [$pin net] != "NULL" } {
+ return 0
+ }
+ return [list $inst $port]
+}
+
+proc connect_pins { net pins } {
+ sta_warn 372 "connect_pins is deprecated. Use connect_pin."
+ # Visit the pins to make sure command will succeed.
+ set insts_ports [parse_connect_pins $pins]
+ if { $insts_ports == 0 } {
+ return 0
+ }
+ set net [get_net_warn "net" $net]
+ if { $net == "NULL" } {
+ return 0
+ }
+ foreach {inst port} $insts_ports {
+ connect_pin_cmd $inst $port $net
+ }
+ return 1
+}
+
+proc parse_connect_pins { arg } {
+ set path_regexp [path_regexp]
+ set inst_ports {}
+ # Copy backslashes that will be removed by foreach.
+ set arg [string map {\\ \\\\} $arg]
+ foreach obj $arg {
+ set inst_port [parse_connect_pin $obj]
+ if { $inst_port == 0 } {
+ return 0
+ }
+ set inst_ports [concat $inst_ports $inst_port]
+ }
+ return $inst_ports
+}
+
+################################################################
+
+proc delete_instance { instance } {
+ if { [is_object $instance] } {
+ set object_type [object_type $instance]
+ if { $object_type == "Instance" } {
+ set inst $obj
+ } else {
+ sta_error 587 "unsupported object type $object_type."
+ }
+ } else {
+ set inst [find_instance $instance]
+ }
+ if { $inst != "NULL" } {
+ delete_instance_cmd $inst
+ }
+}
+
+################################################################
+
+proc delete_net { net } {
+ if { [is_object $net] } {
+ set object_type [object_type $net]
+ if { $object_type != "Net" } {
+ sta_error 588 "unsupported object type $object_type."
+ }
+ } else {
+ set net [find_net $net]
+ }
+ if { $net != "NULL" } {
+ delete_net_cmd $net
+ }
+}
+
+################################################################
+
+proc disconnect_pin { net pin } {
+ set net [get_net_warn "net" $net]
+ if { $net == "NULL" } {
+ return 0
+ }
+ if { $pin == "-all" } {
+ set iter [$net connected_pin_iterator]
+ while {[$iter has_next]} {
+ set pin [$iter next]
+ disconnect_pin_cmd $pin
+ }
+ $iter finish
+ return 1
+ } else {
+ set pin1 [get_port_pin_warn "pin" $pin]
+ if { $pin1 == "NULL" } {
+ return 0
+ } else {
+ disconnect_pin_cmd $pin1
+ return 1
+ }
+ }
+}
+
+proc disconnect_pins { net pins } {
+ sta_warn 603 "disconnect_pins is deprecated. Use disconnect_pin."
+ foreach pin $pins {
+ disconnect_pin $net $pins
+ }
+}
+
+################################################################
+
+proc make_instance { inst_path lib_cell } {
+ set lib_cell [get_lib_cell_warn "lib_cell" $lib_cell]
+ if { $lib_cell != "NULL" } {
+ set path_regexp [path_regexp]
+ if {[regexp $path_regexp $inst_path ignore path_name inst_name]} {
+ set parent [find_instance $path_name]
+ if { $parent == "NULL" } {
+ # Parent instance not found. This could be a typo, but since
+ # SDC does not escape hierarchy dividers it can also be
+ # an escaped name.
+ set inst_name $inst_path
+ set parent [top_instance]
+ }
+ } else {
+ set inst_name $inst_path
+ set parent [top_instance]
+ }
+ return [make_instance_cmd $inst_name $lib_cell $parent]
+ } else {
+ return 0
+ }
+}
+
+################################################################
+
+proc make_net { net_path } {
+ # Copy backslashes that will be removed by foreach.
+ set net_path [string map {\\ \\\\} $net_path]
+ set path_regexp [path_regexp]
+ if {[regexp $path_regexp $net_path ignore path_name net_name]} {
+ set parent [find_instance $path_name]
+ if { $parent == "NULL" } {
+ return 0
+ }
+ } else {
+ set parent [top_instance]
+ set net_name $net_path
+ }
+ return [make_net_cmd $net_name $parent]
+}
+
+################################################################
+
+proc replace_cell { instance lib_cell } {
+ set cell [get_lib_cell_warn "lib_cell" $lib_cell]
+ if { $cell != "NULL" } {
+ set inst [get_instance_error "instance" $instance]
+ set inst_cell [$inst liberty_cell]
+ if { $inst_cell == "NULL" \
+ || ![equiv_cell_ports $inst_cell $cell] } {
+ return 0
+ }
+ replace_cell_cmd $inst $cell
+ return 1
+ } else {
+ return 0
+ }
+}
+
+proc replace_to_scan_cell { instance } {
+ set inst [get_instance_error "instance" $instance]
+ set inst_cell [get_full_name [$inst liberty_cell]]
+ #Target scan cell __d to __sd
+ #example sky130_fd_sc_hd__dfrtp_2 to sky130_fd_sc_hd__sdfrtp_2
+ set inst_scell [regsub -all "__d" $inst_cell "__sd"]
+ puts "Info: Scan Swapping => Instance:$instance From:$inst_cell to:$inst_scell";
+ if { $inst_cell == "NULL"} {
+ return 0
+ }
+ set scell [get_lib_cell_warn "lib_cell" $inst_scell]
+ replace_cell_cmd $inst $scell
+ return 1
+}
+
+proc path_regexp {} {
+ global hierarchy_separator
+ set id_regexp "\[^${hierarchy_separator}\]+"
+ set prefix_regexp "${id_regexp}(?:${hierarchy_separator}${id_regexp})*"
+ return "^(${prefix_regexp})${hierarchy_separator}(${id_regexp})$"
+}
+
+# sta namespace end.
+}
diff --git a/hacks/src/OpenSTA/tcl/Sta.tcl b/hacks/src/OpenSTA/tcl/Sta.tcl
new file mode 100644
index 0000000..a6e8e34
--- /dev/null
+++ b/hacks/src/OpenSTA/tcl/Sta.tcl
@@ -0,0 +1,1557 @@
+# OpenSTA, Static Timing Analyzer
+# Copyright (c) 2021, 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 <https://www.gnu.org/licenses/>.
+
+namespace eval sta {
+
+################################################################
+#
+# Non-SDC commands
+#
+################################################################
+
+define_cmd_args "delete_clock" {[-all] clocks}
+
+proc delete_clock { args } {
+ parse_key_args "delete_clock" args keys {} flags {-all}
+ if { [info exists flags(-all)] } {
+ check_argc_eq0 "delete_clock" $args
+ set clks [all_clocks]
+ } else {
+ check_argc_eq1 "delete_clock" $args
+ set clks [get_clocks_warn "clocks" [lindex $args 0]]
+ }
+ foreach clk $clks {
+ remove_clock_cmd $clk
+ }
+}
+
+################################################################
+
+define_cmd_args "delete_generated_clock" {[-all] clocks}
+
+proc delete_generated_clock { args } {
+ remove_gclk_cmd "delete_generated_clock" $args
+}
+
+################################################################
+
+define_cmd_args "set_disable_inferred_clock_gating" { objects }
+
+proc set_disable_inferred_clock_gating { objects } {
+ set_disable_inferred_clock_gating_cmd $objects
+}
+
+proc set_disable_inferred_clock_gating_cmd { objects } {
+ parse_inst_port_pin_arg $objects insts pins
+ foreach inst $insts {
+ disable_clock_gating_check_inst $inst
+ }
+ foreach pin $pins {
+ disable_clock_gating_check_pin $pin
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_case_analysis" {pins}
+
+proc unset_case_analysis { pins } {
+ set pins1 [get_port_pins_error "pins" $pins]
+ foreach pin $pins1 {
+ unset_case_analysis_cmd $pin
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_clock_groups" \
+ {[-logically_exclusive] [-physically_exclusive]\
+ [-asynchronous] [-name names] [-all]}
+
+proc unset_clock_groups { args } {
+ unset_clk_groups_cmd "unset_clock_groups" $args
+}
+
+proc unset_clk_groups_cmd { cmd cmd_args } {
+ parse_key_args $cmd cmd_args \
+ keys {-name} \
+ flags {-logically_exclusive -physically_exclusive -asynchronous -all}
+
+ set all [info exists flags(-all)]
+ set names {}
+ if {[info exists keys(-name)]} {
+ set names $keys(-name)
+ }
+
+ if { $all && $names != {} } {
+ sta_error 454 "the -all and -name options are mutually exclusive."
+ }
+ if { !$all && $names == {} } {
+ sta_error 455 "either -all or -name options must be specified."
+ }
+
+ set logically_exclusive [info exists flags(-logically_exclusive)]
+ set physically_exclusive [info exists flags(-physically_exclusive)]
+ set asynchronous [info exists flags(-asynchronous)]
+
+ if { ($logically_exclusive+$physically_exclusive+$asynchronous) == 0 } {
+ sta_error 456 "one of -logically_exclusive, -physically_exclusive or -asynchronous is required."
+ }
+ if { ($logically_exclusive+$physically_exclusive+$asynchronous) > 1 } {
+ sta_error 457 "the keywords -logically_exclusive, -physically_exclusive and -asynchronous are mutually exclusive."
+ }
+
+ if { $all } {
+ if { $logically_exclusive } {
+ unset_clock_groups_logically_exclusive "NULL"
+ } elseif { $physically_exclusive } {
+ unset_clock_groups_physically_exclusive "NULL"
+ } elseif { $asynchronous } {
+ unset_clock_groups_asynchronous "NULL"
+ }
+ } else {
+ foreach name $names {
+ if { $logically_exclusive } {
+ unset_clock_groups_logically_exclusive $name
+ } elseif { $physically_exclusive } {
+ unset_clock_groups_physically_exclusive $name
+ } elseif { $asynchronous } {
+ unset_clock_groups_asynchronous $name
+ }
+ }
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_clock_latency" {[-source] [-clock clock] objects}
+
+proc unset_clock_latency { args } {
+ unset_clk_latency_cmd "unset_clock_latency" $args
+}
+
+proc unset_clk_latency_cmd { cmd cmd_args } {
+ parse_key_args $cmd cmd_args keys {-clock} flags {-source}
+ check_argc_eq1 $cmd $cmd_args
+ set objects [lindex $cmd_args 0]
+ parse_clk_port_pin_arg $objects clks pins
+ set pin_clk "NULL"
+ if { [info exists keys(-clock)] } {
+ set pin_clk [get_clock_warn "clock" $keys(-clock)]
+ if { $clks != {} } {
+ sta_warn 303 "-clock ignored for clock objects."
+ }
+ }
+
+ if {[info exists flags(-source)]} {
+ # Source latency.
+ foreach clk $clks {
+ unset_clock_insertion_cmd $clk "NULL"
+ }
+ foreach pin $pins {
+ # Source only allowed on clocks and clock pins.
+ if { ![is_clock_pin $pin] } {
+ sta_error 458 "-source '[$pin path_name]' is not a clock pin."
+ }
+ unset_clock_insertion_cmd $pin_clk $pin
+ }
+ } else {
+ # Latency.
+ foreach clk $clks {
+ unset_clock_latency_cmd $clk "NULL"
+ }
+ foreach pin $pins {
+ unset_clock_latency_cmd $pin_clk $pin
+ }
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_clock_transition" {clocks}
+
+proc unset_clock_transition { args } {
+ check_argc_eq1 "unset_clock_transition" $args
+ set clks [get_clocks_warn "clocks" [lindex $args 0]]
+ foreach clk $clks {
+ unset_clock_slew_cmd $clk
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_clock_uncertainty" \
+ {[-from|-rise_from|-fall_from from_clock]\
+ [-to|-rise_to|-fall_to to_clock] [-rise] [-fall]\
+ [-setup] [-hold] [objects]}
+
+proc unset_clock_uncertainty { args } {
+ unset_clk_uncertainty_cmd "unset_clock_uncertainty" $args
+}
+
+proc unset_clk_uncertainty_cmd { cmd cmd_args } {
+ parse_key_args $cmd cmd_args \
+ keys {-from -rise_from -fall_from -to -rise_to -fall_to} \
+ flags {-rise -fall -setup -hold}
+
+ set min_max "min_max"
+ if { [info exists flags(-setup)] && ![info exists flags(-hold)] } {
+ set min_max "max"
+ }
+ if { [info exists flags(-hold)] && ![info exists flags(-setup)] } {
+ set min_max "min"
+ }
+
+ if { [info exists keys(-from)] } {
+ set from_key "-from"
+ set from_rf "rise_fall"
+ } elseif { [info exists keys(-rise_from)] } {
+ set from_key "-rise_from"
+ set from_rf "rise"
+ } elseif { [info exists keys(-fall_from)] } {
+ set from_key "-fall_from"
+ set from_rf "fall"
+ } else {
+ set from_key "none"
+ }
+
+ if { [info exists keys(-to)] } {
+ set to_key "-to"
+ set to_rf "rise_fall"
+ } elseif { [info exists keys(-rise_to)] } {
+ set to_key "-rise_to"
+ set to_rf "rise"
+ } elseif { [info exists keys(-fall_to)] } {
+ set to_key "-fall_to"
+ set to_rf "fall"
+ } else {
+ set to_key "none"
+ }
+
+ if { $from_key != "none" && $to_key == "none" \
+ || $from_key == "none" && $to_key != "none" } {
+ sta_error 459 "-from/-to must be used together."
+ } elseif { $from_key != "none" && $to_key != "none" } {
+ # Inter-clock uncertainty.
+ check_argc_eq0 "unset_clock_uncertainty" $cmd_args
+
+ # -from/-to can be lists.
+ set from_clks [get_clocks_warn "from_clocks" $keys($from_key)]
+ set to_clks [get_clocks_warn "to_clocks" $keys($to_key)]
+
+ foreach from_clk $from_clks {
+ foreach to_clk $to_clks {
+ unset_inter_clock_uncertainty $from_clk $from_rf \
+ $to_clk $to_rf $min_max
+ }
+ }
+ } else {
+ # Single clock uncertainty.
+ check_argc_eq1 $cmd $cmd_args
+ if { [info exists keys(-rise)] \
+ || [info exists keys(-fall)] } {
+ sta_error 460 "-rise, -fall options not allowed for single clock uncertainty."
+ }
+ set objects [lindex $cmd_args 0]
+ parse_clk_port_pin_arg $objects clks pins
+
+ foreach clk $clks {
+ unset_clock_uncertainty_clk $clk $min_max
+ }
+ foreach pin $pins {
+ unset_clock_uncertainty_pin $pin $min_max
+ }
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_data_check" \
+ {[-from from_pin] [-rise_from from_pin] [-fall_from from_pin]\
+ [-to to_pin] [-rise_to to_pin] [-fall_to to_pin]\
+ [-setup | -hold] [-clock clock]}
+
+proc unset_data_check { args } {
+ unset_data_checks_cmd "unset_data_check" $args
+}
+
+proc unset_data_checks_cmd { cmd cmd_args } {
+ parse_key_args cmd cmd_args \
+ keys {-from -rise_from -fall_from -to -rise_to -fall_to -clock} \
+ flags {-setup -hold}
+ check_argc_eq0 $cmd $cmd_args
+
+ set from_rf "rise_fall"
+ set to_rf "rise_fall"
+ set clk "NULL"
+ set setup_hold "max"
+ if [info exists keys(-from)] {
+ set from [get_port_pin_error "from_pin" $keys(-from)]
+ } elseif [info exists keys(-rise_from)] {
+ set from [get_port_pin_error "from_pin" $keys(-rise_from)]
+ set from_rf "rise"
+ } elseif [info exists keys(-fall_from)] {
+ set from [get_port_pin_error "from_pin" $keys(-fall_from)]
+ set from_rf "fall"
+ } else {
+ sta_error 461 "missing -from, -rise_from or -fall_from argument."
+ }
+
+ if [info exists keys(-to)] {
+ set to [get_port_pin_error "to_pin" $keys(-to)]
+ } elseif [info exists keys(-rise_to)] {
+ set to [get_port_pin_error "to_pin" $keys(-rise_to)]
+ set to_rf "rise"
+ } elseif [info exists keys(-fall_to)] {
+ set to [get_port_pin_error "to_pin" $keys(-fall_to)]
+ set to_rf "fall"
+ } else {
+ sta_error 462 "missing -to, -rise_to or -fall_to argument."
+ }
+
+ if [info exists keys(-clock)] {
+ set clk [get_clock_warn "clock" $keys(-clock)]
+ }
+
+ if { [info exists flags(-setup)] && ![info exists flags(-hold)] } {
+ set setup_hold "setup"
+ } elseif { [info exists flags(-hold)] && ![info exists flags(-setup)] } {
+ set setup_hold "hold"
+ } else {
+ set setup_hold "setup_hold"
+ }
+
+ unset_data_check_cmd $from $from_rf $to $to_rf $clk $setup_hold
+}
+
+################################################################
+
+define_cmd_args "unset_disable_inferred_clock_gating" { objects }
+
+proc unset_disable_inferred_clock_gating { objects } {
+ unset_disable_inferred_clock_gating_cmd $objects
+}
+
+proc unset_disable_inferred_clock_gating_cmd { objects } {
+ parse_inst_port_pin_arg $objects insts pins
+ foreach inst $insts {
+ unset_disable_clock_gating_check_inst $inst
+ }
+ foreach pin $pins {
+ unset_disable_clock_gating_check_pin $pin
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_disable_timing" \
+ {[-from from_port] [-to to_port] objects}
+
+proc unset_disable_timing { args } {
+ unset_disable_cmd "unset_disable_timing" $args
+}
+
+proc unset_disable_cmd { cmd cmd_args } {
+ parse_key_args $cmd cmd_args keys {-from -to} flags {}
+ check_argc_eq1 $cmd $cmd_args
+
+ set from ""
+ if { [info exists keys(-from)] } {
+ set from $keys(-from)
+ }
+ set to ""
+ if { [info exists keys(-to)] } {
+ set to $keys(-to)
+ }
+ parse_libcell_libport_inst_port_pin_edge_timing_arc_set_arg $cmd_args \
+ libcells libports insts ports pins edges timing_arc_sets
+
+ if { ([info exists keys(-from)] || [info exists keys(-to)]) \
+ && ($libports != {} || $pins != {} || $ports != {}) } {
+ sta_warn 304 "-from/-to keywords ignored for lib_pin, port and pin arguments."
+ }
+
+ foreach libcell $libcells {
+ unset_disable_timing_cell $libcell $from $to
+ }
+ foreach libport $libports {
+ unset_disable_lib_port $libport
+ }
+ foreach inst $insts {
+ unset_disable_timing_instance $inst $from $to
+ }
+ foreach pin $pins {
+ unset_disable_pin $pin
+ }
+ foreach port $ports {
+ unset_disable_port $port
+ }
+ foreach edge $edges {
+ unset_disable_edge $edge
+ }
+ foreach timing_arc_set $timing_arc_sets {
+ unset_disable_timing_arc_set $timing_arc_set
+ }
+}
+
+proc unset_disable_timing_cell { cell from to } {
+ set from_ports [parse_disable_cell_ports $cell $from]
+ set to_ports [parse_disable_cell_ports $cell $to]
+ if { $from_ports == "NULL" && $to_ports == "NULL" } {
+ unset_disable_cell $cell "NULL" "NULL"
+ } elseif { $from_ports == "NULL" } {
+ foreach to_port $to_ports {
+ unset_disable_cell $cell "NULL" $to_port
+ }
+ } elseif { $to_ports == "NULL" } {
+ foreach from_port $from_ports {
+ unset_disable_cell $cell $from_port "NULL"
+ }
+ } else {
+ foreach from_port $from_ports {
+ foreach to_port $to_ports {
+ unset_disable_cell $cell $from_port $to_port
+ }
+ }
+ }
+}
+
+proc unset_disable_timing_instance { inst from to } {
+ set from_ports [parse_disable_inst_ports $inst $from]
+ set to_ports [parse_disable_inst_ports $inst $to]
+ if { ![$inst is_leaf] } {
+ sta_error 463 "-from/-to hierarchical instance not supported."
+ }
+ if { $from_ports == "NULL" && $to_ports == "NULL" } {
+ unset_disable_instance $inst "NULL" "NULL"
+ } elseif { $from_ports == "NULL" } {
+ foreach to_port $to_ports {
+ unset_disable_instance $inst "NULL" $to_port
+ }
+ } elseif { $to_ports == "NULL" } {
+ foreach from_port $from_ports {
+ unset_disable_instance $inst $from_port "NULL"
+ }
+ } else {
+ foreach from_port $from_ports {
+ foreach to_port $to_ports {
+ unset_disable_instance $inst $from_port $to_port
+ }
+ }
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_generated_clock" {[-all] clocks}
+
+proc unset_generated_clock { args } {
+ unset_gclk_cmd "unset_generated_clock" $args
+}
+
+proc remove_gclk_cmd { cmd cmd_args } {
+ parse_key_args $cmd cmd_args keys {} flags {-all}
+ if { [info exists flags(-all)] } {
+ check_argc_eq0 $cmd $cmd_args
+ set clks [all_clocks]
+ } else {
+ check_argc_eq1 $cmd $cmd_args
+ set clks [get_clocks_warn "clocks" [lindex $cmd_args 0]]
+ }
+ foreach clk $clks {
+ if { [$clk is_generated] } {
+ remove_clock_cmd $clk
+ }
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_input_delay" \
+ {[-rise] [-fall] [-max] [-min]\
+ [-clock clock] [-clock_fall]\
+ port_pin_list}
+
+proc unset_input_delay { args } {
+ unset_port_delay "unset_input_delay" "unset_input_delay_cmd" $args
+}
+
+################################################################
+
+define_cmd_args "unset_output_delay" \
+ {[-rise] [-fall] [-max] [-min]\
+ [-clock clock] [-clock_fall]\
+ port_pin_list}
+
+proc unset_output_delay { args } {
+ unset_port_delay "unset_output_delay" "unset_output_delay_cmd" $args
+}
+
+proc unset_port_delay { cmd swig_cmd cmd_args } {
+ parse_key_args $cmd cmd_args \
+ keys {-clock -reference_pin} \
+ flags {-rise -fall -max -min -clock_fall }
+ check_argc_eq1 $cmd $cmd_args
+
+ set pins [get_port_pins_error "pins" [lindex $cmd_args 0]]
+
+ set clk "NULL"
+ if [info exists keys(-clock)] {
+ set clk [get_clock_warn "clock" $keys(-clock)]
+ }
+
+ if [info exists flags(-clock_fall)] {
+ set clk_rf "fall"
+ } else {
+ set clk_rf "rise"
+ }
+
+ set tr [parse_rise_fall_flags flags]
+ set min_max [parse_min_max_all_flags flags]
+
+ foreach pin $pins {
+ $swig_cmd $pin $tr $clk $clk_rf $min_max
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_path_exceptions" \
+ {[-setup] [-hold] [-rise] [-fall] [-from from_list]\
+ [-rise_from from_list] [-fall_from from_list]\
+ [-through through_list] [-rise_through through_list]\
+ [-fall_through through_list] [-to to_list] [-rise_to to_list]\
+ [-fall_to to_list]}
+
+proc unset_path_exceptions { args } {
+ unset_path_exceptions_cmd "unset_path_exceptions" $args
+}
+
+proc unset_path_exceptions_cmd { cmd cmd_args } {
+ parse_key_args $cmd cmd_args \
+ keys {-from -rise_from -fall_from -to -rise_to -fall_to} \
+ flags {-setup -hold -rise -fall} 0
+
+ set min_max "min_max"
+ if { [info exists flags(-setup)] && ![info exists flags(-hold)] } {
+ set min_max "max"
+ }
+ if { [info exists flags(-hold)] && ![info exists flags(-setup)] } {
+ set min_max "min"
+ }
+
+ set arg_error 0
+ set from [parse_from_arg keys arg_error]
+ set thrus [parse_thrus_arg cmd_args arg_error]
+ set to [parse_to_arg keys flags arg_error]
+ if { $arg_error } {
+ delete_from_thrus_to $from $thrus $to
+ sta_error 464 "$cmd command failed."
+ return 0
+ }
+
+ check_for_key_args $cmd cmd_args
+ if { $cmd_args != {} } {
+ delete_from_thrus_to $from $thrus $to
+ sta_error 465 "positional arguments not supported."
+ }
+ if { ($from == "NULL" && $thrus == "" && $to == "NULL") } {
+ delete_from_thrus_to $from $thrus $to
+ sta_error 466 "-from, -through or -to required."
+ }
+
+ reset_path_cmd $from $thrus $to $min_max
+ delete_from_thrus_to $from $thrus $to
+}
+
+################################################################
+
+define_cmd_args "unset_propagated_clock" {objects}
+
+proc unset_propagated_clock { objects } {
+ parse_clk_port_pin_arg $objects clks pins
+ foreach clk $clks {
+ unset_propagated_clock_cmd $clk
+ }
+ foreach pin $pins {
+ unset_propagated_clock_pin_cmd $pin
+ }
+}
+
+################################################################
+
+define_cmd_args "unset_timing_derate" {}
+
+proc unset_timing_derate { args } {
+ check_argc_eq0 "unset_timing_derate" $args
+ reset_timing_derate_cmd
+}
+
+################################################################
+#
+# Network editing commands
+#
+################################################################
+
+define_cmd_args "connect_pin" {net pin}
+# deprecated 2.0.16 05/02/2019
+define_cmd_args "connect_pins" {net pins}
+
+define_cmd_args "delete_instance" {inst}
+
+define_cmd_args "delete_net" {net}
+
+define_cmd_args "disconnect_pin" {net -all|pin}
+# deprecated 2.0.16 05/02/2019
+define_cmd_args "disconnect_pins" {net -all|pins}
+
+define_cmd_args "make_instance" {inst_path lib_cell}
+
+define_cmd_args "make_net" {}
+
+define_cmd_args "replace_cell" {instance lib_cell}
+define_cmd_args "replace_to_scan_cell" {instance}
+
+define_cmd_args "insert_buffer" {buffer_name buffer_cell net load_pins\
+ buffer_out_net_name}
+
+################################################################
+#
+# Delay calculation commands
+#
+################################################################
+
+define_cmd_args "set_assigned_delay" \
+ {-cell|-net [-rise] [-fall] [-corner corner_name] [-min] [-max]\
+ [-from from_pins] [-to to_pins] delay}
+
+# Change the delay for timing arcs between from_pins and to_pins matching
+# on cell (instance) or net.
+proc set_assigned_delay { args } {
+ set_assigned_delay_cmd "set_assigned_delay" $args
+}
+
+proc set_assigned_delay_cmd { cmd cmd_args } {
+ parse_key_args $cmd cmd_args keys {-corner -from -to} \
+ flags {-cell -net -rise -fall -max -min}
+ check_argc_eq1 $cmd $cmd_args
+ set corner [parse_corner keys]
+ set min_max [parse_min_max_all_check_flags flags]
+ set to_rf [parse_rise_fall_flags flags]
+
+ if [info exists keys(-from)] {
+ set from_pins [get_port_pins_error "from_pins" $keys(-from)]
+ } else {
+ sta_error 442 "$cmd missing -from argument."
+ }
+ if [info exists keys(-to)] {
+ set to_pins [get_port_pins_error "to_pins" $keys(-to)]
+ } else {
+ sta_error 443 "$cmd missing -to argument."
+ }
+
+ set delay [lindex $cmd_args 0]
+ if {![string is double $delay]} {
+ sta_error 444 "$cmd delay is not a float."
+ }
+ set delay [time_ui_sta $delay]
+
+ if {[info exists flags(-cell)] && [info exists flags(-net)]} {
+ sta_error 445 "set_annotated_delay -cell and -net options are mutually excluive."
+ } elseif {[info exists flags(-cell)]} {
+ if { $from_pins != {} } {
+ set inst [[lindex $from_pins 0] instance]
+ foreach pin $from_pins {
+ if {[$pin instance] != $inst} {
+ sta_error 446 "$cmd pin [get_full_name $pin] is not attached to instance [get_full_name $inst]."
+ }
+ }
+ foreach pin $to_pins {
+ if {[$pin instance] != $inst} {
+ sta_error 447 "$cmd pin [get_full_name $pin] is not attached to instance [get_full_name $inst]"
+ }
+ }
+ }
+ } elseif {![info exists flags(-net)]} {
+ sta_error 448 "$cmd -cell or -net required."
+ }
+ foreach from_pin $from_pins {
+ set from_vertices [$from_pin vertices]
+ set_assigned_delay1 [lindex $from_vertices 0] \
+ $to_pins $to_rf $corner $min_max $delay
+ if { [llength $from_vertices] == 2 } {
+ set_assigned_delay1 [lindex $from_vertices 1] \
+ $to_pins $to_rf $corner $min_max $delay
+ }
+ }
+}
+
+proc set_assigned_delay1 { from_vertex to_pins to_rf corner min_max delay } {
+ foreach to_pin $to_pins {
+ set to_vertices [$to_pin vertices]
+ set_assigned_delay2 $from_vertex [lindex $to_vertices 0] \
+ $to_rf $corner $min_max $delay
+ if { [llength $to_vertices] == 2 } {
+ # Bidirect driver.
+ set_assigned_delay2 $from_vertex [lindex $to_vertices 1] \
+ $to_rf $corner $min_max $delay
+ }
+ }
+}
+
+proc set_assigned_delay2 {from_vertex to_vertex to_rf corner min_max delay} {
+ set edge_iter [$from_vertex out_edge_iterator]
+ while {[$edge_iter has_next]} {
+ set edge [$edge_iter next]
+ if { [$edge to] == $to_vertex \
+ && ![timing_role_is_check [$edge role]] } {
+ set arc_iter [$edge timing_arc_iterator]
+ while {[$arc_iter has_next]} {
+ set arc [$arc_iter next]
+ if { $to_rf == "rise_fall" \
+ || $to_rf eq [$arc to_trans_name] } {
+ set_arc_delay $edge $arc $corner $min_max $delay
+ }
+ }
+ $arc_iter finish
+ }
+ }
+ $edge_iter finish
+}
+
+################################################################
+
+define_cmd_args "set_assigned_check" \
+ {-setup|-hold|-recovery|-removal [-rise] [-fall]\
+ [-corner corner_name] [-min] [-max]\
+ [-from from_pins] [-to to_pins] [-clock rise|fall]\
+ [-cond sdf_cond] [-worst] check_value}
+
+# -worst is ignored.
+proc set_assigned_check { args } {
+ set_assigned_check_cmd "set_assigned_check" $args
+}
+
+# -worst is ignored.
+proc set_assigned_check_cmd { cmd cmd_args } {
+ parse_key_args $cmd cmd_args \
+ keys {-from -to -corner -clock -cond} \
+ flags {-setup -hold -recovery -removal -rise -fall -max -min -worst}
+ check_argc_eq1 $cmd $cmd_args
+
+ if { [info exists keys(-from)] } {
+ set from_pins [get_port_pins_error "from_pins" $keys(-from)]
+ } else {
+ sta_error 449 "$cmd missing -from argument."
+ }
+ set from_rf "rise_fall"
+ if { [info exists keys(-clock)] } {
+ set clk_arg $keys(-clock)
+ if { $clk_arg eq "rise" \
+ || $clk_arg eq "fall" } {
+ set from_rf $clk_arg
+ } else {
+ sta_error 450 "$cmd -clock must be rise or fall."
+ }
+ }
+
+ if { [info exists keys(-to)] } {
+ set to_pins [get_port_pins_error "to_pins" $keys(-to)]
+ } else {
+ sta_error 451 "$cmd missing -to argument."
+ }
+ set to_rf [parse_rise_fall_flags flags]
+ set corner [parse_corner keys]
+ set min_max [parse_min_max_all_check_flags flags]
+
+ if { [info exists flags(-setup)] } {
+ set role "setup"
+ } elseif { [info exists flags(-hold)] } {
+ set role "hold"
+ } elseif { [info exists flags(-recovery)] } {
+ set role "recovery"
+ } elseif { [info exists flags(-removal)] } {
+ set role "removal"
+ } else {
+ sta_error 452 "$cmd missing -setup|-hold|-recovery|-removal check type.."
+ }
+ set cond ""
+ if { [info exists key(-cond)] } {
+ set cond $key(-cond)
+ }
+ set check_value [lindex $cmd_args 0]
+ if { ![string is double $check_value] } {
+ sta_error 453 "$cmd check_value is not a float."
+ }
+ set check_value [time_ui_sta $check_value]
+
+ foreach from_pin $from_pins {
+ set from_vertices [$from_pin vertices]
+ set_assigned_check1 [lindex $from_vertices 0] $from_rf \
+ $to_pins $to_rf $role $corner $min_max $cond $check_value
+ if { [llength $from_vertices] == 2 } {
+ set_assigned_check1 [lindex $from_vertices 1] $from_rf \
+ $to_pins $to_rf $role $corner $min_max $cond $check_value
+ }
+ }
+}
+
+proc set_assigned_check1 { from_vertex from_rf to_pins to_rf \
+ role corner min_max cond check_value } {
+ foreach to_pin $to_pins {
+ set to_vertices [$to_pin vertices]
+ set_assigned_check2 $from_vertex $from_rf [lindex $to_vertices 0] \
+ $to_rf $role $corner $min_max $cond $check_value
+ if { [llength $to_vertices] == 2 } {
+ # Bidirect driver.
+ set_assigned_check2 $from_vertex $from_rf \
+ [lindex $to_vertices 1] $to_rf $role $corner $min_max \
+ $cond $check_value
+ }
+ }
+}
+
+proc set_assigned_check2 { from_vertex from_rf to_vertex to_rf \
+ role corner min_max cond check_value } {
+ set edge_iter [$from_vertex out_edge_iterator]
+ while {[$edge_iter has_next]} {
+ set edge [$edge_iter next]
+ if { [$edge to] == $to_vertex } {
+ set arc_iter [$edge timing_arc_iterator]
+ while {[$arc_iter has_next]} {
+ set arc [$arc_iter next]
+ if { ($from_rf eq "rise_fall" \
+ || $from_rf eq [$arc from_trans_name]) \
+ && ($to_rf eq "rise_fall" \
+ || $to_rf eq [$arc to_trans_name]) \
+ && [$arc role] eq $role \
+ && ($cond eq "" || [$arc sdf_cond] eq $cond) } {
+ set_arc_delay $edge $arc $corner $min_max $check_value
+ }
+ }
+ $arc_iter finish
+ }
+ }
+ $edge_iter finish
+}
+
+################################################################a
+
+define_cmd_args "set_assigned_transition" \
+ {[-rise] [-fall] [-corner corner_name] [-min] [-max] slew pins}
+
+# Change the slew on a list of ports.
+proc set_assigned_transition { args } {
+ parse_key_args "set_assigned_transition" args keys {-corner} \
+ flags {-rise -fall -max -min}
+
+ set corner [parse_corner keys]
+ set min_max [parse_min_max_all_check_flags flags]
+ set tr [parse_rise_fall_flags flags]
+ check_argc_eq2 "set_assigned_transition" $args
+
+ set slew [lindex $args 0]
+ if {![string is double $slew]} {
+ sta_error 428 "set_assigned_transition transition is not a float."
+ }
+ set slew [time_ui_sta $slew]
+ set pins [get_port_pins_error "pins" [lindex $args 1]]
+ foreach pin $pins {
+ set vertices [$pin vertices]
+ set vertex [lindex $vertices 0]
+ set_annotated_slew $vertex $corner $min_max $tr $slew
+ if { [llength $vertices] == 2 } {
+ # Bidirect driver.
+ set vertex [lindex $vertices 1]
+ set_annotated_slew $vertex $min_max $tr $slew
+ }
+ }
+}
+
+################################################################a
+
+# compatibility
+define_cmd_args "read_parasitics" \
+ {[-min]\
+ [-max]\
+ [-elmore]\
+ [-path path]\
+ [-increment]\
+ [-pin_cap_included]\
+ [-keep_capacitive_coupling]\
+ [-coupling_reduction_factor factor]\
+ [-reduce_to pi_elmore|pi_pole_residue2]\
+ [-delete_after_reduce]\
+ [-quiet]\
+ [-save]\
+ filename}
+
+################################################################
+#
+# Utility commands
+#
+################################################################
+
+define_cmd_args "delete_from_list" {list objs}
+
+proc delete_from_list { list objects } {
+ delete_objects_from_list_cmd $list $objects
+}
+
+proc delete_objects_from_list_cmd { list objects } {
+ set list0 [lindex $list 0]
+ set list_is_object [is_object $list0]
+ set list_type [object_type $list0]
+ foreach obj $objects {
+ # If the list is a collection of tcl objects (returned by get_*),
+ # convert the obj to be removed from a name to an object of the same
+ # type.
+ if {$list_is_object && ![is_object $obj]} {
+ if {$list_type == "Clock"} {
+ set obj [find_clock $obj]
+ } elseif {$list_type == "Port"} {
+ set top_instance [top_instance]
+ set top_cell [$top_instance cell]
+ set obj [$top_cell find_port $obj]
+ } elseif {$list_type == "Pin"} {
+ set obj [find_pin $obj]
+ } elseif {$list_type == "Instance"} {
+ set obj [find_instance $obj]
+ } elseif {$list_type == "Net"} {
+ set obj [find_net $obj]
+ } elseif {$list_type == "LibertyLibrary"} {
+ set obj [find_liberty $obj]
+ } elseif {$list_type == "LibertyCell"} {
+ set obj [find_liberty_cell $obj]
+ } elseif {$list_type == "LibertyPort"} {
+ set obj [get_lib_pins $obj]
+ } else {
+ sta_error 439 "unsupported object type $list_type."
+ }
+ }
+ set index [lsearch $list $obj]
+ if { $index != -1 } {
+ set list [lreplace $list $index $index]
+ }
+ }
+ return $list
+}
+
+################################################################
+
+define_cmd_args "get_fanin" \
+ {-to sink_list [-flat] [-only_cells] [-startpoints_only]\
+ [-levels level_count] [-pin_levels pin_count]\
+ [-trace_arcs timing|enabled|all]}
+
+proc get_fanin { args } {
+ parse_key_args "get_fanin" args \
+ keys {-to -levels -pin_levels -trace_arcs} \
+ flags {-flat -only_cells -startpoints_only}
+ if { [llength $args] != 0 } {
+ cmd_usage_error "get_fanin"
+ }
+ if { ![info exists keys(-to)] } {
+ cmd_usage_error "get_fanin"
+ }
+ parse_port_pin_net_arg $keys(-to) pins nets
+ foreach net $nets {
+ lappend pins [net_driver_pins $net]
+ }
+ set flat [info exists flags(-flat)]
+ set only_insts [info exists flags(-only_cells)]
+ set startpoints_only [info exists flags(-startpoints_only)]
+ set inst_levels 0
+ if { [info exists keys(-levels)] } {
+ set inst_levels $keys(-levels)
+ }
+ set pin_levels 0
+ if { [info exists keys(-pin_levels)] } {
+ set pin_levels $keys(-pin_levels)
+ }
+
+ set thru_disabled 0
+ set thru_constants 0
+ if { [info exists keys(-trace_arcs)] } {
+ set trace_arcs $keys(-trace_arcs)
+ if { $trace_arcs == "timing" } {
+ set thru_disabled 0
+ set thru_constants 0
+ } elseif { $trace_arcs == "enabled" } {
+ set thru_disabled 0
+ set thru_constants 0
+ } elseif { $trace_arcs == "all" } {
+ set thru_disabled 1
+ set thru_constants 1
+ } else {
+ cmd_usage_error "get_fanin"
+ }
+ }
+ if { $only_insts } {
+ return [find_fanin_insts $pins $flat $startpoints_only \
+ $inst_levels $pin_levels $thru_disabled $thru_constants]
+ } else {
+ return [find_fanin_pins $pins $flat $startpoints_only \
+ $inst_levels $pin_levels $thru_disabled $thru_constants]
+ }
+}
+
+################################################################
+
+define_cmd_args "get_fanout" \
+ {-from source_list [-flat] [-only_cells] [-endpoints_only]\
+ [-levels level_count] [-pin_levels pin_count]\
+ [-trace_arcs timing|enabled|all]}
+
+proc get_fanout { args } {
+ parse_key_args "get_fanout" args \
+ keys {-from -levels -pin_levels -trace_arcs} \
+ flags {-flat -only_cells -endpoints_only}
+ if { [llength $args] != 0 } {
+ cmd_usage_error "get_fanout"
+ }
+ parse_port_pin_net_arg $keys(-from) pins nets
+ foreach net $nets {
+ lappend pins [net_load_pins $net]
+ }
+ set flat [info exists flags(-flat)]
+ set only_insts [info exists flags(-only_cells)]
+ set endpoints_only [info exists flags(-endpoints_only)]
+
+ set inst_levels 0
+ if { [info exists keys(-levels)] } {
+ set inst_levels $keys(-levels)
+ }
+
+ set pin_levels 0
+ if { [info exists keys(-pin_levels)] } {
+ set pin_levels $keys(-pin_levels)
+ }
+
+ set thru_disabled 0
+ set thru_constants 0
+ if { [info exists keys(-trace_arcs)] } {
+ set trace_arcs $keys(-trace_arcs)
+ if { $trace_arcs == "timing" } {
+ set thru_disabled 0
+ set thru_constants 0
+ } elseif { $trace_arcs == "enabled" } {
+ set thru_disabled 0
+ set thru_constants 0
+ } elseif { $trace_arcs == "all" } {
+ set thru_disabled 1
+ set thru_constants 1
+ } else {
+ cmd_usage_error "get_fanout"
+ }
+ }
+ if { $only_insts } {
+ return [find_fanout_insts $pins $flat $endpoints_only \
+ $inst_levels $pin_levels $thru_disabled $thru_constants]
+ } else {
+ return [find_fanout_pins $pins $flat $endpoints_only \
+ $inst_levels $pin_levels $thru_disabled $thru_constants]
+ }
+}
+
+################################################################
+
+define_cmd_args "get_name" {objects}
+define_cmd_args "get_full_name" {objects}
+
+################################################################
+
+define_cmd_args "get_property" \
+ {[-object_type cell|pin|net|port|clock|timing_arc] object property}
+
+proc get_property { args } {
+ return [get_property_cmd "get_property" "-object_type" $args]
+}
+
+################################################################
+
+proc get_property_cmd { cmd type_key cmd_args } {
+ parse_key_args $cmd cmd_args keys $type_key flags {-quiet}
+ set quiet [info exists flags(-quiet)]
+ check_argc_eq2 $cmd $cmd_args
+ set object [lindex $cmd_args 0]
+ if { $object == "" } {
+ sta_error 491 "$cmd object is null."
+ } elseif { ![is_object $object] } {
+ if [info exists keys($type_key)] {
+ set object_type $keys($type_key)
+ } else {
+ sta_error 492 "$cmd $type_key must be specified with object name argument."
+ }
+ set object [get_property_object_type $object_type $object $quiet]
+ }
+ set prop [lindex $cmd_args 1]
+ return [get_object_property $object $prop]
+}
+
+proc get_object_property { object prop } {
+ if { [is_object $object] } {
+ set object_type [object_type $object]
+ if { $object_type == "Instance" } {
+ return [instance_property $object $prop]
+ } elseif { $object_type == "Pin" } {
+ return [pin_property $object $prop]
+ } elseif { $object_type == "Net" } {
+ return [net_property $object $prop]
+ } elseif { $object_type == "Clock" } {
+ return [clock_property $object $prop]
+ } elseif { $object_type == "Port" } {
+ return [port_property $object $prop]
+ } elseif { $object_type == "LibertyPort" } {
+ return [liberty_port_property $object $prop]
+ } elseif { $object_type == "LibertyCell" } {
+ return [liberty_cell_property $object $prop]
+ } elseif { $object_type == "Cell" } {
+ return [cell_property $object $prop]
+ } elseif { $object_type == "Library" } {
+ return [library_property $object $prop]
+ } elseif { $object_type == "LibertyLibrary" } {
+ return [liberty_library_property $object $prop]
+ } elseif { $object_type == "Edge" } {
+ return [edge_property $object $prop]
+ } elseif { $object_type == "PathEnd" } {
+ return [path_end_property $object $prop]
+ } elseif { $object_type == "PathRef" } {
+ return [path_ref_property $object $prop]
+ } elseif { $object_type == "TimingArcSet" } {
+ return [timing_arc_set_property $object $prop]
+ } else {
+ sta_error 606 "get_property unsupported object type $object_type."
+ }
+ } else {
+ sta_error 493 "get_property $object is not an object."
+ }
+}
+
+proc get_property_object_type { object_type object_name quiet } {
+ set object "NULL"
+ if { $object_type == "cell" } {
+ set object [get_cells -quiet $object_name]
+ } elseif { $object_type == "pin" } {
+ set object [get_pins -quiet $object_name]
+ } elseif { $object_type == "net" } {
+ set object [get_nets -quiet $object_name]
+ } elseif { $object_type == "port" } {
+ set object [get_ports -quiet $object_name]
+ } elseif { $object_type == "clock" } {
+ set object [get_clocks -quiet $object_name]
+ } elseif { $object_type == "lib_cell" } {
+ set object [get_lib_cells -quiet $object_name]
+ } elseif { $object_type == "lib_pin" } {
+ set object [get_lib_pins -quiet $object_name]
+ } elseif { $object_type == "lib" } {
+ set object [get_libs -quiet $object_name]
+ } else {
+ sta_error 494 "$object_type not supported."
+ }
+ if { $object == "NULL" && !$quiet } {
+ sta_error 495 "$object_type '$object_name' not found."
+ }
+ return [lindex $object 0]
+}
+
+proc get_object_type { obj } {
+ set object_type [object_type $obj]
+ if { $object_type == "Clock" } {
+ return "clock"
+ } elseif { $object_type == "LibertyCell" } {
+ return "libcell"
+ } elseif { $object_type == "LibertyPort" } {
+ return "libpin"
+ } elseif { $object_type == "Cell" } {
+ return "design"
+ } elseif { $object_type == "Instance" } {
+ return "cell"
+ } elseif { $object_type == "Port" } {
+ return "port"
+ } elseif { $object_type == "Pin" } {
+ return "pin"
+ } elseif { $object_type == "Net" } {
+ return "net"
+ } elseif { $object_type == "Edge" } {
+ return "timing_arc"
+ } elseif { $object_type == "TimingArcSet" } {
+ return "timing_arc"
+ } else {
+ return "?"
+ }
+}
+
+proc get_name { object } {
+ return [get_object_property $object "name"]
+}
+
+proc get_full_name { object } {
+ return [get_object_property $object "full_name"]
+}
+
+proc sort_by_name { objects } {
+ return [lsort -command name_cmp $objects]
+}
+
+proc name_cmp { obj1 obj2 } {
+ return [string compare [get_name $obj1] [get_name $obj2]]
+}
+
+proc sort_by_full_name { objects } {
+ return [lsort -command full_name_cmp $objects]
+}
+
+proc full_name_cmp { obj1 obj2 } {
+ return [string compare [get_full_name $obj1] [get_full_name $obj2]]
+}
+
+################################################################
+
+define_cmd_args "get_timing_edges" \
+ {[-from from_pin] [-to to_pin] [-of_objects objects] [-filter expr]}
+
+proc get_timing_edges { args } {
+ return [get_timing_edges_cmd "get_timing_edges" $args]
+}
+
+proc get_timing_edges_cmd { cmd cmd_args } {
+ parse_key_args $cmd cmd_args \
+ keys {-from -to -of_objects -filter} flags {}
+ check_argc_eq0 $cmd $cmd_args
+
+ set arcs {}
+ if { [info exists keys(-of_objects)] } {
+ if { [info exists keys(-from)] \
+ || [info exists keys(-from)] } {
+ sta_error 440 "-from/-to arguments not supported with -of_objects."
+ }
+ set arcs [get_timing_arcs_objects $keys(-of_objects)]
+ } elseif { [info exists keys(-from)] \
+ && [info exists keys(-to)] } {
+ set arcs [get_timing_arcs_from_to \
+ [get_port_pin_error "from" $keys(-from)] \
+ [get_port_pin_error "to" $keys(-to)]]
+ } elseif { [info exists keys(-from)] } {
+ set arcs [get_timing_arcs_from $keys(-from)]
+ } elseif { [info exists keys(-to)] } {
+ set arcs [get_timing_arcs_to $keys(-to)]
+ } else {
+ cmd_usage_error $cmd
+ }
+ if [info exists keys(-filter)] {
+ set arcs [filter_timing_arcs1 $keys(-filter) $arcs]
+ }
+ return $arcs
+}
+
+proc get_timing_arcs_objects { object_arg } {
+ parse_libcell_inst_arg $object_arg libcells insts
+ if { $insts != {} } {
+ set edges {}
+ foreach inst $insts {
+ lappend edges [instance_edges $inst]
+ }
+ return $edges
+ } elseif { $libcells != {} } {
+ set arc_sets {}
+ foreach libcell $libcells {
+ lappend arc_sets [libcell_timing_arc_sets $libcell]
+ }
+ return $arc_sets
+ }
+}
+
+proc instance_edges { inst } {
+ set edges {}
+ set pin_iter [$inst pin_iterator]
+ while {[$pin_iter has_next]} {
+ set pin [$pin_iter next]
+ foreach vertex [$pin vertices] {
+ set edge_iter [$vertex out_edge_iterator]
+ while {[$edge_iter has_next]} {
+ set edge [$edge_iter next]
+ if { [$edge role] != "wire" } {
+ lappend edges $edge
+ }
+ }
+ $edge_iter finish
+ }
+ }
+ $pin_iter finish
+ return $edges
+}
+
+proc libcell_timing_arc_sets { libcell } {
+ set arc_sets {}
+ set arc_iter [$libcell timing_arc_set_iterator]
+ while { [$arc_iter has_next] } {
+ lappend arc_sets [$arc_iter next]
+ }
+ $arc_iter finish
+ return $arc_sets
+}
+
+proc get_timing_arcs_from_to { from_pin_arg to_pin_arg } {
+ set edges {}
+ set from_pin [get_port_pin_error "from" $from_pin_arg]
+ set to_pin [get_port_pin_error "to" $to_pin_arg]
+ foreach from_vertex [$from_pin vertices] {
+ foreach to_vertex [$to_pin vertices] {
+ set edge_iter [$from_vertex out_edge_iterator]
+ while {[$edge_iter has_next]} {
+ set edge [$edge_iter next]
+ if { [$edge to] == $to_vertex } {
+ lappend edges $edge
+ }
+ }
+ $edge_iter finish
+ }
+ }
+ return $edges
+}
+
+proc get_timing_arcs_from { from_pin_arg } {
+ set from_pin [get_port_pin_error "from" $from_pin_arg]
+ set edges {}
+ foreach from_vertex [$from_pin vertices] {
+ set edge_iter [$from_vertex out_edge_iterator]
+ while {[$edge_iter has_next]} {
+ set edge [$edge_iter next]
+ lappend edges $edge
+ }
+ $edge_iter finish
+ }
+ return $edges
+}
+
+proc get_timing_arcs_to { to_pin_arg } {
+ set to_pin [get_port_pin_error "to" $to_pin_arg]
+ set edges {}
+ foreach to_vertex [$to_pin vertices] {
+ set edge_iter [$to_vertex in_edge_iterator]
+ while {[$edge_iter has_next]} {
+ set edge [$edge_iter next]
+ lappend edges $edge
+ }
+ $edge_iter finish
+ }
+ return $edges
+}
+
+proc filter_timing_arcs1 { filter objects } {
+ variable filter_regexp1
+ variable filter_or_regexp
+ variable filter_and_regexp
+ set filtered_objects {}
+ # Ignore sub-exprs in filter_regexp1 for expr2 match var.
+ if { [regexp $filter_or_regexp $filter ignore expr1 \
+ ignore ignore ignore expr2] } {
+ regexp $filter_regexp1 $expr1 ignore attr_name op arg
+ set filtered_objects1 [filter_timing_arcs $attr_name $op $arg $objects]
+ regexp $filter_regexp1 $expr2 ignore attr_name op arg
+ set filtered_objects2 [filter_timing_arcs $attr_name $op $arg $objects]
+ set filtered_objects [concat $filtered_objects1 $filtered_objects2]
+ } elseif { [regexp $filter_and_regexp $filter ignore expr1 \
+ ignore ignore ignore expr2] } {
+ regexp $filter_regexp1 $expr1 ignore attr_name op arg
+ set filtered_objects [filter_timing_arcs $attr_name $op $arg $objects]
+ regexp $filter_regexp1 $expr2 ignore attr_name op arg
+ set filtered_objects [filter_timing_arcs $attr_name $op \
+ $arg $filtered_objects]
+ } elseif { [regexp $filter_regexp1 $filter ignore attr_name op arg] } {
+ set filtered_objects [filter_timing_arcs $attr_name $op $arg $objects]
+ } else {
+ sta_error 441 "unsupported -filter expression."
+ }
+ return $filtered_objects
+}
+
+################################################################
+
+define_cmd_args "report_clock_properties" {[clocks]}
+
+proc_redirect report_clock_properties {
+ check_argc_eq0or1 "report_clock_properties" $args
+ update_generated_clks
+ report_line "Clock Period Waveform"
+ report_line "----------------------------------------------------"
+ if { [llength $args] == 0 } {
+ set clk_iter [clock_iterator]
+ while {[$clk_iter has_next]} {
+ set clk [$clk_iter next]
+ report_clock1 $clk
+ }
+ $clk_iter finish
+ } else {
+ foreach clk [get_clocks_warn "clock_name" [lindex $args 0]] {
+ report_clock1 $clk
+ }
+ }
+}
+
+proc report_clock1 { clk } {
+ global sta_report_default_digits
+
+ if { [$clk waveform_valid] } {
+ set digits $sta_report_default_digits
+ set waveform [$clk waveform]
+ if { $waveform == {} } {
+ set wave " "
+ } else {
+ set wave ""
+ foreach edge $waveform {
+ set wave "$wave[format "%10s" [format_time $edge $digits]]"
+ }
+ }
+ if {[$clk is_generated]} {
+ set generated " (generated)"
+ } else {
+ set generated ""
+ }
+ report_line "[format %-20s [get_name $clk]][format %10s [format_time [$clk period] $digits]] $wave$generated"
+ }
+}
+
+################################################################
+
+define_cmd_args "report_object_full_names" {objects}
+
+proc report_object_full_names { objects } {
+ foreach obj [sort_by_full_name $objects] {
+ report_line [get_full_name $obj]
+ }
+}
+
+define_cmd_args "report_object_names" {objects}
+
+proc report_object_names { objects } {
+ foreach obj [sort_by_name $objects] {
+ report_line [get_name $obj]
+ }
+}
+
+################################################################
+
+define_cmd_args "report_units" {}
+
+proc report_units { args } {
+ check_argc_eq0 "report_units" $args
+ foreach unit {"time" "capacitance" "resistance" "voltage" "current" "power" "distance"} {
+ report_line " $unit 1[unit_scale_abreviation $unit][unit_suffix $unit]"
+ }
+}
+
+################################################################
+
+define_cmd_args "with_output_to_variable" { var { cmds }}
+
+# with_output_to_variable variable { command args... }
+proc with_output_to_variable { var_name args } {
+ upvar 1 $var_name var
+
+ set body [lindex $args 0]
+ sta::redirect_string_begin;
+ catch $body ret
+ set var [sta::redirect_string_end]
+ return $ret
+}
+
+define_cmd_args "set_pocv_sigma_factor" { factor }
+
+################################################################
+
+define_cmd_args "write_path_spice" { -path_args path_args\
+ -spice_directory spice_directory\
+ -lib_subckt_file lib_subckts_file\
+ -model_file model_file\
+ -power power\
+ -ground ground}
+
+proc write_path_spice { args } {
+ parse_key_args "write_path_spice" args \
+ keys {-spice_directory -lib_subckt_file -model_file \
+ -power -ground -path_args} \
+ flags {}
+
+ if { [info exists keys(-spice_directory)] } {
+ set spice_dir [file nativename $keys(-spice_directory)]
+ if { ![file exists $spice_dir] } {
+ sta_error 496 "Directory $spice_dir not found."
+ }
+ if { ![file isdirectory $spice_dir] } {
+ sta_error 497 "$spice_dir is not a directory."
+ }
+ if { ![file writable $spice_dir] } {
+ sta_error 498 "Cannot write in $spice_dir."
+ }
+ } else {
+ sta_error 499 "No -spice_directory specified."
+ }
+
+ if { [info exists keys(-lib_subckt_file)] } {
+ set lib_subckt_file [file nativename $keys(-lib_subckt_file)]
+ if { ![file readable $lib_subckt_file] } {
+ sta_error 500 "-lib_subckt_file $lib_subckt_file is not readable."
+ }
+ } else {
+ sta_error 501 "No -lib_subckt_file specified."
+ }
+
+ if { [info exists keys(-model_file)] } {
+ set model_file [file nativename $keys(-model_file)]
+ if { ![file readable $model_file] } {
+ sta_error 502 "-model_file $model_file is not readable."
+ }
+ } else {
+ sta_error 503 "No -model_file specified."
+ }
+
+ if { [info exists keys(-power)] } {
+ set power $keys(-power)
+ } else {
+ sta_error 504 "No -power specified."
+ }
+
+ if { [info exists keys(-ground)] } {
+ set ground $keys(-ground)
+ } else {
+ sta_error 505 "No -ground specified."
+ }
+
+ if { ![info exists keys(-path_args)] } {
+ sta_error 506 "No -path_args specified."
+ }
+ set path_args $keys(-path_args)
+ set path_ends [eval [concat find_timing_paths $path_args]]
+ if { $path_ends == {} } {
+ sta_error 507 "No paths found for -path_args $path_args."
+ } else {
+ set path_index 1
+ foreach path_end $path_ends {
+ set path [$path_end path]
+ set path_name "path_$path_index"
+ set spice_file [file join $spice_dir "$path_name.sp"]
+ set subckt_file [file join $spice_dir "$path_name.subckt"]
+ write_path_spice_cmd $path $spice_file $subckt_file \
+ $lib_subckt_file $model_file $power $ground
+ incr path_index
+ }
+ }
+}
+
+# sta namespace end.
+}
diff --git a/hacks/src/openlane/io_place.py b/hacks/src/openlane/io_place.py
new file mode 100644
index 0000000..f3333f6
--- /dev/null
+++ b/hacks/src/openlane/io_place.py
@@ -0,0 +1,482 @@
+#!/usr/bin/env python3
+# Copyright 2020 Efabless Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+Places the IOs according to an input file. Supports regexes.
+File format:
+#N|#S|#E|#W
+pin1_regex
+pin2_regex
+...
+
+#S|#N|#E|#W
+...
+...
+"""
+
+import os
+import re
+import sys
+import argparse
+import random
+import odb
+
+parser = argparse.ArgumentParser(description='''
+Places the IOs according to an input file. Supports regexes.
+File format:
+#N|#S|#E|#W
+pin1_regex (low co-ordinates to high co-ordinates; e.g., bot to top and left to right)
+pin2_regex
+...
+
+#S|#N|#E|#W
+...
+...
+''')
+
+parser.add_argument('--input-def', '-d', required=True,
+ help='Input DEF')
+
+parser.add_argument('--input-lef', '-l', required=True,
+ help='Input LEF')
+
+parser.add_argument('--output-def', '-o',
+ default='output.def', help='Output DEF with new pin placements')
+
+parser.add_argument('--config', '-cfg',
+ help='Configuration file. See -h for format')
+
+parser.add_argument('--ver-layer', '-vl',
+ default=3,
+ help='Number of metal layer to place the vertical pins on. Defaults to SKY130 metal layer names. 1-based.')
+
+parser.add_argument('--hor-layer', '-hl',
+ default=4,
+ help='Number of metal layer to place the horizontal pins on. Defaults to SKY130 metal layer names. 1-based.')
+
+parser.add_argument('--hor-width-mult', '-hwm',
+ default=2,
+ help='')
+
+parser.add_argument('--ver-width-mult', '-vwm',
+ default=2,
+ help='')
+
+parser.add_argument('--length', '-len', type=float,
+ default=2,
+ help='')
+
+parser.add_argument('--hor-extension', '-hext', type=float,
+ default=0.0,
+ help='')
+
+parser.add_argument('--ver-extension', '-vext', type=float,
+ default=0.0,
+ help='')
+
+parser.add_argument('--reverse', '-rev',
+ choices=['N', 'E', 'S', 'W'],
+ nargs='+',
+ required=False,
+ default=[],
+ help='')
+
+parser.add_argument('--bus-sort', '-bsort', action='store_true',
+ default=False,
+ help='Sort pins so that bus bits with the same index are grouped'
+ 'together. e.g., a[0] b[0] c[0] a[1] b[1] c[1]')
+# TODO
+# width, length, and extension multipliers
+
+args = parser.parse_args()
+
+def_file_name = args.input_def
+lef_file_name = args.input_lef
+output_def_file_name = args.output_def
+config_file_name = args.config
+bus_sort_flag = args.bus_sort
+
+#Manual Pad Placement - Dinesh A
+manual_place_flag = False
+
+h_layer_index = int(args.hor_layer)
+v_layer_index = int(args.ver_layer)
+
+h_width_mult = int(args.hor_width_mult)
+v_width_mult = int(args.ver_width_mult)
+
+LENGTH = int(1000*args.length)
+
+H_EXTENSION = int(1000*args.hor_extension)
+V_EXTENSION = int(1000*args.ver_extension)
+
+if H_EXTENSION < 0:
+ H_EXTENSION = 0
+
+if V_EXTENSION < 0:
+ V_EXTENSION = 0
+
+reverse_arr = args.reverse
+reverse_arr = ["#"+rev for rev in reverse_arr]
+
+def getGrid(origin, count, step):
+ tracks = []
+ pos = origin
+ for i in range(count):
+ tracks.append(pos)
+ pos += step
+ assert len(tracks) > 0
+ tracks.sort()
+
+ return tracks
+
+def equallySpacedSeq(m, arr):
+ seq = []
+ n = len(arr)
+ # Bresenham
+ indices = [i*n//m + n//(2*m) for i in range(m)]
+ for i in indices:
+ seq.append(arr[i])
+ return seq
+
+# HUMAN SORTING: https://stackoverflow.com/questions/5967500/how-to-correctly-sort-a-string-with-a-number-inside
+def atof(text):
+ try:
+ retval = float(text)
+ except ValueError:
+ retval = text
+ return retval
+
+def natural_keys(enum):
+ text = enum[0]
+ text = re.sub("(\[|\]|\.|\$)", "", text)
+ '''
+ alist.sort(key=natural_keys) sorts in human order
+ http://nedbatchelder.com/blog/200712/human_sorting.html
+ (see toothy's implementation in the comments)
+ float regex comes from https://stackoverflow.com/a/12643073/190597
+ '''
+ return [atof(c) for c in re.split(r'[+-]?([0-9]+(?:[.][0-9]*)?|[.][0-9]+)', text)]
+
+def bus_keys(enum):
+ text = enum[0]
+ m = re.match("^.*\[(\d+)\]$", text)
+ if not m:
+ return -1
+ else:
+ return int(m.group(1))
+
+# Find the Slot matching next nearest slot-DineshA
+def findSlot(val, arr):
+ for i in arr:
+ if(i > val):
+ return i
+ print("ERROR: Next Valid Position not found :",val)
+ return -1
+
+
+# read config
+
+pin_placement_cfg = {"#N": [], "#E": [], "#S": [], "#W": []}
+cur_side = None
+if config_file_name is not None and config_file_name != "":
+ with open(config_file_name, 'r') as config_file:
+ for line in config_file:
+ line = line.split()
+ if len(line) == 0:
+ continue
+
+ if(manual_place_flag == False):
+ if len(line) > 1:
+ print("Only one entry allowed per line.")
+ sys.exit(1)
+
+ token = line[0]
+ else:
+ #During Manual Place we are allowing Four field
+ # <Pad Name> <Offset> <Position> <Multiplier>
+ # Causion: Make sure that you have given absolute name, else it will give issue
+ if len(line) > 4:
+ print("Only Four entry allowed per line.")
+ sys.exit(1)
+ if line[0] not in ["#N", "#E", "#S", "#W", "#NR", "#ER", "#SR", "#WR"]:
+ token = line
+ else:
+ token = line[0]
+
+ if cur_side is not None and token[0] != "#":
+ pin_placement_cfg[cur_side].append(token)
+ elif token not in ["#N", "#E", "#S", "#W", "#NR", "#ER", "#SR", "#WR", "#BUS_SORT","#MANUAL_PLACE"]:
+ print("Invalid token: ",token)
+ print("Valid directives are #N, #E, #S, or #W. Append R for reversing the default order.",
+ "Use #BUS_SORT to group 'bus bits' by index.",
+ "Use #MANUAL_PLACE Manual Placement <padname> <offset> <pin number>.",
+ "Please make sure you have set a valid side first before listing pins")
+ sys.exit(1)
+ elif token == "#BUS_SORT":
+ bus_sort_flag = True
+ elif token == "#MANUAL_PLACE":
+ print("Input token ",token)
+ manual_place_flag = True
+ else:
+ if len(token) == 3:
+ token = token[0:2]
+ reverse_arr.append(token)
+ cur_side = token
+
+# build a list of pins
+
+db_top = odb.dbDatabase.create()
+odb.read_lef(db_top, lef_file_name)
+odb.read_def(db_top, def_file_name)
+
+chip_top = db_top.getChip()
+block_top = chip_top.getBlock()
+top_design_name = block_top.getName()
+tech = db_top.getTech()
+
+H_LAYER = tech.findRoutingLayer(h_layer_index)
+V_LAYER = tech.findRoutingLayer(v_layer_index)
+
+H_WIDTH = h_width_mult * H_LAYER.getWidth()
+V_WIDTH = v_width_mult * V_LAYER.getWidth()
+
+print("Top-level design name:", top_design_name)
+
+bterms = block_top.getBTerms()
+bterms_enum = []
+for bterm in bterms:
+ pin_name = bterm.getName()
+ bterms_enum.append((pin_name, bterm))
+
+# sort them "humanly"
+bterms_enum.sort(key=natural_keys)
+if bus_sort_flag:
+ bterms_enum.sort(key=bus_keys)
+bterms = [bterm[1] for bterm in bterms_enum]
+
+pin_placement = {"#N": [], "#E": [], "#S": [], "#W": []}
+bterm_regex_map = {}
+if(manual_place_flag == False):
+ for side in pin_placement_cfg:
+ for regex in pin_placement_cfg[side]: # going through them in order
+ regex += "$" # anchor
+ for bterm in bterms:
+ # if a pin name matches multiple regexes, their order will be
+ # arbitrary. More refinement requires more strict regexes (or just
+ # the exact pin name).
+ pin_name = bterm.getName()
+ if re.match(regex, pin_name) is not None:
+ if bterm in bterm_regex_map:
+ print("Warning: Multiple regexes matched", pin_name,
+ ". Those are", bterm_regex_map[bterm], "and", regex)
+ print("Only the first one is taken into consideration.")
+ continue
+ # sys.exit(1)
+ bterm_regex_map[bterm] = regex
+ pin_placement[side].append(bterm) # to maintain the order
+
+ unmatched_bterms = [bterm for bterm in bterms if bterm not in bterm_regex_map]
+
+ if len(unmatched_bterms) > 0:
+ print("Warning: Some pins weren't matched by the config file")
+ print("Those are:", [bterm.getName() for bterm in unmatched_bterms])
+ if True:
+ print("Assigning random sides to the above pins")
+ for bterm in unmatched_bterms:
+ random_side = random.choice(list(pin_placement.keys()))
+ pin_placement[random_side].append(bterm)
+ else:
+ sys.exit(1)
+else:
+ for side in pin_placement_cfg:
+ for regex in pin_placement_cfg[side]: # going through them in order
+ regex = regex[0] # take first value
+ regex += "$" # anchor
+ for bterm in bterms:
+ # if a pin name matches multiple regexes, their order will be
+ # arbitrary. More refinement requires more strict regexes (or just
+ # the exact pin name).
+ pin_name = bterm.getName()
+ if re.match(regex, pin_name) is not None:
+ print("Debug: Serching Pin match",regex)
+ if bterm in bterm_regex_map:
+ #print("Warning: Multiple regexes matched", pin_name)
+ # ". Those are", bterm_regex_map[bterm], "and", regex)
+ sys.exit(1)
+ bterm_regex_map[bterm] = regex
+ pin_placement[side].append(bterm) # to maintain the order
+
+ unmatched_bterms = [bterm for bterm in bterms if bterm not in bterm_regex_map]
+
+ if len(unmatched_bterms) > 0:
+ print("Warning: Some pins weren't matched by the config file")
+ print("Those are:", [bterm.getName() for bterm in unmatched_bterms])
+ sys.exit(1)
+
+
+assert len(block_top.getBTerms()) == len(pin_placement["#N"] + pin_placement["#E"] + pin_placement["#S"] + pin_placement["#W"])
+
+# generate slots
+
+
+DIE_AREA = block_top.getDieArea()
+BLOCK_LL_X = DIE_AREA.xMin()
+BLOCK_LL_Y = DIE_AREA.yMin()
+BLOCK_UR_X = DIE_AREA.xMax()
+BLOCK_UR_Y = DIE_AREA.yMax()
+
+print("Block boundaries:", BLOCK_LL_X, BLOCK_LL_Y, BLOCK_UR_X, BLOCK_UR_Y)
+
+
+origin, count, step = block_top.findTrackGrid(H_LAYER).getGridPatternY(0)
+
+# Save the horizontal origin and step - DineshA
+h_origin = origin
+h_step = step
+
+h_tracks = getGrid(origin, count, step)
+
+origin, count, step = block_top.findTrackGrid(V_LAYER).getGridPatternX(0)
+
+# Save the horizontal origin and step - DineshA
+v_origin = origin
+v_step = step
+
+v_tracks = getGrid(origin, count, step)
+
+for rev in reverse_arr:
+ pin_placement[rev].reverse()
+
+if(manual_place_flag == False):
+ # create the pins
+ # old logic
+ for side in pin_placement:
+ start = 0
+ if side in ["#N", "#S"]:
+ slots = equallySpacedSeq(len(pin_placement[side]), v_tracks)
+ else:
+ slots = equallySpacedSeq(len(pin_placement[side]), h_tracks)
+
+ assert len(slots) == len(pin_placement[side])
+
+ for i in range(len(pin_placement[side])):
+ bterm = pin_placement[side][i]
+ slot = slots[i]
+
+ pin_name = bterm.getName()
+ pins = bterm.getBPins()
+ if len(pins) > 0:
+ print("Warning:", pin_name, "already has shapes. Modifying them")
+ assert len(pins) == 1
+ pin_bpin = pins[0]
+ else:
+ pin_bpin = odb.dbBPin_create(bterm)
+
+ print("Dinesh: Placing Pad:" ,pin_name, " At Side: ", side, " Slot: ", slot)
+ pin_bpin.setPlacementStatus("PLACED")
+
+ if side in ["#N", "#S"]:
+ rect = odb.Rect(0, 0, V_WIDTH, LENGTH+V_EXTENSION)
+ if side == "#N":
+ y = BLOCK_UR_Y-LENGTH
+ else:
+ y = BLOCK_LL_Y-V_EXTENSION
+ rect.moveTo(slot-V_WIDTH//2, y)
+ odb.dbBox_create(pin_bpin, V_LAYER, *rect.ll(), *rect.ur())
+ else:
+ rect = odb.Rect(0, 0, LENGTH+H_EXTENSION, H_WIDTH)
+ if side == "#E":
+ x = BLOCK_UR_X-LENGTH
+ else:
+ x = BLOCK_LL_X-H_EXTENSION
+ rect.moveTo(x, slot-H_WIDTH//2)
+ odb.dbBox_create(pin_bpin, H_LAYER, *rect.ll(), *rect.ur())
+else:
+ #New Logic, Manual Pin Placement - Dinesh A
+ #print("Allowed VTracks",v_tracks)
+ #print("Allowed hTracks",h_tracks)
+
+ for side in pin_placement:
+
+ if(len(pin_placement[side]) != len(pin_placement_cfg[side])):
+ print("ERROR : At Side:", side, " Total Pin Defined ",len(pin_placement_cfg[side]), "More than available:",len(pin_placement[side]))
+
+ #check defined pad are more than avaibale one
+ assert len(pin_placement[side]) == len(pin_placement_cfg[side])
+ start = 0
+
+ start_loc = 0
+ pad_pos = 0
+ slot_pre = 0
+ #Dinesh: Give Step Multipler size *2 for better pad placement
+ multiplier= 2
+ for i in range(len(pin_placement_cfg[side])):
+ #Dinesh: Multiply the offset by 1000 for micro conversion
+ if(len(pin_placement_cfg[side][i]) > 1):
+ start_loc = int(pin_placement_cfg[side][i][1])
+ if(len(pin_placement_cfg[side][i]) > 2):
+ pad_pos = int(pin_placement_cfg[side][i][2])
+ if(len(pin_placement_cfg[side][i]) > 3):
+ multiplier = int(pin_placement_cfg[side][i][3])
+
+ if side in ["#N", "#S"]:
+ slott = start_loc*1000+int(v_origin)+(int(v_step) * pad_pos * multiplier)
+ slot =findSlot(slott,v_tracks)
+ else:
+ slott = start_loc*1000+int(h_origin)+(int(h_step) * pad_pos * multiplier)
+ slot =findSlot(slott,h_tracks)
+
+ pad_pos +=1
+ bterm = pin_placement[side][i]
+
+ pin_name = bterm.getName()
+ pins = bterm.getBPins()
+ if len(pins) > 0:
+ print("Warning:", pin_name, "already has shapes. Modifying them")
+ assert len(pins) == 1
+ pin_bpin = pins[0]
+ else:
+ pin_bpin = odb.dbBPin_create(bterm)
+
+ if(slot < slot_pre):
+ print("ERROR:", "Current Pad:", pin_name, " Slot:" , slot, " is less than Previous One:",slot_pre)
+ sys.exit(1)
+
+ slot_pre = slot
+
+ print("Dinesh: Placing Pad:" ,pin_name, " At Side: ", side, " Slot: ", slot)
+ pin_bpin.setPlacementStatus("PLACED")
+
+ if side in ["#N", "#S"]:
+ rect = odb.Rect(0, 0, V_WIDTH, LENGTH+V_EXTENSION)
+ if side == "#N":
+ y = BLOCK_UR_Y-LENGTH
+ else:
+ y = BLOCK_LL_Y-V_EXTENSION
+ rect.moveTo(slot-V_WIDTH//2, y)
+ odb.dbBox_create(pin_bpin, V_LAYER, *rect.ll(), *rect.ur())
+ else:
+ rect = odb.Rect(0, 0, LENGTH+H_EXTENSION, H_WIDTH)
+ if side == "#E":
+ x = BLOCK_UR_X-LENGTH
+ else:
+ x = BLOCK_LL_X-H_EXTENSION
+ rect.moveTo(x, slot-H_WIDTH//2)
+ odb.dbBox_create(pin_bpin, H_LAYER, *rect.ll(), *rect.ur())
+
+
+print("Writing", output_def_file_name)
+odb.write_def(block_top, output_def_file_name)
diff --git a/hacks/src/openlane/synth.tcl b/hacks/src/openlane/synth.tcl
new file mode 100755
index 0000000..2cf1003
--- /dev/null
+++ b/hacks/src/openlane/synth.tcl
@@ -0,0 +1,373 @@
+# Copyright 2020 Efabless Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# inputs expected as env vars
+#set opt $::env(SYNTH_OPT)
+set buffering $::env(SYNTH_BUFFERING)
+set sizing $::env(SYNTH_SIZING)
+
+yosys -import
+
+set vtop $::env(DESIGN_NAME)
+#set sdc_file $::env(SDC_FILE)
+set sclib $::env(LIB_SYNTH)
+
+if { [info exists ::env(SYNTH_DEFINES) ] } {
+ foreach define $::env(SYNTH_DEFINES) {
+ log "Defining $define"
+ verilog_defines -D$define
+ }
+}
+
+set vIdirsArgs ""
+if {[info exist ::env(VERILOG_INCLUDE_DIRS)]} {
+ foreach dir $::env(VERILOG_INCLUDE_DIRS) {
+ lappend vIdirsArgs "-I$dir"
+ }
+ set vIdirsArgs [join $vIdirsArgs]
+}
+
+if { $::env(SYNTH_READ_BLACKBOX_LIB) } {
+ log "Reading $::env(LIB_SYNTH_COMPLETE_NO_PG) as a blackbox"
+ foreach lib $::env(LIB_SYNTH_COMPLETE_NO_PG) {
+ read_liberty -lib -ignore_miss_dir -setattr blackbox $lib
+ }
+}
+
+if { [info exists ::env(EXTRA_LIBS) ] } {
+ foreach lib $::env(EXTRA_LIBS) {
+ read_liberty -lib -ignore_miss_dir -setattr blackbox $lib
+ }
+}
+
+if { [info exists ::env(VERILOG_FILES_BLACKBOX)] } {
+ foreach verilog_file $::env(VERILOG_FILES_BLACKBOX) {
+ read_verilog -sv -lib {*}$vIdirsArgs $verilog_file
+ }
+}
+
+
+# ns expected (in sdc as well)
+set clock_period [expr {$::env(CLOCK_PERIOD)*1000}]
+
+set driver $::env(SYNTH_DRIVING_CELL)
+set cload $::env(SYNTH_CAP_LOAD)
+# input pin cap of IN_3VX8
+set max_FO $::env(SYNTH_MAX_FANOUT)
+if {![info exist ::env(SYNTH_MAX_TRAN)]} {
+ set ::env(SYNTH_MAX_TRAN) [expr {0.1*$clock_period}]
+} else {
+ set ::env(SYNTH_MAX_TRAN) [expr {$::env(SYNTH_MAX_TRAN) * 1000}]
+}
+set max_Tran $::env(SYNTH_MAX_TRAN)
+
+
+# Mapping parameters
+set A_factor 0.00
+set B_factor 0.88
+set F_factor 0.00
+
+# Don't change these unless you know what you are doing
+set stat_ext ".stat.rpt"
+set chk_ext ".chk.rpt"
+set gl_ext ".gl.v"
+set constr_ext ".$clock_period.constr"
+set timing_ext ".timing.txt"
+set abc_ext ".abc"
+
+
+# get old sdc, add library specific stuff for abc scripts
+set sdc_file $::env(yosys_tmp_file_tag).sdc
+set outfile [open ${sdc_file} w]
+#puts $outfile $sdc_data
+puts $outfile "set_driving_cell ${driver}"
+puts $outfile "set_load ${cload}"
+close $outfile
+
+
+# ABC Scrips
+set abc_rs_K "resub,-K,"
+set abc_rs "resub"
+set abc_rsz "resub,-z"
+set abc_rw_K "rewrite,-K,"
+set abc_rw "rewrite"
+set abc_rwz "rewrite,-z"
+set abc_rf "refactor"
+set abc_rfz "refactor,-z"
+set abc_b "balance"
+
+set abc_resyn2 "${abc_b}; ${abc_rw}; ${abc_rf}; ${abc_b}; ${abc_rw}; ${abc_rwz}; ${abc_b}; ${abc_rfz}; ${abc_rwz}; ${abc_b}"
+set abc_share "strash; multi,-m; ${abc_resyn2}"
+set abc_resyn2a "${abc_b};${abc_rw};${abc_b};${abc_rw};${abc_rwz};${abc_b};${abc_rwz};${abc_b}"
+set abc_resyn3 "balance;resub;resub,-K,6;balance;resub,-z;resub,-z,-K,6;balance;resub,-z,-K,5;balance"
+set abc_resyn2rs "${abc_b};${abc_rs_K},6;${abc_rw};${abc_rs_K},6,-N,2;${abc_rf};${abc_rs_K},8;${abc_rw};${abc_rs_K},10;${abc_rwz};${abc_rs_K},10,-N,2;${abc_b},${abc_rs_K},12;${abc_rfz};${abc_rs_K},12,-N,2;${abc_rwz};${abc_b}"
+
+set abc_choice "fraig_store; ${abc_resyn2}; fraig_store; ${abc_resyn2}; fraig_store; fraig_restore"
+set abc_choice2 "fraig_store; balance; fraig_store; ${abc_resyn2}; fraig_store; ${abc_resyn2}; fraig_store; ${abc_resyn2}; fraig_store; fraig_restore"
+
+set abc_map_old_cnt "map,-p,-a,-B,0.2,-A,0.9,-M,0"
+set abc_map_old_dly "map,-p,-B,0.2,-A,0.9,-M,0"
+set abc_retime_area "retime,-D,{D},-M,5"
+set abc_retime_dly "retime,-D,{D},-M,6"
+set abc_map_new_area "amap,-m,-Q,0.1,-F,20,-A,20,-C,5000"
+
+set abc_area_recovery_1 "${abc_choice}; map;"
+set abc_area_recovery_2 "${abc_choice2}; map;"
+
+set map_old_cnt "map,-p,-a,-B,0.2,-A,0.9,-M,0"
+set map_old_dly "map,-p,-B,0.2,-A,0.9,-M,0"
+set abc_retime_area "retime,-D,{D},-M,5"
+set abc_retime_dly "retime,-D,{D},-M,6"
+set abc_map_new_area "amap,-m,-Q,0.1,-F,20,-A,20,-C,5000"
+
+if {$buffering==1} {
+ set abc_fine_tune "buffer,-N,${max_FO},-S,${max_Tran};upsize,{D};dnsize,{D}"
+} elseif {$sizing} {
+ set abc_fine_tune "upsize,{D};dnsize,{D}"
+} else {
+ set abc_fine_tune ""
+}
+
+
+set delay_scripts [list \
+ "+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_dly}; scleanup;${abc_map_old_dly};retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
+ \
+ "+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_dly}; scleanup;${abc_choice2};${abc_map_old_dly};${abc_area_recovery_2}; retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
+ \
+ "+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_dly}; scleanup;${abc_choice};${abc_map_old_dly};${abc_area_recovery_1}; retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
+ \
+ "+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};${abc_choice2};${abc_map_old_dly};retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
+ ]
+
+set area_scripts [list \
+ "+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
+ \
+ "+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_resyn2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};${abc_choice2};${abc_map_new_area};retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
+ \
+ "+read_constr,${sdc_file};fx;mfs;strash;refactor;${abc_choice2};${abc_retime_area};scleanup;${abc_choice2};${abc_map_new_area};${abc_choice2};${abc_map_new_area};retime,-D,{D};${abc_fine_tune};stime,-p;print_stats -m" \
+ ]
+
+set all_scripts [list {*}$delay_scripts {*}$area_scripts]
+
+set strategy_parts [split $::env(SYNTH_STRATEGY)]
+
+proc synth_strategy_format_err { } {
+ upvar area_scripts area_scripts
+ upvar delay_scripts delay_scripts
+ log -stderr "\[ERROR] Misformatted SYNTH_STRATEGY (\"$::env(SYNTH_STRATEGY)\")."
+ log -stderr "\[ERROR] Correct format is \"DELAY|AREA 0-[expr [llength $delay_scripts]-1]|0-[expr [llength $area_scripts]-1]\"."
+ exit 1
+}
+
+if { [llength $strategy_parts] != 2 } {
+ synth_strategy_format_err
+}
+
+set strategy_type [lindex $strategy_parts 0]
+set strategy_type_idx [lindex $strategy_parts 1]
+
+if { $strategy_type != "AREA" && $strategy_type != "DELAY" } {
+ log -stderr "\[ERROR] AREA|DELAY tokens not found. ($strategy_type)"
+ synth_strategy_format_err
+}
+
+if { $strategy_type == "DELAY" && $strategy_type_idx >= [llength $delay_scripts] } {
+ log -stderr "\[ERROR] strategy index ($strategy_type_idx) is too high."
+ synth_strategy_format_err
+}
+
+if { $strategy_type == "AREA" && $strategy_type_idx >= [llength $area_scripts] } {
+ log -stderr "\[ERROR] strategy index ($strategy_type_idx) is too high."
+ synth_strategy_format_err
+}
+
+if { $strategy_type == "DELAY" } {
+ set strategy $strategy_type_idx
+} else {
+ set strategy [expr {[llength $delay_scripts]+$strategy_type_idx}]
+}
+
+set adder_type $::env(SYNTH_ADDER_TYPE)
+if { !($adder_type in [list "YOSYS" "FA" "RCA" "CSA"]) } {
+ log -stderr "\[ERROR] Misformatted SYNTH_ADDER_TYPE (\"$::env(SYNTH_ADDER_TYPE)\")."
+ log -stderr "\[ERROR] Correct format is \"YOSYS|FA|RCA|CSA\"."
+ exit 1
+}
+
+for { set i 0 } { $i < [llength $::env(VERILOG_FILES)] } { incr i } {
+ read_verilog -sv {*}$vIdirsArgs [lindex $::env(VERILOG_FILES) $i]
+}
+## Module level parameter overide - Dinesh
+if { [info exists ::env(SYNTH_PARAMS) ] } {
+ log "Reading $::env(SYNTH_PARAMS) as a parameter"
+ set records [split $::env(SYNTH_PARAMS) ","]
+ foreach rec $records {
+ chparam -set [lindex $rec 0] [lindex $rec 1] $vtop
+ }
+} else {
+ log "No parameter define found"
+}
+
+select -module $vtop
+
+show -format dot -prefix $::env(TMP_DIR)/synthesis/hierarchy
+select -clear
+
+hierarchy -check -top $vtop
+
+# Infer tri-state buffers.
+set tbuf_map false
+if { [info exists ::env(TRISTATE_BUFFER_MAP)] } {
+ if { [file exists $::env(TRISTATE_BUFFER_MAP)] } {
+ set tbuf_map true
+ tribuf
+ } else {
+ log "WARNING: TRISTATE_BUFFER_MAP is defined but could not be found: $::env(TRISTATE_BUFFER_MAP)"
+ }
+}
+
+# handle technology mapping of rca and csa adders
+if { $adder_type == "RCA"} {
+ if { [info exists ::env(RIPPLE_CARRY_ADDER_MAP)] && [file exists $::env(RIPPLE_CARRY_ADDER_MAP)] } {
+ techmap -map $::env(RIPPLE_CARRY_ADDER_MAP)
+ }
+} elseif { $adder_type == "CSA"} {
+ if { [info exists ::env(CARRY_SELECT_ADDER_MAP)] && [file exists $::env(CARRY_SELECT_ADDER_MAP)] } {
+ techmap -map $::env(CARRY_SELECT_ADDER_MAP)
+ }
+}
+
+if { $::env(SYNTH_NO_FLAT) } {
+ synth -top $vtop
+} else {
+ synth -top $vtop -flatten
+}
+
+# write a post techmap dot file
+show -format dot -prefix $::env(TMP_DIR)/synthesis/post_techmap
+
+if { $::env(SYNTH_SHARE_RESOURCES) } {
+ share -aggressive
+}
+
+set fa_map false
+if { $adder_type == "FA" } {
+ if { [info exists ::env(FULL_ADDER_MAP)] && [file exists $::env(FULL_ADDER_MAP)] } {
+ extract_fa -fa -v
+ extract_fa -ha -v
+ set fa_map true
+ }
+}
+
+opt
+opt_clean -purge
+
+tee -o "$::env(yosys_report_file_tag)_pre.stat" stat
+
+# Map tri-state buffers.
+if { $tbuf_map } {
+ log {mapping tbuf}
+ techmap -map $::env(TRISTATE_BUFFER_MAP)
+ simplemap
+}
+
+# Map Full Adders.
+if { $fa_map } {
+ techmap -map $::env(FULL_ADDER_MAP)
+}
+
+# handle technology mapping of 4-MUX, and tell Yosys to infer 4-muxes
+if { [info exists ::env(SYNTH_MUX4_MAP)] && [file exists $::env(SYNTH_MUX4_MAP)] } {
+ muxcover -mux4
+ techmap -map $::env(SYNTH_MUX4_MAP)
+ simplemap
+}
+
+# handle technology mapping of 2-MUX
+if { [info exists ::env(SYNTH_MUX_MAP)] && [file exists $::env(SYNTH_MUX_MAP)] } {
+ techmap -map $::env(SYNTH_MUX_MAP)
+ simplemap
+}
+
+# handle technology mapping of latches
+if { [info exists ::env(SYNTH_LATCH_MAP)] && [file exists $::env(SYNTH_LATCH_MAP)] } {
+ techmap -map $::env(SYNTH_LATCH_MAP)
+ simplemap
+}
+
+dfflibmap -liberty $sclib
+tee -o "$::env(yosys_report_file_tag)_dff.stat" stat
+
+if { [info exists ::env(SYNTH_EXPLORE)] && $::env(SYNTH_EXPLORE) } {
+ design -save myDesign
+
+ for { set index 0 } { $index < [llength $all_scripts] } { incr index } {
+ log "\[INFO\]: ABC: WireLoad : S_$index"
+ design -load myDesign
+
+ abc -D $clock_period \
+ -constr "$sdc_file" \
+ -liberty $sclib \
+ -script [lindex $all_scripts $index]
+
+ setundef -zero
+
+ hilomap -hicell {*}$::env(SYNTH_TIEHI_PORT) -locell {*}$::env(SYNTH_TIELO_PORT)
+
+ splitnets
+ opt_clean -purge
+ insbuf -buf {*}$::env(SYNTH_MIN_BUF_PORT)
+
+ tee -o "$::env(yosys_report_file_tag)_$index$chk_ext" check
+ tee -o "$::env(yosys_report_file_tag)$index$stat_ext" stat -top $vtop -liberty [lindex $::env(LIB_SYNTH_COMPLETE_NO_PG) 0]
+ write_verilog -noattr -noexpr -nohex -nodec -defparam "$::env(yosys_result_file_tag)_$index.v"
+ design -reset
+ }
+} else {
+
+ log "\[INFO\]: ABC: WireLoad : S_$strategy"
+
+ abc -D $clock_period \
+ -constr "$sdc_file" \
+ -liberty $sclib \
+ -script [lindex $all_scripts $strategy] \
+ -showtmp;
+
+ setundef -zero
+
+ hilomap -hicell {*}$::env(SYNTH_TIEHI_PORT) -locell {*}$::env(SYNTH_TIELO_PORT)
+
+ # get rid of the assignments that make init_floorplan fail
+ splitnets
+ opt_clean -purge
+ insbuf -buf {*}$::env(SYNTH_MIN_BUF_PORT)
+
+ tee -o "$::env(yosys_report_file_tag)_$strategy$chk_ext" check
+ tee -o "$::env(yosys_report_file_tag)_$strategy$stat_ext" stat -top $vtop -liberty [lindex $::env(LIB_SYNTH_COMPLETE_NO_PG) 0]
+ write_verilog -noattr -noexpr -nohex -nodec -defparam "$::env(SAVE_NETLIST)"
+}
+
+if { $::env(SYNTH_NO_FLAT) } {
+ design -reset
+ read_liberty -lib -ignore_miss_dir -setattr blackbox $::env(LIB_SYNTH_COMPLETE_NO_PG)
+ file copy -force $::env(SAVE_NETLIST) $::env(yosys_tmp_file_tag)_unflat.v
+ read_verilog -sv $::env(SAVE_NETLIST)
+ synth -top $vtop -flatten
+ splitnets
+ opt_clean -purge
+ insbuf -buf {*}$::env(SYNTH_MIN_BUF_PORT)
+ write_verilog -noattr -noexpr -nohex -nodec -defparam "$::env(SAVE_NETLIST)"
+ tee -o "$::env(yosys_report_file_tag)_$strategy$chk_ext" check
+ tee -o "$::env(yosys_report_file_tag)_$strategy$stat_ext" stat -top $vtop -liberty [lindex $::env(LIB_SYNTH_COMPLETE_NO_PG) 0]
+}
diff --git a/hacks/src/openlane/synth_top.tcl b/hacks/src/openlane/synth_top.tcl
new file mode 100755
index 0000000..65bb8fa
--- /dev/null
+++ b/hacks/src/openlane/synth_top.tcl
@@ -0,0 +1,97 @@
+# Copyright 2020 Efabless Corporation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set vtop $::env(DESIGN_NAME)
+#set sdc_file $::env(SDC_FILE)
+set sclib $::env(LIB_SYNTH)
+yosys -import
+
+set stat_ext ".stat.rpt"
+set chk_ext ".chk.rpt"
+set gl_ext ".gl.v"
+set timing_ext ".timing.txt"
+set abc_ext ".abc"
+
+if { $::env(SYNTH_READ_BLACKBOX_LIB) } {
+ foreach lib $::env(LIB_SYNTH_COMPLETE_NO_PG) {
+ read_liberty -lib -ignore_miss_dir -setattr blackbox $lib
+ }
+}
+
+if { [info exists ::env(EXTRA_LIBS) ] } {
+ foreach lib $::env(EXTRA_LIBS) {
+ read_liberty -lib -ignore_miss_dir -setattr blackbox $lib
+ }
+}
+
+
+if { [info exists ::env(SYNTH_DEFINES) ] } {
+ foreach define $::env(SYNTH_DEFINES) {
+ verilog_defines -D$define
+ }
+}
+
+set vIdirsArgs ""
+if {[info exist ::env(VERILOG_INCLUDE_DIRS)]} {
+ foreach dir $::env(VERILOG_INCLUDE_DIRS) {
+ lappend vIdirsArgs "-I$dir"
+ }
+ set vIdirsArgs [join $vIdirsArgs]
+}
+
+if { [info exists ::env(VERILOG_FILES_BLACKBOX)] } {
+ foreach verilog_file $::env(VERILOG_FILES_BLACKBOX) {
+ read_verilog -lib {*}$vIdirsArgs $verilog_file
+ }
+}
+
+
+for { set i 0 } { $i < [llength $::env(VERILOG_FILES)] } { incr i } {
+ read_verilog {*}$vIdirsArgs [lindex $::env(VERILOG_FILES) $i]
+}
+
+select -module $vtop
+
+## Module level parameter overide - Dinesh
+if { [info exists ::env(SYNTH_PARAMS) ] } {
+ log "Reading $::env(SYNTH_PARAMS) as a parameter"
+ set records [split $::env(SYNTH_PARAMS) ","]
+ foreach rec $records {
+ chparam -set [lindex $rec 0] [lindex $rec 1] $vtop
+ }
+} else {
+ log "No parameter define found"
+}
+
+
+show -format dot -prefix $::env(TMP_DIR)/synthesis/hierarchy
+select -clear
+
+hierarchy -check -top $vtop
+if { $::env(SYNTH_FLAT_TOP) } {
+ flatten
+}
+
+setattr -set keep 1
+#synth -top $vtop
+tee -o "$::env(yosys_report_file_tag)_synth.stat" stat
+
+
+#debug opt_clean -purge
+#setundef -zero
+splitnets
+opt_clean -purge
+tee -o "$::env(yosys_report_file_tag)_$chk_ext" check
+tee -o "$::env(yosys_report_file_tag)$stat_ext" stat -top $vtop -liberty $sclib
+write_verilog -noattr -noexpr -nohex -nodec -defparam "$::env(SAVE_NETLIST)"
diff --git a/openlane/user_project_wrapper/pdn.tcl b/openlane/user_project_wrapper/pdn_cfg.tcl
similarity index 100%
rename from openlane/user_project_wrapper/pdn.tcl
rename to openlane/user_project_wrapper/pdn_cfg.tcl