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 &reg_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