blob: 300c6c476bd6c8aa71efc5de9e78c3d4c9dffe68 [file] [log] [blame]
/////////////////////////////////////////////////////////////////////////////
//
// 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 "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 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 = 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_ ? core_.closestPtInside(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)
{
// Stay inside the lines.
if (core_exists_)
pt = core_.closestPtInside(pt);
dbInst *dinst = db_network_->staToDb(inst);
dinst->setPlacementStatus(dbPlacementStatus::PLACED);
dinst->setLocation(pt.getX(), pt.getY());
}
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 = 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.
static double length_margin = .05;
bool split_wire = false;
int split_length = std::numeric_limits<int>::max();
if (max_length > 0 && wire_length > max_length) {
split_length = min(split_length, max_length);
split_wire = true;
}
if (wire_cap > 0.0
&& pin_cap < max_cap
&& load_cap > max_cap) {
split_length = min(split_length, metersToDbu((max_cap - pin_cap) / wire_cap));
split_wire = true;
}
if (load_slew > max_load_slew
// Check that zero length wire meets max slew.
&& r_drvr*pin_cap*k_threshold < 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);
if (l > 0.0) {
split_length = min(split_length, metersToDbu(l));
split_wire = true;
}
}
if (split_wire) {
// Distance from pt to repeater backward toward prev_pt.
double buf_dist = length - (wire_length - split_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));
}
else
break;
}
}
}
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 = 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 = makeInstance(tie_cell, tie_name.c_str(),
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,
int max_passes)
{
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)
&& pass <= max_passes) {
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_);
const Pin *drvr_pin = drvr_vertex->pin();
LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
LibertyCell *drvr_cell = drvr_port ? drvr_port->libertyCell() : nullptr;
int fanout = this->fanout(drvr_vertex);
debugPrint(logger_, RSZ, "repair_setup", 2, "{} {} fanout = {}",
network_->pathName(drvr_pin),
drvr_cell ? drvr_cell->name() : "none",
fanout);
if (upsizeDrvr(drvr_path, drvr_index, &expanded)) {
changed = true;
break;
}
// 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 rebuffer_count = rebuffer(drvr_pin);
if (rebuffer_count > 0) {
debugPrint(logger_, RSZ, "repair_setup", 2, "rebuffer {} inserted {}",
network_->pathName(drvr_pin),
rebuffer_count);
changed = true;
break;
}
}
// Don't split loads on low fanout nets.
if (fanout > split_load_min_fanout_
&& !tristate_drvr) {
splitLoads(drvr_path, drvr_index, path_slack, &expanded);
changed = true;
break;
}
}
}
return changed;
}
bool
Resizer::upsizeDrvr(PathRef *drvr_path,
int drvr_index,
PathExpanded *expanded)
{
Pin *drvr_pin = drvr_path->pin(this);
const DcalcAnalysisPt *dcalc_ap = drvr_path->dcalcAnalysisPt(sta_);
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;
LibertyPort *drvr_port = network_->libertyPort(drvr_pin);
LibertyCell *upsize = upsizeCell(in_port, drvr_port, load_cap,
prev_drive, dcalc_ap);
if (upsize) {
Instance *drvr = network_->instance(drvr_pin);
debugPrint(logger_, RSZ, "repair_setup", 2, "resize {} {} -> {}",
network_->pathName(drvr_pin),
drvr_port->libertyCell()->name(),
upsize->name());
if (replaceCell(drvr, upsize, true)) {
resize_count_++;
return true;
}
}
return 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::splitLoads(PathRef *drvr_path,
int drvr_index,
Slack drvr_slack,
PathExpanded *expanded)
{
Pin *drvr_pin = drvr_path->pin(this);
PathRef *load_path = expanded->path(drvr_index + 1);
Vertex *load_vertex = load_path->vertex(sta_);
Pin *load_pin = load_vertex->pin();
// Divide and conquer.
debugPrint(logger_, RSZ, "repair_setup", 2, "split loads {} -> {}",
network_->pathName(drvr_pin),
network_->pathName(load_pin));
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;
});
Net *net = network_->net(drvr_pin);
string buffer_name = makeUniqueInstName("split");
Instance *parent = db_network_->topInstance();
LibertyCell *buffer_cell = buffer_lowest_drive_;
Instance *buffer = 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);
}
////////////////////////////////////////////////////////////////
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);