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