OpenSTA/search/Sta.cc

5319 lines
118 KiB
C++

// OpenSTA, Static Timing Analyzer
// Copyright (c) 2020, 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 "Sta.hh"
#include "DispatchQueue.hh"
#include "ReportTcl.hh"
#include "Debug.hh"
#include "Stats.hh"
#include "Fuzzy.hh"
#include "Units.hh"
#include "TimingArc.hh"
#include "FuncExpr.hh"
#include "EquivCells.hh"
#include "Liberty.hh"
#include "liberty/LibertyReader.hh"
#include "SdcNetwork.hh"
#include "MakeConcreteNetwork.hh"
#include "PortDirection.hh"
#include "VerilogReader.hh"
#include "Graph.hh"
#include "GraphCmp.hh"
#include "Sdc.hh"
#include "WriteSdc.hh"
#include "ExceptionPath.hh"
#include "MakeConcreteParasitics.hh"
#include "Parasitics.hh"
#include "parasitics/SpefReader.hh"
#include "DelayCalc.hh"
#include "ArcDelayCalc.hh"
#include "dcalc/GraphDelayCalc1.hh"
#include "sdf/SdfWriter.hh"
#include "Levelize.hh"
#include "Sim.hh"
#include "ClkInfo.hh"
#include "TagGroup.hh"
#include "PathAnalysisPt.hh"
#include "Corner.hh"
#include "Search.hh"
#include "Latches.hh"
#include "PathGroup.hh"
#include "CheckTiming.hh"
#include "CheckSlewLimits.hh"
#include "CheckFanoutLimits.hh"
#include "CheckCapacitanceLimits.hh"
#include "CheckMinPulseWidths.hh"
#include "CheckMinPeriods.hh"
#include "CheckMaxSkews.hh"
#include "ClkSkew.hh"
#include "FindRegister.hh"
#include "ReportPath.hh"
#include "VisitPathGroupVertices.hh"
#include "Genclks.hh"
#include "ClkNetwork.hh"
#include "Power.hh"
namespace sta {
using std::min;
static const ClockEdge *clk_edge_wildcard = reinterpret_cast<ClockEdge*>(1);
static bool
libertyPortCapsEqual(LibertyPort *port1,
LibertyPort *port2);
static bool
hasDisabledArcs(Edge *edge,
Graph *graph);
static InstanceSet *
pinInstances(PinSet *pins,
const Network *network);
////////////////////////////////////////////////////////////////
//
// Observers are used to propagate updates from a component
// to other components.
//
////////////////////////////////////////////////////////////////
// When an incremental change is made the delay calculation
// changes downstream. This invalidates the required times
// for all vertices upstream of the changes.
class StaDelayCalcObserver : public DelayCalcObserver
{
public:
explicit StaDelayCalcObserver(Search *search);
virtual void delayChangedFrom(Vertex *vertex);
virtual void delayChangedTo(Vertex *vertex);
virtual void checkDelayChangedTo(Vertex *vertex);
private:
DISALLOW_COPY_AND_ASSIGN(StaDelayCalcObserver);
Search *search_;
};
StaDelayCalcObserver::StaDelayCalcObserver(Search *search) :
DelayCalcObserver(),
search_(search)
{
}
void
StaDelayCalcObserver::delayChangedFrom(Vertex *vertex)
{
search_->requiredInvalid(vertex);
}
void
StaDelayCalcObserver::delayChangedTo(Vertex *vertex)
{
search_->arrivalInvalid(vertex);
}
void
StaDelayCalcObserver::checkDelayChangedTo(Vertex *vertex)
{
search_->requiredInvalid(vertex);
}
////////////////////////////////////////////////////////////////
class StaSimObserver : public SimObserver
{
public:
StaSimObserver(GraphDelayCalc *graph_delay_calc,
Levelize *levelize,
Search *search);
virtual void valueChangeAfter(Vertex *vertex);
virtual void faninEdgesChangeAfter(Vertex *vertex);
virtual void fanoutEdgesChangeAfter(Vertex *vertex);
private:
DISALLOW_COPY_AND_ASSIGN(StaSimObserver);
GraphDelayCalc *graph_delay_calc_;
Levelize *levelize_;
Search *search_;
};
StaSimObserver::StaSimObserver(GraphDelayCalc *graph_delay_calc,
Levelize *levelize,
Search *search) :
SimObserver(),
graph_delay_calc_(graph_delay_calc),
levelize_(levelize),
search_(search)
{
}
// When pins with constant values are incrementally connected to a net
// the downstream delays and arrivals will not be updated (removed)
// because the search predicate does not search through constants.
// This observer makes sure the delays and arrivals are invalidated.
void
StaSimObserver::valueChangeAfter(Vertex *vertex)
{
graph_delay_calc_->delayInvalid(vertex);
search_->arrivalInvalid(vertex);
search_->requiredInvalid(vertex);
search_->endpointInvalid(vertex);
levelize_->invalidFrom(vertex);
}
void
StaSimObserver::faninEdgesChangeAfter(Vertex *vertex)
{
graph_delay_calc_->delayInvalid(vertex);
search_->arrivalInvalid(vertex);
search_->endpointInvalid(vertex);
}
void
StaSimObserver::fanoutEdgesChangeAfter(Vertex *vertex)
{
search_->requiredInvalid(vertex);
search_->endpointInvalid(vertex);
}
////////////////////////////////////////////////////////////////
class StaLevelizeObserver : public LevelizeObserver
{
public:
StaLevelizeObserver(Search *search);
virtual void levelChangedBefore(Vertex *vertex);
private:
DISALLOW_COPY_AND_ASSIGN(StaLevelizeObserver);
Search *search_;
};
StaLevelizeObserver::StaLevelizeObserver(Search *search) :
search_(search)
{
}
void
StaLevelizeObserver::levelChangedBefore(Vertex *vertex)
{
search_->levelChangedBefore(vertex);
}
////////////////////////////////////////////////////////////////
void
initSta()
{
initElapsedTime();
TimingRole::init();
PortDirection::init();
initTmpStrings();
initLiberty();
initDelayConstants();
registerDelayCalcs();
initPathSenseThru();
}
void
deleteAllMemory()
{
// Verilog modules refer to the network in the sta so it has
// to deleted before the sta.
deleteVerilogReader();
Sta *sta = Sta::sta();
if (sta) {
delete sta;
Sta::setSta(nullptr);
}
deleteDelayCalcs();
deleteTmpStrings();
TimingRole::destroy();
PortDirection::destroy();
deleteLiberty();
}
////////////////////////////////////////////////////////////////
// Singleton used by TCL commands.
Sta *Sta::sta_;
Sta::Sta() :
StaState(),
current_instance_(nullptr),
check_timing_(nullptr),
check_slew_limits_(nullptr),
check_fanout_limits_(nullptr),
check_capacitance_limits_(nullptr),
check_min_pulse_widths_(nullptr),
check_min_periods_(nullptr),
check_max_skews_(nullptr),
clk_skews_(nullptr),
report_path_(nullptr),
power_(nullptr),
link_make_black_boxes_(true),
update_genclks_(false),
equiv_cells_(nullptr),
graph_sdc_annotated_(false)
{
}
void
Sta::makeComponents()
{
makeReport();
makeDebug();
makeUnits();
makeNetwork();
makeSdc();
makeLevelize();
makeParasitics();
makeCorners();
makeArcDelayCalc();
makeGraphDelayCalc();
makeSim();
makeSearch();
makeLatches();
makeClkNetwork();
makeSdcNetwork();
makeReportPath();
makePower();
setCmdNamespace(CmdNamespace::sdc);
updateComponentsState();
makeObservers();
// This must follow updateComponentsState.
corners_->makeParasiticAnalysisPtsSingle();
setThreadCount(defaultThreadCount());
}
void
Sta::makeObservers()
{
graph_delay_calc_->setObserver(new StaDelayCalcObserver(search_));
sim_->setObserver(new StaSimObserver(graph_delay_calc_, levelize_, search_));
levelize_->setObserver(new StaLevelizeObserver(search_));
}
int
Sta::defaultThreadCount() const
{
return 1;
}
void
Sta::setThreadCount(int thread_count)
{
thread_count_ = thread_count;
if (dispatch_queue_)
dispatch_queue_->setThreadCount(thread_count);
else if (thread_count > 1)
dispatch_queue_ = new DispatchQueue(thread_count);
updateComponentsState();
}
void
Sta::updateComponentsState()
{
// These components do not use StaState:
// units_
network_->copyState(this);
cmd_network_->copyState(this);
sdc_network_->copyState(this);
if (graph_)
graph_->copyState(this);
sdc_->copyState(this);
corners_->copyState(this);
levelize_->copyState(this);
parasitics_->copyState(this);
if (arc_delay_calc_)
arc_delay_calc_->copyState(this);
sim_->copyState(this);
search_->copyState(this);
latches_->copyState(this);
graph_delay_calc_->copyState(this);
report_path_->copyState(this);
if (check_timing_)
check_timing_->copyState(this);
clk_network_->copyState(this);
if (power_)
power_->copyState(this);
}
void
Sta::makeReport()
{
report_ = new ReportTcl();
}
void
Sta::makeDebug()
{
debug_ = new Debug(report_);
}
void
Sta::makeUnits()
{
units_ = new Units();
}
void
Sta::makeNetwork()
{
network_ = makeConcreteNetwork();
}
void
Sta::makeSdc()
{
sdc_ = new Sdc(this);
}
void
Sta::makeLevelize()
{
levelize_ = new Levelize(this);
}
void
Sta::makeParasitics()
{
parasitics_ = makeConcreteParasitics(this);
}
void
Sta::makeArcDelayCalc()
{
arc_delay_calc_ = makeDelayCalc("dmp_ceff_elmore", this);
}
void
Sta::makeGraphDelayCalc()
{
graph_delay_calc_ = new GraphDelayCalc1(this);
}
void
Sta::makeSim()
{
sim_ = new Sim(this);
}
void
Sta::makeSearch()
{
search_ = new Search(this);
}
void
Sta::makeLatches()
{
latches_ = new Latches(this);
}
void
Sta::makeSdcNetwork()
{
sdc_network_ = sta::makeSdcNetwork(network_);
}
void
Sta::makeCheckTiming()
{
check_timing_ = new CheckTiming(this);
}
void
Sta::makeCheckSlewLimits()
{
check_slew_limits_ = new CheckSlewLimits(this);
}
void
Sta::makeCheckFanoutLimits()
{
check_fanout_limits_ = new CheckFanoutLimits(this);
}
void
Sta::makeCheckCapacitanceLimits()
{
check_capacitance_limits_ = new CheckCapacitanceLimits(this);
}
void
Sta::makeCheckMinPulseWidths()
{
check_min_pulse_widths_ = new CheckMinPulseWidths(this);
}
void
Sta::makeCheckMinPeriods()
{
check_min_periods_ = new CheckMinPeriods(this);
}
void
Sta::makeCheckMaxSkews()
{
check_max_skews_ = new CheckMaxSkews(this);
}
void
Sta::makeReportPath()
{
report_path_ = new ReportPath(this);
}
void
Sta::makeClkNetwork()
{
clk_network_ = new ClkNetwork(this);
}
void
Sta::makePower()
{
power_ = new Power(this);
}
void
Sta::setSta(Sta *sta)
{
sta_ = sta;
}
Sta *
Sta::sta()
{
return sta_;
}
Sta::~Sta()
{
// Delete "top down" to minimize chance of referencing deleted memory.
delete check_slew_limits_;
delete check_fanout_limits_;
delete check_capacitance_limits_;
delete check_min_pulse_widths_;
delete check_min_periods_;
delete check_max_skews_;
delete clk_skews_;
delete check_timing_;
delete report_path_;
// Constraints reference search filter, so delete search first.
delete search_;
delete latches_;
delete parasitics_;
if (arc_delay_calc_)
delete arc_delay_calc_;
delete graph_delay_calc_;
delete sim_;
delete levelize_;
delete sdc_;
delete corners_;
delete graph_;
delete sdc_network_;
delete network_;
delete debug_;
delete units_;
delete report_;
delete clk_network_;
delete power_;
delete equiv_cells_;
delete dispatch_queue_;
}
void
Sta::clear()
{
clkPinsInvalid();
// Constraints reference search filter, so clear search first.
search_->clear();
sdc_->clear();
graph_sdc_annotated_ = false;
// corners are NOT cleared because they are used to index liberty files.
levelize_->clear();
if (parasitics_)
parasitics_->clear();
graph_delay_calc_->clear();
sim_->clear();
if (check_min_pulse_widths_)
check_min_pulse_widths_->clear();
if (check_min_periods_)
check_min_periods_->clear();
delete graph_;
graph_ = nullptr;
current_instance_ = nullptr;
// Notify components that graph is toast.
updateComponentsState();
}
void
Sta::networkChanged()
{
// Everything else from clear().
search_->clear();
levelize_->clear();
if (parasitics_)
parasitics_->clear();
graph_delay_calc_->clear();
sim_->clear();
if (check_min_pulse_widths_)
check_min_pulse_widths_->clear();
if (check_min_periods_)
check_min_periods_->clear();
delete graph_;
graph_ = nullptr;
graph_sdc_annotated_ = false;
current_instance_ = nullptr;
updateComponentsState();
}
void
Sta::setTclInterp(Tcl_Interp *interp)
{
tcl_interp_ = interp;
report_->setTclInterp(interp);
}
Tcl_Interp *
Sta::tclInterp()
{
return tcl_interp_;
}
CmdNamespace
Sta::cmdNamespace()
{
return cmd_namespace_;
}
void
Sta::setCmdNamespace(CmdNamespace namespc)
{
cmd_namespace_ = namespc;
switch (cmd_namespace_) {
case CmdNamespace::sta:
cmd_network_ = network_;
break;
case CmdNamespace::sdc:
cmd_network_ = sdc_network_;
break;
}
updateComponentsState();
}
Instance *
Sta::currentInstance() const
{
if (current_instance_ == nullptr)
return network_->topInstance();
else
return current_instance_;
}
void
Sta::setCurrentInstance(Instance *inst)
{
current_instance_ = inst;
}
////////////////////////////////////////////////////////////////
LibertyLibrary *
Sta::readLiberty(const char *filename,
Corner *corner,
const MinMaxAll *min_max,
bool infer_latches)
{
Stats stats(debug_, report_);
LibertyLibrary *library = readLibertyFile(filename, corner, min_max,
infer_latches, network_);
if (library
// The default library is the first library read.
// This corresponds to a link_path of '*'.
&& network_->defaultLibertyLibrary() == nullptr) {
network_->setDefaultLibertyLibrary(library);
// Set units from default (first) library.
*units_ = *library->units();
}
stats.report("Read liberty");
return library;
}
LibertyLibrary *
Sta::readLibertyFile(const char *filename,
Corner *corner,
const MinMaxAll *min_max,
bool infer_latches,
Network *network)
{
LibertyLibrary *liberty = sta::readLibertyFile(filename, infer_latches,
network);
if (liberty) {
// Don't map liberty cells if they are redefined by reading another
// library with the same cell names.
if (min_max == MinMaxAll::all()) {
readLibertyAfter(liberty, corner, MinMax::min());
readLibertyAfter(liberty, corner, MinMax::max());
}
else
readLibertyAfter(liberty, corner, min_max->asMinMax());
network_->readLibertyAfter(liberty);
}
return liberty;
}
LibertyLibrary *
Sta::readLibertyFile(const char *filename,
bool infer_latches,
Network *network)
{
return sta::readLibertyFile(filename, infer_latches, network);
}
void
Sta::readLibertyAfter(LibertyLibrary *liberty,
Corner *corner,
const MinMax *min_max)
{
corner->addLiberty(liberty, min_max);
LibertyLibrary::makeCornerMap(liberty, corner->libertyIndex(min_max),
network_, report_);
}
bool
Sta::setMinLibrary(const char *min_filename,
const char *max_filename)
{
LibertyLibrary *max_lib = network_->findLibertyFilename(max_filename);
if (max_lib) {
LibertyLibrary *min_lib = readLibertyFile(min_filename, cmd_corner_,
MinMaxAll::min(), false,
network_);
return min_lib != nullptr;
}
else
return false;
}
void
Sta::readNetlistBefore()
{
clear();
NetworkReader *network_reader = networkReader();
if (network_reader)
network_reader->readNetlistBefore();
}
bool
Sta::linkDesign(const char *top_cell_name)
{
clear();
Stats stats(debug_, report_);
bool status = network_->linkNetwork(top_cell_name,
link_make_black_boxes_,
report_);
stats.report("Link");
return status;
}
bool
Sta::linkMakeBlackBoxes() const
{
return link_make_black_boxes_;
}
void
Sta::setLinkMakeBlackBoxes(bool make)
{
link_make_black_boxes_ = make;
}
////////////////////////////////////////////////////////////////
void
Sta::setDebugLevel(const char *what,
int level)
{
debug_->setLevel(what, level);
}
////////////////////////////////////////////////////////////////
void
Sta::setAnalysisType(AnalysisType analysis_type)
{
if (analysis_type != sdc_->analysisType()) {
sdc_->setAnalysisType(analysis_type);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
search_->deletePathGroups();
corners_->analysisTypeChanged();
if (graph_)
graph_->setDelayCount(corners_->dcalcAnalysisPtCount());
}
}
OperatingConditions *
Sta::operatingConditions(const MinMax *min_max) const
{
return sdc_->operatingConditions(min_max);
}
void
Sta::setOperatingConditions(OperatingConditions *op_cond,
const MinMaxAll *min_max)
{
sdc_->setOperatingConditions(op_cond, min_max);
corners_->operatingConditionsChanged();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
Pvt *
Sta::pvt(Instance *inst,
const MinMax *min_max)
{
return sdc_->pvt(inst, min_max);
}
void
Sta::setPvt(Instance *inst,
const MinMaxAll *min_max,
float process,
float voltage,
float temperature)
{
Pvt *pvt = new Pvt(process, voltage, temperature);
setPvt(inst, min_max, pvt);
}
void
Sta::setPvt(Instance *inst,
const MinMaxAll *min_max,
Pvt *pvt)
{
sdc_->setPvt(inst, min_max, pvt);
delaysInvalidFrom(inst);
}
void
Sta::setTimingDerate(TimingDerateType type,
PathClkOrData clk_data,
const RiseFallBoth *rf,
const EarlyLate *early_late,
float derate)
{
sdc_->setTimingDerate(type, clk_data, rf, early_late, derate);
// Delay calculation results are still valid.
// The search derates delays while finding arrival times.
search_->arrivalsInvalid();
}
void
Sta::setTimingDerate(const Net *net,
PathClkOrData clk_data,
const RiseFallBoth *rf,
const EarlyLate *early_late,
float derate)
{
sdc_->setTimingDerate(net, clk_data, rf, early_late, derate);
// Delay calculation results are still valid.
// The search derates delays while finding arrival times.
search_->arrivalsInvalid();
}
void
Sta::setTimingDerate(const Instance *inst,
TimingDerateType type,
PathClkOrData clk_data,
const RiseFallBoth *rf,
const EarlyLate *early_late,
float derate)
{
sdc_->setTimingDerate(inst, type, clk_data, rf, early_late, derate);
// Delay calculation results are still valid.
// The search derates delays while finding arrival times.
search_->arrivalsInvalid();
}
void
Sta::setTimingDerate(const LibertyCell *cell,
TimingDerateType type,
PathClkOrData clk_data,
const RiseFallBoth *rf,
const EarlyLate *early_late,
float derate)
{
sdc_->setTimingDerate(cell, type, clk_data, rf, early_late, derate);
// Delay calculation results are still valid.
// The search derates delays while finding arrival times.
search_->arrivalsInvalid();
}
void
Sta::unsetTimingDerate()
{
sdc_->unsetTimingDerate();
// Delay calculation results are still valid.
// The search derates delays while finding arrival times.
search_->arrivalsInvalid();
}
void
Sta::setInputSlew(Port *port,
const RiseFallBoth *rf,
const MinMaxAll *min_max,
float slew)
{
sdc_->setInputSlew(port, rf, min_max, slew);
delaysInvalidFrom(port);
}
void
Sta::setDriveCell(LibertyLibrary *library,
LibertyCell *cell,
Port *port,
LibertyPort *from_port,
float *from_slews,
LibertyPort *to_port,
const RiseFallBoth *rf,
const MinMaxAll *min_max)
{
sdc_->setDriveCell(library, cell, port, from_port, from_slews, to_port,
rf, min_max);
delaysInvalidFrom(port);
}
void
Sta::setDriveResistance(Port *port,
const RiseFallBoth *rf,
const MinMaxAll *min_max,
float res)
{
sdc_->setDriveResistance(port, rf, min_max, res);
delaysInvalidFrom(port);
}
void
Sta::setLatchBorrowLimit(Pin *pin,
float limit)
{
sdc_->setLatchBorrowLimit(pin, limit);
search_->requiredInvalid(pin);
}
void
Sta::setLatchBorrowLimit(Instance *inst,
float limit)
{
sdc_->setLatchBorrowLimit(inst, limit);
search_->requiredInvalid(inst);
}
void
Sta::setLatchBorrowLimit(Clock *clk,
float limit)
{
sdc_->setLatchBorrowLimit(clk, limit);
search_->arrivalsInvalid();
}
void
Sta::setMinPulseWidth(const RiseFallBoth *rf,
float min_width)
{
sdc_->setMinPulseWidth(rf, min_width);
}
void
Sta::setMinPulseWidth(const Pin *pin,
const RiseFallBoth *rf,
float min_width)
{
sdc_->setMinPulseWidth(pin, rf, min_width);
}
void
Sta::setMinPulseWidth(const Instance *inst,
const RiseFallBoth *rf,
float min_width)
{
sdc_->setMinPulseWidth(inst, rf, min_width);
}
void
Sta::setMinPulseWidth(const Clock *clk,
const RiseFallBoth *rf,
float min_width)
{
sdc_->setMinPulseWidth(clk, rf, min_width);
}
void
Sta::setWireloadMode(WireloadMode mode)
{
sdc_->setWireloadMode(mode);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
void
Sta::setWireload(Wireload *wireload,
const MinMaxAll *min_max)
{
sdc_->setWireload(wireload, min_max);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
void
Sta::setWireloadSelection(WireloadSelection *selection,
const MinMaxAll *min_max)
{
sdc_->setWireloadSelection(selection, min_max);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
void
Sta::setSlewLimit(Clock *clk,
const RiseFallBoth *rf,
const PathClkOrData clk_data,
const MinMax *min_max,
float slew)
{
sdc_->setSlewLimit(clk, rf, clk_data, min_max, slew);
}
void
Sta::setSlewLimit(Port *port,
const MinMax *min_max,
float slew)
{
sdc_->setSlewLimit(port, min_max, slew);
}
void
Sta::setSlewLimit(Cell *cell,
const MinMax *min_max,
float slew)
{
sdc_->setSlewLimit(cell, min_max, slew);
}
void
Sta::setCapacitanceLimit(Cell *cell,
const MinMax *min_max,
float cap)
{
sdc_->setCapacitanceLimit(cell, min_max, cap);
}
void
Sta::setCapacitanceLimit(Port *port,
const MinMax *min_max,
float cap)
{
sdc_->setCapacitanceLimit(port, min_max, cap);
}
void
Sta::setCapacitanceLimit(Pin *pin,
const MinMax *min_max,
float cap)
{
sdc_->setCapacitanceLimit(pin, min_max, cap);
}
void
Sta::setFanoutLimit(Cell *cell,
const MinMax *min_max,
float fanout)
{
sdc_->setFanoutLimit(cell, min_max, fanout);
}
void
Sta::setFanoutLimit(Port *port,
const MinMax *min_max,
float fanout)
{
sdc_->setFanoutLimit(port, min_max, fanout);
}
void
Sta::setMaxArea(float area)
{
sdc_->setMaxArea(area);
}
void
Sta::makeClock(const char *name,
PinSet *pins,
bool add_to_pins,
float period,
FloatSeq *waveform,
char *comment)
{
sdc_->makeClock(name, pins, add_to_pins, period, waveform, comment);
update_genclks_ = true;
search_->arrivalsInvalid();
}
void
Sta::makeGeneratedClock(const char *name,
PinSet *pins,
bool add_to_pins,
Pin *src_pin,
Clock *master_clk,
Pin *pll_out,
Pin *pll_fdbk,
int divide_by,
int multiply_by,
float duty_cycle,
bool invert,
bool combinational,
IntSeq *edges,
FloatSeq *edge_shifts,
char *comment)
{
sdc_->makeGeneratedClock(name, pins, add_to_pins,
src_pin, master_clk,
pll_out, pll_fdbk,
divide_by, multiply_by, duty_cycle,
invert, combinational,
edges, edge_shifts, comment);
update_genclks_ = true;
search_->arrivalsInvalid();
}
void
Sta::removeClock(Clock *clk)
{
sdc_->removeClock(clk);
search_->arrivalsInvalid();
}
bool
Sta::isClockSrc(const Pin *pin) const
{
return sdc_->isClock(pin);
}
void
Sta::setPropagatedClock(Clock *clk)
{
sdc_->setPropagatedClock(clk);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
clkPinsInvalid();
}
void
Sta::removePropagatedClock(Clock *clk)
{
sdc_->removePropagatedClock(clk);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
clkPinsInvalid();
}
void
Sta::setPropagatedClock(Pin *pin)
{
sdc_->setPropagatedClock(pin);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
clkPinsInvalid();
}
void
Sta::removePropagatedClock(Pin *pin)
{
sdc_->removePropagatedClock(pin);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
clkPinsInvalid();
}
void
Sta::setClockSlew(Clock *clk,
const RiseFallBoth *rf,
const MinMaxAll *min_max,
float slew)
{
sdc_->setClockSlew(clk, rf, min_max, slew);
clockSlewChanged(clk);
}
void
Sta::removeClockSlew(Clock *clk)
{
sdc_->removeClockSlew(clk);
clockSlewChanged(clk);
}
void
Sta::clockSlewChanged(Clock *clk)
{
for (Pin *pin : clk->pins())
graph_delay_calc_->delayInvalid(pin);
search_->arrivalsInvalid();
}
void
Sta::setClockLatency(Clock *clk,
Pin *pin,
const RiseFallBoth *rf,
const MinMaxAll *min_max,
float delay)
{
sdcChangedGraph();
sdc_->setClockLatency(clk, pin, rf, min_max, delay);
search_->arrivalsInvalid();
}
void
Sta::sdcChangedGraph()
{
if (graph_sdc_annotated_)
sdc_->removeGraphAnnotations();
graph_sdc_annotated_ = false;
}
void
Sta::ensureGraphSdcAnnotated()
{
if (!graph_sdc_annotated_) {
sdc_->annotateGraph();
graph_sdc_annotated_ = true;
}
}
void
Sta::removeClockLatency(const Clock *clk,
const Pin *pin)
{
sdcChangedGraph();
sdc_->removeClockLatency(clk, pin);
search_->arrivalsInvalid();
}
void
Sta::setClockInsertion(const Clock *clk,
const Pin *pin,
const RiseFallBoth *rf,
const MinMaxAll *min_max,
const EarlyLateAll *early_late,
float delay)
{
sdc_->setClockInsertion(clk, pin, rf, min_max, early_late, delay);
search_->arrivalsInvalid();
}
void
Sta::removeClockInsertion(const Clock *clk,
const Pin *pin)
{
sdc_->removeClockInsertion(clk, pin);
search_->arrivalsInvalid();
}
void
Sta::setClockUncertainty(Clock *clk,
const SetupHoldAll *setup_hold,
float uncertainty)
{
clk->setUncertainty(setup_hold, uncertainty);
search_->arrivalsInvalid();
}
void
Sta::removeClockUncertainty(Clock *clk,
const SetupHoldAll *setup_hold)
{
clk->removeUncertainty(setup_hold);
search_->arrivalsInvalid();
}
void
Sta::setClockUncertainty(Pin *pin,
const SetupHoldAll *setup_hold,
float uncertainty)
{
sdc_->setClockUncertainty(pin, setup_hold, uncertainty);
search_->arrivalsInvalid();
}
void
Sta::removeClockUncertainty(Pin *pin,
const SetupHoldAll *setup_hold)
{
sdc_->removeClockUncertainty(pin, setup_hold);
search_->arrivalsInvalid();
}
void
Sta::setClockUncertainty(Clock *from_clk,
const RiseFallBoth *from_rf,
Clock *to_clk,
const RiseFallBoth *to_rf,
const SetupHoldAll *setup_hold,
float uncertainty)
{
sdc_->setClockUncertainty(from_clk, from_rf, to_clk, to_rf,
setup_hold, uncertainty);
search_->arrivalsInvalid();
}
void
Sta::removeClockUncertainty(Clock *from_clk,
const RiseFallBoth *from_rf,
Clock *to_clk,
const RiseFallBoth *to_rf,
const SetupHoldAll *setup_hold)
{
sdc_->removeClockUncertainty(from_clk, from_rf, to_clk, to_rf,
setup_hold);
search_->arrivalsInvalid();
}
ClockGroups *
Sta::makeClockGroups(const char *name,
bool logically_exclusive,
bool physically_exclusive,
bool asynchronous,
bool allow_paths,
const char *comment)
{
ClockGroups *groups = sdc_->makeClockGroups(name,
logically_exclusive,
physically_exclusive,
asynchronous,
allow_paths,
comment);
search_->requiredsInvalid();
return groups;
}
void
Sta::removeClockGroupsLogicallyExclusive(const char *name)
{
sdc_->removeClockGroupsLogicallyExclusive(name);
search_->requiredsInvalid();
}
void
Sta::removeClockGroupsPhysicallyExclusive(const char *name)
{
sdc_->removeClockGroupsPhysicallyExclusive(name);
search_->requiredsInvalid();
}
void
Sta::removeClockGroupsAsynchronous(const char *name)
{
sdc_->removeClockGroupsAsynchronous(name);
search_->requiredsInvalid();
}
void
Sta::makeClockGroup(ClockGroups *clk_groups,
ClockSet *clks)
{
sdc_->makeClockGroup(clk_groups, clks);
}
void
Sta::setClockSense(PinSet *pins,
ClockSet *clks,
ClockSense sense)
{
sdc_->setClockSense(pins, clks, sense);
search_->arrivalsInvalid();
}
////////////////////////////////////////////////////////////////
void
Sta::setClockGatingCheck(const RiseFallBoth *rf,
const SetupHold *setup_hold,
float margin)
{
sdc_->setClockGatingCheck(rf, setup_hold, margin);
search_->arrivalsInvalid();
}
void
Sta::setClockGatingCheck(Clock *clk,
const RiseFallBoth *rf,
const SetupHold *setup_hold,
float margin)
{
sdc_->setClockGatingCheck(clk, rf, setup_hold, margin);
search_->arrivalsInvalid();
}
void
Sta::setClockGatingCheck(Instance *inst,
const RiseFallBoth *rf,
const SetupHold *setup_hold,
float margin,
LogicValue active_value)
{
sdc_->setClockGatingCheck(inst, rf, setup_hold, margin,active_value);
search_->arrivalsInvalid();
}
void
Sta::setClockGatingCheck(Pin *pin,
const RiseFallBoth *rf,
const SetupHold *setup_hold,
float margin,
LogicValue active_value)
{
sdc_->setClockGatingCheck(pin, rf, setup_hold, margin,active_value);
search_->arrivalsInvalid();
}
void
Sta::setDataCheck(Pin *from,
const RiseFallBoth *from_rf,
Pin *to,
const RiseFallBoth *to_rf,
Clock *clk,
const SetupHoldAll *setup_hold,
float margin)
{
sdcChangedGraph();
sdc_->setDataCheck(from, from_rf, to, to_rf, clk, setup_hold,margin);
search_->requiredInvalid(to);
}
void
Sta::removeDataCheck(Pin *from,
const RiseFallBoth *from_rf,
Pin *to,
const RiseFallBoth *to_rf,
Clock *clk,
const SetupHoldAll *setup_hold)
{
sdc_->removeDataCheck(from, from_rf, to, to_rf, clk, setup_hold);
search_->requiredInvalid(to);
}
////////////////////////////////////////////////////////////////
void
Sta::disable(Pin *pin)
{
sdcChangedGraph();
sdc_->disable(pin);
// Levelization respects disabled edges.
levelize_->invalid();
graph_delay_calc_->delayInvalid(pin);
search_->arrivalsInvalid();
}
void
Sta::removeDisable(Pin *pin)
{
sdcChangedGraph();
sdc_->removeDisable(pin);
disableAfter();
// Levelization respects disabled edges.
levelize_->invalid();
graph_delay_calc_->delayInvalid(pin);
search_->arrivalsInvalid();
}
void
Sta::disable(Instance *inst,
LibertyPort *from,
LibertyPort *to)
{
sdcChangedGraph();
sdc_->disable(inst, from, to);
if (from) {
Pin *from_pin = network_->findPin(inst, from);
graph_delay_calc_->delayInvalid(from_pin);
}
if (to) {
Pin *to_pin = network_->findPin(inst, to);
graph_delay_calc_->delayInvalid(to_pin);
}
if (from == nullptr && to == nullptr) {
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
graph_delay_calc_->delayInvalid(pin);
}
delete pin_iter;
}
// Levelization respects disabled edges.
levelize_->invalid();
search_->arrivalsInvalid();
}
void
Sta::removeDisable(Instance *inst,
LibertyPort *from,
LibertyPort *to)
{
sdcChangedGraph();
sdc_->removeDisable(inst, from, to);
if (from) {
Pin *from_pin = network_->findPin(inst, from);
graph_delay_calc_->delayInvalid(from_pin);
}
if (to) {
Pin *to_pin = network_->findPin(inst, to);
graph_delay_calc_->delayInvalid(to_pin);
}
if (from == nullptr && to == nullptr) {
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
graph_delay_calc_->delayInvalid(pin);
}
delete pin_iter;
}
// Levelization respects disabled edges.
levelize_->invalid();
search_->arrivalsInvalid();
}
void
Sta::disable(LibertyCell *cell,
LibertyPort *from,
LibertyPort *to)
{
sdc_->disable(cell, from, to);
disableAfter();
}
void
Sta::removeDisable(LibertyCell *cell,
LibertyPort *from,
LibertyPort *to)
{
sdc_->removeDisable(cell, from, to);
disableAfter();
}
void
Sta::disable(LibertyPort *port)
{
sdcChangedGraph();
sdc_->disable(port);
disableAfter();
}
void
Sta::removeDisable(LibertyPort *port)
{
sdcChangedGraph();
sdc_->removeDisable(port);
disableAfter();
}
void
Sta::disable(Port *port)
{
sdc_->disable(port);
disableAfter();
}
void
Sta::removeDisable(Port *port)
{
sdc_->removeDisable(port);
disableAfter();
}
void
Sta::disable(Edge *edge)
{
sdc_->disable(edge);
disableAfter();
}
void
Sta::removeDisable(Edge *edge)
{
sdc_->removeDisable(edge);
disableAfter();
}
void
Sta::disable(TimingArcSet *arc_set)
{
sdc_->disable(arc_set);
disableAfter();
}
void
Sta::removeDisable(TimingArcSet *arc_set)
{
sdc_->removeDisable(arc_set);
disableAfter();
}
void
Sta::disableAfter()
{
// Levelization respects disabled edges.
levelize_->invalid();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
////////////////////////////////////////////////////////////////
EdgeSeq *
Sta::disabledEdges()
{
ensureLevelized();
EdgeSeq *disabled_edges = new EdgeSeq;
VertexIterator vertex_iter(graph_);
while (vertex_iter.hasNext()) {
Vertex *vertex = vertex_iter.next();
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (isDisabledConstant(edge)
|| isDisabledCondDefault(edge)
|| isDisabledConstraint(edge)
|| edge->isDisabledLoop()
|| isDisabledPresetClr(edge))
disabled_edges->push_back(edge);
}
}
return disabled_edges;
}
EdgeSeq *
Sta::disabledEdgesSorted()
{
EdgeSeq *disabled_edges = disabledEdges();
sortEdges(disabled_edges, network_, graph_);
return disabled_edges;
}
bool
Sta::isDisabledConstraint(Edge *edge)
{
Pin *from_pin = edge->from(graph_)->pin();
Pin *to_pin = edge->to(graph_)->pin();
const Instance *inst = network_->instance(from_pin);
TimingArcSet *arc_set = edge->timingArcSet();
return sdc_->isDisabled(from_pin)
|| sdc_->isDisabled(to_pin)
|| sdc_->isDisabled(inst, from_pin, to_pin, edge->role())
|| sdc_->isDisabled(edge)
|| sdc_->isDisabled(arc_set);
}
bool
Sta::isDisabledConstant(Edge *edge)
{
sim_->ensureConstantsPropagated();
const TimingRole *role = edge->role();
Vertex *from_vertex = edge->from(graph_);
Pin *from_pin = from_vertex->pin();
Vertex *to_vertex = edge->to(graph_);
Pin *to_pin = to_vertex->pin();
const Instance *inst = network_->instance(from_pin);
return sim_->logicZeroOne(from_vertex)
|| sim_->logicZeroOne(to_vertex)
|| (!role->isWire()
&& (isCondDisabled(edge, inst, from_pin, to_pin, network_, sim_)
|| isModeDisabled(edge, inst, network_, sim_)
|| isTestDisabled(inst, from_pin, to_pin, network_, sim_)
|| hasDisabledArcs(edge, graph_)));
}
static bool
hasDisabledArcs(Edge *edge,
Graph *graph)
{
TimingArcSet *arc_set = edge->timingArcSet();
TimingArcSetArcIterator arc_iter(arc_set);
while (arc_iter.hasNext()) {
TimingArc *arc = arc_iter.next();
if (!searchThru(edge, arc, graph))
return true;
}
return false;
}
bool
Sta::isDisabledLoop(Edge *edge) const
{
return levelize_->isDisabledLoop(edge);
}
bool
Sta::isDisabledCondDefault(Edge *edge)
{
return sdc_->isDisabledCondDefault(edge);
}
PinSet *
Sta::disabledConstantPins(Edge *edge)
{
sim_->ensureConstantsPropagated();
PinSet *pins = new PinSet;
Vertex *from_vertex = edge->from(graph_);
Pin *from_pin = from_vertex->pin();
Vertex *to_vertex = edge->to(graph_);
Pin *to_pin = to_vertex->pin();
if (sim_->logicZeroOne(from_vertex))
pins->insert(from_pin);
if (edge->role()->isWire()) {
if (sim_->logicZeroOne(to_vertex))
pins->insert(to_pin);
}
else {
Instance *inst = network_->instance(to_pin);
bool is_disabled;
FuncExpr *disable_cond;
isCondDisabled(edge, inst, from_pin, to_pin, network_, sim_,
is_disabled, disable_cond);
if (is_disabled)
exprConstantPins(disable_cond, inst, pins);
isModeDisabled(edge, inst, network_, sim_,
is_disabled, disable_cond);
if (is_disabled)
exprConstantPins(disable_cond, inst, pins);
Pin *scan_enable;
isTestDisabled(inst, from_pin, to_pin, network_, sim_,
is_disabled, scan_enable);
if (is_disabled)
pins->insert(scan_enable);
if (hasDisabledArcs(edge, graph_)) {
LibertyPort *to_port = network_->libertyPort(to_pin);
if (to_port) {
FuncExpr *func = to_port->function();
if (func
&& sim_->functionSense(inst, from_pin, to_pin) != edge->sense())
exprConstantPins(func, inst, pins);
}
}
}
return pins;
}
TimingSense
Sta::simTimingSense(Edge *edge)
{
Pin *from_pin = edge->from(graph_)->pin();
Pin *to_pin = edge->to(graph_)->pin();
Instance *inst = network_->instance(from_pin);
return sim_->functionSense(inst, from_pin, to_pin);
}
void
Sta::exprConstantPins(FuncExpr *expr, Instance *inst, PinSet *pins)
{
FuncExprPortIterator port_iter(expr);
while (port_iter.hasNext()) {
LibertyPort *port = port_iter.next();
Pin *pin = network_->findPin(inst, port);
if (pin) {
LogicValue value = sim_->logicValue(pin);
if (value != LogicValue::unknown)
pins->insert(pin);
}
}
}
bool
Sta::isDisabledBidirectInstPath(Edge *edge) const
{
return !sdc_->bidirectInstPathsEnabled()
&& edge->isBidirectInstPath();
}
bool
Sta::isDisabledBidirectNetPath(Edge *edge) const
{
return !sdc_->bidirectNetPathsEnabled()
&& edge->isBidirectNetPath();
}
bool
Sta::isDisabledPresetClr(Edge *edge) const
{
return !sdc_->presetClrArcsEnabled()
&& edge->role() == TimingRole::regSetClr();
}
void
Sta::disableClockGatingCheck(Instance *inst)
{
sdc_->disableClockGatingCheck(inst);
search_->endpointsInvalid();
}
void
Sta::disableClockGatingCheck(Pin *pin)
{
sdc_->disableClockGatingCheck(pin);
search_->endpointsInvalid();
}
void
Sta::removeDisableClockGatingCheck(Instance *inst)
{
sdc_->removeDisableClockGatingCheck(inst);
search_->endpointsInvalid();
}
void
Sta::removeDisableClockGatingCheck(Pin *pin)
{
sdc_->removeDisableClockGatingCheck(pin);
search_->endpointsInvalid();
}
void
Sta::setLogicValue(Pin *pin,
LogicValue value)
{
sdc_->setLogicValue(pin, value);
// Levelization respects constant disabled edges.
levelize_->invalid();
sim_->constantsInvalid();
// Constants disable edges which isolate downstream vertices of the
// graph from the delay calculator's BFS search. This means that
// simply invaldating the delays downstream from the constant pin
// fails. This could be more incremental if the graph delay
// calculator searched thru disabled edges but ignored their
// results.
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
void
Sta::setCaseAnalysis(Pin *pin,
LogicValue value)
{
sdc_->setCaseAnalysis(pin, value);
// Levelization respects constant disabled edges.
levelize_->invalid();
sim_->constantsInvalid();
// Constants disable edges which isolate downstream vertices of the
// graph from the delay calculator's BFS search. This means that
// simply invaldating the delays downstream from the constant pin
// fails. This could be handled incrementally by invalidating delays
// on the output of gates one level downstream.
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
void
Sta::removeCaseAnalysis(Pin *pin)
{
sdc_->removeCaseAnalysis(pin);
// Levelization respects constant disabled edges.
levelize_->invalid();
sim_->constantsInvalid();
// Constants disable edges which isolate downstream vertices of the
// graph from the delay calculator's BFS search. This means that
// simply invaldating the delays downstream from the constant pin
// fails. This could be handled incrementally by invalidating delays
// on the output of gates one level downstream.
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
void
Sta::setInputDelay(Pin *pin,
const RiseFallBoth *rf,
Clock *clk,
const RiseFall *clk_rf,
Pin *ref_pin,
bool source_latency_included,
bool network_latency_included,
const MinMaxAll *min_max,
bool add,
float delay)
{
sdc_->setInputDelay(pin, rf, clk, clk_rf, ref_pin,
source_latency_included, network_latency_included,
min_max, add, delay);
search_->arrivalInvalid(pin);
}
void
Sta::removeInputDelay(Pin *pin,
RiseFallBoth *rf,
Clock *clk,
RiseFall *clk_rf,
MinMaxAll *min_max)
{
sdc_->removeInputDelay(pin, rf, clk, clk_rf, min_max);
search_->arrivalInvalid(pin);
}
void
Sta::setOutputDelay(Pin *pin,
const RiseFallBoth *rf,
Clock *clk,
const RiseFall *clk_rf,
Pin *ref_pin,
bool source_latency_included,
bool network_latency_included,
const MinMaxAll *min_max,
bool add,
float delay)
{
sdc_->setOutputDelay(pin, rf, clk, clk_rf, ref_pin,
source_latency_included,network_latency_included,
min_max, add, delay);
sdcChangedGraph();
search_->requiredInvalid(pin);
}
void
Sta::removeOutputDelay(Pin *pin,
RiseFallBoth *rf,
Clock *clk,
RiseFall *clk_rf,
MinMaxAll *min_max)
{
sdc_->removeOutputDelay(pin, rf, clk, clk_rf, min_max);
sdcChangedGraph();
search_->arrivalInvalid(pin);
}
void
Sta::makeFalsePath(ExceptionFrom *from,
ExceptionThruSeq *thrus,
ExceptionTo *to,
const MinMaxAll *min_max,
const char *comment)
{
sdc_->makeFalsePath(from, thrus, to, min_max, comment);
search_->arrivalsInvalid();
}
void
Sta::makeMulticyclePath(ExceptionFrom *from,
ExceptionThruSeq *thrus,
ExceptionTo *to,
const MinMaxAll *min_max,
bool use_end_clk,
int path_multiplier,
const char *comment)
{
sdc_->makeMulticyclePath(from, thrus, to, min_max,
use_end_clk, path_multiplier,
comment);
search_->arrivalsInvalid();
}
void
Sta::makePathDelay(ExceptionFrom *from,
ExceptionThruSeq *thrus,
ExceptionTo *to,
const MinMax *min_max,
bool ignore_clk_latency,
float delay,
const char *comment)
{
sdc_->makePathDelay(from, thrus, to, min_max,
ignore_clk_latency, delay,
comment);
search_->endpointsInvalid();
search_->arrivalsInvalid();
}
void
Sta::resetPath(ExceptionFrom *from,
ExceptionThruSeq *thrus,
ExceptionTo *to,
const MinMaxAll *min_max)
{
sdc_->resetPath(from, thrus, to, min_max);
search_->arrivalsInvalid();
}
void
Sta::makeGroupPath(const char *name,
bool is_default,
ExceptionFrom *from,
ExceptionThruSeq *thrus,
ExceptionTo *to,
const char *comment)
{
sdc_->makeGroupPath(name, is_default, from, thrus, to,
comment);
search_->arrivalsInvalid();
}
bool
Sta::isGroupPathName(const char *group_name)
{
return PathGroups::isGroupPathName(group_name)
|| sdc_->findClock(group_name)
|| sdc_->isGroupPathName(group_name);
}
ExceptionFrom *
Sta::makeExceptionFrom(PinSet *from_pins,
ClockSet *from_clks,
InstanceSet *from_insts,
const RiseFallBoth *from_rf)
{
return sdc_->makeExceptionFrom(from_pins, from_clks, from_insts,
from_rf);
}
void
Sta::checkExceptionFromPins(ExceptionFrom *from,
const char *file,
int line) const
{
if (from) {
PinSet::ConstIterator pin_iter(from->pins());
while (pin_iter.hasNext()) {
const Pin *pin = pin_iter.next();
if (exceptionFromInvalid(pin)) {
if (line)
report_->fileWarn(160, file, line, "'%s' is not a valid startpoint.",
cmd_network_->pathName(pin));
else
report_->warn(16, "'%s' is not a valid startoint.",
cmd_network_->pathName(pin));
}
}
}
}
bool
Sta::exceptionFromInvalid(const Pin *pin) const
{
Net *net = network_->net(pin);
// Floating pins are invalid.
return (net == nullptr
&& !network_->isTopLevelPort(pin))
|| (net
// Pins connected to power/ground are invalid.
&& (network_->isPower(net)
|| network_->isGround(net)))
|| !((network_->isTopLevelPort(pin)
&& network_->direction(pin)->isAnyInput())
|| network_->isRegClkPin(pin)
|| network_->isLatchData(pin));
}
void
Sta::deleteExceptionFrom(ExceptionFrom *from)
{
delete from;
}
ExceptionThru *
Sta::makeExceptionThru(PinSet *pins,
NetSet *nets,
InstanceSet *insts,
const RiseFallBoth *rf)
{
return sdc_->makeExceptionThru(pins, nets, insts, rf);
}
void
Sta::deleteExceptionThru(ExceptionThru *thru)
{
delete thru;
}
ExceptionTo *
Sta::makeExceptionTo(PinSet *to_pins,
ClockSet *to_clks,
InstanceSet *to_insts,
const RiseFallBoth *rf,
RiseFallBoth *end_rf)
{
return sdc_->makeExceptionTo(to_pins, to_clks, to_insts, rf, end_rf);
}
void
Sta::deleteExceptionTo(ExceptionTo *to)
{
delete to;
}
void
Sta::checkExceptionToPins(ExceptionTo *to,
const char *file,
int line) const
{
if (to) {
PinSet::Iterator pin_iter(to->pins());
while (pin_iter.hasNext()) {
const Pin *pin = pin_iter.next();
if (sdc_->exceptionToInvalid(pin)) {
if (line)
report_->fileWarn(161, file, line, "'%s' is not a valid endpoint.",
cmd_network_->pathName(pin));
else
report_->warn(17, "'%s' is not a valid endpoint.",
cmd_network_->pathName(pin));
}
}
}
}
void
Sta::removeConstraints()
{
levelize_->invalid();
graph_delay_calc_->clear();
search_->clear();
sim_->constantsInvalid();
if (graph_)
sdc_->removeGraphAnnotations();
sdc_->clear();
clk_network_->clear();
}
void
Sta::constraintsChanged()
{
levelize_->invalid();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
sim_->constantsInvalid();
}
void
Sta::writeSdc(const char *filename,
bool leaf,
bool native,
bool no_timestamp,
int digits)
{
sta::writeSdc(network_->topInstance(), filename, "write_sdc",
leaf, native, no_timestamp, digits, sdc_);
}
////////////////////////////////////////////////////////////////
CheckErrorSeq &
Sta::checkTiming(bool no_input_delay,
bool no_output_delay,
bool reg_multiple_clks,
bool reg_no_clks,
bool unconstrained_endpoints,
bool loops,
bool generated_clks)
{
searchPreamble();
if (unconstrained_endpoints)
// Only need non-clock arrivals for unconstrained_endpoints.
search_->findAllArrivals();
else
search_->findClkArrivals();
if (check_timing_ == nullptr)
makeCheckTiming();
return check_timing_->check(no_input_delay, no_output_delay,
reg_multiple_clks, reg_no_clks,
unconstrained_endpoints,
loops, generated_clks);
}
bool
Sta::crprEnabled() const
{
return sdc_->crprEnabled();
}
void
Sta::setCrprEnabled(bool enabled)
{
// Pessimism is only relevant for on_chip_variation analysis.
if (sdc_->analysisType() == AnalysisType::ocv
&& enabled != sdc_->crprEnabled())
search_->arrivalsInvalid();
sdc_->setCrprEnabled(enabled);
}
CrprMode
Sta::crprMode() const
{
return sdc_->crprMode();
}
void
Sta::setCrprMode(CrprMode mode)
{
// Pessimism is only relevant for on_chip_variation analysis.
if (sdc_->analysisType() == AnalysisType::ocv
&& sdc_->crprEnabled()
&& sdc_->crprMode() != mode)
search_->arrivalsInvalid();
sdc_->setCrprMode(mode);
}
bool
Sta::pocvEnabled() const
{
return pocv_enabled_;
}
void
Sta::setPocvEnabled(bool enabled)
{
if (enabled != pocv_enabled_) {
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
pocv_enabled_ = enabled;
updateComponentsState();
}
void
Sta::setSigmaFactor(float factor)
{
if (!fuzzyEqual(factor, sigma_factor_)) {
sigma_factor_ = factor;
search_->arrivalsInvalid();
updateComponentsState();
}
}
bool
Sta::propagateGatedClockEnable() const
{
return sdc_->propagateGatedClockEnable();
}
void
Sta::setPropagateGatedClockEnable(bool enable)
{
if (sdc_->propagateGatedClockEnable() != enable)
search_->arrivalsInvalid();
sdc_->setPropagateGatedClockEnable(enable);
}
bool
Sta::presetClrArcsEnabled() const
{
return sdc_->presetClrArcsEnabled();
}
void
Sta::setPresetClrArcsEnabled(bool enable)
{
if (sdc_->presetClrArcsEnabled() != enable) {
levelize_->invalid();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
sdc_->setPresetClrArcsEnabled(enable);
}
bool
Sta::condDefaultArcsEnabled() const
{
return sdc_->condDefaultArcsEnabled();
}
void
Sta::setCondDefaultArcsEnabled(bool enabled)
{
if (sdc_->condDefaultArcsEnabled() != enabled) {
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
sdc_->setCondDefaultArcsEnabled(enabled);
}
}
bool
Sta::bidirectInstPathsEnabled() const
{
return sdc_->bidirectInstPathsEnabled();
}
void
Sta::setBidirectInstPathsEnabled(bool enabled)
{
if (sdc_->bidirectInstPathsEnabled() != enabled) {
levelize_->invalid();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
sdc_->setBidirectInstPathsEnabled(enabled);
}
}
bool
Sta::bidirectNetPathsEnabled() const
{
return sdc_->bidirectNetPathsEnabled();
}
void
Sta::setBidirectNetPathsEnabled(bool enabled)
{
if (sdc_->bidirectNetPathsEnabled() != enabled) {
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
sdc_->setBidirectNetPathsEnabled(enabled);
}
}
bool
Sta::recoveryRemovalChecksEnabled() const
{
return sdc_->recoveryRemovalChecksEnabled();
}
void
Sta::setRecoveryRemovalChecksEnabled(bool enabled)
{
if (sdc_->recoveryRemovalChecksEnabled() != enabled) {
search_->arrivalsInvalid();
sdc_->setRecoveryRemovalChecksEnabled(enabled);
}
}
bool
Sta::gatedClkChecksEnabled() const
{
return sdc_->gatedClkChecksEnabled();
}
void
Sta::setGatedClkChecksEnabled(bool enabled)
{
if (sdc_->gatedClkChecksEnabled() != enabled) {
search_->arrivalsInvalid();
sdc_->setGatedClkChecksEnabled(enabled);
}
}
bool
Sta::dynamicLoopBreaking() const
{
return sdc_->dynamicLoopBreaking();
}
void
Sta::setDynamicLoopBreaking(bool enable)
{
if (sdc_->dynamicLoopBreaking() != enable) {
sdc_->setDynamicLoopBreaking(enable);
search_->arrivalsInvalid();
}
}
bool
Sta::useDefaultArrivalClock() const
{
return sdc_->useDefaultArrivalClock();
}
void
Sta::setUseDefaultArrivalClock(bool enable)
{
if (sdc_->useDefaultArrivalClock() != enable) {
sdc_->setUseDefaultArrivalClock(enable);
search_->arrivalsInvalid();
}
}
bool
Sta::propagateAllClocks() const
{
return sdc_->propagateAllClocks();
}
void
Sta::setPropagateAllClocks(bool prop)
{
sdc_->setPropagateAllClocks(prop);
}
bool
Sta::clkThruTristateEnabled() const
{
return sdc_->clkThruTristateEnabled();
}
void
Sta::setClkThruTristateEnabled(bool enable)
{
if (enable != sdc_->clkThruTristateEnabled()) {
search_->arrivalsInvalid();
sdc_->setClkThruTristateEnabled(enable);
}
}
////////////////////////////////////////////////////////////////
Corner *
Sta::findCorner(const char *corner_name)
{
return corners_->findCorner(corner_name);
}
bool
Sta::multiCorner()
{
return corners_->multiCorner();
}
// Init one corner named "default".
void
Sta::makeCorners()
{
corners_ = new Corners(this);
StringSet corner_names;
corner_names.insert("default");
corners_->makeCorners(&corner_names);
cmd_corner_ = corners_->findCorner(0);
}
void
Sta::makeCorners(StringSet *corner_names)
{
parasitics_->deleteParasitics();
corners_->makeCorners(corner_names);
corners_->makeParasiticAnalysisPtsSingle();
cmd_corner_ = corners_->findCorner(0);
}
Corner *
Sta::cmdCorner() const
{
return cmd_corner_;
}
void
Sta::setCmdCorner(Corner *corner)
{
cmd_corner_ = corner;
}
////////////////////////////////////////////////////////////////
// from/thrus/to are owned and deleted by Search.
// Returned sequence is owned by the caller.
// PathEnds are owned by Search PathGroups and deleted on next call.
PathEndSeq *
Sta::findPathEnds(ExceptionFrom *from,
ExceptionThruSeq *thrus,
ExceptionTo *to,
bool unconstrained,
const Corner *corner,
const MinMaxAll *min_max,
int group_count,
int endpoint_count,
bool unique_pins,
float slack_min,
float slack_max,
bool sort_by_slack,
PathGroupNameSet *group_names,
bool setup,
bool hold,
bool recovery,
bool removal,
bool clk_gating_setup,
bool clk_gating_hold)
{
searchPreamble();
return search_->findPathEnds(from, thrus, to, unconstrained,
corner, min_max, group_count, endpoint_count,
unique_pins, slack_min, slack_max,
sort_by_slack, group_names,
setup, hold,
recovery, removal,
clk_gating_setup, clk_gating_hold);
}
////////////////////////////////////////////////////////////////
// Overall flow:
// make graph
// propagate constants
// levelize
// delay calculation
// update generated clocks
// find arrivals
void
Sta::searchPreamble()
{
findDelays();
updateGeneratedClks();
sdc_->searchPreamble();
search_->deleteFilteredArrivals();
}
void
Sta::setReportPathFormat(ReportPathFormat format)
{
report_path_->setPathFormat(format);
}
void
Sta::setReportPathFieldOrder(StringSeq *field_names)
{
report_path_->setReportFieldOrder(field_names);
}
void
Sta::setReportPathFields(bool report_input_pin,
bool report_net,
bool report_cap,
bool report_slew)
{
report_path_->setReportFields(report_input_pin, report_net, report_cap,
report_slew);
}
ReportField *
Sta::findReportPathField(const char *name)
{
return report_path_->findField(name);
}
void
Sta::setReportPathDigits(int digits)
{
report_path_->setDigits(digits);
}
void
Sta::setReportPathNoSplit(bool no_split)
{
report_path_->setNoSplit(no_split);
}
void
Sta::setReportPathSigmas(bool report_sigmas)
{
report_path_->setReportSigmas(report_sigmas);
}
void
Sta::reportPathEnds(PathEndSeq *ends)
{
report_path_->reportPathEnds(ends);
}
void
Sta::reportPathEndHeader()
{
report_path_->reportPathEndHeader();
}
void
Sta::reportPathEndFooter()
{
report_path_->reportPathEndFooter();
}
void
Sta::reportPathEnd(PathEnd *end)
{
report_path_->reportPathEnd(end);
}
void
Sta::reportPathEnd(PathEnd *end,
PathEnd *prev_end)
{
report_path_->reportPathEnd(end, prev_end);
}
void
Sta::reportPath(Path *path)
{
report_path_->reportPath(path);
}
void
Sta::updateTiming(bool full)
{
searchPreamble();
if (full)
search_->arrivalsInvalid();
search_->findAllArrivals();
}
void
Sta::reportClkSkew(ClockSet *clks,
const Corner *corner,
const SetupHold *setup_hold,
int digits)
{
ensureClkArrivals();
if (clk_skews_ == nullptr)
clk_skews_ = new ClkSkews(this);
clk_skews_->reportClkSkew(clks, corner, setup_hold, digits);
}
////////////////////////////////////////////////////////////////
void
Sta::delaysInvalid()
{
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
void
Sta::arrivalsInvalid()
{
search_->arrivalsInvalid();
}
void
Sta::ensureClkArrivals()
{
searchPreamble();
search_->findClkArrivals();
}
////////////////////////////////////////////////////////////////
void
Sta::visitStartpoints(VertexVisitor *visitor)
{
ensureGraph();
search_->visitStartpoints(visitor);
}
void
Sta::visitEndpoints(VertexVisitor *visitor)
{
ensureGraph();
search_->visitEndpoints(visitor);
}
PinSet *
Sta::findGroupPathPins(const char *group_path_name)
{
if (!(search_->havePathGroups()
&& search_->arrivalsValid())) {
PathEndSeq *path_ends = findPathEnds(// from, thrus, to, unconstrained
nullptr, nullptr, nullptr, false,
// corner, min_max,
nullptr, MinMaxAll::max(),
// group_count, endpoint_count, unique_pins
1, 1, false,
-INF, INF, // slack_min, slack_max,
false, // sort_by_slack
nullptr, // group_names
// setup, hold, recovery, removal,
true, true, true, true,
// clk_gating_setup, clk_gating_hold
true, true);
// No use for the path end sequence.
delete path_ends;
}
PathGroup *path_group = search_->findPathGroup(group_path_name,
MinMax::max());
PinSet *pins = new PinSet;
VertexPinCollector visitor(pins);
visitPathGroupVertices(path_group, &visitor, this);
return pins;
}
////////////////////////////////////////////////////////////////
void
Sta::findRequireds()
{
searchPreamble();
search_->findAllArrivals();
search_->findRequireds();
}
////////////////////////////////////////////////////////////////
VertexPathIterator *
Sta::vertexPathIterator(Vertex *vertex,
const RiseFall *rf,
const PathAnalysisPt *path_ap)
{
return new VertexPathIterator(vertex, rf, path_ap, this);
}
VertexPathIterator *
Sta::vertexPathIterator(Vertex *vertex,
const RiseFall *rf,
const MinMax *min_max)
{
return new VertexPathIterator(vertex, rf, min_max, this);
}
void
Sta::vertexWorstArrivalPath(Vertex *vertex,
const RiseFall *rf,
const MinMax *min_max,
// Return value.
PathRef &worst_path)
{
Arrival worst_arrival = min_max->initValue();
VertexPathIterator path_iter(vertex, rf, min_max, this);
while (path_iter.hasNext()) {
PathVertex *path = path_iter.next();
Arrival arrival = path->arrival(this);
if (!path->tag(this)->isGenClkSrcPath()
&& delayGreater(arrival, worst_arrival, min_max, this)) {
worst_arrival = arrival;
worst_path.init(path);
}
}
}
void
Sta::vertexWorstArrivalPath(Vertex *vertex,
const MinMax *min_max,
// Return value.
PathRef &worst_path)
{
Arrival worst_arrival = min_max->initValue();
VertexPathIterator path_iter(vertex, this);
while (path_iter.hasNext()) {
PathVertex *path = path_iter.next();
Arrival arrival = path->arrival(this);
if (path->minMax(this) == min_max
&& !path->tag(this)->isGenClkSrcPath()
&& delayGreater(arrival, worst_arrival, min_max, this)) {
worst_arrival = arrival;
worst_path.init(path);
}
}
}
void
Sta::vertexWorstSlackPath(Vertex *vertex,
const RiseFall *rf,
const MinMax *min_max,
// Return value.
PathRef &worst_path)
{
Slack min_slack = MinMax::min()->initValue();
VertexPathIterator path_iter(vertex, rf, min_max, this);
while (path_iter.hasNext()) {
PathVertex *path = path_iter.next();
Slack slack = path->slack(this);
if (!path->tag(this)->isGenClkSrcPath()
&& delayLess(slack, min_slack, this)) {
min_slack = slack;
worst_path.init(path);
}
}
}
void
Sta::vertexWorstSlackPath(Vertex *vertex,
const MinMax *min_max,
// Return value.
PathRef &worst_path)
{
Slack min_slack = MinMax::min()->initValue();
VertexPathIterator path_iter(vertex, this);
while (path_iter.hasNext()) {
PathVertex *path = path_iter.next();
if (path->minMax(this) == min_max
&& !path->tag(this)->isGenClkSrcPath()) {
Slack slack = path->slack(this);
if (delayLess(slack, min_slack, this)) {
min_slack = slack;
worst_path.init(path);
}
}
}
}
Arrival
Sta::vertexArrival(Vertex *vertex,
const RiseFall *rf,
const PathAnalysisPt *path_ap)
{
return vertexArrival(vertex, rf, clk_edge_wildcard, path_ap);
}
Arrival
Sta::vertexArrival(Vertex *vertex,
const RiseFall *rf,
const ClockEdge *clk_edge,
const PathAnalysisPt *path_ap)
{
searchPreamble();
search_->findArrivals(vertex->level());
const MinMax *min_max = path_ap->pathMinMax();
Arrival arrival = min_max->initValue();
VertexPathIterator path_iter(vertex, rf, path_ap, this);
while (path_iter.hasNext()) {
Path *path = path_iter.next();
const Arrival &path_arrival = path->arrival(this);
ClkInfo *clk_info = path->clkInfo(search_);
if ((clk_edge == clk_edge_wildcard
|| clk_info->clkEdge() == clk_edge)
&& !clk_info->isGenClkSrcPath()
&& delayGreater(path->arrival(this), arrival, min_max, this))
arrival = path_arrival;
}
return arrival;
}
Required
Sta::vertexRequired(Vertex *vertex,
const MinMax *min_max)
{
findRequired(vertex);
const MinMax *req_min_max = min_max->opposite();
Required required = req_min_max->initValue();
VertexPathIterator path_iter(vertex, this);
while (path_iter.hasNext()) {
const Path *path = path_iter.next();
if (path->minMax(this) == min_max) {
const Required path_required = path->required(this);
if (delayGreater(path_required, required, req_min_max, this))
required = path_required;
}
}
return required;
}
Required
Sta::vertexRequired(Vertex *vertex,
const RiseFall *rf,
const PathAnalysisPt *path_ap)
{
return vertexRequired(vertex, rf, clk_edge_wildcard, path_ap);
}
Required
Sta::vertexRequired(Vertex *vertex,
const RiseFall *rf,
const ClockEdge *clk_edge,
const PathAnalysisPt *path_ap)
{
findRequired(vertex);
const MinMax *min_max = path_ap->pathMinMax()->opposite();
Required required = min_max->initValue();
VertexPathIterator path_iter(vertex, rf, path_ap, this);
while (path_iter.hasNext()) {
const Path *path = path_iter.next();
const Required path_required = path->required(this);
if ((clk_edge == clk_edge_wildcard
|| path->clkEdge(search_) == clk_edge)
&& delayGreater(path_required, required, min_max, this))
required = path_required;
}
return required;
}
Slack
Sta::netSlack(const Net *net,
const MinMax *min_max)
{
ensureGraph();
Slack slack = MinMax::min()->initValue();
NetPinIterator *pin_iter = network_->pinIterator(net);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
if (network_->isLoad(pin)) {
Vertex *vertex = graph_->pinLoadVertex(pin);
Slack pin_slack = vertexSlack(vertex, min_max);
if (delayLess(pin_slack, slack, this))
slack = pin_slack;
}
}
return slack;
}
Slack
Sta::pinSlack(const Pin *pin,
const MinMax *min_max)
{
ensureGraph();
Vertex *vertex, *bidirect_drvr_vertex;
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
Slack slack = MinMax::min()->initValue();
if (vertex)
slack = vertexSlack(vertex, min_max);
if (bidirect_drvr_vertex) {
Slack slack1 = vertexSlack(bidirect_drvr_vertex, min_max);
if (delayLess(slack1, slack, this))
slack = slack1;
}
return slack;
}
Slack
Sta::pinSlack(const Pin *pin,
const RiseFall *rf,
const MinMax *min_max)
{
ensureGraph();
Vertex *vertex, *bidirect_drvr_vertex;
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
Slack slack = MinMax::min()->initValue();
if (vertex)
slack = vertexSlack(vertex, rf, min_max);
if (bidirect_drvr_vertex) {
Slack slack1 = vertexSlack(bidirect_drvr_vertex, rf, min_max);
if (delayLess(slack1, slack, this))
slack = slack1;
}
return slack;
}
Slack
Sta::vertexSlack(Vertex *vertex,
const MinMax *min_max)
{
findRequired(vertex);
MinMax *min = MinMax::min();
Slack slack = min->initValue();
VertexPathIterator path_iter(vertex, this);
while (path_iter.hasNext()) {
Path *path = path_iter.next();
if (path->minMax(this) == min_max) {
Slack path_slack = path->slack(this);
if (delayLess(path_slack, slack, this))
slack = path_slack;
}
}
return slack;
}
Slack
Sta::vertexSlack(Vertex *vertex,
const RiseFall *rf,
const MinMax *min_max)
{
findRequired(vertex);
Slack slack = MinMax::min()->initValue();
VertexPathIterator path_iter(vertex, rf, min_max, this);
while (path_iter.hasNext()) {
Path *path = path_iter.next();
Slack path_slack = path->slack(this);
if (delayLess(path_slack, slack, this))
slack = path_slack;
}
return slack;
}
Slack
Sta::vertexSlack(Vertex *vertex,
const RiseFall *rf,
const PathAnalysisPt *path_ap)
{
findRequired(vertex);
return vertexSlack1(vertex, rf, clk_edge_wildcard, path_ap);
}
Slack
Sta::vertexSlack(Vertex *vertex,
const RiseFall *rf,
const ClockEdge *clk_edge,
const PathAnalysisPt *path_ap)
{
findRequired(vertex);
return vertexSlack1(vertex, rf, clk_edge, path_ap);
}
Slack
Sta::vertexSlack1(Vertex *vertex,
const RiseFall *rf,
const ClockEdge *clk_edge,
const PathAnalysisPt *path_ap)
{
MinMax *min = MinMax::min();
Slack slack = min->initValue();
VertexPathIterator path_iter(vertex, rf, path_ap, this);
while (path_iter.hasNext()) {
Path *path = path_iter.next();
Slack path_slack = path->slack(this);
if ((clk_edge == clk_edge_wildcard
|| path->clkEdge(search_) == clk_edge)
&& delayLess(path_slack, slack, this))
slack = path_slack;
}
return slack;
}
void
Sta::vertexSlacks(Vertex *vertex,
Slack (&slacks)[RiseFall::index_count][MinMax::index_count])
{
findRequired(vertex);
for(int rf_index : RiseFall::rangeIndex()) {
for(MinMax *min_max : MinMax::range()) {
slacks[rf_index][min_max->index()] = MinMax::min()->initValue();
}
}
VertexPathIterator path_iter(vertex, this);
while (path_iter.hasNext()) {
Path *path = path_iter.next();
Slack path_slack = path->slack(this);
int rf_index = path->rfIndex(this);
int mm_index = path->minMax(this)->index();
if (delayLess(path_slack, slacks[rf_index][mm_index], this))
slacks[rf_index][mm_index] = path_slack;
}
}
void
Sta::findRequired(Vertex *vertex)
{
searchPreamble();
search_->findAllArrivals();
search_->findRequireds(vertex->level());
if (sdc_->crprEnabled()
&& search_->crprPathPruningEnabled()
&& !search_->crprApproxMissingRequireds()
// Clocks invariably have requireds that are pruned but isn't
// worth finding arrivals and requireds all over again for
// the entire fanout of the clock.
&& !search_->isClock(vertex)
&& vertex->requiredsPruned()) {
// Invalidate arrivals and requireds and disable
// path pruning on fanout vertices with DFS.
int fanout = 0;
disableFanoutCrprPruning(vertex, fanout);
debugPrint(debug_, "search", 1, "resurrect pruned required %s fanout %d",
vertex->name(sdc_network_),
fanout);
// Find fanout arrivals and requireds with pruning disabled.
search_->findArrivals();
search_->findRequireds(vertex->level());
}
}
void
Sta::disableFanoutCrprPruning(Vertex *vertex,
int &fanout)
{
if (!vertex->crprPathPruningDisabled()) {
search_->arrivalInvalid(vertex);
search_->requiredInvalid(vertex);
vertex->setCrprPathPruningDisabled(true);
fanout++;
SearchPred *pred = search_->searchAdj();
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *to_vertex = edge->to(graph_);
if (pred->searchThru(edge)
&& pred->searchTo(to_vertex))
disableFanoutCrprPruning(to_vertex, fanout);
}
}
}
Slack
Sta::totalNegativeSlack(const MinMax *min_max)
{
searchPreamble();
return search_->totalNegativeSlack(min_max);
}
Slack
Sta::totalNegativeSlack(const Corner *corner,
const MinMax *min_max)
{
searchPreamble();
return search_->totalNegativeSlack(corner, min_max);
}
void
Sta::worstSlack(const MinMax *min_max,
// Return values.
Slack &worst_slack,
Vertex *&worst_vertex)
{
searchPreamble();
return search_->worstSlack(min_max, worst_slack, worst_vertex);
}
void
Sta::worstSlack(const Corner *corner,
const MinMax *min_max,
// Return values.
Slack &worst_slack,
Vertex *&worst_vertex)
{
searchPreamble();
return search_->worstSlack(corner, min_max, worst_slack, worst_vertex);
}
////////////////////////////////////////////////////////////////
string *
Sta::reportDelayCalc(Edge *edge,
TimingArc *arc,
const Corner *corner,
const MinMax *min_max,
int digits)
{
findDelays();
return graph_delay_calc_->reportDelayCalc(edge, arc, corner, min_max, digits);
}
void
Sta::setArcDelayCalc(const char *delay_calc_name)
{
delete arc_delay_calc_;
arc_delay_calc_ = makeDelayCalc(delay_calc_name, sta_);
// Update pointers to arc_delay_calc.
updateComponentsState();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
void
Sta::findDelays(Vertex *to_vertex)
{
delayCalcPreamble();
graph_delay_calc_->findDelays(to_vertex->level());
}
void
Sta::findDelays()
{
delayCalcPreamble();
graph_delay_calc_->findDelays(levelize_->maxLevel());
}
void
Sta::findDelays(Level level)
{
delayCalcPreamble();
graph_delay_calc_->findDelays(level);
}
void
Sta::delayCalcPreamble()
{
ensureClkNetwork();
}
void
Sta::setIncrementalDelayTolerance(float tol)
{
graph_delay_calc_->setIncrementalDelayTolerance(tol);
}
ArcDelay
Sta::arcDelay(Edge *edge,
TimingArc *arc,
const DcalcAnalysisPt *dcalc_ap)
{
findDelays(edge->to(graph_));
return graph_->arcDelay(edge, arc, dcalc_ap->index());
}
bool
Sta::arcDelayAnnotated(Edge *edge,
TimingArc *arc,
DcalcAnalysisPt *dcalc_ap)
{
return graph_->arcDelayAnnotated(edge, arc, dcalc_ap->index());
}
void
Sta::setArcDelayAnnotated(Edge *edge,
TimingArc *arc,
DcalcAnalysisPt *dcalc_ap,
bool annotated)
{
graph_->setArcDelayAnnotated(edge, arc, dcalc_ap->index(), annotated);
Vertex *to = edge->to(graph_);
search_->arrivalInvalid(to);
search_->requiredInvalid(edge->from(graph_));
if (!annotated)
graph_delay_calc_->delayInvalid(to);
}
Slew
Sta::vertexSlew(Vertex *vertex,
const RiseFall *rf,
const DcalcAnalysisPt *dcalc_ap)
{
findDelays(vertex);
return graph_->slew(vertex, rf, dcalc_ap->index());
}
Slew
Sta::vertexSlew(Vertex *vertex,
const RiseFall *rf,
const MinMax *min_max)
{
findDelays(vertex);
Slew mm_slew = min_max->initValue();
for (DcalcAnalysisPt *dcalc_ap : corners_->dcalcAnalysisPts()) {
Slew slew = graph_->slew(vertex, rf, dcalc_ap->index());
if (delayGreater(slew, mm_slew, min_max, this))
mm_slew = slew;
}
return mm_slew;
}
////////////////////////////////////////////////////////////////
Graph *
Sta::ensureGraph()
{
if (graph_ == nullptr && network_) {
makeGraph();
// Update pointers to graph.
updateComponentsState();
}
return graph_;
}
void
Sta::makeGraph()
{
graph_ = new Graph(this, 2, true, corners_->dcalcAnalysisPtCount());
graph_->makeGraph();
}
void
Sta::ensureLevelized()
{
ensureGraph();
ensureGraphSdcAnnotated();
// Need constant propagation before levelization to know edges that
// are disabled by constants.
sim_->ensureConstantsPropagated();
levelize_->ensureLevelized();
}
void
Sta::updateGeneratedClks()
{
if (update_genclks_) {
ensureLevelized();
bool gen_clk_changed = true;
while (gen_clk_changed) {
gen_clk_changed = false;
for (Clock *clk : sdc_->clks()) {
if (clk->isGenerated() && !clk->waveformValid()) {
search_->genclks()->ensureMaster(clk);
Clock *master_clk = clk->masterClk();
if (master_clk && master_clk->waveformValid()) {
clk->generate(master_clk);
gen_clk_changed = true;
}
}
}
}
}
update_genclks_ = false;
}
Level
Sta::vertexLevel(Vertex *vertex)
{
ensureLevelized();
return vertex->level();
}
GraphLoopSeq *
Sta::graphLoops()
{
ensureLevelized();
return levelize_->loops();
}
PathAnalysisPt *
Sta::pathAnalysisPt(Path *path)
{
return path->pathAnalysisPt(this);
}
DcalcAnalysisPt *
Sta::pathDcalcAnalysisPt(Path *path)
{
return pathAnalysisPt(path)->dcalcAnalysisPt();
}
Vertex *
Sta::maxArrivalCountVertex() const
{
Vertex *max_vertex = nullptr;
int max_count = 0;
VertexIterator vertex_iter(graph_);
while (vertex_iter.hasNext()) {
Vertex *vertex = vertex_iter.next();
int count = vertexArrivalCount(vertex);
if (count > max_count) {
max_count = count;
max_vertex = vertex;
}
}
return max_vertex;
}
int
Sta::vertexArrivalCount(Vertex *vertex) const
{
TagGroup *tag_group = search_->tagGroup(vertex);
if (tag_group)
return tag_group->arrivalCount();
else
return 0;
}
int
Sta::arrivalCount() const
{
int count = 0;
VertexIterator vertex_iter(graph_);
while (vertex_iter.hasNext()) {
Vertex *vertex = vertex_iter.next();
count += vertexArrivalCount(vertex);
}
return count;
}
TagIndex
Sta::tagCount() const
{
return search_->tagCount();
}
TagGroupIndex
Sta::tagGroupCount() const
{
return search_->tagGroupCount();
}
int
Sta::clkInfoCount() const
{
return search_->clkInfoCount();
}
void
Sta::setArcDelay(Edge *edge,
TimingArc *arc,
const Corner *corner,
const MinMaxAll *min_max,
ArcDelay delay)
{
for (MinMax *mm : min_max->range()) {
const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm);
DcalcAPIndex ap_index = dcalc_ap->index();
graph_->setArcDelay(edge, arc, ap_index, delay);
// Don't let delay calculation clobber the value.
graph_->setArcDelayAnnotated(edge, arc, ap_index, true);
}
if (edge->role()->isTimingCheck())
search_->requiredInvalid(edge->to(graph_));
else {
search_->arrivalInvalid(edge->to(graph_));
search_->requiredInvalid(edge->from(graph_));
}
}
void
Sta::setAnnotatedSlew(Vertex *vertex,
const Corner *corner,
const MinMaxAll *min_max,
const RiseFallBoth *rf,
float slew)
{
for (MinMax *mm : min_max->range()) {
const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(mm);
DcalcAPIndex ap_index = dcalc_ap->index();
for (RiseFall *rf1 : rf->range()) {
graph_->setSlew(vertex, rf1, ap_index, slew);
// Don't let delay calculation clobber the value.
vertex->setSlewAnnotated(true, rf1, ap_index);
}
}
graph_delay_calc_->delayInvalid(vertex);
}
void
Sta::writeSdf(const char *filename,
Corner *corner,
char sdf_divider,
int digits,
bool gzip,
bool no_timestamp,
bool no_version)
{
findDelays();
sta::writeSdf(filename, corner, sdf_divider, digits, gzip, no_timestamp,
no_version, this);
}
void
Sta::removeDelaySlewAnnotations()
{
graph_->removeDelaySlewAnnotations();
graph_delay_calc_->delaysInvalid();
}
LogicValue
Sta::simLogicValue(const Pin *pin)
{
ensureGraph();
sim_->ensureConstantsPropagated();
return sim_->logicValue(pin);
}
float
Sta::portExtPinCap(Port *port,
const RiseFall *rf,
const MinMax *min_max)
{
float pin_cap, wire_cap;
int fanout;
bool pin_exists, wire_exists, fanout_exists;
sdc_->portExtCap(port, rf, min_max,
pin_cap, pin_exists,
wire_cap, wire_exists,
fanout, fanout_exists);
if (pin_exists)
return pin_cap;
else
return 0.0;
}
void
Sta::setPortExtPinCap(Port *port,
const RiseFallBoth *rf,
const MinMaxAll *min_max,
float cap)
{
for (RiseFall *rf1 : rf->range()) {
for (MinMax *mm : min_max->range()) {
sdc_->setPortExtPinCap(port, rf1, mm, cap);
}
}
delaysInvalidFromFanin(port);
}
float
Sta::portExtWireCap(Port *port,
const RiseFall *rf,
const MinMax *min_max)
{
float pin_cap, wire_cap;
int fanout;
bool pin_exists, wire_exists, fanout_exists;
sdc_->portExtCap(port, rf, min_max,
pin_cap, pin_exists,
wire_cap, wire_exists,
fanout, fanout_exists);
if (wire_exists)
return wire_cap;
else
return 0.0;
}
void
Sta::setPortExtWireCap(Port *port,
bool subtract_pin_cap,
const RiseFallBoth *rf,
const MinMaxAll *min_max,
float cap)
{
Corner *corner = cmd_corner_;
for (RiseFall *rf1 : rf->range()) {
for (MinMax *mm : min_max->range()) {
sdc_->setPortExtWireCap(port, subtract_pin_cap, rf1, corner, mm, cap);
}
}
delaysInvalidFromFanin(port);
}
void
Sta::removeNetLoadCaps() const
{
sdc_->removeNetLoadCaps();
graph_delay_calc_->delaysInvalid();
}
int
Sta::portExtFanout(Port *port,
const MinMax *min_max)
{
return sdc_->portExtFanout(port, min_max);
}
void
Sta::setPortExtFanout(Port *port,
int fanout,
const MinMaxAll *min_max)
{
for (MinMax *mm : min_max->range())
sdc_->setPortExtFanout(port, mm, fanout);
delaysInvalidFromFanin(port);
}
void
Sta::setNetWireCap(Net *net,
bool subtract_pin_cap,
const Corner *corner,
const MinMaxAll *min_max,
float cap)
{
for (MinMax *mm : min_max->range())
sdc_->setNetWireCap(net, subtract_pin_cap, corner, mm, cap);
delaysInvalidFromFanin(net);
}
void
Sta::connectedCap(Pin *drvr_pin,
const RiseFall *rf,
const Corner *corner,
const MinMax *min_max,
float &pin_cap,
float &wire_cap) const
{
pin_cap = 0.0;
wire_cap = 0.0;
bool cap_exists = false;
const DcalcAnalysisPt *dcalc_ap = corner->findDcalcAnalysisPt(min_max);
Parasitic *parasitic = arc_delay_calc_->findParasitic(drvr_pin, rf, dcalc_ap);
float ap_pin_cap = 0.0;
float ap_wire_cap = 0.0;
graph_delay_calc_->loadCap(drvr_pin, parasitic, rf, dcalc_ap,
ap_pin_cap, ap_wire_cap);
arc_delay_calc_->finishDrvrPin();
if (!cap_exists
|| min_max->compare(ap_pin_cap, pin_cap)) {
pin_cap = ap_pin_cap;
wire_cap = ap_wire_cap;
cap_exists = true;
}
}
void
Sta::connectedCap(Net *net,
const RiseFall *rf,
const Corner *corner,
const MinMax *min_max,
float &pin_cap,
float &wire_cap) const
{
Pin *drvr_pin = findNetParasiticDrvrPin(net);
if (drvr_pin)
connectedCap(drvr_pin, rf, corner, min_max, pin_cap, wire_cap);
else {
pin_cap = 0.0;
wire_cap = 0.0;
}
}
// Look for a driver to find a parasitic if the net has one.
// Settle for a load pin if there are no drivers.
Pin *
Sta::findNetParasiticDrvrPin(Net *net) const
{
Pin *load_pin = nullptr;
NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
if (network_->isDriver(pin)) {
delete pin_iter;
return pin;
}
if (network_->isLoad(pin))
load_pin = pin;
}
delete pin_iter;
return load_pin;
}
void
Sta::setResistance(Net *net,
const MinMaxAll *min_max,
float res)
{
sdc_->setResistance(net, min_max, res);
}
////////////////////////////////////////////////////////////////
bool
Sta::readSpef(const char *filename,
Instance *instance,
const MinMaxAll *min_max,
bool increment,
bool pin_cap_included,
bool keep_coupling_caps,
float coupling_cap_factor,
ReducedParasiticType reduce_to,
bool delete_after_reduce,
bool quiet)
{
Corner *corner = cmd_corner_;
const MinMax *cnst_min_max;
ParasiticAnalysisPt *ap;
if (min_max == MinMaxAll::all()) {
corners_->makeParasiticAnalysisPtsSingle();
ap = corner->findParasiticAnalysisPt(MinMax::max());
cnst_min_max = MinMax::max();
}
else {
corners_->makeParasiticAnalysisPtsMinMax();
cnst_min_max = min_max->asMinMax();
ap = corner->findParasiticAnalysisPt(cnst_min_max);
}
const OperatingConditions *op_cond =
sdc_->operatingConditions(cnst_min_max);
bool success = readSpefFile(filename, instance, ap, increment,
pin_cap_included,
keep_coupling_caps, coupling_cap_factor,
reduce_to, delete_after_reduce,
op_cond, corner, cnst_min_max, quiet,
report_, network_, parasitics_);
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
return success;
}
void
Sta::findPiElmore(Pin *drvr_pin,
const RiseFall *rf,
const MinMax *min_max,
float &c2,
float &rpi,
float &c1,
bool &exists) const
{
Corner *corner = cmd_corner_;
const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max);
Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap);
if (pi_elmore) {
parasitics_->piModel(pi_elmore, c2, rpi, c1);
exists = true;
}
else
exists = false;
}
void
Sta::makePiElmore(Pin *drvr_pin,
const RiseFall *rf,
const MinMaxAll *min_max,
float c2,
float rpi,
float c1)
{
Corner *corner = cmd_corner_;
for (MinMax *mm : min_max->range()) {
ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm);
parasitics_->makePiElmore(drvr_pin, rf, ap, c2, rpi, c1);
}
delaysInvalidFrom(drvr_pin);
}
void
Sta::findElmore(Pin *drvr_pin,
Pin *load_pin,
const RiseFall *rf,
const MinMax *min_max,
float &elmore,
bool &exists) const
{
Corner *corner = cmd_corner_;
const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(min_max);
Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap);
if (pi_elmore) {
exists = false;
parasitics_->findElmore(pi_elmore, load_pin, elmore, exists);
}
else
exists = false;
}
void
Sta::setElmore(Pin *drvr_pin,
Pin *load_pin,
const RiseFall *rf,
const MinMaxAll *min_max,
float elmore)
{
Corner *corner = cmd_corner_;
for (MinMax *mm : min_max->range()) {
const ParasiticAnalysisPt *ap = corner->findParasiticAnalysisPt(mm);
Parasitic *pi_elmore = parasitics_->findPiElmore(drvr_pin, rf, ap);
if (pi_elmore)
parasitics_->setElmore(pi_elmore, load_pin, elmore);
}
delaysInvalidFrom(drvr_pin);
}
void
Sta::deleteParasitics()
{
parasitics_->deleteParasitics();
graph_delay_calc_->delaysInvalid();
search_->arrivalsInvalid();
}
////////////////////////////////////////////////////////////////
//
// Network edit commands.
//
// This implementation calls Sta before/after methods to
// update the Sta components.
// A different implementation may let the network edits
// call the before/after methods implicitly so these functions
// should not (Verific).
//
////////////////////////////////////////////////////////////////
NetworkEdit *
Sta::networkCmdEdit()
{
return dynamic_cast<NetworkEdit*>(cmd_network_);
}
Instance *
Sta::makeInstance(const char *name,
LibertyCell *cell,
Instance *parent)
{
NetworkEdit *network = networkCmdEdit();
Instance *inst = network->makeInstance(cell, name, parent);
network->makePins(inst);
makeInstanceAfter(inst);
return inst;
}
void
Sta::deleteInstance(Instance *inst)
{
NetworkEdit *network = networkCmdEdit();
deleteInstanceBefore(inst);
network->deleteInstance(inst);
}
void
Sta::replaceCell(Instance *inst,
LibertyCell *to_lib_cell)
{
Cell *to_cell = network_->cell(to_lib_cell);
replaceCell(inst, to_cell, to_lib_cell);
}
void
Sta::replaceCell(Instance *inst,
Cell *to_cell)
{
LibertyCell *to_lib_cell = network_->libertyCell(to_cell);
replaceCell(inst, to_cell, to_lib_cell);
}
void
Sta::replaceCell(Instance *inst,
Cell *to_cell,
LibertyCell *to_lib_cell)
{
NetworkEdit *network = networkCmdEdit();
LibertyCell *from_lib_cell = network->libertyCell(inst);
if (sta::equivCells(from_lib_cell, to_lib_cell)) {
replaceEquivCellBefore(inst, to_lib_cell);
network->replaceCell(inst, to_cell);
replaceEquivCellAfter(inst);
}
else {
replaceCellBefore(inst, to_lib_cell);
network->replaceCell(inst, to_cell);
replaceCellAfter(inst);
}
}
Net *
Sta::makeNet(const char *name,
Instance *parent)
{
NetworkEdit *network = networkCmdEdit();
Net *net = network->makeNet(name, parent);
// Sta notification unnecessary.
return net;
}
void
Sta::deleteNet(Net *net)
{
NetworkEdit *network = networkCmdEdit();
deleteNetBefore(net);
network->deleteNet(net);
}
void
Sta::connectPin(Instance *inst,
Port *port,
Net *net)
{
NetworkEdit *network = networkCmdEdit();
Pin *pin = network->connect(inst, port, net);
connectPinAfter(pin);
}
void
Sta::connectPin(Instance *inst,
LibertyPort *port,
Net *net)
{
NetworkEdit *network = networkCmdEdit();
Pin *pin = network->connect(inst, port, net);
connectPinAfter(pin);
}
void
Sta::disconnectPin(Pin *pin)
{
NetworkEdit *network = networkCmdEdit();
disconnectPinBefore(pin);
network->disconnectPin(pin);
}
////////////////////////////////////////////////////////////////
//
// Network edit before/after methods.
//
////////////////////////////////////////////////////////////////
void
Sta::makeInstanceAfter(Instance *inst)
{
// There is no user "connect_pin" called for internal pins,
// so call it implicitly.
LibertyCell *lib_cell = network_->libertyCell(inst);
if (lib_cell
&& lib_cell->hasInternalPorts()) {
LibertyCellPortBitIterator port_iter(lib_cell);
while (port_iter.hasNext()) {
LibertyPort *lib_port = port_iter.next();
if (lib_port->direction()->isInternal()) {
Pin *pin = network_->findPin(inst, lib_port);
if (pin)
connectPinAfter(pin);
}
}
}
}
// Not used by Sta (connectPinAfter).
void
Sta::makePinAfter(Pin *pin)
{
if (!network_->isHierarchical(pin) && graph_) {
Vertex *vertex, *bidir_drvr_vertex;
graph_->makePinVertices(pin, vertex, bidir_drvr_vertex);
graph_->makePinInstanceEdges(pin);
search_->arrivalInvalid(vertex);
search_->requiredInvalid(vertex);
if (bidir_drvr_vertex) {
search_->arrivalInvalid(bidir_drvr_vertex);
search_->requiredInvalid(bidir_drvr_vertex);
}
if (network_->net(pin))
connectPinAfter(pin);
}
sim_->makePinAfter(pin);
}
void
Sta::replaceEquivCellBefore(Instance *inst,
LibertyCell *to_cell)
{
if (graph_) {
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
LibertyPort *port = network_->libertyPort(pin);
if (port->direction()->isAnyInput()) {
Vertex *vertex = graph_->pinLoadVertex(pin);
replaceCellPinInvalidate(port, vertex, to_cell);
// Replace the timing arc sets in the graph edges.
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *to_vertex = edge->to(graph_);
if (network_->instance(to_vertex->pin()) == inst) {
TimingArcSet *from_set = edge->timingArcSet();
// Find corresponding timing arc set.
TimingArcSet *to_set = to_cell->findTimingArcSet(from_set);
if (to_set)
edge->setTimingArcSet(to_set);
else
report_->critical(264, "corresponding timing arc set not found in equiv cells");
}
}
}
else {
// Force delay calculation on output pins.
Vertex *vertex = graph_->pinDrvrVertex(pin);
graph_delay_calc_->delayInvalid(vertex);
}
}
delete pin_iter;
}
}
void
Sta::replaceEquivCellAfter(Instance *inst)
{
if (graph_) {
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
if (network_->direction(pin)->isAnyInput())
parasitics_->loadPinCapacitanceChanged(pin);
}
delete pin_iter;
}
}
void
Sta::replaceCellPinInvalidate(LibertyPort *from_port,
Vertex *vertex,
LibertyCell *to_cell)
{
LibertyPort *to_port = to_cell->findLibertyPort(from_port->name());
if (!libertyPortCapsEqual(to_port, from_port)
// If this is an ideal clock pin, do not invalidate
// arrivals and delay calc on the clock pin driver.
&& !(to_port->isClock()
&& idealClockMode()))
// Input port capacitance changed, so invalidate delay
// calculation from input driver.
delaysInvalidFromFanin(vertex);
else
delaysInvalidFrom(vertex);
}
bool
Sta::idealClockMode()
{
for (Clock *clk : sdc_->clks()) {
if (clk->isPropagated())
return false;
}
return true;
}
static bool
libertyPortCapsEqual(LibertyPort *port1,
LibertyPort *port2)
{
return port1->capacitance(RiseFall::rise(), MinMax::min())
== port2->capacitance(RiseFall::rise(), MinMax::min())
&& port1->capacitance(RiseFall::rise(), MinMax::max())
== port2->capacitance(RiseFall::rise(), MinMax::max())
&& port1->capacitance(RiseFall::fall(), MinMax::min())
== port2->capacitance(RiseFall::fall(), MinMax::min())
&& port1->capacitance(RiseFall::fall(), MinMax::max())
== port2->capacitance(RiseFall::fall(), MinMax::max());
}
void
Sta::replaceCellBefore(Instance *inst,
LibertyCell *to_cell)
{
if (graph_) {
// Delete all graph edges between instance pins.
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
LibertyPort *port = network_->libertyPort(pin);
if (port->direction()->isAnyInput()) {
Vertex *vertex = graph_->pinLoadVertex(pin);
replaceCellPinInvalidate(port, vertex, to_cell);
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *to_vertex = edge->to(graph_);
if (network_->instance(to_vertex->pin()) == inst)
deleteEdge(edge);
}
}
}
delete pin_iter;
}
}
void
Sta::replaceCellAfter(Instance *inst)
{
if (graph_) {
graph_->makeInstanceEdges(inst);
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
sim_->pinSetFuncAfter(pin);
if (network_->direction(pin)->isAnyInput())
parasitics_->loadPinCapacitanceChanged(pin);
}
delete pin_iter;
}
}
void
Sta::connectPinAfter(Pin *pin)
{
if (graph_) {
if (network_->isHierarchical(pin)) {
graph_->makeWireEdgesThruPin(pin);
EdgesThruHierPinIterator edge_iter(pin, network_, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->role()->isWire()) {
connectDrvrPinAfter(edge->from(graph_));
connectLoadPinAfter(edge->to(graph_));
}
}
}
else {
Vertex *vertex, *bidir_drvr_vertex;
if (network_->vertexId(pin) == vertex_id_null) {
graph_->makePinVertices(pin, vertex, bidir_drvr_vertex);
graph_->makePinInstanceEdges(pin);
}
else
graph_->pinVertices(pin, vertex, bidir_drvr_vertex);
search_->arrivalInvalid(vertex);
search_->requiredInvalid(vertex);
if (bidir_drvr_vertex) {
search_->arrivalInvalid(bidir_drvr_vertex);
search_->requiredInvalid(bidir_drvr_vertex);
}
// Make interconnect edges from/to pin.
if (network_->isDriver(pin)) {
graph_->makeWireEdgesFromPin(pin);
connectDrvrPinAfter(bidir_drvr_vertex ? bidir_drvr_vertex : vertex);
}
// Note that a bidirect is both a driver and a load so this
// is NOT an else clause for the above "if".
if (network_->isLoad(pin)) {
graph_->makeWireEdgesToPin(pin);
connectLoadPinAfter(vertex);
}
}
}
sdc_->connectPinAfter(pin);
sim_->connectPinAfter(pin);
}
void
Sta::connectDrvrPinAfter(Vertex *vertex)
{
// Invalidate arrival at fanout vertices.
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *to_vertex = edge->to(graph_);
search_->arrivalInvalid(to_vertex);
search_->endpointInvalid(to_vertex);
sdc_->clkHpinDisablesChanged(to_vertex->pin());
}
Pin *pin = vertex->pin();
sdc_->clkHpinDisablesChanged(pin);
graph_delay_calc_->delayInvalid(vertex);
search_->requiredInvalid(vertex);
search_->endpointInvalid(vertex);
levelize_->invalidFrom(vertex);
clk_network_->connectPinAfter(pin);
}
void
Sta::connectLoadPinAfter(Vertex *vertex)
{
// Invalidate delays and required at fanin vertices.
VertexInEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *from_vertex = edge->from(graph_);
graph_delay_calc_->delayInvalid(from_vertex);
search_->requiredInvalid(from_vertex);
sdc_->clkHpinDisablesChanged(from_vertex->pin());
}
Pin *pin = vertex->pin();
sdc_->clkHpinDisablesChanged(pin);
graph_delay_calc_->delayInvalid(vertex);
levelize_->invalidFrom(vertex);
search_->arrivalInvalid(vertex);
search_->endpointInvalid(vertex);
clk_network_->connectPinAfter(pin);
}
void
Sta::disconnectPinBefore(Pin *pin)
{
parasitics_->disconnectPinBefore(pin);
sdc_->disconnectPinBefore(pin);
sim_->disconnectPinBefore(pin);
if (graph_) {
if (network_->isDriver(pin)) {
Vertex *vertex = graph_->pinDrvrVertex(pin);
// Delete wire edges from pin.
if (vertex) {
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->role()->isWire())
deleteEdge(edge);
}
clk_network_->disconnectPinBefore(pin);
}
}
if (network_->isLoad(pin)) {
// Delete wire edges to pin.
Vertex *vertex = graph_->pinLoadVertex(pin);
if (vertex) {
VertexInEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->role()->isWire())
deleteEdge(edge);
}
clk_network_->disconnectPinBefore(pin);
}
}
if (network_->isHierarchical(pin)) {
// Delete wire edges thru pin.
EdgesThruHierPinIterator edge_iter(pin, network_, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->role()->isWire()) {
deleteEdge(edge);
clk_network_->disconnectPinBefore(edge->from(graph_)->pin());
}
}
}
}
}
void
Sta::deleteEdge(Edge *edge)
{
Vertex *from = edge->from(graph_);
Vertex *to = edge->to(graph_);
search_->arrivalInvalid(to);
search_->requiredInvalid(from);
graph_delay_calc_->delayInvalid(to);
levelize_->relevelizeFrom(to);
levelize_->deleteEdgeBefore(edge);
sdc_->clkHpinDisablesChanged(edge->from(graph_)->pin());
graph_->deleteEdge(edge);
}
void
Sta::deleteNetBefore(Net *net)
{
if (graph_) {
NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
if (!network_->isHierarchical(pin)) {
disconnectPinBefore(pin);
// Delete wire edges on net pins.
Vertex *vertex = graph_->pinDrvrVertex(pin);
if (vertex) {
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->role()->isWire())
deleteEdge(edge);
}
}
}
}
delete pin_iter;
}
sdc_->deleteNetBefore(net);
}
void
Sta::deleteInstanceBefore(Instance *inst)
{
if (network_->isLeaf(inst)) {
deleteInstancePinsBefore(inst);
deleteLeafInstanceBefore(inst);
}
else {
// Delete hierarchical instance children.
InstanceChildIterator *child_iter = network_->childIterator(inst);
while (child_iter->hasNext()) {
Instance *child = child_iter->next();
deleteInstanceBefore(child);
}
delete child_iter;
}
}
void
Sta::deleteLeafInstanceBefore(Instance *inst)
{
sim_->deleteInstanceBefore(inst);
}
void
Sta::deleteInstancePinsBefore(Instance *inst)
{
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
deletePinBefore(pin);
}
delete pin_iter;
}
void
Sta::deletePinBefore(Pin *pin)
{
if (graph_) {
if (network_->isLoad(pin)) {
Vertex *vertex = graph_->pinLoadVertex(pin);
if (vertex) {
levelize_->deleteVertexBefore(vertex);
graph_delay_calc_->deleteVertexBefore(vertex);
search_->deleteVertexBefore(vertex);
VertexInEdgeIterator in_edge_iter(vertex, graph_);
while (in_edge_iter.hasNext()) {
Edge *edge = in_edge_iter.next();
if (edge->role()->isWire()) {
Vertex *from = edge->from(graph_);
// Only notify from vertex (to vertex will be deleted).
search_->requiredInvalid(from);
}
levelize_->deleteEdgeBefore(edge);
}
graph_->deleteVertex(vertex);
}
}
if (network_->isDriver(pin)) {
Vertex *vertex = graph_->pinDrvrVertex(pin);
if (vertex) {
levelize_->deleteVertexBefore(vertex);
graph_delay_calc_->deleteVertexBefore(vertex);
search_->deleteVertexBefore(vertex);
VertexOutEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
if (edge->role()->isWire()) {
// Only notify to vertex (from will be deleted).
Vertex *to = edge->to(graph_);
// to->prev_paths point to vertex, so delete them.
search_->arrivalInvalidDelete(to);
graph_delay_calc_->delayInvalid(to);
levelize_->relevelizeFrom(to);
}
levelize_->deleteEdgeBefore(edge);
}
graph_->deleteVertex(vertex);
}
}
if (network_->direction(pin) == PortDirection::internal()) {
// Internal pins are not loads or drivers.
Vertex *vertex = graph_->pinLoadVertex(pin);
if (vertex) {
levelize_->deleteVertexBefore(vertex);
graph_delay_calc_->deleteVertexBefore(vertex);
search_->deleteVertexBefore(vertex);
graph_->deleteVertex(vertex);
}
}
}
sim_->deletePinBefore(pin);
clk_network_->deletePinBefore(pin);
}
void
Sta::delaysInvalidFrom(Port *port)
{
if (graph_) {
Instance *top_inst = network_->topInstance();
Pin *pin = network_->findPin(top_inst, port);
delaysInvalidFrom(pin);
}
}
void
Sta::delaysInvalidFrom(Instance *inst)
{
if (graph_) {
InstancePinIterator *pin_iter = network_->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
delaysInvalidFrom(pin);
}
delete pin_iter;
}
}
void
Sta::delaysInvalidFrom(Pin *pin)
{
if (graph_) {
Vertex *vertex, *bidirect_drvr_vertex;
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
delaysInvalidFrom(vertex);
if (bidirect_drvr_vertex)
delaysInvalidFrom(bidirect_drvr_vertex);
}
}
void
Sta::delaysInvalidFrom(Vertex *vertex)
{
search_->arrivalInvalid(vertex);
search_->requiredInvalid(vertex);
graph_delay_calc_->delayInvalid(vertex);
}
void
Sta::delaysInvalidFromFanin(Port *port)
{
if (graph_) {
Instance *top_inst = network_->topInstance();
Pin *pin = network_->findPin(top_inst, port);
Vertex *vertex, *bidirect_drvr_vertex;
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
delaysInvalidFromFanin(vertex);
if (bidirect_drvr_vertex)
delaysInvalidFromFanin(bidirect_drvr_vertex);
}
}
void
Sta::delaysInvalidFromFanin(Pin *pin)
{
if (graph_) {
Vertex *vertex, *bidirect_drvr_vertex;
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
if (vertex)
delaysInvalidFromFanin(vertex);
if (bidirect_drvr_vertex)
delaysInvalidFromFanin(bidirect_drvr_vertex);
}
}
void
Sta::delaysInvalidFromFanin(Net *net)
{
if (graph_) {
NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
if (!network_->isHierarchical(pin)) {
Vertex *vertex, *bidirect_drvr_vertex;
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
if (vertex)
delaysInvalidFrom(vertex);
if (bidirect_drvr_vertex)
delaysInvalidFrom(bidirect_drvr_vertex);
}
}
delete pin_iter;
}
}
void
Sta::delaysInvalidFromFanin(Vertex *vertex)
{
VertexInEdgeIterator edge_iter(vertex, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *from_vertex = edge->from(graph_);
delaysInvalidFrom(from_vertex);
search_->requiredInvalid(from_vertex);
}
}
////////////////////////////////////////////////////////////////
void
Sta::clocks(const Pin *pin,
// Return value.
ClockSet &clks)
{
ensureClkArrivals();
search_->clocks(pin, clks);
}
////////////////////////////////////////////////////////////////
InstanceSet *
Sta::findRegisterInstances(ClockSet *clks,
const RiseFallBoth *clk_rf,
bool edge_triggered,
bool latches)
{
findRegisterPreamble();
return findRegInstances(clks, clk_rf, edge_triggered, latches, this);
}
PinSet *
Sta::findRegisterDataPins(ClockSet *clks,
const RiseFallBoth *clk_rf,
bool edge_triggered,
bool latches)
{
findRegisterPreamble();
return findRegDataPins(clks, clk_rf, edge_triggered, latches, this);
}
PinSet *
Sta::findRegisterClkPins(ClockSet *clks,
const RiseFallBoth *clk_rf,
bool edge_triggered,
bool latches)
{
findRegisterPreamble();
return findRegClkPins(clks, clk_rf, edge_triggered, latches, this);
}
PinSet *
Sta::findRegisterAsyncPins(ClockSet *clks,
const RiseFallBoth *clk_rf,
bool edge_triggered,
bool latches)
{
findRegisterPreamble();
return findRegAsyncPins(clks, clk_rf, edge_triggered, latches, this);
}
PinSet *
Sta::findRegisterOutputPins(ClockSet *clks,
const RiseFallBoth *clk_rf,
bool edge_triggered,
bool latches)
{
findRegisterPreamble();
return findRegOutputPins(clks, clk_rf, edge_triggered, latches, this);
}
void
Sta::findRegisterPreamble()
{
ensureGraph();
ensureGraphSdcAnnotated();
sim_->ensureConstantsPropagated();
}
////////////////////////////////////////////////////////////////
class FanInOutSrchPred : public SearchPred
{
public:
FanInOutSrchPred(bool thru_disabled,
bool thru_constants,
const StaState *sta);
virtual bool searchFrom(const Vertex *from_vertex);
virtual bool searchThru(Edge *edge);
virtual bool searchTo(const Vertex *to_vertex);
protected:
bool crossesHierarchy(Edge *edge);
virtual bool searchThruRole(Edge *edge);
bool thru_disabled_;
bool thru_constants_;
const StaState *sta_;
};
FanInOutSrchPred::FanInOutSrchPred(bool thru_disabled,
bool thru_constants,
const StaState *sta) :
SearchPred(),
thru_disabled_(thru_disabled),
thru_constants_(thru_constants),
sta_(sta)
{
}
bool
FanInOutSrchPred::searchFrom(const Vertex *from_vertex)
{
return (thru_disabled_
|| !from_vertex->isDisabledConstraint())
&& (thru_constants_
|| !from_vertex->isConstant());
}
bool
FanInOutSrchPred::searchThru(Edge *edge)
{
const Sdc *sdc = sta_->sdc();
return searchThruRole(edge)
&& (thru_disabled_
|| !(edge->isDisabledConstraint()
|| edge->isDisabledCond()
|| sdc->isDisabledCondDefault(edge)))
&& (thru_constants_
|| edge->simTimingSense() != TimingSense::none);
}
bool
FanInOutSrchPred::searchThruRole(Edge *edge)
{
TimingRole *role = edge->role();
return role == TimingRole::wire()
|| role == TimingRole::combinational()
|| role == TimingRole::tristateEnable()
|| role == TimingRole::tristateDisable();
}
bool
FanInOutSrchPred::crossesHierarchy(Edge *edge)
{
Network *network = sta_->network();
Graph *graph = sta_->graph();
Vertex *from = edge->from(graph);
Vertex *to = edge->to(graph);
Instance *from_inst = network->instance(from->pin());
Instance *to_inst = network->instance(to->pin());
return network->parent(from_inst) != network->parent(to_inst);
}
bool
FanInOutSrchPred::searchTo(const Vertex *to_vertex)
{
return (thru_disabled_
|| !to_vertex->isDisabledConstraint())
&& (thru_constants_
|| !to_vertex->isConstant());
}
class FaninSrchPred : public FanInOutSrchPred
{
public:
FaninSrchPred(bool thru_disabled,
bool thru_constants,
const StaState *sta);
protected:
virtual bool searchThruRole(Edge *edge);
};
FaninSrchPred::FaninSrchPred(bool thru_disabled,
bool thru_constants,
const StaState *sta) :
FanInOutSrchPred(thru_disabled, thru_constants, sta)
{
}
bool
FaninSrchPred::searchThruRole(Edge *edge)
{
TimingRole *role = edge->role();
return role == TimingRole::wire()
|| role == TimingRole::combinational()
|| role == TimingRole::tristateEnable()
|| role == TimingRole::tristateDisable()
|| role == TimingRole::regClkToQ()
|| role == TimingRole::latchEnToQ();
}
PinSet *
Sta::findFaninPins(PinSeq *to,
bool flat,
bool startpoints_only,
int inst_levels,
int pin_levels,
bool thru_disabled,
bool thru_constants)
{
ensureGraph();
ensureLevelized();
PinSet *fanin = new PinSet;
FaninSrchPred pred(thru_disabled, thru_constants, this);
PinSeq::Iterator to_iter(to);
while (to_iter.hasNext()) {
Pin *pin = to_iter.next();
if (network_->isHierarchical(pin)) {
EdgesThruHierPinIterator edge_iter(pin, network_, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
findFaninPins(edge->from(graph_), flat, startpoints_only,
inst_levels, pin_levels, fanin, pred);
}
}
else {
Vertex *vertex = graph_->pinLoadVertex(pin);
findFaninPins(vertex, flat, startpoints_only,
inst_levels, pin_levels, fanin, pred);
}
}
return fanin;
}
void
Sta::findFaninPins(Vertex *vertex,
bool flat,
bool startpoints_only,
int inst_levels,
int pin_levels,
PinSet *fanin,
SearchPred &pred)
{
VertexSet visited;
findFaninPins(vertex, flat, inst_levels,
pin_levels, visited, &pred, 0, 0);
VertexSet::Iterator visited_iter(visited);
while (visited_iter.hasNext()) {
Vertex *visited_vertex = visited_iter.next();
Pin *visited_pin = visited_vertex->pin();
if (!startpoints_only
|| network_->isRegClkPin(visited_pin)
|| !hasFanin(visited_vertex, &pred, graph_))
fanin->insert(visited_pin);
}
}
void
Sta::findFaninPins(Vertex *to,
bool flat,
int inst_levels,
int pin_levels,
VertexSet &visited,
SearchPred *pred,
int inst_level,
int pin_level)
{
debugPrint(debug_, "fanin", 1, "%s",
to->name(sdc_network_));
if (!visited.hasKey(to)) {
visited.insert(to);
Pin *to_pin = to->pin();
bool is_reg_clk_pin = network_->isRegClkPin(to_pin);
if (!is_reg_clk_pin
&& (inst_levels <= 0
|| inst_level < inst_levels)
&& (pin_levels <= 0
|| pin_level < pin_levels)
&& pred->searchTo(to)) {
VertexInEdgeIterator edge_iter(to, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *from_vertex = edge->from(graph_);
if (pred->searchThru(edge)
&& (flat
|| !crossesHierarchy(edge))
&& pred->searchFrom(from_vertex)) {
findFaninPins(from_vertex, flat, inst_levels,
pin_levels, visited, pred,
edge->role()->isWire() ? inst_level : inst_level+1,
pin_level+1);
}
}
}
}
}
InstanceSet *
Sta::findFaninInstances(PinSeq *to,
bool flat,
bool startpoints_only,
int inst_levels,
int pin_levels,
bool thru_disabled,
bool thru_constants)
{
PinSet *pins = findFaninPins(to, flat, startpoints_only, inst_levels,
pin_levels, thru_disabled, thru_constants);
return pinInstances(pins, network_);
}
PinSet *
Sta::findFanoutPins(PinSeq *from,
bool flat,
bool endpoints_only,
int inst_levels,
int pin_levels,
bool thru_disabled,
bool thru_constants)
{
ensureGraph();
ensureLevelized();
PinSet *fanout = new PinSet;
FanInOutSrchPred pred(thru_disabled, thru_constants, this);
PinSeq::Iterator from_iter(from);
while (from_iter.hasNext()) {
Pin *pin = from_iter.next();
if (network_->isHierarchical(pin)) {
EdgesThruHierPinIterator edge_iter(pin, network_, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
findFanoutPins(edge->to(graph_), flat, endpoints_only,
inst_levels, pin_levels, fanout, pred);
}
}
else {
Vertex *vertex = graph_->pinDrvrVertex(pin);
findFanoutPins(vertex, flat, endpoints_only,
inst_levels, pin_levels, fanout, pred);
}
}
return fanout;
}
void
Sta::findFanoutPins(Vertex *vertex,
bool flat,
bool endpoints_only,
int inst_levels,
int pin_levels,
PinSet *fanout,
SearchPred &pred)
{
VertexSet visited;
findFanoutPins(vertex, flat, inst_levels,
pin_levels, visited, &pred, 0, 0);
VertexSet::Iterator visited_iter(visited);
while (visited_iter.hasNext()) {
Vertex *visited_vertex = visited_iter.next();
Pin *visited_pin = visited_vertex->pin();
if (!endpoints_only
|| search_->isEndpoint(visited_vertex, &pred))
fanout->insert(visited_pin);
}
}
// DFS to support level limits.
void
Sta::findFanoutPins(Vertex *from,
bool flat,
int inst_levels,
int pin_levels,
VertexSet &visited,
SearchPred *pred,
int inst_level,
int pin_level)
{
debugPrint(debug_, "fanout", 1, "%s",
from->name(sdc_network_));
if (!visited.hasKey(from)) {
visited.insert(from);
if (!search_->isEndpoint(from, pred)
&& (inst_levels <= 0
|| inst_level < inst_levels)
&& (pin_levels <= 0
|| pin_level < pin_levels)
&& pred->searchFrom(from)) {
VertexOutEdgeIterator edge_iter(from, graph_);
while (edge_iter.hasNext()) {
Edge *edge = edge_iter.next();
Vertex *to_vertex = edge->to(graph_);
if (pred->searchThru(edge)
&& (flat
|| !crossesHierarchy(edge))
&& pred->searchTo(to_vertex)) {
findFanoutPins(to_vertex, flat, inst_levels,
pin_levels, visited, pred,
edge->role()->isWire() ? inst_level : inst_level+1,
pin_level+1);
}
}
}
}
}
InstanceSet *
Sta::findFanoutInstances(PinSeq *from,
bool flat,
bool endpoints_only,
int inst_levels,
int pin_levels,
bool thru_disabled,
bool thru_constants)
{
PinSet *pins = findFanoutPins(from, flat, endpoints_only, inst_levels,
pin_levels, thru_disabled, thru_constants);
return pinInstances(pins, network_);
}
static InstanceSet *
pinInstances(PinSet *pins,
const Network *network)
{
InstanceSet *insts = new InstanceSet;
PinSet::Iterator pin_iter(pins);
while (pin_iter.hasNext()) {
Pin *pin = pin_iter.next();
insts->insert(network->instance(pin));
}
delete pins;
return insts;
}
bool
Sta::crossesHierarchy(Edge *edge) const
{
Vertex *from = edge->from(graph_);
Vertex *to = edge->to(graph_);
const Pin *from_pin = from->pin();
Instance *from_inst = network_->instance(from_pin);
Instance *to_inst = network_->instance(to->pin());
Instance *from_parent, *to_parent;
// Treat input/output port pins as "inside".
if (network_->isTopInstance(from_inst))
from_parent = from_inst;
else
from_parent = network_->parent(from_inst);
if (network_->isTopInstance(to_inst))
to_parent = to_inst;
else
to_parent = network_->parent(to_inst);
return from_parent != to_parent;
}
////////////////////////////////////////////////////////////////
class InstanceMaxSlewGreater
{
public:
explicit InstanceMaxSlewGreater(const StaState *sta);
bool operator()(const Instance *inst1,
const Instance *inst2) const;
protected:
Slew instMaxSlew(const Instance *inst) const;
const StaState *sta_;
};
InstanceMaxSlewGreater::InstanceMaxSlewGreater(const StaState *sta) :
sta_(sta)
{
}
bool
InstanceMaxSlewGreater::operator()(const Instance *inst1,
const Instance *inst2) const
{
return delayGreater(instMaxSlew(inst1), instMaxSlew(inst2), sta_);
}
Slew
InstanceMaxSlewGreater::instMaxSlew(const Instance *inst) const
{
Network *network = sta_->network();
Graph *graph = sta_->graph();
Slew max_slew = 0.0;
InstancePinIterator *pin_iter = network->pinIterator(inst);
while (pin_iter->hasNext()) {
Pin *pin = pin_iter->next();
if (network->isDriver(pin)) {
Vertex *vertex = graph->pinDrvrVertex(pin);
for (RiseFall *rf : RiseFall::range()) {
for (DcalcAnalysisPt *dcalc_ap : sta_->corners()->dcalcAnalysisPts()) {
Slew slew = graph->slew(vertex, rf, dcalc_ap->index());
if (delayGreater(slew, max_slew, sta_))
max_slew = slew;
}
}
}
}
delete pin_iter;
return max_slew;
}
SlowDrvrIterator *
Sta::slowDrvrIterator()
{
InstanceSeq *insts = new InstanceSeq;
LeafInstanceIterator *leaf_iter = network_->leafInstanceIterator();
while (leaf_iter->hasNext()) {
Instance *leaf = leaf_iter->next();
insts->push_back(leaf);
}
delete leaf_iter;
sort(insts, InstanceMaxSlewGreater(this));
return new SlowDrvrIterator(insts);
}
////////////////////////////////////////////////////////////////
void
Sta::checkSlewLimitPreamble()
{
if (sdc_->haveClkSlewLimits())
// Arrivals are needed to know pin clock domains.
updateTiming(false);
else
findDelays();
if (check_slew_limits_ == nullptr)
makeCheckSlewLimits();
ensureClkNetwork();
}
Pin *
Sta::pinMinSlewLimitSlack(const Corner *corner,
const MinMax *min_max)
{
checkSlewLimitPreamble();
return check_slew_limits_->pinMinSlewLimitSlack(corner, min_max);
}
PinSeq *
Sta::pinSlewLimitViolations(const Corner *corner,
const MinMax *min_max)
{
checkSlewLimitPreamble();
return check_slew_limits_->pinSlewLimitViolations(corner, min_max);
}
void
Sta::reportSlewLimitShortHeader()
{
report_path_->reportLimitShortHeader(report_path_->fieldSlew());
}
void
Sta::reportSlewLimitShort(Pin *pin,
const Corner *corner,
const MinMax *min_max)
{
const Corner *corner1;
const RiseFall *rf;
Slew slew;
float limit, slack;
check_slew_limits_->checkSlew(pin, corner, min_max, true,
corner1, rf, slew, limit, slack);
report_path_->reportLimitShort(report_path_->fieldSlew(), pin,
delayAsFloat(slew), limit, slack);
}
void
Sta::reportSlewLimitVerbose(Pin *pin,
const Corner *corner,
const MinMax *min_max)
{
const Corner *corner1;
const RiseFall *rf;
Slew slew;
float limit, slack;
check_slew_limits_->checkSlew(pin, corner, min_max, true,
corner1, rf, slew, limit, slack);
report_path_->reportLimitVerbose(report_path_->fieldSlew(), pin, rf,
delayAsFloat(slew),
limit, slack, min_max);
}
void
Sta::checkSlew(const Pin *pin,
const Corner *corner,
const MinMax *min_max,
bool check_clks,
// Return values.
const Corner *&corner1,
const RiseFall *&rf,
Slew &slew,
float &limit,
float &slack)
{
check_slew_limits_->checkSlew(pin, corner, min_max, check_clks,
corner1, rf, slew, limit, slack);
}
////////////////////////////////////////////////////////////////'
void
Sta::checkFanoutLimitPreamble()
{
if (check_fanout_limits_ == nullptr)
makeCheckFanoutLimits();
ensureClkNetwork();
}
Pin *
Sta::pinMinFanoutLimitSlack(const MinMax *min_max)
{
checkFanoutLimitPreamble();
return check_fanout_limits_->pinMinFanoutLimitSlack(min_max);
}
PinSeq *
Sta::pinFanoutLimitViolations(const MinMax *min_max)
{
checkFanoutLimitPreamble();
return check_fanout_limits_->pinFanoutLimitViolations(min_max);
}
void
Sta::reportFanoutLimitShortHeader()
{
report_path_->reportLimitShortHeader(report_path_->fieldFanout());
}
void
Sta::reportFanoutLimitShort(Pin *pin,
const MinMax *min_max)
{
float fanout, limit, slack;
check_fanout_limits_->checkFanout(pin, min_max,
fanout, limit, slack);
report_path_->reportLimitShort(report_path_->fieldFanout(),
pin, fanout, limit, slack);
}
void
Sta::reportFanoutLimitVerbose(Pin *pin,
const MinMax *min_max)
{
float fanout, limit, slack;
check_fanout_limits_->checkFanout(pin, min_max,
fanout, limit, slack);
report_path_->reportLimitVerbose(report_path_->fieldFanout(),
pin, nullptr, fanout,
limit, slack, min_max);
}
void
Sta::checkFanout(const Pin *pin,
const MinMax *min_max,
// Return values.
float &fanout,
float &limit,
float &slack)
{
check_fanout_limits_->checkFanout(pin, min_max,
fanout, limit, slack);
}
////////////////////////////////////////////////////////////////'
void
Sta::checkCapacitanceLimitPreamble()
{
if (check_capacitance_limits_ == nullptr)
makeCheckCapacitanceLimits();
ensureClkNetwork();
}
Pin *
Sta::pinMinCapacitanceLimitSlack(const Corner *corner,
const MinMax *min_max)
{
checkCapacitanceLimitPreamble();
return check_capacitance_limits_->pinMinCapacitanceLimitSlack(corner, min_max);
}
PinSeq *
Sta::pinCapacitanceLimitViolations(const Corner *corner,
const MinMax *min_max)
{
checkCapacitanceLimitPreamble();
return check_capacitance_limits_->pinCapacitanceLimitViolations(corner, min_max);
}
void
Sta::reportCapacitanceLimitShortHeader()
{
report_path_->reportLimitShortHeader(report_path_->fieldCapacitance());
}
void
Sta::reportCapacitanceLimitShort(Pin *pin,
const Corner *corner,
const MinMax *min_max)
{
const Corner *corner1;
const RiseFall *rf;
float capacitance, limit, slack;
check_capacitance_limits_->checkCapacitance(pin, corner, min_max,
corner1, rf, capacitance,
limit, slack);
report_path_->reportLimitShort(report_path_->fieldCapacitance(),
pin, capacitance, limit, slack);
}
void
Sta::reportCapacitanceLimitVerbose(Pin *pin,
const Corner *corner,
const MinMax *min_max)
{
const Corner *corner1;
const RiseFall *rf;
float capacitance, limit, slack;
check_capacitance_limits_->checkCapacitance(pin, corner, min_max,
corner1, rf, capacitance,
limit, slack);
report_path_->reportLimitVerbose(report_path_->fieldCapacitance(),
pin, rf, capacitance,
limit, slack, min_max);
}
void
Sta::checkCapacitance(const Pin *pin,
const Corner *corner,
const MinMax *min_max,
// Return values.
const Corner *&corner1,
const RiseFall *&rf,
float &capacitance,
float &limit,
float &slack)
{
check_capacitance_limits_->checkCapacitance(pin, corner, min_max,
corner1, rf, capacitance,
limit, slack);
}
////////////////////////////////////////////////////////////////
void
Sta::minPulseWidthPreamble()
{
ensureClkArrivals();
if (check_min_pulse_widths_ == nullptr)
makeCheckMinPulseWidths();
}
MinPulseWidthCheckSeq &
Sta::minPulseWidthChecks(PinSeq *pins,
const Corner *corner)
{
minPulseWidthPreamble();
return check_min_pulse_widths_->check(pins, corner);
}
MinPulseWidthCheckSeq &
Sta::minPulseWidthChecks(const Corner *corner)
{
minPulseWidthPreamble();
return check_min_pulse_widths_->check(corner);
}
MinPulseWidthCheckSeq &
Sta::minPulseWidthViolations(const Corner *corner)
{
minPulseWidthPreamble();
return check_min_pulse_widths_->violations(corner);
}
MinPulseWidthCheck *
Sta::minPulseWidthSlack(const Corner *corner)
{
minPulseWidthPreamble();
return check_min_pulse_widths_->minSlackCheck(corner);
}
void
Sta::reportMpwChecks(MinPulseWidthCheckSeq *checks,
bool verbose)
{
report_path_->reportMpwChecks(checks, verbose);
}
void
Sta::reportMpwCheck(MinPulseWidthCheck *check,
bool verbose)
{
report_path_->reportMpwCheck(check, verbose);
}
////////////////////////////////////////////////////////////////
MinPeriodCheckSeq &
Sta::minPeriodViolations()
{
minPeriodPreamble();
return check_min_periods_->violations();
}
MinPeriodCheck *
Sta::minPeriodSlack()
{
minPeriodPreamble();
return check_min_periods_->minSlackCheck();
}
void
Sta::minPeriodPreamble()
{
// Need clk arrivals to know what clks arrive at the clk tree endpoints.
ensureClkArrivals();
if (check_min_periods_ == nullptr)
makeCheckMinPeriods();
}
void
Sta::reportChecks(MinPeriodCheckSeq *checks,
bool verbose)
{
report_path_->reportChecks(checks, verbose);
}
void
Sta::reportCheck(MinPeriodCheck *check,
bool verbose)
{
report_path_->reportCheck(check, verbose);
}
////////////////////////////////////////////////////////////////
MaxSkewCheckSeq &
Sta::maxSkewViolations()
{
maxSkewPreamble();
return check_max_skews_->violations();
}
MaxSkewCheck *
Sta::maxSkewSlack()
{
maxSkewPreamble();
return check_max_skews_->minSlackCheck();
}
void
Sta::maxSkewPreamble()
{
ensureClkArrivals();
if (check_max_skews_ == nullptr)
makeCheckMaxSkews();
}
void
Sta::reportChecks(MaxSkewCheckSeq *checks,
bool verbose)
{
report_path_->reportChecks(checks, verbose);
}
void
Sta::reportCheck(MaxSkewCheck *check,
bool verbose)
{
report_path_->reportCheck(check, verbose);
}
////////////////////////////////////////////////////////////////
void
Sta::makeEquivCells(LibertyLibrarySeq *equiv_libs,
LibertyLibrarySeq *map_libs)
{
delete equiv_cells_;
equiv_cells_ = new EquivCells(equiv_libs, map_libs);
}
LibertyCellSeq *
Sta::equivCells(LibertyCell *cell)
{
if (equiv_cells_)
return equiv_cells_->equivs(cell);
else
return nullptr;
}
////////////////////////////////////////////////////////////////
void
Sta::powerPreamble()
{
// Use arrivals to find clocking info.
searchPreamble();
search_->findAllArrivals();
}
void
Sta::power(const Corner *corner,
// Return values.
PowerResult &total,
PowerResult &sequential,
PowerResult &combinational,
PowerResult &macro,
PowerResult &pad)
{
powerPreamble();
power_->power(corner, total, sequential, combinational, macro, pad);
}
void
Sta::power(const Instance *inst,
const Corner *corner,
// Return values.
PowerResult &result)
{
powerPreamble();
power_->power(inst, corner, result);
}
////////////////////////////////////////////////////////////////
void
Sta::ensureClkNetwork()
{
ensureLevelized();
clk_network_->ensureClkNetwork();
}
bool
Sta::isClock(const Pin *pin) const
{
return clk_network_->isClock(pin);
}
bool
Sta::isClock(const Net *net) const
{
return clk_network_->isClock(net);
}
bool
Sta::isIdealClock(const Pin *pin) const
{
return clk_network_->isIdealClock(pin);
}
const PinSet *
Sta::pins(const Clock *clk)
{
return clk_network_->pins(clk);
}
void
Sta::clkPinsInvalid()
{
clk_network_->clkPinsInvalid();
}
} // namespace