2018-09-28 17:54:21 +02:00
|
|
|
// OpenSTA, Static Timing Analyzer
|
2019-01-01 21:26:11 +01:00
|
|
|
// Copyright (c) 2019, Parallax Software, Inc.
|
2018-09-28 17:54:21 +02:00
|
|
|
//
|
|
|
|
|
// 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 <algorithm>
|
|
|
|
|
#include "Machine.hh"
|
|
|
|
|
#include "DisallowCopyAssign.hh"
|
|
|
|
|
#include "Stats.hh"
|
|
|
|
|
#include "Debug.hh"
|
2019-03-13 01:25:53 +01:00
|
|
|
#include "Mutex.hh"
|
2018-09-28 17:54:21 +02:00
|
|
|
#include "Report.hh"
|
|
|
|
|
#include "PatternMatch.hh"
|
|
|
|
|
#include "MinMax.hh"
|
|
|
|
|
#include "PortDirection.hh"
|
|
|
|
|
#include "RiseFallMinMax.hh"
|
|
|
|
|
#include "Transition.hh"
|
|
|
|
|
#include "TimingRole.hh"
|
|
|
|
|
#include "TimingArc.hh"
|
|
|
|
|
#include "Liberty.hh"
|
|
|
|
|
#include "Network.hh"
|
|
|
|
|
#include "Clock.hh"
|
|
|
|
|
#include "ClockLatency.hh"
|
|
|
|
|
#include "ClockInsertion.hh"
|
|
|
|
|
#include "CycleAccting.hh"
|
|
|
|
|
#include "PortDelay.hh"
|
|
|
|
|
#include "ExceptionPath.hh"
|
|
|
|
|
#include "PortExtCap.hh"
|
|
|
|
|
#include "DisabledPorts.hh"
|
|
|
|
|
#include "InputDrive.hh"
|
|
|
|
|
#include "DataCheck.hh"
|
|
|
|
|
#include "ClockGatingCheck.hh"
|
|
|
|
|
#include "ClockGroups.hh"
|
|
|
|
|
#include "DeratingFactors.hh"
|
|
|
|
|
#include "Graph.hh"
|
|
|
|
|
#include "Levelize.hh"
|
|
|
|
|
#include "HpinDrvrLoad.hh"
|
|
|
|
|
#include "Corner.hh"
|
|
|
|
|
#include "Sdc.hh"
|
|
|
|
|
|
|
|
|
|
namespace sta {
|
|
|
|
|
|
|
|
|
|
bool
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockPairLess::operator()(const ClockPair &pair1,
|
|
|
|
|
const ClockPair &pair2) const
|
|
|
|
|
{
|
|
|
|
|
int first1 = pair1.first->index();
|
|
|
|
|
int second1 = pair1.second->index();
|
|
|
|
|
if (first1 > second1)
|
|
|
|
|
std::swap(first1, second1);
|
|
|
|
|
int first2 = pair2.first->index();
|
|
|
|
|
int second2 = pair2.second->index();
|
|
|
|
|
if (first2 > second2)
|
|
|
|
|
std::swap(first2, second2);
|
2018-09-28 17:54:21 +02:00
|
|
|
return (first1 < first2)
|
|
|
|
|
|| (first1 == first2
|
|
|
|
|
&& second1 < second2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
typedef Vector<ClockPair> ClockPairSeq;
|
2018-09-28 17:54:21 +02:00
|
|
|
typedef Set<Pvt*> PvtSet;
|
|
|
|
|
|
|
|
|
|
static ExceptionThruSeq *
|
|
|
|
|
clone(ExceptionThruSeq *thrus,
|
|
|
|
|
Network *network);
|
|
|
|
|
static void
|
|
|
|
|
annotateGraphDisabledWireEdge(Pin *from_pin,
|
|
|
|
|
Pin *to_pin,
|
|
|
|
|
bool annotate,
|
|
|
|
|
Graph *graph);
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
Sdc::Sdc(StaState *sta) :
|
|
|
|
|
StaState(sta),
|
2019-03-13 01:25:53 +01:00
|
|
|
derating_factors_(nullptr),
|
|
|
|
|
net_derating_factors_(nullptr),
|
|
|
|
|
inst_derating_factors_(nullptr),
|
|
|
|
|
cell_derating_factors_(nullptr),
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_index_(0),
|
2019-03-13 01:25:53 +01:00
|
|
|
clk_insertions_(nullptr),
|
|
|
|
|
clk_group_exclusions_(nullptr),
|
|
|
|
|
clk_group_same_(nullptr),
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_sense_map_(network_),
|
2019-03-13 01:25:53 +01:00
|
|
|
clk_gating_check_(nullptr),
|
2018-09-28 17:54:21 +02:00
|
|
|
input_delay_index_(0),
|
2019-03-13 01:25:53 +01:00
|
|
|
port_cap_map_(nullptr),
|
|
|
|
|
net_wire_cap_map_(nullptr),
|
|
|
|
|
drvr_pin_wire_cap_map_(nullptr),
|
|
|
|
|
first_from_pin_exceptions_(nullptr),
|
|
|
|
|
first_from_clk_exceptions_(nullptr),
|
|
|
|
|
first_from_inst_exceptions_(nullptr),
|
|
|
|
|
first_thru_pin_exceptions_(nullptr),
|
|
|
|
|
first_thru_inst_exceptions_(nullptr),
|
|
|
|
|
first_thru_net_exceptions_(nullptr),
|
|
|
|
|
first_to_pin_exceptions_(nullptr),
|
|
|
|
|
first_to_clk_exceptions_(nullptr),
|
|
|
|
|
first_to_inst_exceptions_(nullptr),
|
|
|
|
|
first_thru_edge_exceptions_(nullptr),
|
|
|
|
|
path_delay_internal_startpoints_(nullptr),
|
|
|
|
|
path_delay_internal_endpoints_(nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
initVariables();
|
|
|
|
|
sdc_ = this;
|
2019-03-13 01:25:53 +01:00
|
|
|
setWireload(nullptr, MinMaxAll::all());
|
|
|
|
|
setWireloadSelection(nullptr, MinMaxAll::all());
|
|
|
|
|
setOperatingConditions(nullptr, MinMaxAll::all());
|
2018-09-28 17:54:21 +02:00
|
|
|
makeDefaultArrivalClock();
|
|
|
|
|
initInstancePvtMaps();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeDefaultArrivalClock()
|
|
|
|
|
{
|
|
|
|
|
FloatSeq *waveform = new FloatSeq;
|
|
|
|
|
waveform->push_back(0.0);
|
|
|
|
|
waveform->push_back(0.0);
|
|
|
|
|
default_arrival_clk_ = new Clock("input port clock", clk_index_++);
|
2019-10-10 06:02:33 +02:00
|
|
|
default_arrival_clk_->initClk(0, false, 0.0, waveform, nullptr, network_);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Sdc::~Sdc()
|
|
|
|
|
{
|
|
|
|
|
deleteConstraints();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This does NOT call initVariables() because those variable values
|
|
|
|
|
// survive linking a new design.
|
|
|
|
|
void
|
|
|
|
|
Sdc::clear()
|
|
|
|
|
{
|
|
|
|
|
removeLibertyAnnotations();
|
|
|
|
|
deleteConstraints();
|
|
|
|
|
propagated_clk_pins_.clear();
|
|
|
|
|
clocks_.clear();
|
|
|
|
|
clock_name_map_.clear();
|
|
|
|
|
clock_pin_map_.clear();
|
|
|
|
|
clock_vertex_pin_map_.clear();
|
|
|
|
|
clk_latencies_.clear();
|
|
|
|
|
edge_clk_latency_.clear();
|
|
|
|
|
if (clk_insertions_)
|
|
|
|
|
clk_insertions_->clear();
|
|
|
|
|
|
|
|
|
|
pin_clk_uncertainty_map_.clear();
|
|
|
|
|
inter_clk_uncertainties_.clear();
|
|
|
|
|
|
|
|
|
|
clk_groups_name_map_.clear();
|
|
|
|
|
clearClkGroupExclusions();
|
|
|
|
|
|
|
|
|
|
clk_gating_check_map_.clear();
|
|
|
|
|
inst_clk_gating_check_map_.clear();
|
|
|
|
|
pin_clk_gating_check_map_.clear();
|
|
|
|
|
data_checks_from_map_.clear();
|
|
|
|
|
data_checks_to_map_.clear();
|
|
|
|
|
|
|
|
|
|
input_delay_map_.clear();
|
|
|
|
|
input_delay_index_ = 0;
|
|
|
|
|
input_delay_ref_pin_map_.clear();
|
|
|
|
|
input_delay_vertex_map_.clear();
|
|
|
|
|
input_delay_internal_pin_map_.clear();
|
|
|
|
|
output_delay_map_.clear();
|
|
|
|
|
output_delay_vertex_map_.clear();
|
|
|
|
|
|
|
|
|
|
port_slew_limit_map_.clear();
|
|
|
|
|
cell_slew_limit_map_.clear();
|
|
|
|
|
have_clk_slew_limits_ = false;
|
|
|
|
|
|
|
|
|
|
cell_cap_limit_map_.clear();
|
|
|
|
|
port_cap_limit_map_.clear();
|
|
|
|
|
pin_cap_limit_map_.clear();
|
|
|
|
|
|
|
|
|
|
port_fanout_limit_map_.clear();
|
|
|
|
|
cell_fanout_limit_map_.clear();
|
|
|
|
|
|
|
|
|
|
disabled_pins_.clear();
|
|
|
|
|
disabled_ports_.clear();
|
|
|
|
|
disabled_lib_ports_.clear();
|
|
|
|
|
disabled_edges_.clear();
|
|
|
|
|
disabled_cell_ports_.clear();
|
|
|
|
|
disabled_inst_ports_.clear();
|
|
|
|
|
|
|
|
|
|
disabled_clk_gating_checks_inst_.clear();
|
|
|
|
|
disabled_clk_gating_checks_pin_.clear();
|
|
|
|
|
|
|
|
|
|
input_drive_map_.clear();
|
|
|
|
|
logic_value_map_.clear();
|
|
|
|
|
case_value_map_.clear();
|
|
|
|
|
|
|
|
|
|
pin_latch_borrow_limit_map_.clear();
|
|
|
|
|
inst_latch_borrow_limit_map_.clear();
|
|
|
|
|
clk_latch_borrow_limit_map_.clear();
|
|
|
|
|
|
|
|
|
|
min_pulse_width_.clear();
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
setWireload(nullptr, MinMaxAll::all());
|
|
|
|
|
setWireloadSelection(nullptr, MinMaxAll::all());
|
2018-09-28 17:54:21 +02:00
|
|
|
// Operating conditions are owned by Liberty libraries.
|
2019-03-13 01:25:53 +01:00
|
|
|
setOperatingConditions(nullptr, MinMaxAll::all());
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_index_ = 0;
|
|
|
|
|
makeDefaultArrivalClock();
|
|
|
|
|
|
|
|
|
|
unsetTimingDerate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::initVariables()
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
analysis_type_ = AnalysisType::ocv;
|
2018-09-28 17:54:21 +02:00
|
|
|
use_default_arrival_clock_ = false;
|
|
|
|
|
crpr_enabled_ = true;
|
2019-03-13 01:25:53 +01:00
|
|
|
crpr_mode_ = CrprMode::same_pin;
|
2018-09-28 17:54:21 +02:00
|
|
|
propagate_gated_clock_enable_ = true;
|
|
|
|
|
preset_clr_arcs_enabled_ = false;
|
|
|
|
|
cond_default_arcs_enabled_ = true;
|
|
|
|
|
bidirect_net_paths_enabled_ = false;
|
|
|
|
|
bidirect_inst_paths_enabled_ = false;
|
|
|
|
|
recovery_removal_checks_enabled_ = true;
|
|
|
|
|
gated_clk_checks_enabled_ = true;
|
|
|
|
|
clk_thru_tristate_enabled_ = false;
|
|
|
|
|
dynamic_loop_breaking_ = false;
|
|
|
|
|
propagate_all_clks_ = false;
|
2019-03-13 01:25:53 +01:00
|
|
|
wireload_mode_ = WireloadMode::unknown;
|
2018-09-28 17:54:21 +02:00
|
|
|
max_area_ = 0.0;
|
|
|
|
|
path_delays_without_to_ = false;
|
|
|
|
|
clk_hpin_disables_valid_ = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteConstraints()
|
|
|
|
|
{
|
|
|
|
|
clocks_.deleteContents();
|
|
|
|
|
delete default_arrival_clk_;
|
|
|
|
|
clock_pin_map_.deleteContents();
|
|
|
|
|
clock_vertex_pin_map_.deleteContents();
|
|
|
|
|
clk_latencies_.deleteContents();
|
|
|
|
|
if (clk_insertions_) {
|
|
|
|
|
clk_insertions_->deleteContents();
|
|
|
|
|
delete clk_insertions_;
|
2019-03-13 01:25:53 +01:00
|
|
|
clk_insertions_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clk_groups_name_map_.deleteContents();
|
|
|
|
|
clearClkGroupExclusions();
|
|
|
|
|
|
|
|
|
|
pin_clk_uncertainty_map_.deleteContents();
|
|
|
|
|
inter_clk_uncertainties_.deleteContents();
|
|
|
|
|
delete clk_gating_check_;
|
2019-03-13 01:25:53 +01:00
|
|
|
clk_gating_check_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_gating_check_map_.deleteContents();
|
|
|
|
|
inst_clk_gating_check_map_.deleteContents();
|
|
|
|
|
pin_clk_gating_check_map_.deleteContents();
|
|
|
|
|
input_drive_map_.deleteContents();
|
|
|
|
|
disabled_cell_ports_.deleteContents();
|
|
|
|
|
disabled_inst_ports_.deleteContents();
|
|
|
|
|
pin_min_pulse_width_map_.deleteContentsClear();
|
|
|
|
|
inst_min_pulse_width_map_.deleteContentsClear();
|
|
|
|
|
clk_min_pulse_width_map_.deleteContentsClear();
|
|
|
|
|
|
|
|
|
|
DataChecksMap::Iterator data_checks_iter1(data_checks_from_map_);
|
|
|
|
|
while (data_checks_iter1.hasNext()) {
|
|
|
|
|
DataCheckSet *checks = data_checks_iter1.next();
|
|
|
|
|
checks->deleteContents();
|
|
|
|
|
delete checks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DataChecksMap::Iterator data_checks_iter2(data_checks_to_map_);
|
|
|
|
|
while (data_checks_iter2.hasNext()) {
|
|
|
|
|
DataCheckSet *checks = data_checks_iter2.next();
|
|
|
|
|
delete checks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDelayMap::Iterator input_iter(input_delay_map_);
|
|
|
|
|
while (input_iter.hasNext()) {
|
|
|
|
|
InputDelay *input_delay = input_iter.next();
|
|
|
|
|
while (input_delay) {
|
|
|
|
|
InputDelay *next = input_delay->next();
|
|
|
|
|
delete input_delay;
|
|
|
|
|
input_delay = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDelayRefPinMap::Iterator input_set_iter(input_delay_ref_pin_map_);
|
|
|
|
|
while (input_set_iter.hasNext()) {
|
|
|
|
|
InputDelaySet *input_delays = input_set_iter.next();
|
|
|
|
|
delete input_delays;
|
|
|
|
|
}
|
|
|
|
|
input_delay_internal_pin_map_.deleteContents();
|
|
|
|
|
|
|
|
|
|
OutputDelayMap::Iterator output_iter(output_delay_map_);
|
|
|
|
|
while (output_iter.hasNext()) {
|
|
|
|
|
OutputDelay *output_delay = output_iter.next();
|
|
|
|
|
while (output_delay) {
|
|
|
|
|
OutputDelay *next = output_delay->next();
|
|
|
|
|
delete output_delay;
|
|
|
|
|
output_delay = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clk_hpin_disables_.deleteContentsClear();
|
|
|
|
|
clk_hpin_disables_valid_ = false;
|
|
|
|
|
|
|
|
|
|
clearCycleAcctings();
|
|
|
|
|
deleteExceptions();
|
|
|
|
|
clearGroupPathMap();
|
|
|
|
|
deleteInstancePvts();
|
|
|
|
|
deleteDeratingFactors();
|
|
|
|
|
removeLoadCaps();
|
2019-03-13 01:25:53 +01:00
|
|
|
clk_sense_map_.clear();
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteInstancePvts()
|
|
|
|
|
{
|
|
|
|
|
// Multiple instances can share a pvt, so put them in a set
|
|
|
|
|
// so they are only deleted once.
|
|
|
|
|
PvtSet pvts;
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto mm_index : MinMax::rangeIndex()) {
|
|
|
|
|
InstancePvtMap *pvt_map = instance_pvt_maps_[mm_index];
|
2018-09-28 17:54:21 +02:00
|
|
|
InstancePvtMap::Iterator pvt_iter(pvt_map);
|
|
|
|
|
while (pvt_iter.hasNext()) {
|
|
|
|
|
Pvt *pvt = pvt_iter.next();
|
|
|
|
|
pvts.insert(pvt);
|
|
|
|
|
}
|
|
|
|
|
delete pvt_map;
|
|
|
|
|
}
|
|
|
|
|
pvts.deleteContents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeNetLoadCaps()
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
delete [] net_wire_cap_map_;
|
2019-03-13 01:25:53 +01:00
|
|
|
net_wire_cap_map_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
delete [] drvr_pin_wire_cap_map_;
|
2019-03-13 01:25:53 +01:00
|
|
|
drvr_pin_wire_cap_map_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeLoadCaps()
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
if (port_cap_map_) {
|
|
|
|
|
port_cap_map_->deleteContents();
|
|
|
|
|
delete port_cap_map_;
|
2019-03-13 01:25:53 +01:00
|
|
|
port_cap_map_ = nullptr;
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
|
|
|
|
removeNetLoadCaps();
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeLibertyAnnotations()
|
|
|
|
|
{
|
|
|
|
|
DisabledCellPortsMap::Iterator disabled_iter(disabled_cell_ports_);
|
|
|
|
|
while (disabled_iter.hasNext()) {
|
|
|
|
|
DisabledCellPorts *disable = disabled_iter.next();
|
|
|
|
|
LibertyCell *cell = disable->cell();
|
|
|
|
|
if (disable->all())
|
|
|
|
|
cell->setIsDisabledConstraint(false);
|
|
|
|
|
|
|
|
|
|
LibertyPortSet::Iterator from_iter(disable->from());
|
|
|
|
|
while (from_iter.hasNext()) {
|
|
|
|
|
LibertyPort *from = from_iter.next();
|
|
|
|
|
from->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LibertyPortSet::Iterator to_iter(disable->to());
|
|
|
|
|
while (to_iter.hasNext()) {
|
|
|
|
|
LibertyPort *to = to_iter.next();
|
|
|
|
|
to->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (disable->timingArcSets()) {
|
|
|
|
|
TimingArcSetSet::Iterator arc_iter(disable->timingArcSets());
|
|
|
|
|
while (arc_iter.hasNext()) {
|
|
|
|
|
TimingArcSet *arc_set = arc_iter.next();
|
|
|
|
|
arc_set->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LibertyPortPairSet::Iterator from_to_iter(disable->fromTo());
|
|
|
|
|
while (from_to_iter.hasNext()) {
|
|
|
|
|
LibertyPortPair *pair = from_to_iter.next();
|
2018-12-05 23:18:41 +01:00
|
|
|
const LibertyPort *from = pair->first;
|
|
|
|
|
const LibertyPort *to = pair->second;
|
|
|
|
|
LibertyCellTimingArcSetIterator arc_iter(cell, from, to);
|
|
|
|
|
while (arc_iter.hasNext()) {
|
|
|
|
|
TimingArcSet *arc_set = arc_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
arc_set->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LibertyPortSet::Iterator port_iter(disabled_lib_ports_);
|
|
|
|
|
while (port_iter.hasNext()) {
|
|
|
|
|
LibertyPort *port = port_iter.next();
|
|
|
|
|
port->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::initInstancePvtMaps()
|
|
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto mm_index : MinMax::rangeIndex())
|
|
|
|
|
instance_pvt_maps_[mm_index] = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::searchPreamble()
|
|
|
|
|
{
|
|
|
|
|
ensureClkHpinDisables();
|
|
|
|
|
ensureClkGroupExclusions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isConstrained(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
Pin *pin1 = const_cast<Pin*>(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
Port *port = network_->isTopLevelPort(pin) ? network_->port(pin) : nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
return clock_pin_map_.hasKey(pin)
|
|
|
|
|
|| propagated_clk_pins_.hasKey(pin1)
|
|
|
|
|
|| hasClockLatency(pin)
|
|
|
|
|
|| hasClockInsertion(pin)
|
|
|
|
|
|| pin_clk_uncertainty_map_.hasKey(pin)
|
|
|
|
|
|| pin_clk_gating_check_map_.hasKey(pin)
|
|
|
|
|
|| data_checks_from_map_.hasKey(pin)
|
|
|
|
|
|| data_checks_to_map_.hasKey(pin)
|
|
|
|
|
|| input_delay_map_.hasKey(pin)
|
|
|
|
|
|| output_delay_map_.hasKey(pin)
|
|
|
|
|
|| port_slew_limit_map_.hasKey(port)
|
|
|
|
|
|| pin_cap_limit_map_.hasKey(pin1)
|
|
|
|
|
|| port_cap_limit_map_.hasKey(port)
|
|
|
|
|
|| port_fanout_limit_map_.hasKey(port)
|
2018-11-26 18:15:52 +01:00
|
|
|
|| hasPortExtCap(port)
|
2018-09-28 17:54:21 +02:00
|
|
|
|| disabled_pins_.hasKey(pin1)
|
|
|
|
|
|| disabled_ports_.hasKey(port)
|
|
|
|
|
|| disabled_clk_gating_checks_pin_.hasKey(pin1)
|
|
|
|
|
|| (first_from_pin_exceptions_
|
|
|
|
|
&& first_from_pin_exceptions_->hasKey(pin))
|
|
|
|
|
|| (first_thru_pin_exceptions_
|
|
|
|
|
&& first_thru_pin_exceptions_->hasKey(pin))
|
|
|
|
|
|| (first_to_pin_exceptions_
|
|
|
|
|
&& first_to_pin_exceptions_->hasKey(pin))
|
|
|
|
|
|| input_drive_map_.hasKey(port)
|
|
|
|
|
|| logic_value_map_.hasKey(pin)
|
|
|
|
|
|| case_value_map_.hasKey(pin)
|
|
|
|
|
|| pin_latch_borrow_limit_map_.hasKey(pin)
|
|
|
|
|
|| pin_min_pulse_width_map_.hasKey(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isConstrained(const Instance *inst) const
|
|
|
|
|
{
|
|
|
|
|
Instance *inst1 = const_cast<Instance*>(inst);
|
|
|
|
|
return (instance_pvt_maps_[MinMax::minIndex()]
|
|
|
|
|
&& instance_pvt_maps_[MinMax::minIndex()]->hasKey(inst1))
|
|
|
|
|
|| (instance_pvt_maps_[MinMax::maxIndex()]
|
|
|
|
|
&& instance_pvt_maps_[MinMax::maxIndex()]->hasKey(inst1))
|
|
|
|
|
|| (inst_derating_factors_
|
|
|
|
|
&& inst_derating_factors_->hasKey(inst))
|
|
|
|
|
|| inst_clk_gating_check_map_.hasKey(inst)
|
|
|
|
|
|| disabled_inst_ports_.hasKey(inst1)
|
|
|
|
|
|| (first_from_inst_exceptions_
|
|
|
|
|
&& first_from_inst_exceptions_->hasKey(inst))
|
|
|
|
|
|| (first_thru_inst_exceptions_
|
|
|
|
|
&& first_thru_inst_exceptions_->hasKey(inst))
|
|
|
|
|
|| (first_to_inst_exceptions_->hasKey(inst)
|
|
|
|
|
&& first_to_inst_exceptions_)
|
|
|
|
|
|| inst_latch_borrow_limit_map_.hasKey(inst)
|
|
|
|
|
|| inst_min_pulse_width_map_.hasKey(inst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isConstrained(const Net *net) const
|
|
|
|
|
{
|
|
|
|
|
Net *net1 = const_cast<Net*>(net);
|
|
|
|
|
return (net_derating_factors_
|
|
|
|
|
&& net_derating_factors_->hasKey(net))
|
2018-11-26 18:15:52 +01:00
|
|
|
|| hasNetWireCap(net1)
|
2018-09-28 17:54:21 +02:00
|
|
|
|| net_res_map_.hasKey(net1)
|
|
|
|
|
|| (first_thru_net_exceptions_
|
|
|
|
|
&& first_thru_net_exceptions_->hasKey(net));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setAnalysisType(AnalysisType analysis_type)
|
|
|
|
|
{
|
|
|
|
|
analysis_type_ = analysis_type;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setOperatingConditions(OperatingConditions *op_cond,
|
|
|
|
|
const MinMaxAll *min_max)
|
|
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto mm_index : min_max->rangeIndex())
|
2018-09-28 17:54:21 +02:00
|
|
|
operating_conditions_[mm_index] = op_cond;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setOperatingConditions(OperatingConditions *op_cond,
|
|
|
|
|
const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
int mm_index = min_max->index();
|
|
|
|
|
operating_conditions_[mm_index] = op_cond;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OperatingConditions *
|
|
|
|
|
Sdc::operatingConditions(const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
int mm_index = min_max->index();
|
|
|
|
|
return operating_conditions_[mm_index];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Pvt *
|
|
|
|
|
Sdc::pvt(Instance *inst,
|
|
|
|
|
const MinMax *min_max) const
|
|
|
|
|
{
|
|
|
|
|
InstancePvtMap *pvt_map = instance_pvt_maps_[min_max->index()];
|
|
|
|
|
if (pvt_map)
|
|
|
|
|
return pvt_map->findKey(inst);
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setPvt(Instance *inst, const
|
|
|
|
|
MinMaxAll *min_max,
|
|
|
|
|
Pvt *pvt)
|
|
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto mm_index : min_max->rangeIndex()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
InstancePvtMap *pvt_map = instance_pvt_maps_[mm_index];
|
2019-03-13 01:25:53 +01:00
|
|
|
if (pvt_map == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
pvt_map = new InstancePvtMap;
|
|
|
|
|
instance_pvt_maps_[mm_index] = pvt_map;
|
|
|
|
|
}
|
|
|
|
|
(*pvt_map)[inst] = pvt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setTimingDerate(TimingDerateType type,
|
|
|
|
|
PathClkOrData clk_data,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
float derate)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (derating_factors_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
derating_factors_ = new DeratingFactorsGlobal;
|
|
|
|
|
derating_factors_->setFactor(type, clk_data, tr, early_late, derate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setTimingDerate(const Net *net,
|
|
|
|
|
PathClkOrData clk_data,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
float derate)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (net_derating_factors_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
net_derating_factors_ = new NetDeratingFactorsMap;
|
|
|
|
|
DeratingFactorsNet *factors = net_derating_factors_->findKey(net);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (factors == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
factors = new DeratingFactorsNet;
|
|
|
|
|
(*net_derating_factors_)[net] = factors;
|
|
|
|
|
}
|
|
|
|
|
factors->setFactor(clk_data, tr, early_late, derate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setTimingDerate(const Instance *inst,
|
|
|
|
|
TimingDerateType type,
|
|
|
|
|
PathClkOrData clk_data,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
float derate)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (inst_derating_factors_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
inst_derating_factors_ = new InstDeratingFactorsMap;
|
|
|
|
|
DeratingFactorsCell *factors = inst_derating_factors_->findKey(inst);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (factors == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
factors = new DeratingFactorsCell;
|
|
|
|
|
(*inst_derating_factors_)[inst] = factors;
|
|
|
|
|
}
|
|
|
|
|
factors->setFactor(type, clk_data, tr, early_late, derate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setTimingDerate(const LibertyCell *cell,
|
|
|
|
|
TimingDerateType type,
|
|
|
|
|
PathClkOrData clk_data,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
float derate)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (cell_derating_factors_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
cell_derating_factors_ = new CellDeratingFactorsMap;
|
|
|
|
|
DeratingFactorsCell *factors = cell_derating_factors_->findKey(cell);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (factors == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
factors = new DeratingFactorsCell;
|
|
|
|
|
(*cell_derating_factors_)[cell] = factors;
|
|
|
|
|
}
|
|
|
|
|
factors->setFactor(type, clk_data, tr, early_late, derate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
Sdc::timingDerateInstance(const Pin *pin,
|
|
|
|
|
TimingDerateType type,
|
|
|
|
|
PathClkOrData clk_data,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const EarlyLate *early_late) const
|
|
|
|
|
{
|
|
|
|
|
if (inst_derating_factors_) {
|
|
|
|
|
const Instance *inst = network_->instance(pin);
|
|
|
|
|
DeratingFactorsCell *factors = inst_derating_factors_->findKey(inst);
|
|
|
|
|
if (factors) {
|
|
|
|
|
float factor;
|
|
|
|
|
bool exists;
|
|
|
|
|
factors->factor(type, clk_data, tr, early_late, factor, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
return factor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cell_derating_factors_) {
|
|
|
|
|
const Instance *inst = network_->instance(pin);
|
|
|
|
|
const LibertyCell *cell = network_->libertyCell(inst);
|
|
|
|
|
if (cell) {
|
|
|
|
|
DeratingFactorsCell *factors = cell_derating_factors_->findKey(cell);
|
|
|
|
|
float factor;
|
|
|
|
|
bool exists;
|
|
|
|
|
if (factors) {
|
|
|
|
|
factors->factor(type, clk_data, tr, early_late, factor, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
return factor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (derating_factors_) {
|
|
|
|
|
float factor;
|
|
|
|
|
bool exists;
|
|
|
|
|
derating_factors_->factor(type, clk_data, tr, early_late, factor, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
return factor;
|
|
|
|
|
}
|
|
|
|
|
return 1.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
Sdc::timingDerateNet(const Pin *pin,
|
|
|
|
|
PathClkOrData clk_data,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const EarlyLate *early_late) const
|
|
|
|
|
{
|
|
|
|
|
if (net_derating_factors_) {
|
|
|
|
|
const Net *net = network_->net(pin);
|
|
|
|
|
DeratingFactorsNet *factors = net_derating_factors_->findKey(net);
|
|
|
|
|
if (factors) {
|
|
|
|
|
float factor;
|
|
|
|
|
bool exists;
|
|
|
|
|
factors->factor(clk_data, tr, early_late, factor, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
return factor;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (derating_factors_) {
|
|
|
|
|
float factor;
|
|
|
|
|
bool exists;
|
2019-03-13 01:25:53 +01:00
|
|
|
derating_factors_->factor(TimingDerateType::net_delay, clk_data, tr,
|
2018-09-28 17:54:21 +02:00
|
|
|
early_late, factor, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
return factor;
|
|
|
|
|
}
|
|
|
|
|
return 1.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unsetTimingDerate()
|
|
|
|
|
{
|
|
|
|
|
deleteDeratingFactors();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteDeratingFactors()
|
|
|
|
|
{
|
|
|
|
|
if (net_derating_factors_) {
|
|
|
|
|
NetDeratingFactorsMap::Iterator net_iter(net_derating_factors_);
|
|
|
|
|
while (net_iter.hasNext()) {
|
|
|
|
|
DeratingFactorsNet *factors = net_iter.next();
|
|
|
|
|
delete factors;
|
|
|
|
|
}
|
|
|
|
|
delete net_derating_factors_;
|
2019-03-13 01:25:53 +01:00
|
|
|
net_derating_factors_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (inst_derating_factors_) {
|
|
|
|
|
InstDeratingFactorsMap::Iterator inst_iter(inst_derating_factors_);
|
|
|
|
|
while (inst_iter.hasNext()) {
|
|
|
|
|
DeratingFactorsCell *factors = inst_iter.next();
|
|
|
|
|
delete factors;
|
|
|
|
|
}
|
|
|
|
|
delete inst_derating_factors_;
|
2019-03-13 01:25:53 +01:00
|
|
|
inst_derating_factors_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cell_derating_factors_) {
|
|
|
|
|
CellDeratingFactorsMap::Iterator cell_iter(cell_derating_factors_);
|
|
|
|
|
while (cell_iter.hasNext()) {
|
|
|
|
|
DeratingFactorsCell *factors = cell_iter.next();
|
|
|
|
|
delete factors;
|
|
|
|
|
}
|
|
|
|
|
delete cell_derating_factors_;
|
2019-03-13 01:25:53 +01:00
|
|
|
cell_derating_factors_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete derating_factors_;
|
2019-03-13 01:25:53 +01:00
|
|
|
derating_factors_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
2019-02-16 21:07:59 +01:00
|
|
|
Sdc::setDriveCell(LibertyLibrary *library,
|
2018-09-28 17:54:21 +02:00
|
|
|
LibertyCell *cell,
|
2019-02-16 21:07:59 +01:00
|
|
|
Port *port,
|
2018-09-28 17:54:21 +02:00
|
|
|
LibertyPort *from_port,
|
|
|
|
|
float *from_slews,
|
|
|
|
|
LibertyPort *to_port,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const MinMaxAll *min_max)
|
|
|
|
|
{
|
2019-02-16 21:07:59 +01:00
|
|
|
ensureInputDrive(port)->setDriveCell(library, cell, from_port, from_slews,
|
|
|
|
|
to_port, tr, min_max);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setInputSlew(Port *port,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
float slew)
|
|
|
|
|
{
|
|
|
|
|
ensureInputDrive(port)->setSlew(tr, min_max, slew);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setDriveResistance(Port *port,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
float res)
|
|
|
|
|
{
|
|
|
|
|
ensureInputDrive(port)->setDriveResistance(tr, min_max, res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDrive *
|
|
|
|
|
Sdc::ensureInputDrive(Port *port)
|
|
|
|
|
{
|
|
|
|
|
InputDrive *drive = input_drive_map_.findKey(port);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (drive == nullptr) {
|
|
|
|
|
drive = new InputDrive;
|
2018-09-28 17:54:21 +02:00
|
|
|
input_drive_map_[port] = drive;
|
|
|
|
|
}
|
|
|
|
|
return drive;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setSlewLimit(Clock *clk,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const PathClkOrData clk_data,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float slew)
|
|
|
|
|
{
|
|
|
|
|
clk->setSlewLimit(tr, clk_data, min_max, slew);
|
|
|
|
|
have_clk_slew_limits_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::haveClkSlewLimits() const
|
|
|
|
|
{
|
|
|
|
|
return have_clk_slew_limits_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::slewLimit(Clock *clk, const TransRiseFall *tr,
|
|
|
|
|
const PathClkOrData clk_data,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &slew,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
clk->slewLimit(tr, clk_data, min_max, slew, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::slewLimit(Port *port,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &slew,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
slew = 0.0;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
port_slew_limit_map_.findKey(port, values, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
values.value(min_max, slew, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setSlewLimit(Port *port,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float slew)
|
|
|
|
|
{
|
|
|
|
|
MinMaxFloatValues &values = port_slew_limit_map_[port];
|
|
|
|
|
values.setValue(min_max, slew);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::slewLimit(const Pin *pin,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &slew,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
slew = 0.0;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
pin_slew_limit_map_.findKey(pin, values, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
values.value(min_max, slew, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setSlewLimit(const Pin *pin,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float slew)
|
|
|
|
|
{
|
|
|
|
|
MinMaxFloatValues &values = pin_slew_limit_map_[pin];
|
|
|
|
|
values.setValue(min_max, slew);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::slewLimitPins(ConstPinSeq &pins)
|
|
|
|
|
{
|
|
|
|
|
PinSlewLimitMap::Iterator iter(pin_slew_limit_map_);
|
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
|
const Pin *pin;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
iter.next(pin, values);
|
|
|
|
|
pins.push_back(pin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::slewLimit(Cell *cell,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &slew,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
slew = 0.0;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
cell_slew_limit_map_.findKey(cell, values, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
values.value(min_max, slew, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setSlewLimit(Cell *cell,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float slew)
|
|
|
|
|
{
|
|
|
|
|
MinMaxFloatValues &values = cell_slew_limit_map_[cell];
|
|
|
|
|
values.setValue(min_max, slew);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::capacitanceLimit(Cell *cell,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &cap,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
cap = 0.0;
|
|
|
|
|
exists = false;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
cell_cap_limit_map_.findKey(cell, values, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
values.value(min_max, cap, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setCapacitanceLimit(Cell *cell,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float cap)
|
|
|
|
|
{
|
|
|
|
|
MinMaxFloatValues &values = cell_cap_limit_map_[cell];
|
|
|
|
|
values.setValue(min_max, cap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::capacitanceLimit(Port *port,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &cap,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
cap = 0.0;
|
|
|
|
|
exists = false;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
port_cap_limit_map_.findKey(port, values, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
values.value(min_max, cap, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setCapacitanceLimit(Port *port,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float cap)
|
|
|
|
|
{
|
|
|
|
|
MinMaxFloatValues &values = port_cap_limit_map_[port];
|
|
|
|
|
values.setValue(min_max, cap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::capacitanceLimit(Pin *pin,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &cap,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
cap = 0.0;
|
|
|
|
|
exists = false;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
pin_cap_limit_map_.findKey(pin, values, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
values.value(min_max, cap, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setCapacitanceLimit(Pin *pin,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float cap)
|
|
|
|
|
{
|
|
|
|
|
MinMaxFloatValues &values = pin_cap_limit_map_[pin];
|
|
|
|
|
values.setValue(min_max, cap);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::fanoutLimit(Cell *cell,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &fanout,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
fanout = 0.0;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
cell_fanout_limit_map_.findKey(cell, values, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
values.value(min_max, fanout, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setFanoutLimit(Cell *cell,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float fanout)
|
|
|
|
|
{
|
|
|
|
|
MinMaxFloatValues &values = cell_fanout_limit_map_[cell];
|
|
|
|
|
values.setValue(min_max, fanout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::fanoutLimit(Port *port,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &fanout,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
fanout = 0.0;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
port_fanout_limit_map_.findKey(port, values, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
values.value(min_max, fanout, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setFanoutLimit(Port *port,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float fanout)
|
|
|
|
|
{
|
|
|
|
|
MinMaxFloatValues &values = port_fanout_limit_map_[port];
|
|
|
|
|
values.setValue(min_max, fanout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setMaxArea(float area)
|
|
|
|
|
{
|
|
|
|
|
max_area_ = area;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
Sdc::maxArea() const
|
|
|
|
|
{
|
|
|
|
|
return max_area_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
Clock *
|
|
|
|
|
Sdc::makeClock(const char *name,
|
|
|
|
|
PinSet *pins,
|
|
|
|
|
bool add_to_pins,
|
|
|
|
|
float period,
|
|
|
|
|
FloatSeq *waveform,
|
|
|
|
|
const char *comment)
|
|
|
|
|
{
|
|
|
|
|
Clock *clk = clock_name_map_.findKey(name);
|
|
|
|
|
if (!add_to_pins)
|
|
|
|
|
deletePinClocks(clk, pins);
|
|
|
|
|
if (clk)
|
|
|
|
|
// Named clock redefinition.
|
|
|
|
|
deleteClkPinMappings(clk);
|
|
|
|
|
else {
|
|
|
|
|
// Fresh clock definition.
|
|
|
|
|
clk = new Clock(name, clk_index_++);
|
2019-10-10 06:02:33 +02:00
|
|
|
clk->setIsPropagated(propagate_all_clks_);
|
2018-09-28 17:54:21 +02:00
|
|
|
clocks_.push_back(clk);
|
|
|
|
|
// Use the copied name in the map.
|
|
|
|
|
clock_name_map_[clk->name()] = clk;
|
|
|
|
|
}
|
2019-10-10 06:02:33 +02:00
|
|
|
clk->initClk(pins, add_to_pins, period, waveform, comment, network_);
|
2018-09-28 17:54:21 +02:00
|
|
|
makeClkPinMappings(clk);
|
|
|
|
|
clearCycleAcctings();
|
|
|
|
|
invalidateGeneratedClks();
|
|
|
|
|
clkHpinDisablesInvalid();
|
|
|
|
|
return clk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Clock *
|
|
|
|
|
Sdc::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,
|
|
|
|
|
const char *comment)
|
|
|
|
|
{
|
|
|
|
|
Clock *clk = clock_name_map_.findKey(name);
|
|
|
|
|
if (!add_to_pins)
|
|
|
|
|
deletePinClocks(clk, pins);
|
|
|
|
|
if (clk)
|
|
|
|
|
deleteClkPinMappings(clk);
|
|
|
|
|
else {
|
|
|
|
|
clk = new Clock(name, clk_index_++);
|
|
|
|
|
clocks_.push_back(clk);
|
|
|
|
|
clock_name_map_[clk->name()] = clk;
|
|
|
|
|
}
|
|
|
|
|
clk->initGeneratedClk(pins, add_to_pins, src_pin, master_clk,
|
|
|
|
|
pll_out, pll_fdbk,
|
|
|
|
|
divide_by, multiply_by, duty_cycle,
|
|
|
|
|
invert, combinational,
|
|
|
|
|
edges, edge_shifts, propagate_all_clks_,
|
|
|
|
|
comment, network_);
|
|
|
|
|
makeClkPinMappings(clk);
|
|
|
|
|
clearCycleAcctings();
|
|
|
|
|
invalidateGeneratedClks();
|
|
|
|
|
clkHpinDisablesInvalid();
|
|
|
|
|
return clk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::invalidateGeneratedClks() const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto clk : clocks_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (clk->isGenerated())
|
|
|
|
|
clk->waveformInvalid();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the clock is not defined with the -add option, any pins that already
|
|
|
|
|
// have a clock attached to them are removed from the pin. If the clock
|
|
|
|
|
// is not the clock being defined and has no pins it is removed.
|
|
|
|
|
void
|
|
|
|
|
Sdc::deletePinClocks(Clock *defining_clk,
|
|
|
|
|
PinSet *pins)
|
|
|
|
|
{
|
|
|
|
|
// Find all the clocks defined on pins to avoid finding the clock's
|
|
|
|
|
// vertex pins multiple times.
|
|
|
|
|
ClockSet clks;
|
|
|
|
|
PinSet::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
ClockSet *pin_clks = clock_pin_map_.findKey(pin);
|
|
|
|
|
if (pin_clks) {
|
|
|
|
|
ClockSet::Iterator clk_iter(pin_clks);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
|
|
|
|
clks.insert(clk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
ClockSet::Iterator clk_iter(clks);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
|
|
|
|
deleteClkPinMappings(clk);
|
|
|
|
|
PinSet::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
clk->deletePin(pin);
|
|
|
|
|
}
|
|
|
|
|
if (clk != defining_clk) {
|
|
|
|
|
if (clk->pins()->empty())
|
|
|
|
|
removeClock(clk);
|
|
|
|
|
else {
|
|
|
|
|
clk->makeVertexPins(network_);
|
|
|
|
|
// One of the remaining clock pins may use a vertex pin that
|
|
|
|
|
// was deleted above.
|
|
|
|
|
makeClkPinMappings(clk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteClkPinMappings(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
ClockPinIterator pin_iter1(clk);
|
|
|
|
|
while (pin_iter1.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter1.next();
|
|
|
|
|
ClockSet *pin_clks = clock_pin_map_.findKey(pin);
|
|
|
|
|
if (pin_clks) {
|
|
|
|
|
pin_clks->erase(clk);
|
|
|
|
|
if (pin_clks->empty()) {
|
2019-04-11 05:36:48 +02:00
|
|
|
clock_pin_map_.erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
delete pin_clks;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClockVertexPinIterator pin_iter2(clk);
|
|
|
|
|
while (pin_iter2.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter2.next();
|
|
|
|
|
ClockSet *pin_clks = clock_vertex_pin_map_.findKey(pin);
|
|
|
|
|
if (pin_clks) {
|
|
|
|
|
pin_clks->erase(clk);
|
|
|
|
|
if (pin_clks->empty()) {
|
2019-04-11 05:36:48 +02:00
|
|
|
clock_vertex_pin_map_.erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
delete pin_clks;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeClkPinMappings(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
ClockPinIterator pin_iter1(clk);
|
|
|
|
|
while (pin_iter1.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter1.next();
|
|
|
|
|
ClockSet *pin_clks = clock_pin_map_.findKey(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (pin_clks == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
pin_clks = new ClockSet;
|
|
|
|
|
clock_pin_map_.insert(pin, pin_clks);
|
|
|
|
|
}
|
|
|
|
|
pin_clks->insert(clk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClockVertexPinIterator pin_iter2(clk);
|
|
|
|
|
while (pin_iter2.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter2.next();
|
|
|
|
|
ClockSet *pin_clks = clock_vertex_pin_map_.findKey(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (pin_clks == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
pin_clks = new ClockSet;
|
|
|
|
|
clock_vertex_pin_map_.insert(pin, pin_clks);
|
|
|
|
|
}
|
|
|
|
|
pin_clks->insert(clk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeClock(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
deleteExceptionsReferencing(clk);
|
|
|
|
|
deleteInputDelaysReferencing(clk);
|
|
|
|
|
deleteOutputDelaysReferencing(clk);
|
|
|
|
|
deleteClockLatenciesReferencing(clk);
|
|
|
|
|
deleteClockInsertionsReferencing(clk);
|
|
|
|
|
deleteInterClockUncertaintiesReferencing(clk);
|
|
|
|
|
deleteLatchBorrowLimitsReferencing(clk);
|
|
|
|
|
deleteMinPulseWidthReferencing(clk);
|
|
|
|
|
deleteMasterClkRefs(clk);
|
|
|
|
|
clockGroupsDeleteClkRefs(clk);
|
|
|
|
|
clearCycleAcctings();
|
|
|
|
|
|
|
|
|
|
deleteClkPinMappings(clk);
|
|
|
|
|
clocks_.eraseObject(clk);
|
2019-04-11 05:36:48 +02:00
|
|
|
clock_name_map_.erase(clk->name());
|
2018-09-28 17:54:21 +02:00
|
|
|
delete clk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Delete references to clk as a master clock.
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteMasterClkRefs(Clock *clk)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto gclk : clocks_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (gclk->isGenerated()
|
|
|
|
|
&& gclk->masterClk() == clk) {
|
2019-03-13 01:25:53 +01:00
|
|
|
gclk->setMasterClk(nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockDeletePin(Clock *clk,
|
|
|
|
|
Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
ClockSet *pin_clks = clock_pin_map_.findKey(pin);
|
|
|
|
|
pin_clks->erase(clk);
|
|
|
|
|
if (pin_clks->empty())
|
2019-04-11 05:36:48 +02:00
|
|
|
clock_pin_map_.erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
clk->deletePin(pin);
|
|
|
|
|
clk->makeVertexPins(network_);
|
|
|
|
|
makeClkPinMappings(clk);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Clock *
|
|
|
|
|
Sdc::findClock(const char *name) const
|
|
|
|
|
{
|
|
|
|
|
return clock_name_map_.findKey(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isClock(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
ClockSet *clks = findClocks(pin);
|
|
|
|
|
return clks && !clks->empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isVertexPinClock(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
ClockSet *clks = findVertexPinClocks(pin);
|
|
|
|
|
return clks && !clks->empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isVertexPinNonGeneratedClock(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
ClockSet *clks = findVertexPinClocks(pin);
|
|
|
|
|
if (clks) {
|
|
|
|
|
ClockSet::Iterator clk_iter(clks);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
|
|
|
|
if (!clk->isGenerated())
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClockSet *
|
|
|
|
|
Sdc::findVertexPinClocks(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
return clock_vertex_pin_map_.findKey(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClockSet *
|
|
|
|
|
Sdc::findClocks(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
return clock_pin_map_.findKey(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::findClocksMatching(PatternMatch *pattern,
|
|
|
|
|
ClockSeq *clks) const
|
|
|
|
|
{
|
|
|
|
|
if (!pattern->hasWildcards()) {
|
|
|
|
|
Clock *clk = findClock(pattern->pattern());
|
|
|
|
|
if (clk)
|
|
|
|
|
clks->push_back(clk);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto clk : clocks_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
if (pattern->match(clk->name()))
|
|
|
|
|
clks->push_back(clk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClockIterator *
|
2019-03-13 01:25:53 +01:00
|
|
|
Sdc::clockIterator()
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return new ClockIterator(clocks_);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::sortedClocks(ClockSeq &clks)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto clk : clocks_)
|
2018-09-28 17:54:21 +02:00
|
|
|
clks.push_back(clk);
|
|
|
|
|
sort(clks, ClkNameLess());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
class ClkHpinDisable
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ClkHpinDisable(const Clock *clk,
|
|
|
|
|
const Pin *from_pin,
|
|
|
|
|
const Pin *to_pin);
|
|
|
|
|
const Clock *clk() const { return clk_; }
|
|
|
|
|
const Pin *fromPin() const { return from_pin_; }
|
|
|
|
|
const Pin *toPin() const { return to_pin_; }
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
const Clock *clk_;
|
|
|
|
|
const Pin *from_pin_;
|
|
|
|
|
const Pin *to_pin_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ClkHpinDisable::ClkHpinDisable(const Clock *clk,
|
|
|
|
|
const Pin *from_pin,
|
|
|
|
|
const Pin *to_pin) :
|
|
|
|
|
clk_(clk),
|
|
|
|
|
from_pin_(from_pin),
|
|
|
|
|
to_pin_(to_pin)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ClkHpinDisableLess::operator()(const ClkHpinDisable *disable1,
|
|
|
|
|
const ClkHpinDisable *disable2) const
|
|
|
|
|
{
|
|
|
|
|
int clk_index1 = disable1->clk()->index();
|
|
|
|
|
int clk_index2 = disable2->clk()->index();
|
|
|
|
|
if (clk_index1 == clk_index2) {
|
|
|
|
|
const Pin *from_pin1 = disable1->fromPin();
|
|
|
|
|
const Pin *from_pin2 = disable2->fromPin();
|
|
|
|
|
if (from_pin1 == from_pin2) {
|
|
|
|
|
const Pin *to_pin1 = disable1->toPin();
|
|
|
|
|
const Pin *to_pin2 = disable2->toPin();
|
|
|
|
|
return to_pin1 < to_pin2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return from_pin1 < from_pin2;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return clk_index1 < clk_index2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class FindClkHpinDisables : public HpinDrvrLoadVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
FindClkHpinDisables(Clock *clk,
|
|
|
|
|
const Network *network,
|
|
|
|
|
Sdc *sdc);
|
|
|
|
|
~FindClkHpinDisables();
|
|
|
|
|
bool drvrLoadExists(Pin *drvr,
|
|
|
|
|
Pin *load);
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
virtual void visit(HpinDrvrLoad *drvr_load);
|
|
|
|
|
void makeClkHpinDisables(Pin *clk_src,
|
|
|
|
|
Pin *drvr,
|
|
|
|
|
Pin *load);
|
|
|
|
|
|
|
|
|
|
Clock *clk_;
|
|
|
|
|
PinPairSet drvr_loads_;
|
|
|
|
|
const Network *network_;
|
|
|
|
|
Sdc *sdc_;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(FindClkHpinDisables);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FindClkHpinDisables::FindClkHpinDisables(Clock *clk,
|
|
|
|
|
const Network *network,
|
|
|
|
|
Sdc *sdc) :
|
|
|
|
|
HpinDrvrLoadVisitor(),
|
|
|
|
|
clk_(clk),
|
|
|
|
|
network_(network),
|
|
|
|
|
sdc_(sdc)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FindClkHpinDisables::~FindClkHpinDisables()
|
|
|
|
|
{
|
|
|
|
|
drvr_loads_.deleteContents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
FindClkHpinDisables::visit(HpinDrvrLoad *drvr_load)
|
|
|
|
|
{
|
|
|
|
|
Pin *drvr = drvr_load->drvr();
|
|
|
|
|
Pin *load = drvr_load->load();
|
|
|
|
|
|
|
|
|
|
makeClkHpinDisables(drvr, drvr, load);
|
|
|
|
|
|
|
|
|
|
PinSet *hpins_from_drvr = drvr_load->hpinsFromDrvr();
|
|
|
|
|
PinSet::Iterator hpin_iter(hpins_from_drvr);
|
|
|
|
|
while (hpin_iter.hasNext()) {
|
|
|
|
|
Pin *hpin = hpin_iter.next();
|
|
|
|
|
makeClkHpinDisables(hpin, drvr, load);
|
|
|
|
|
}
|
|
|
|
|
drvr_loads_.insert(new PinPair(drvr, load));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
FindClkHpinDisables::makeClkHpinDisables(Pin *clk_src,
|
|
|
|
|
Pin *drvr,
|
|
|
|
|
Pin *load)
|
|
|
|
|
{
|
|
|
|
|
ClockSet *clks = sdc_->findClocks(clk_src);
|
|
|
|
|
ClockSet::Iterator clk_iter(clks);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
|
|
|
|
if (clk != clk_)
|
|
|
|
|
// Do not propagate clock from source pin if another
|
|
|
|
|
// clock is defined on a hierarchical pin between the
|
|
|
|
|
// driver and load.
|
|
|
|
|
sdc_->makeClkHpinDisable(clk, drvr, load);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
FindClkHpinDisables::drvrLoadExists(Pin *drvr,
|
|
|
|
|
Pin *load)
|
|
|
|
|
{
|
|
|
|
|
PinPair probe(drvr, load);
|
|
|
|
|
return drvr_loads_.hasKey(&probe);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeClkHpinDisable(Clock *clk,
|
|
|
|
|
Pin *drvr,
|
|
|
|
|
Pin *load)
|
|
|
|
|
{
|
|
|
|
|
ClkHpinDisable probe(clk, drvr, load);
|
|
|
|
|
if (!clk_hpin_disables_.hasKey(&probe)) {
|
|
|
|
|
ClkHpinDisable *disable = new ClkHpinDisable(clk, drvr, load);
|
|
|
|
|
clk_hpin_disables_.insert(disable);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::ensureClkHpinDisables()
|
|
|
|
|
{
|
|
|
|
|
if (!clk_hpin_disables_valid_) {
|
|
|
|
|
clk_hpin_disables_.deleteContentsClear();
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto clk : clocks_) {
|
2018-09-28 17:54:21 +02:00
|
|
|
PinSet *srcs = clk->pins();
|
|
|
|
|
PinSet::Iterator src_iter(srcs);
|
|
|
|
|
while (src_iter.hasNext()) {
|
|
|
|
|
Pin *src = src_iter.next();
|
|
|
|
|
if (network_->isHierarchical(src)) {
|
|
|
|
|
FindClkHpinDisables visitor(clk, network_, this);
|
|
|
|
|
visitHpinDrvrLoads(src, network_, &visitor);
|
|
|
|
|
// Disable fanouts from the src driver pins that do
|
|
|
|
|
// not go thru the hierarchical src pin.
|
|
|
|
|
PinSet *vpins = clk->vertexPins();
|
|
|
|
|
PinSet::Iterator vpin_iter(vpins);
|
|
|
|
|
while (vpin_iter.hasNext()) {
|
|
|
|
|
Pin *vpin = vpin_iter.next();
|
|
|
|
|
Vertex *vertex, *bidirect_drvr_vertex;
|
|
|
|
|
graph_->pinVertices(vpin, vertex, bidirect_drvr_vertex);
|
|
|
|
|
makeVertexClkHpinDisables(clk, vertex, visitor);
|
|
|
|
|
if (bidirect_drvr_vertex)
|
|
|
|
|
makeVertexClkHpinDisables(clk, bidirect_drvr_vertex, visitor);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
clk_hpin_disables_valid_ = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeVertexClkHpinDisables(Clock *clk,
|
|
|
|
|
Vertex *vertex,
|
|
|
|
|
FindClkHpinDisables &visitor)
|
|
|
|
|
{
|
|
|
|
|
VertexOutEdgeIterator edge_iter(vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
if (edge->isWire()) {
|
|
|
|
|
Pin *drvr = edge->from(graph_)->pin();
|
|
|
|
|
Pin *load = edge->to(graph_)->pin();
|
|
|
|
|
if (!visitor.drvrLoadExists(drvr, load))
|
|
|
|
|
makeClkHpinDisable(clk, drvr, load);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clkHpinDisablesInvalid()
|
|
|
|
|
{
|
|
|
|
|
clk_hpin_disables_valid_ = false;
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto clk : clocks_)
|
2018-09-28 17:54:21 +02:00
|
|
|
clk->makeVertexPins(network_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check that driver/load edge goes thru clock hpin.
|
|
|
|
|
// Check for disable by hierarchical clock pin between driver and load.
|
|
|
|
|
bool
|
|
|
|
|
Sdc::clkDisabledByHpinThru(const Clock *clk,
|
|
|
|
|
const Pin *from_pin,
|
|
|
|
|
const Pin *to_pin)
|
|
|
|
|
{
|
|
|
|
|
if (clk->vertexPins()
|
|
|
|
|
&& clk->vertexPins()->hasKey(const_cast<Pin*>(from_pin))) {
|
|
|
|
|
ClkHpinDisable probe(clk, from_pin, to_pin);
|
|
|
|
|
return clk_hpin_disables_.hasKey(&probe);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setPropagatedClock(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
clk->setIsPropagated(true);
|
2019-03-13 01:25:53 +01:00
|
|
|
removeClockLatency(clk, nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removePropagatedClock(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
clk->setIsPropagated(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setPropagatedClock(Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
propagated_clk_pins_.insert(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
removeClockLatency(nullptr, pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removePropagatedClock(Pin *pin)
|
|
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
propagated_clk_pins_.erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isPropagatedClock(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
return propagated_clk_pins_.hasKey(const_cast<Pin*>(pin));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockSlew(Clock *clk,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
float slew)
|
|
|
|
|
{
|
|
|
|
|
clk->setSlew(tr, min_max, slew);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeClockSlew(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
clk->removeSlew();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockLatency(Clock *clk,
|
|
|
|
|
Pin *pin,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
float delay)
|
|
|
|
|
{
|
|
|
|
|
ClockLatency probe(clk, pin);
|
|
|
|
|
ClockLatency *latency = clk_latencies_.findKey(&probe);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (latency == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
latency = new ClockLatency(clk, pin);
|
|
|
|
|
clk_latencies_.insert(latency);
|
|
|
|
|
}
|
|
|
|
|
latency->setDelay(tr, min_max, delay);
|
|
|
|
|
if (pin && graph_ && network_->isHierarchical(pin))
|
|
|
|
|
annotateHierClkLatency(pin, latency);
|
|
|
|
|
|
|
|
|
|
// set_clock_latency removes set_propagated_clock on the same object.
|
2019-03-13 01:25:53 +01:00
|
|
|
if (clk && pin == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
removePropagatedClock(clk);
|
|
|
|
|
if (pin)
|
|
|
|
|
removePropagatedClock(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeClockLatency(const Clock *clk,
|
|
|
|
|
const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
ClockLatency probe(clk, pin);
|
|
|
|
|
ClockLatency *latency = clk_latencies_.findKey(&probe);
|
|
|
|
|
if (latency)
|
|
|
|
|
deleteClockLatency(latency);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteClockLatency(ClockLatency *latency)
|
|
|
|
|
{
|
|
|
|
|
const Pin *pin = latency->pin();
|
|
|
|
|
if (pin && graph_ && network_->isHierarchical(pin))
|
|
|
|
|
deannotateHierClkLatency(pin);
|
2019-04-11 05:36:48 +02:00
|
|
|
clk_latencies_.erase(latency);
|
2018-09-28 17:54:21 +02:00
|
|
|
delete latency;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteClockLatenciesReferencing(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
ClockLatencies::Iterator latency_iter(clk_latencies_);
|
|
|
|
|
while (latency_iter.hasNext()) {
|
|
|
|
|
ClockLatency *latency = latency_iter.next();
|
|
|
|
|
if (latency->clock() == clk)
|
|
|
|
|
deleteClockLatency(latency);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::hasClockLatency(const Pin *pin) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockLatency probe(nullptr, pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
return clk_latencies_.hasKey(&probe);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockLatency(const Clock *clk,
|
|
|
|
|
const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
// Return values.
|
|
|
|
|
float &latency,
|
|
|
|
|
bool &exists) const
|
|
|
|
|
{
|
|
|
|
|
latency = 0.0;
|
|
|
|
|
exists = false;
|
|
|
|
|
if (pin && clk) {
|
|
|
|
|
ClockLatency probe(clk, pin);
|
|
|
|
|
ClockLatency *latencies = clk_latencies_.findKey(&probe);
|
|
|
|
|
if (latencies)
|
|
|
|
|
latencies->delay(tr, min_max, latency, exists);
|
|
|
|
|
}
|
|
|
|
|
if (!exists) {
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockLatency probe(nullptr, pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
ClockLatency *latencies = clk_latencies_.findKey(&probe);
|
|
|
|
|
if (latencies)
|
|
|
|
|
latencies->delay(tr, min_max, latency, exists);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockLatency(const Clock *clk,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
// Return values.
|
|
|
|
|
float &latency,
|
|
|
|
|
bool &exists) const
|
|
|
|
|
{
|
|
|
|
|
latency = 0.0;
|
|
|
|
|
exists = false;
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockLatency probe(clk, nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
ClockLatency *latencies = clk_latencies_.findKey(&probe);
|
|
|
|
|
if (latencies)
|
|
|
|
|
latencies->delay(tr, min_max, latency, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
Sdc::clockLatency(const Clock *clk,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max) const
|
|
|
|
|
{
|
|
|
|
|
float latency;
|
|
|
|
|
bool exists;
|
|
|
|
|
clockLatency(clk, tr, min_max,
|
|
|
|
|
latency, exists);
|
|
|
|
|
return latency;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockUncertainty(Pin *pin,
|
|
|
|
|
const SetupHoldAll *setup_hold,
|
|
|
|
|
float uncertainty)
|
|
|
|
|
{
|
|
|
|
|
ClockUncertainties *uncertainties = pin_clk_uncertainty_map_.findKey(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (uncertainties == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
uncertainties = new ClockUncertainties;
|
|
|
|
|
pin_clk_uncertainty_map_[pin] = uncertainties;
|
|
|
|
|
}
|
|
|
|
|
uncertainties->setValue(setup_hold, uncertainty);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeClockUncertainty(Pin *pin,
|
|
|
|
|
const SetupHoldAll *setup_hold)
|
|
|
|
|
{
|
|
|
|
|
ClockUncertainties *uncertainties = pin_clk_uncertainty_map_.findKey(pin);
|
|
|
|
|
if (uncertainties) {
|
|
|
|
|
uncertainties->removeValue(setup_hold);
|
|
|
|
|
if (uncertainties->empty()) {
|
|
|
|
|
delete uncertainties;
|
2019-04-11 05:36:48 +02:00
|
|
|
pin_clk_uncertainty_map_.erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClockUncertainties *
|
|
|
|
|
Sdc::clockUncertainties(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
return pin_clk_uncertainty_map_.findKey(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockUncertainty(const Pin *pin,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
float &uncertainty,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
ClockUncertainties *uncertainties = clockUncertainties(pin);
|
|
|
|
|
if (uncertainties)
|
|
|
|
|
uncertainties->value(setup_hold, uncertainty, exists);
|
|
|
|
|
else {
|
|
|
|
|
uncertainty = 0.0;
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockUncertainty(const Clock *src_clk,
|
|
|
|
|
const TransRiseFall *src_tr,
|
|
|
|
|
const Clock *tgt_clk,
|
|
|
|
|
const TransRiseFall *tgt_tr,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
float &uncertainty,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
InterClockUncertainty probe(src_clk, tgt_clk);
|
|
|
|
|
InterClockUncertainty *uncertainties =
|
|
|
|
|
inter_clk_uncertainties_.findKey(&probe);
|
|
|
|
|
if (uncertainties)
|
|
|
|
|
uncertainties->uncertainty(src_tr, tgt_tr, setup_hold,
|
|
|
|
|
uncertainty, exists);
|
|
|
|
|
else {
|
|
|
|
|
uncertainty = 0.0;
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockUncertainty(Clock *from_clk,
|
|
|
|
|
const TransRiseFallBoth *from_tr,
|
|
|
|
|
Clock *to_clk,
|
|
|
|
|
const TransRiseFallBoth *to_tr,
|
|
|
|
|
const SetupHoldAll *setup_hold,
|
|
|
|
|
float uncertainty)
|
|
|
|
|
{
|
|
|
|
|
InterClockUncertainty probe(from_clk, to_clk);
|
|
|
|
|
InterClockUncertainty *uncertainties =
|
|
|
|
|
inter_clk_uncertainties_.findKey(&probe);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (uncertainties == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
uncertainties = new InterClockUncertainty(from_clk, to_clk);
|
|
|
|
|
inter_clk_uncertainties_.insert(uncertainties);
|
|
|
|
|
}
|
|
|
|
|
uncertainties->setUncertainty(from_tr, to_tr, setup_hold, uncertainty);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeClockUncertainty(Clock *from_clk,
|
|
|
|
|
const TransRiseFallBoth *from_tr,
|
|
|
|
|
Clock *to_clk,
|
|
|
|
|
const TransRiseFallBoth *to_tr,
|
|
|
|
|
const SetupHoldAll *setup_hold)
|
|
|
|
|
{
|
|
|
|
|
InterClockUncertainty probe(from_clk, to_clk);
|
|
|
|
|
InterClockUncertainty *uncertainties =
|
|
|
|
|
inter_clk_uncertainties_.findKey(&probe);
|
|
|
|
|
if (uncertainties) {
|
|
|
|
|
uncertainties->removeUncertainty(from_tr, to_tr, setup_hold);
|
|
|
|
|
if (uncertainties->empty()) {
|
2019-04-11 05:36:48 +02:00
|
|
|
inter_clk_uncertainties_.erase(uncertainties);
|
2018-09-28 17:54:21 +02:00
|
|
|
delete uncertainties;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteInterClockUncertainty(InterClockUncertainty *uncertainties)
|
|
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
inter_clk_uncertainties_.erase(uncertainties);
|
2018-09-28 17:54:21 +02:00
|
|
|
delete uncertainties;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteInterClockUncertaintiesReferencing(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
InterClockUncertaintySet::Iterator inter_iter(inter_clk_uncertainties_);
|
|
|
|
|
while (inter_iter.hasNext()) {
|
|
|
|
|
InterClockUncertainty *uncertainties = inter_iter.next();
|
|
|
|
|
if (uncertainties->src() == clk
|
|
|
|
|
|| uncertainties->target() == clk)
|
|
|
|
|
deleteInterClockUncertainty(uncertainties);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockInsertion(const Clock *clk,
|
|
|
|
|
const Pin *pin,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
const EarlyLateAll *early_late,
|
|
|
|
|
float delay)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (clk_insertions_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_insertions_ = new ClockInsertions;
|
|
|
|
|
ClockInsertion probe(clk, pin);
|
|
|
|
|
ClockInsertion *insertion = clk_insertions_->findKey(&probe);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (insertion == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
insertion = new ClockInsertion(clk, pin);
|
|
|
|
|
clk_insertions_->insert(insertion);
|
|
|
|
|
}
|
|
|
|
|
insertion->setDelay(tr, min_max, early_late, delay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockInsertion(const Clock *clk,
|
|
|
|
|
const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
float delay)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (clk_insertions_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_insertions_ = new ClockInsertions;
|
|
|
|
|
ClockInsertion probe(clk, pin);
|
|
|
|
|
ClockInsertion *insertion = clk_insertions_->findKey(&probe);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (insertion == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
insertion = new ClockInsertion(clk, pin);
|
|
|
|
|
clk_insertions_->insert(insertion);
|
|
|
|
|
}
|
|
|
|
|
insertion->setDelay(tr, min_max, early_late, delay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeClockInsertion(const Clock *clk,
|
|
|
|
|
const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
if (clk_insertions_) {
|
|
|
|
|
ClockInsertion probe(clk, pin);
|
|
|
|
|
ClockInsertion *insertion = clk_insertions_->findKey(&probe);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (insertion != nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
deleteClockInsertion(insertion);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteClockInsertion(ClockInsertion *insertion)
|
|
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
clk_insertions_->erase(insertion);
|
2018-09-28 17:54:21 +02:00
|
|
|
delete insertion;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteClockInsertionsReferencing(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
ClockInsertions::Iterator insertion_iter(clk_insertions_);
|
|
|
|
|
while (insertion_iter.hasNext()) {
|
|
|
|
|
ClockInsertion *insertion = insertion_iter.next();
|
|
|
|
|
if (insertion->clock() == clk)
|
|
|
|
|
deleteClockInsertion(insertion);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
Sdc::clockInsertion(const Clock *clk,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
const EarlyLate *early_late) const
|
|
|
|
|
{
|
|
|
|
|
float insertion;
|
|
|
|
|
bool exists;
|
2019-03-13 01:25:53 +01:00
|
|
|
clockInsertion(clk, nullptr, tr, min_max, early_late, insertion, exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
return insertion;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::hasClockInsertion(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
if (clk_insertions_) {
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockInsertion probe(nullptr, pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
return clk_insertions_->hasKey(&probe);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockInsertion(const Clock *clk,
|
|
|
|
|
const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
const EarlyLate *early_late,
|
|
|
|
|
// Return values.
|
|
|
|
|
float &insertion,
|
|
|
|
|
bool &exists) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockInsertion *insert = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (clk_insertions_) {
|
|
|
|
|
if (clk && pin) {
|
|
|
|
|
ClockInsertion probe(clk, pin);
|
|
|
|
|
insert = clk_insertions_->findKey(&probe);
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
if (insert == nullptr && pin) {
|
|
|
|
|
ClockInsertion probe(nullptr, pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
insert = clk_insertions_->findKey(&probe);
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
if (insert == nullptr && clk) {
|
|
|
|
|
ClockInsertion probe(clk, nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
insert = clk_insertions_->findKey(&probe);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (insert)
|
|
|
|
|
insert->delay(tr, min_max, early_late, insertion, exists);
|
|
|
|
|
else {
|
|
|
|
|
insertion = 0.0;
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ClockLatencyPinClkLess::operator()(const ClockLatency *latency1,
|
|
|
|
|
const ClockLatency *latency2) const
|
|
|
|
|
{
|
|
|
|
|
const Clock *clk1 = latency1->clock();
|
|
|
|
|
const Clock *clk2 = latency2->clock();
|
|
|
|
|
const Pin *pin1 = latency1->pin();
|
|
|
|
|
const Pin *pin2 = latency2->pin();
|
|
|
|
|
return clk1 < clk2
|
|
|
|
|
|| (clk1 == clk2
|
|
|
|
|
&& pin1 < pin2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ClockInsertionPinClkLess::operator()(const ClockInsertion *insert1,
|
|
|
|
|
const ClockInsertion *insert2)const
|
|
|
|
|
{
|
|
|
|
|
const Clock *clk1 = insert1->clock();
|
|
|
|
|
const Clock *clk2 = insert2->clock();
|
|
|
|
|
const Pin *pin1 = insert1->pin();
|
|
|
|
|
const Pin *pin2 = insert2->pin();
|
|
|
|
|
return (clk1 && clk2
|
|
|
|
|
&& (clk1 < clk2
|
|
|
|
|
|| (clk1 == clk2
|
|
|
|
|
&& pin1 && pin2
|
|
|
|
|
&& pin1 < pin2)))
|
2019-03-13 01:25:53 +01:00
|
|
|
|| (clk1 == nullptr && clk2)
|
|
|
|
|
|| (clk1 == nullptr && clk2 == nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
&& ((pin1 && pin2
|
|
|
|
|
&& pin1 < pin2)
|
2019-03-13 01:25:53 +01:00
|
|
|
|| (pin1 == nullptr && pin2)));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
ClockGroups *
|
|
|
|
|
Sdc::makeClockGroups(const char *name,
|
|
|
|
|
bool logically_exclusive,
|
|
|
|
|
bool physically_exclusive,
|
|
|
|
|
bool asynchronous,
|
|
|
|
|
bool allow_paths,
|
|
|
|
|
const char *comment)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
char *gen_name = nullptr;
|
|
|
|
|
if (name == nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
|| name[0] == '\0')
|
2019-01-17 00:37:31 +01:00
|
|
|
name = gen_name = makeClockGroupsName();
|
|
|
|
|
else {
|
|
|
|
|
ClockGroups *groups = clk_groups_name_map_.findKey(name);
|
|
|
|
|
if (groups)
|
|
|
|
|
removeClockGroups(groups);
|
|
|
|
|
}
|
|
|
|
|
ClockGroups *groups = new ClockGroups(name, logically_exclusive,
|
|
|
|
|
physically_exclusive,
|
|
|
|
|
asynchronous, allow_paths, comment);
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_groups_name_map_[groups->name()] = groups;
|
2019-01-17 00:37:31 +01:00
|
|
|
stringDelete(gen_name);
|
2018-09-28 17:54:21 +02:00
|
|
|
return groups;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate a name for the clock group.
|
|
|
|
|
char *
|
|
|
|
|
Sdc::makeClockGroupsName()
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
char *name = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
int i = 0;
|
|
|
|
|
do {
|
|
|
|
|
i++;
|
2019-01-17 00:37:31 +01:00
|
|
|
stringDelete(name);
|
|
|
|
|
name = stringPrint("group%d", i);
|
2018-09-28 17:54:21 +02:00
|
|
|
} while (clk_groups_name_map_.hasKey(name));
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeClockGroup(ClockGroups *clk_groups,
|
|
|
|
|
ClockSet *clks)
|
|
|
|
|
{
|
|
|
|
|
clk_groups->makeClockGroup(clks);
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-16 21:07:59 +01:00
|
|
|
ClockGroupIterator *
|
|
|
|
|
Sdc::clockGroupIterator()
|
|
|
|
|
{
|
|
|
|
|
return new ClockGroupIterator(clk_groups_name_map_);
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
void
|
|
|
|
|
Sdc::ensureClkGroupExclusions()
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (clk_group_exclusions_ == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_group_exclusions_ = new ClockPairSet;
|
2018-11-09 19:04:16 +01:00
|
|
|
clk_group_same_ = new ClockPairSet;
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto name_clk_groups : clk_groups_name_map_)
|
|
|
|
|
makeClkGroupExclusions(name_clk_groups.second);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeClkGroupExclusions(ClockGroups *clk_groups)
|
|
|
|
|
{
|
|
|
|
|
if (!(clk_groups->asynchronous()
|
|
|
|
|
&& clk_groups->allowPaths())) {
|
|
|
|
|
ClockGroupSet *groups = clk_groups->groups();
|
2018-11-09 19:04:16 +01:00
|
|
|
if (groups->size() == 1)
|
|
|
|
|
makeClkGroupExclusions1(groups);
|
|
|
|
|
else
|
|
|
|
|
makeClkGroupExclusions(groups);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If there is only one group all clocks not in the group
|
|
|
|
|
// are excluded.
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeClkGroupExclusions1(ClockGroupSet *groups)
|
|
|
|
|
{
|
|
|
|
|
ClockGroupSet::Iterator group_iter1(groups);
|
|
|
|
|
ClockGroup *group1 = group_iter1.next();
|
|
|
|
|
ClockSet *clks1 = group1->clks();
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto clk1 : *clks1) {
|
|
|
|
|
for (Clock *clk2 : clocks_) {
|
2018-11-09 19:04:16 +01:00
|
|
|
if (clk2 != clk1
|
2019-03-13 01:25:53 +01:00
|
|
|
&& !group1->isMember(clk2))
|
|
|
|
|
clk_group_exclusions_->insert(ClockPair(clk1, clk2));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2018-11-09 19:04:16 +01:00
|
|
|
}
|
|
|
|
|
makeClkGroupSame(group1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeClkGroupExclusions(ClockGroupSet *groups)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto group1 : *groups) {
|
2018-11-09 19:04:16 +01:00
|
|
|
ClockSet *clks1 = group1->clks();
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto group2 : *groups) {
|
2018-11-09 19:04:16 +01:00
|
|
|
if (group1 != group2) {
|
|
|
|
|
ClockSet *clks2 = group2->clks();
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto clk1 : *clks1) {
|
|
|
|
|
for (auto clk2 : *clks2) {
|
2018-11-09 19:04:16 +01:00
|
|
|
// ClockPair is symmetric so only add one clk1/clk2 pair.
|
|
|
|
|
if (clk1->index() < clk2->index()) {
|
2019-03-13 01:25:53 +01:00
|
|
|
clk_group_exclusions_->insert(ClockPair(clk1, clk2));
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-09 19:04:16 +01:00
|
|
|
makeClkGroupSame(group1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeClkGroupSame(ClockGroup *group)
|
|
|
|
|
{
|
|
|
|
|
ClockSet *clks = group->clks();
|
2019-03-13 01:25:53 +01:00
|
|
|
for (auto clk1 : *clks) {
|
|
|
|
|
for (auto clk2 : *clks) {
|
2018-11-09 19:04:16 +01:00
|
|
|
if (clk1->index() <= clk2->index()) {
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockPair clk_pair(clk1, clk2);
|
|
|
|
|
if (!clk_group_same_->hasKey(clk_pair))
|
2018-11-09 19:04:16 +01:00
|
|
|
clk_group_same_->insert(clk_pair);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clearClkGroupExclusions()
|
|
|
|
|
{
|
|
|
|
|
if (clk_group_exclusions_) {
|
|
|
|
|
delete clk_group_exclusions_;
|
2018-11-09 19:04:16 +01:00
|
|
|
delete clk_group_same_;
|
2019-03-13 01:25:53 +01:00
|
|
|
clk_group_exclusions_ = nullptr;
|
|
|
|
|
clk_group_same_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::sameClockGroup(const Clock *clk1,
|
2018-11-09 19:04:16 +01:00
|
|
|
const Clock *clk2)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
if (clk1 && clk2) {
|
|
|
|
|
ClockPair clk_pair(clk1, clk2);
|
2019-03-13 01:25:53 +01:00
|
|
|
bool excluded = clk_group_exclusions_->hasKey(clk_pair);
|
2018-09-28 17:54:21 +02:00
|
|
|
return !excluded;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-09 19:04:16 +01:00
|
|
|
bool
|
|
|
|
|
Sdc::sameClockGroupExplicit(const Clock *clk1,
|
|
|
|
|
const Clock *clk2)
|
|
|
|
|
{
|
|
|
|
|
ClockPair clk_pair(clk1, clk2);
|
2019-03-13 01:25:53 +01:00
|
|
|
return clk_group_same_->hasKey(clk_pair);
|
2018-11-09 19:04:16 +01:00
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
void
|
|
|
|
|
Sdc::removeClockGroups(const char *name)
|
|
|
|
|
{
|
|
|
|
|
ClockGroups *clk_groups = clk_groups_name_map_.findKey(name);
|
|
|
|
|
if (clk_groups)
|
|
|
|
|
removeClockGroups(clk_groups);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeClockGroupsLogicallyExclusive(const char *name)
|
|
|
|
|
{
|
|
|
|
|
if (name) {
|
|
|
|
|
ClockGroups *groups = clk_groups_name_map_.findKey(name);
|
|
|
|
|
if (groups->logicallyExclusive())
|
|
|
|
|
removeClockGroups(groups);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ClockGroupsNameMap::Iterator groups_iter(clk_groups_name_map_);
|
|
|
|
|
while (groups_iter.hasNext()) {
|
|
|
|
|
ClockGroups *groups = groups_iter.next();
|
|
|
|
|
if (groups->logicallyExclusive())
|
|
|
|
|
removeClockGroups(groups);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeClockGroupsPhysicallyExclusive(const char *name)
|
|
|
|
|
{
|
|
|
|
|
if (name) {
|
|
|
|
|
ClockGroups *groups = clk_groups_name_map_.findKey(name);
|
|
|
|
|
if (groups->physicallyExclusive())
|
|
|
|
|
removeClockGroups(groups);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ClockGroupsNameMap::Iterator groups_iter(clk_groups_name_map_);
|
|
|
|
|
while (groups_iter.hasNext()) {
|
|
|
|
|
ClockGroups *groups = groups_iter.next();
|
|
|
|
|
if (groups->physicallyExclusive())
|
|
|
|
|
removeClockGroups(groups);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeClockGroupsAsynchronous(const char *name)
|
|
|
|
|
{
|
|
|
|
|
if (name) {
|
|
|
|
|
ClockGroups *groups = clk_groups_name_map_.findKey(name);
|
|
|
|
|
if (groups->asynchronous())
|
|
|
|
|
removeClockGroups(groups);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ClockGroupsNameMap::Iterator groups_iter(clk_groups_name_map_);
|
|
|
|
|
while (groups_iter.hasNext()) {
|
|
|
|
|
ClockGroups *groups = groups_iter.next();
|
|
|
|
|
if (groups->asynchronous())
|
|
|
|
|
removeClockGroups(groups);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeClockGroups(ClockGroups *groups)
|
|
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
clk_groups_name_map_.erase(groups->name());
|
2018-09-28 17:54:21 +02:00
|
|
|
delete groups;
|
|
|
|
|
// Can't delete excluded clock pairs for deleted clock groups because
|
|
|
|
|
// some other clock groups may exclude the same clock pair.
|
|
|
|
|
clearClkGroupExclusions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockGroupsDeleteClkRefs(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
ClockGroupsNameMap::Iterator groups_iter(clk_groups_name_map_);
|
|
|
|
|
while (groups_iter.hasNext()) {
|
|
|
|
|
ClockGroups *groups = groups_iter.next();
|
|
|
|
|
groups->removeClock(clk);
|
|
|
|
|
}
|
|
|
|
|
clearClkGroupExclusions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockSense(PinSet *pins,
|
2019-06-22 20:17:13 +02:00
|
|
|
ClockSet *clks,
|
|
|
|
|
ClockSense sense)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-06-22 20:17:13 +02:00
|
|
|
if (clks && clks->empty()) {
|
2018-09-28 17:54:21 +02:00
|
|
|
delete clks;
|
2019-03-13 01:25:53 +01:00
|
|
|
clks = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
PinSet::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
if (clks) {
|
|
|
|
|
ClockSet::Iterator clk_iter(clks);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
|
|
|
|
setClockSense(pin, clk, sense);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
setClockSense(pin, nullptr, sense);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
delete pins;
|
|
|
|
|
delete clks;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockSense(const Pin *pin,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
ClockSense sense)
|
|
|
|
|
{
|
|
|
|
|
PinClockPair probe(pin, clk);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (clk_sense_map_.hasKey(probe))
|
|
|
|
|
clk_sense_map_[probe] = sense;
|
2018-09-28 17:54:21 +02:00
|
|
|
else {
|
2019-03-13 01:25:53 +01:00
|
|
|
PinClockPair pin_clk(pin, clk);
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_sense_map_[pin_clk] = sense;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::clkStopPropagation(const Pin *pin,
|
|
|
|
|
const Clock *clk) const
|
|
|
|
|
{
|
|
|
|
|
PinClockPair pin_clk(pin, clk);
|
|
|
|
|
ClockSense sense;
|
|
|
|
|
bool exists;
|
2019-03-13 01:25:53 +01:00
|
|
|
clk_sense_map_.findKey(pin_clk, sense, exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!exists) {
|
2019-03-13 01:25:53 +01:00
|
|
|
PinClockPair pin_clk1(pin, nullptr);
|
|
|
|
|
clk_sense_map_.findKey(pin_clk1, sense, exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
return exists
|
2019-03-13 01:25:53 +01:00
|
|
|
&& sense == ClockSense::stop;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::clkStopSense(const Pin *to_pin,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
const TransRiseFall *from_tr,
|
|
|
|
|
const TransRiseFall *to_tr) const
|
|
|
|
|
{
|
|
|
|
|
PinClockPair pin_clk(to_pin, clk);
|
|
|
|
|
ClockSense sense;
|
|
|
|
|
bool exists;
|
2019-03-13 01:25:53 +01:00
|
|
|
clk_sense_map_.findKey(pin_clk, sense, exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (!exists) {
|
2019-03-13 01:25:53 +01:00
|
|
|
PinClockPair pin(to_pin, nullptr);
|
|
|
|
|
clk_sense_map_.findKey(pin, sense, exists);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
return exists
|
2019-03-13 01:25:53 +01:00
|
|
|
&& (sense == ClockSense::stop
|
|
|
|
|
|| (sense == ClockSense::positive
|
2018-09-28 17:54:21 +02:00
|
|
|
&& from_tr != to_tr)
|
2019-03-13 01:25:53 +01:00
|
|
|
|| (sense == ClockSense::negative
|
2018-09-28 17:54:21 +02:00
|
|
|
&& from_tr == to_tr));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::clkStopPropagation(const Clock *clk,
|
|
|
|
|
const Pin *from_pin,
|
|
|
|
|
const TransRiseFall *from_tr,
|
|
|
|
|
const Pin *to_pin,
|
|
|
|
|
const TransRiseFall *to_tr) const
|
|
|
|
|
{
|
|
|
|
|
return clkStopPropagation(from_pin, clk)
|
|
|
|
|
|| clkStopSense(to_pin, clk, from_tr, to_tr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PinClockPairLess::PinClockPairLess(const Network *network) :
|
|
|
|
|
network_(network)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2019-03-13 01:25:53 +01:00
|
|
|
PinClockPairLess::operator()(const PinClockPair &pin_clk1,
|
|
|
|
|
const PinClockPair &pin_clk2) const
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
const Pin *pin1 = pin_clk1.first;
|
|
|
|
|
const Pin *pin2 = pin_clk2.first;
|
|
|
|
|
const Clock *clk1 = pin_clk1.second;
|
|
|
|
|
const Clock *clk2 = pin_clk2.second;
|
2018-09-28 17:54:21 +02:00
|
|
|
return pin1 < pin2
|
|
|
|
|
|| (pin1 == pin2
|
2019-03-13 01:25:53 +01:00
|
|
|
&& ((clk1 == nullptr && clk2)
|
2018-09-28 17:54:21 +02:00
|
|
|
|| (clk1 && clk2
|
|
|
|
|
&& clk1->index() < clk2->index())));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockGatingCheck(const TransRiseFallBoth *tr,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
float margin)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (clk_gating_check_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
clk_gating_check_ = new ClockGatingCheck;
|
|
|
|
|
clk_gating_check_->margins()->setValue(tr, setup_hold, margin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockGatingCheck(Clock *clk,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
float margin)
|
|
|
|
|
{
|
|
|
|
|
ClockGatingCheck *check = clk_gating_check_map_.findKey(clk);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (check == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
check = new ClockGatingCheck();
|
|
|
|
|
clk_gating_check_map_[clk] = check;
|
|
|
|
|
}
|
|
|
|
|
check->margins()->setValue(tr, setup_hold, margin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockGatingCheck(Instance *inst,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
float margin,
|
|
|
|
|
LogicValue active_value)
|
|
|
|
|
{
|
|
|
|
|
ClockGatingCheck *check = inst_clk_gating_check_map_.findKey(inst);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (check == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
check = new ClockGatingCheck();
|
|
|
|
|
inst_clk_gating_check_map_[inst] = check;
|
|
|
|
|
}
|
|
|
|
|
check->margins()->setValue(tr, setup_hold, margin);
|
|
|
|
|
check->setActiveValue(active_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClockGatingCheck(const Pin *pin,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
float margin,
|
|
|
|
|
LogicValue active_value)
|
|
|
|
|
{
|
|
|
|
|
ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (check == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
check = new ClockGatingCheck();
|
|
|
|
|
pin_clk_gating_check_map_[pin] = check;
|
|
|
|
|
}
|
|
|
|
|
check->margins()->setValue(tr, setup_hold, margin);
|
|
|
|
|
check->setActiveValue(active_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockGatingMarginEnablePin(const Pin *enable_pin,
|
|
|
|
|
const TransRiseFall *enable_tr,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
bool &exists, float &margin)
|
|
|
|
|
{
|
|
|
|
|
ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(enable_pin);
|
|
|
|
|
if (check)
|
|
|
|
|
check->margins()->value(enable_tr, setup_hold, margin, exists);
|
|
|
|
|
else
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockGatingMarginInstance(Instance *inst,
|
|
|
|
|
const TransRiseFall *enable_tr,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
bool &exists,
|
|
|
|
|
float &margin)
|
|
|
|
|
{
|
|
|
|
|
ClockGatingCheck *check = inst_clk_gating_check_map_.findKey(inst);
|
|
|
|
|
if (check)
|
|
|
|
|
check->margins()->value(enable_tr, setup_hold, margin, exists);
|
|
|
|
|
else
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockGatingMarginClkPin(const Pin *clk_pin,
|
|
|
|
|
const TransRiseFall *enable_tr,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
bool &exists,
|
|
|
|
|
float &margin)
|
|
|
|
|
{
|
|
|
|
|
ClockGatingCheck *check = pin_clk_gating_check_map_.findKey(clk_pin);
|
|
|
|
|
if (check)
|
|
|
|
|
check->margins()->value(enable_tr, setup_hold, margin, exists);
|
|
|
|
|
else
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockGatingMarginClk(const Clock *clk,
|
|
|
|
|
const TransRiseFall *enable_tr,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
bool &exists,
|
|
|
|
|
float &margin)
|
|
|
|
|
{
|
|
|
|
|
ClockGatingCheck *check = clk_gating_check_map_.findKey(clk);
|
|
|
|
|
if (check)
|
|
|
|
|
check->margins()->value(enable_tr, setup_hold, margin, exists);
|
|
|
|
|
else
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockGatingMargin(const TransRiseFall *enable_tr,
|
|
|
|
|
const SetupHold *setup_hold,
|
|
|
|
|
bool &exists,
|
|
|
|
|
float &margin)
|
|
|
|
|
{
|
|
|
|
|
if (clk_gating_check_)
|
|
|
|
|
clk_gating_check_->margins()->value(enable_tr, setup_hold, margin, exists);
|
|
|
|
|
else
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogicValue
|
|
|
|
|
Sdc::clockGatingActiveValue(const Pin *clk_pin,
|
|
|
|
|
const Pin *enable_pin)
|
|
|
|
|
{
|
|
|
|
|
ClockGatingCheck *check;
|
|
|
|
|
check = pin_clk_gating_check_map_.findKey(enable_pin);
|
|
|
|
|
if (check)
|
|
|
|
|
return check->activeValue();
|
|
|
|
|
Instance *inst = network_->instance(enable_pin);
|
|
|
|
|
check = inst_clk_gating_check_map_.findKey(inst);
|
|
|
|
|
if (check)
|
|
|
|
|
return check->activeValue();
|
|
|
|
|
check = pin_clk_gating_check_map_.findKey(clk_pin);
|
|
|
|
|
if (check)
|
|
|
|
|
return check->activeValue();
|
2019-03-13 01:25:53 +01:00
|
|
|
return LogicValue::unknown;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// Determine cycle accounting "on demand".
|
|
|
|
|
CycleAccting *
|
|
|
|
|
Sdc::cycleAccting(const ClockEdge *src,
|
|
|
|
|
const ClockEdge *tgt)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (src == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
src = tgt;
|
|
|
|
|
CycleAccting probe(src, tgt);
|
|
|
|
|
CycleAccting *acct = cycle_acctings_.findKey(&probe);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (acct == nullptr) {
|
|
|
|
|
UniqueLock lock(cycle_acctings_lock_);
|
2019-09-07 21:37:05 +02:00
|
|
|
// Recheck with lock.
|
|
|
|
|
acct = cycle_acctings_.findKey(&probe);
|
|
|
|
|
if (acct == nullptr) {
|
|
|
|
|
acct = new CycleAccting(src, tgt);
|
|
|
|
|
if (src == defaultArrivalClockEdge())
|
|
|
|
|
acct->findDefaultArrivalSrcDelays();
|
|
|
|
|
else
|
|
|
|
|
acct->findDelays(this);
|
|
|
|
|
cycle_acctings_.insert(acct);
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
return acct;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::reportClkToClkMaxCycleWarnings()
|
|
|
|
|
{
|
|
|
|
|
// Find cycle acctings that exceed max cycle count. Eliminate
|
|
|
|
|
// duplicate warnings between different src/tgt clk edges.
|
|
|
|
|
ClockPairSet clk_warnings;
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockPairSeq clk_warnings2;
|
2019-09-07 21:37:05 +02:00
|
|
|
CycleAcctingSet::Iterator acct_iter(cycle_acctings_);
|
|
|
|
|
while (acct_iter.hasNext()) {
|
|
|
|
|
CycleAccting *acct = acct_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
if (acct->maxCyclesExceeded()) {
|
|
|
|
|
Clock *src = acct->src()->clock();
|
|
|
|
|
Clock *tgt = acct->target()->clock();
|
2019-03-13 01:25:53 +01:00
|
|
|
// Canonicalize the warning wrt src/tgt.
|
|
|
|
|
if (src->index() > tgt->index())
|
|
|
|
|
std::swap(src, tgt);
|
2018-09-28 17:54:21 +02:00
|
|
|
ClockPair clk_pair(src, tgt);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (!clk_warnings.hasKey(clk_pair)) {
|
|
|
|
|
clk_warnings.insert(clk_pair);
|
|
|
|
|
clk_warnings2.push_back(clk_pair);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
// Sort clk pairs so that results are stable.
|
|
|
|
|
sort(clk_warnings2, ClockPairLess());
|
2019-03-13 01:25:53 +01:00
|
|
|
|
|
|
|
|
for (auto pair : clk_warnings2) {
|
2019-01-17 00:37:31 +01:00
|
|
|
report_->warn("No common period was found between clocks %s and %s.\n",
|
2019-03-13 01:25:53 +01:00
|
|
|
pair.first->name(),
|
|
|
|
|
pair.second->name());
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clearCycleAcctings()
|
|
|
|
|
{
|
|
|
|
|
cycle_acctings_.deleteContentsClear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setDataCheck(Pin *from,
|
|
|
|
|
const TransRiseFallBoth *from_tr,
|
|
|
|
|
Pin *to,
|
|
|
|
|
const TransRiseFallBoth *to_tr,
|
|
|
|
|
Clock *clk,
|
2019-06-27 00:58:23 +02:00
|
|
|
const SetupHoldAll *setup_hold,
|
2018-09-28 17:54:21 +02:00
|
|
|
float margin)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
DataCheck *check = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
DataCheckSet *checks = data_checks_from_map_.findKey(from);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (checks == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
checks = new DataCheckSet(DataCheckLess(network_));
|
|
|
|
|
data_checks_from_map_[from] = checks;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
DataCheck probe(from, to, clk);
|
|
|
|
|
check = checks->findKey(&probe);
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
if (check == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
check = new DataCheck(from, to, clk);
|
|
|
|
|
check->setMargin(from_tr, to_tr, setup_hold, margin);
|
|
|
|
|
checks->insert(check);
|
|
|
|
|
|
|
|
|
|
checks = data_checks_to_map_.findKey(to);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (checks == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
checks = new DataCheckSet(DataCheckLess(network_));
|
|
|
|
|
data_checks_to_map_[to] = checks;
|
|
|
|
|
}
|
|
|
|
|
checks->insert(check);
|
|
|
|
|
|
|
|
|
|
if (graph_)
|
|
|
|
|
annotateGraphConstrained(to, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDataCheck(Pin *from,
|
|
|
|
|
const TransRiseFallBoth *from_tr,
|
|
|
|
|
Pin *to,
|
|
|
|
|
const TransRiseFallBoth *to_tr,
|
|
|
|
|
Clock *clk,
|
2019-06-27 00:58:23 +02:00
|
|
|
const SetupHoldAll *setup_hold)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
DataCheck probe(from, to, clk);
|
|
|
|
|
DataCheckSet *checks = data_checks_from_map_.findKey(from);
|
|
|
|
|
if (checks) {
|
|
|
|
|
DataCheck *check = checks->findKey(&probe);
|
|
|
|
|
if (check) {
|
|
|
|
|
check->removeMargin(from_tr, to_tr, setup_hold);
|
|
|
|
|
if (check->empty()) {
|
2019-04-11 05:36:48 +02:00
|
|
|
checks->erase(check);
|
2018-09-28 17:54:21 +02:00
|
|
|
checks = data_checks_to_map_.findKey(to);
|
|
|
|
|
if (checks)
|
2019-04-11 05:36:48 +02:00
|
|
|
checks->erase(check);
|
2018-09-28 17:54:21 +02:00
|
|
|
delete check;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DataCheckSet *
|
|
|
|
|
Sdc::dataChecksFrom(const Pin *from) const
|
|
|
|
|
{
|
|
|
|
|
return data_checks_from_map_.findKey(from);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DataCheckSet *
|
|
|
|
|
Sdc::dataChecksTo(const Pin *to) const
|
|
|
|
|
{
|
|
|
|
|
return data_checks_to_map_.findKey(to);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setLatchBorrowLimit(Pin *pin,
|
|
|
|
|
float limit)
|
|
|
|
|
{
|
|
|
|
|
pin_latch_borrow_limit_map_[pin] = limit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setLatchBorrowLimit(Instance *inst,
|
|
|
|
|
float limit)
|
|
|
|
|
{
|
|
|
|
|
inst_latch_borrow_limit_map_[inst] = limit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setLatchBorrowLimit(Clock *clk,
|
|
|
|
|
float limit)
|
|
|
|
|
{
|
|
|
|
|
clk_latch_borrow_limit_map_[clk] = limit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteLatchBorrowLimitsReferencing(Clock *clk)
|
|
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
clk_latch_borrow_limit_map_.erase(clk);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::latchBorrowLimit(Pin *data_pin,
|
|
|
|
|
Pin *enable_pin,
|
|
|
|
|
Clock *clk,
|
|
|
|
|
// Return values.
|
|
|
|
|
float &limit,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
pin_latch_borrow_limit_map_.findKey(data_pin, limit, exists);
|
|
|
|
|
if (!exists) {
|
|
|
|
|
pin_latch_borrow_limit_map_.findKey(enable_pin, limit, exists);
|
|
|
|
|
if (!exists) {
|
|
|
|
|
Instance *inst = network_->instance(data_pin);
|
|
|
|
|
inst_latch_borrow_limit_map_.findKey(inst, limit, exists);
|
|
|
|
|
if (!exists)
|
|
|
|
|
clk_latch_borrow_limit_map_.findKey(clk, limit, exists);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setMinPulseWidth(const TransRiseFallBoth *tr,
|
|
|
|
|
float min_width)
|
|
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto tr1 : tr->range())
|
|
|
|
|
min_pulse_width_.setValue(tr1, min_width);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setMinPulseWidth(const Pin *pin,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
float min_width)
|
|
|
|
|
{
|
|
|
|
|
RiseFallValues *widths = pin_min_pulse_width_map_.findKey(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (widths == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
widths = new RiseFallValues;
|
|
|
|
|
pin_min_pulse_width_map_[pin] = widths;
|
|
|
|
|
}
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto tr1 : tr->range())
|
|
|
|
|
widths->setValue(tr1, min_width);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setMinPulseWidth(const Instance *inst,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
float min_width)
|
|
|
|
|
{
|
|
|
|
|
RiseFallValues *widths = inst_min_pulse_width_map_.findKey(inst);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (widths == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
widths = new RiseFallValues;
|
|
|
|
|
inst_min_pulse_width_map_[inst] = widths;
|
|
|
|
|
}
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto tr1 : tr->range())
|
|
|
|
|
widths->setValue(tr1, min_width);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setMinPulseWidth(const Clock *clk,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
float min_width)
|
|
|
|
|
{
|
|
|
|
|
RiseFallValues *widths = clk_min_pulse_width_map_.findKey(clk);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (widths == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
widths = new RiseFallValues;
|
|
|
|
|
clk_min_pulse_width_map_[clk] = widths;
|
|
|
|
|
}
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto tr1 : tr->range())
|
|
|
|
|
widths->setValue(tr1, min_width);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::minPulseWidth(const Pin *pin,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
const TransRiseFall *hi_low,
|
|
|
|
|
float &min_width,
|
|
|
|
|
bool &exists) const
|
|
|
|
|
{
|
|
|
|
|
RiseFallValues *widths = pin_min_pulse_width_map_.findKey(pin);
|
|
|
|
|
if (widths)
|
|
|
|
|
widths->value(hi_low, min_width, exists);
|
|
|
|
|
else {
|
|
|
|
|
if (pin) {
|
|
|
|
|
const Instance *inst = network_->instance(pin);
|
|
|
|
|
widths = inst_min_pulse_width_map_.findKey(inst);
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
if (widths == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
widths = clk_min_pulse_width_map_.findKey(clk);
|
|
|
|
|
if (widths)
|
|
|
|
|
widths->value(hi_low, min_width, exists);
|
|
|
|
|
else
|
|
|
|
|
min_pulse_width_.value(hi_low, min_width, exists);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteMinPulseWidthReferencing(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
RiseFallValues *widths = clk_min_pulse_width_map_.findKey(clk);
|
|
|
|
|
if (widths) {
|
|
|
|
|
delete widths;
|
2019-04-11 05:36:48 +02:00
|
|
|
clk_min_pulse_width_map_.erase(clk);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
InputDrive *
|
|
|
|
|
Sdc::findInputDrive(Port *port)
|
|
|
|
|
{
|
|
|
|
|
return input_drive_map_.findKey(port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setInputDelay(Pin *pin,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
Clock *clk,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
Pin *ref_pin,
|
|
|
|
|
bool source_latency_included,
|
|
|
|
|
bool network_latency_included,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
bool add,
|
|
|
|
|
float delay)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockEdge *clk_edge = clk ? clk->edge(clk_tr) : nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
InputDelay *input_delay = ensureInputDelay(pin, clk_edge, ref_pin);
|
|
|
|
|
RiseFallMinMax *delays = input_delay->delays();
|
|
|
|
|
if (add)
|
|
|
|
|
delays->mergeValue(tr, min_max, delay);
|
|
|
|
|
else {
|
|
|
|
|
deleteInputDelays(pin, input_delay);
|
|
|
|
|
delays->setValue(tr, min_max, delay);
|
|
|
|
|
}
|
|
|
|
|
input_delay->setSourceLatencyIncluded(source_latency_included);
|
|
|
|
|
input_delay->setNetworkLatencyIncluded(network_latency_included);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDelay *
|
|
|
|
|
Sdc::ensureInputDelay(Pin *pin,
|
|
|
|
|
ClockEdge *clk_edge,
|
|
|
|
|
Pin *ref_pin)
|
|
|
|
|
{
|
|
|
|
|
InputDelay *input_delay = findInputDelay(pin, clk_edge, ref_pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (input_delay == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
input_delay = new InputDelay(pin, clk_edge, ref_pin, input_delay_index_++,
|
|
|
|
|
network_);
|
|
|
|
|
input_delay->setNext(input_delay_map_[pin]);
|
|
|
|
|
input_delay_map_[pin] = input_delay;
|
|
|
|
|
if (ref_pin) {
|
|
|
|
|
InputDelaySet *ref_inputs = input_delay_ref_pin_map_.findKey(ref_pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (ref_inputs == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
ref_inputs = new InputDelaySet;
|
|
|
|
|
input_delay_ref_pin_map_[ref_pin] = ref_inputs;
|
|
|
|
|
}
|
|
|
|
|
ref_inputs->insert(input_delay);
|
|
|
|
|
}
|
|
|
|
|
InputDelayVertexPinIterator vpin_iter(input_delay);
|
|
|
|
|
while (vpin_iter.hasNext()) {
|
|
|
|
|
Pin *vpin = vpin_iter.next();
|
|
|
|
|
input_delay_vertex_map_[vpin] = input_delay;
|
|
|
|
|
if (!network_->isTopLevelPort(vpin)) {
|
|
|
|
|
InputDelaySet *input_set = input_delay_internal_pin_map_[vpin];
|
2019-03-13 01:25:53 +01:00
|
|
|
if (input_set == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
input_set = new InputDelaySet;
|
|
|
|
|
input_delay_internal_pin_map_[vpin] = input_set;
|
|
|
|
|
}
|
|
|
|
|
input_set->insert(input_delay);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return input_delay;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDelay *
|
|
|
|
|
Sdc::findInputDelay(const Pin *pin,
|
|
|
|
|
ClockEdge *clk_edge,
|
|
|
|
|
Pin *ref_pin)
|
|
|
|
|
{
|
|
|
|
|
for (InputDelay *input_delay = input_delay_map_.findKey(pin);
|
|
|
|
|
input_delay;
|
|
|
|
|
input_delay = input_delay->next()) {
|
|
|
|
|
if (input_delay->clkEdge() == clk_edge
|
|
|
|
|
&& input_delay->refPin() == ref_pin)
|
|
|
|
|
return input_delay;
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeInputDelay(Pin *pin,
|
|
|
|
|
TransRiseFallBoth *tr,
|
|
|
|
|
Clock *clk,
|
|
|
|
|
TransRiseFall *clk_tr,
|
|
|
|
|
MinMaxAll *min_max)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockEdge *clk_edge = clk ? clk->edge(clk_tr) : nullptr;
|
|
|
|
|
InputDelay *input_delay = findInputDelay(pin, clk_edge, nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (input_delay) {
|
|
|
|
|
RiseFallMinMax *delays = input_delay->delays();
|
|
|
|
|
delays->removeValue(tr, min_max);
|
|
|
|
|
if (delays->empty())
|
|
|
|
|
deleteInputDelay(input_delay);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteInputDelays(Pin *pin,
|
|
|
|
|
InputDelay *except)
|
|
|
|
|
{
|
|
|
|
|
InputDelay *input_delay = input_delay_map_.findKey(pin);
|
|
|
|
|
while (input_delay) {
|
|
|
|
|
InputDelay *next = input_delay->next();
|
|
|
|
|
if (input_delay == except)
|
2019-03-13 01:25:53 +01:00
|
|
|
input_delay->setNext(nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
|
|
|
|
delete input_delay;
|
|
|
|
|
input_delay = next;
|
|
|
|
|
}
|
|
|
|
|
input_delay_map_[pin] = except;
|
|
|
|
|
InputDelayVertexPinIterator vpin_iter(except);
|
|
|
|
|
while (vpin_iter.hasNext()) {
|
|
|
|
|
Pin *vpin = vpin_iter.next();
|
|
|
|
|
input_delay_vertex_map_[vpin] = except;
|
|
|
|
|
InputDelaySet *input_delays =
|
|
|
|
|
input_delay_internal_pin_map_.findKey(vpin);
|
|
|
|
|
if (input_delays) {
|
|
|
|
|
input_delays->clear();
|
|
|
|
|
input_delays->insert(except);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDelaySet *
|
|
|
|
|
Sdc::refPinInputDelays(const Pin *ref_pin) const
|
|
|
|
|
{
|
|
|
|
|
return input_delay_ref_pin_map_.findKey(ref_pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDelayIterator *
|
|
|
|
|
Sdc::inputDelayIterator()
|
|
|
|
|
{
|
|
|
|
|
return new InputDelayIterator(input_delay_map_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDelayVertexPinsIterator *
|
|
|
|
|
Sdc::inputDelayVertexPinsIterator()
|
|
|
|
|
{
|
|
|
|
|
return new InputDelayVertexPinsIterator(input_delay_vertex_map_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PinInputDelayIterator *
|
|
|
|
|
Sdc::inputDelayIterator(const Pin *pin) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
|
|
|
|
|
return new PinInputDelayIterator(pin, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PinInputDelayIterator *
|
|
|
|
|
Sdc::inputDelayVertexIterator(const Pin *vertex_pin) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return new VertexPinInputDelayIterator(vertex_pin, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::hasInputDelay(const Pin *vertex_pin) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return input_delay_vertex_map_.findKey(vertex_pin) != nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isInputDelayInternal(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
InputDelaySet *input_delays =
|
|
|
|
|
input_delay_internal_pin_map_.findKey(pin);
|
|
|
|
|
return input_delays && !input_delays->empty();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteInputDelaysReferencing(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
InputDelayIterator input_iter(input_delay_map_);
|
|
|
|
|
while (input_iter.hasNext()) {
|
|
|
|
|
InputDelay *input_delay = input_iter.next();
|
|
|
|
|
if (input_delay->clock() == clk)
|
|
|
|
|
deleteInputDelay(input_delay);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteInputDelay(InputDelay *input_delay)
|
|
|
|
|
{
|
|
|
|
|
Pin *pin = input_delay->pin();
|
|
|
|
|
InputDelay *head = input_delay_map_.findKey(pin);
|
|
|
|
|
if (head == input_delay) {
|
|
|
|
|
InputDelay *next = input_delay->next();
|
|
|
|
|
if (next)
|
|
|
|
|
input_delay_map_[pin] = next;
|
|
|
|
|
else
|
2019-04-11 05:36:48 +02:00
|
|
|
input_delay_map_.erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
head = next;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (InputDelay *delay = head; delay; ) {
|
|
|
|
|
InputDelay *next = delay->next();
|
|
|
|
|
if (next == input_delay) {
|
|
|
|
|
delay->setNext(input_delay->next());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
delay = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDelayVertexPinIterator vpin_iter(input_delay);
|
|
|
|
|
while (vpin_iter.hasNext()) {
|
|
|
|
|
Pin *vpin = vpin_iter.next();
|
|
|
|
|
if (head)
|
|
|
|
|
input_delay_vertex_map_[vpin] = head;
|
|
|
|
|
else
|
2019-04-11 05:36:48 +02:00
|
|
|
input_delay_vertex_map_.erase(vpin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete input_delay;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setOutputDelay(Pin *pin,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
Clock *clk,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
Pin *ref_pin,
|
|
|
|
|
bool source_latency_included,
|
|
|
|
|
bool network_latency_included,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
bool add,
|
|
|
|
|
float delay)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockEdge *clk_edge = clk ? clk->edge(clk_tr) : nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
OutputDelay *output_delay = ensureOutputDelay(pin, clk_edge, ref_pin);
|
|
|
|
|
RiseFallMinMax *delays = output_delay->delays();
|
|
|
|
|
if (add)
|
|
|
|
|
delays->mergeValue(tr, min_max, delay);
|
|
|
|
|
else {
|
|
|
|
|
deleteOutputDelays(pin, output_delay);
|
|
|
|
|
delays->setValue(tr, min_max, delay);
|
|
|
|
|
}
|
|
|
|
|
output_delay->setSourceLatencyIncluded(source_latency_included);
|
|
|
|
|
output_delay->setNetworkLatencyIncluded(network_latency_included);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputDelay *
|
|
|
|
|
Sdc::ensureOutputDelay(Pin *pin,
|
|
|
|
|
ClockEdge *clk_edge,
|
|
|
|
|
Pin *ref_pin)
|
|
|
|
|
{
|
|
|
|
|
OutputDelay *output_delay = findOutputDelay(pin, clk_edge, ref_pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (output_delay == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
output_delay = new OutputDelay(pin, clk_edge, ref_pin, network_);
|
|
|
|
|
output_delay->setNext(output_delay_map_[pin]);
|
|
|
|
|
output_delay_map_[pin] = output_delay;
|
|
|
|
|
OutputDelayVertexPinIterator vpin_iter(output_delay);
|
|
|
|
|
while (vpin_iter.hasNext()) {
|
|
|
|
|
Pin *vpin = vpin_iter.next();
|
|
|
|
|
output_delay_vertex_map_[vpin] = output_delay;
|
|
|
|
|
if (graph_)
|
|
|
|
|
annotateGraphConstrained(vpin, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return output_delay;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputDelay *
|
|
|
|
|
Sdc::findOutputDelay(const Pin *pin,
|
|
|
|
|
ClockEdge *clk_edge,
|
|
|
|
|
Pin *ref_pin)
|
|
|
|
|
{
|
|
|
|
|
for (OutputDelay *output_delay = output_delay_map_.findKey(pin);
|
|
|
|
|
output_delay;
|
|
|
|
|
output_delay = output_delay->next()) {
|
|
|
|
|
if (output_delay->clkEdge() == clk_edge
|
|
|
|
|
&& output_delay->refPin() == ref_pin)
|
|
|
|
|
return output_delay;
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeOutputDelay(Pin *pin,
|
|
|
|
|
TransRiseFallBoth *tr,
|
|
|
|
|
Clock *clk,
|
|
|
|
|
TransRiseFall *clk_tr,
|
|
|
|
|
MinMaxAll *min_max)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
ClockEdge *clk_edge = clk ? clk->edge(clk_tr) : nullptr;
|
|
|
|
|
InputDelay *input_delay = findInputDelay(pin, clk_edge, nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (input_delay) {
|
|
|
|
|
RiseFallMinMax *delays = input_delay->delays();
|
|
|
|
|
delays->removeValue(tr, min_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteOutputDelays(Pin *pin,
|
|
|
|
|
OutputDelay *except)
|
|
|
|
|
{
|
|
|
|
|
OutputDelay *output_delay = output_delay_map_.findKey(pin);
|
|
|
|
|
while (output_delay) {
|
|
|
|
|
OutputDelay *next = output_delay->next();
|
|
|
|
|
if (output_delay == except)
|
2019-03-13 01:25:53 +01:00
|
|
|
output_delay->setNext(nullptr);
|
2018-09-28 17:54:21 +02:00
|
|
|
else
|
|
|
|
|
delete output_delay;
|
|
|
|
|
output_delay = next;
|
|
|
|
|
}
|
|
|
|
|
output_delay_map_[pin] = except;
|
|
|
|
|
OutputDelayVertexPinIterator vpin_iter(except);
|
|
|
|
|
while (vpin_iter. hasNext()) {
|
|
|
|
|
Pin *vpin = vpin_iter.next();
|
|
|
|
|
output_delay_vertex_map_[vpin] = except;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputDelayIterator *
|
|
|
|
|
Sdc::outputDelayIterator()
|
|
|
|
|
{
|
|
|
|
|
return new OutputDelayIterator(output_delay_map_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PinOutputDelayIterator *
|
|
|
|
|
Sdc::outputDelayIterator(const Pin *pin) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return new PinOutputDelayIterator(pin, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PinOutputDelayIterator *
|
|
|
|
|
Sdc::outputDelayVertexIterator(const Pin *vertex_pin) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return new VertexPinOutputDelayIterator(vertex_pin, this);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::hasOutputDelay(const Pin *vertex_pin) const
|
|
|
|
|
{
|
|
|
|
|
return output_delay_vertex_map_.hasKey(vertex_pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteOutputDelaysReferencing(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
OutputDelayIterator output_iter(output_delay_map_);
|
|
|
|
|
while (output_iter.hasNext()) {
|
|
|
|
|
OutputDelay *output_delay = output_iter.next();
|
|
|
|
|
if (output_delay->clock() == clk)
|
|
|
|
|
deleteOutputDelay(output_delay);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteOutputDelay(OutputDelay *output_delay)
|
|
|
|
|
{
|
|
|
|
|
Pin *pin = output_delay->pin();
|
|
|
|
|
OutputDelay *head = output_delay_map_.findKey(pin);
|
|
|
|
|
if (head == output_delay) {
|
|
|
|
|
OutputDelay *next = output_delay->next();
|
|
|
|
|
if (next)
|
|
|
|
|
output_delay_map_[pin] = next;
|
|
|
|
|
else
|
2019-04-11 05:36:48 +02:00
|
|
|
output_delay_map_.erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
head = next;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (OutputDelay *delay = head; delay; ) {
|
|
|
|
|
OutputDelay *next = delay->next();
|
|
|
|
|
if (next == output_delay) {
|
|
|
|
|
delay->setNext(output_delay->next());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
delay = next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputDelayVertexPinIterator vpin_iter(output_delay);
|
|
|
|
|
while (vpin_iter.hasNext()) {
|
|
|
|
|
Pin *vpin = vpin_iter.next();
|
|
|
|
|
if (head)
|
|
|
|
|
output_delay_vertex_map_[vpin] = head;
|
|
|
|
|
else
|
2019-04-11 05:36:48 +02:00
|
|
|
output_delay_vertex_map_.erase(vpin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
delete output_delay;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setPortExtPinCap(Port *port,
|
2018-11-26 18:15:52 +01:00
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float cap)
|
2018-09-28 17:54:21 +02:00
|
|
|
{
|
|
|
|
|
PortExtCap *port_cap = ensurePortExtPinCap(port);
|
|
|
|
|
port_cap->setPinCap(cap, tr, min_max);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setPortExtWireCap(Port *port,
|
|
|
|
|
bool subtract_pin_cap,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float cap)
|
|
|
|
|
{
|
|
|
|
|
PortExtCap *port_cap = ensurePortExtPinCap(port);
|
|
|
|
|
if (subtract_pin_cap) {
|
|
|
|
|
Pin *pin = network_->findPin(network_->name(port));
|
|
|
|
|
const OperatingConditions *op_cond = operatingConditions(min_max);
|
|
|
|
|
cap -= connectedPinCap(pin, tr, op_cond, corner, min_max);
|
|
|
|
|
if (cap < 0.0)
|
|
|
|
|
cap = 0.0;
|
|
|
|
|
}
|
|
|
|
|
port_cap->setWireCap(cap, tr, min_max);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PortExtCap *
|
|
|
|
|
Sdc::portExtCap(Port *port) const
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
if (port_cap_map_)
|
|
|
|
|
return port_cap_map_->findKey(port);
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::hasPortExtCap(Port *port) const
|
|
|
|
|
{
|
|
|
|
|
if (port_cap_map_)
|
|
|
|
|
return port_cap_map_->hasKey(port);
|
|
|
|
|
else
|
|
|
|
|
return false;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::portExtCap(Port *port,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
// Return values.
|
|
|
|
|
float &pin_cap,
|
|
|
|
|
bool &has_pin_cap,
|
|
|
|
|
float &wire_cap,
|
|
|
|
|
bool &has_wire_cap,
|
|
|
|
|
int &fanout,
|
|
|
|
|
bool &has_fanout) const
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
if (port_cap_map_) {
|
|
|
|
|
PortExtCap *port_cap = port_cap_map_->findKey(port);
|
|
|
|
|
if (port_cap) {
|
|
|
|
|
port_cap->pinCap(tr, min_max, pin_cap, has_pin_cap);
|
|
|
|
|
port_cap->wireCap(tr, min_max, wire_cap, has_wire_cap);
|
|
|
|
|
port_cap->fanout(min_max, fanout, has_fanout);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
2018-11-26 18:15:52 +01:00
|
|
|
pin_cap = 0.0F;
|
|
|
|
|
has_pin_cap = false;
|
|
|
|
|
wire_cap = 0.0F;
|
|
|
|
|
has_wire_cap = false;
|
|
|
|
|
fanout = 0.0F;
|
|
|
|
|
has_fanout = false;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
Sdc::portExtCap(Port *port,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max) const
|
|
|
|
|
{
|
|
|
|
|
float pin_cap, wire_cap;
|
|
|
|
|
int fanout;
|
|
|
|
|
bool has_pin_cap, has_wire_cap, has_fanout;
|
|
|
|
|
portExtCap(port, tr, min_max,
|
|
|
|
|
pin_cap, has_pin_cap,
|
|
|
|
|
wire_cap, has_wire_cap,
|
|
|
|
|
fanout, has_fanout);
|
|
|
|
|
float cap = 0.0;
|
|
|
|
|
if (has_pin_cap)
|
|
|
|
|
cap += pin_cap;
|
|
|
|
|
if (has_wire_cap)
|
|
|
|
|
cap += wire_cap;
|
|
|
|
|
return cap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::drvrPinHasWireCap(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
return drvr_pin_wire_cap_map_
|
|
|
|
|
&& drvr_pin_wire_cap_map_->hasKey(const_cast<Pin*>(pin));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::drvrPinWireCap(const Pin *pin,
|
2018-11-26 18:15:52 +01:00
|
|
|
const Corner *corner,
|
2018-09-28 17:54:21 +02:00
|
|
|
const MinMax *min_max,
|
|
|
|
|
// Return values.
|
|
|
|
|
float &cap,
|
|
|
|
|
bool &exists) const
|
|
|
|
|
{
|
|
|
|
|
if (drvr_pin_wire_cap_map_) {
|
|
|
|
|
MinMaxFloatValues *values =
|
2018-11-26 18:15:52 +01:00
|
|
|
drvr_pin_wire_cap_map_[corner->index()].findKey(const_cast<Pin*>(pin));
|
2018-09-28 17:54:21 +02:00
|
|
|
if (values)
|
|
|
|
|
return values->value(min_max, cap, exists);
|
|
|
|
|
}
|
|
|
|
|
cap = 0.0;
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setNetWireCap(Net *net,
|
|
|
|
|
bool subtract_pin_cap,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float cap)
|
|
|
|
|
{
|
|
|
|
|
float wire_cap = cap;
|
|
|
|
|
if (subtract_pin_cap) {
|
|
|
|
|
OperatingConditions *op_cond = operatingConditions(min_max);
|
|
|
|
|
NetConnectedPinIterator *pin_iter = network_->connectedPinIterator(net);
|
|
|
|
|
if (pin_iter->hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter->next();
|
|
|
|
|
float pin_cap_rise = connectedPinCap(pin, TransRiseFall::rise(),
|
|
|
|
|
op_cond, corner, min_max);
|
|
|
|
|
float pin_cap_fall = connectedPinCap(pin, TransRiseFall::fall(),
|
|
|
|
|
op_cond, corner, min_max);
|
|
|
|
|
float pin_cap = (pin_cap_rise + pin_cap_fall) / 2.0F;
|
|
|
|
|
wire_cap -= pin_cap;
|
|
|
|
|
if ((wire_cap + pin_cap) < 0.0)
|
|
|
|
|
wire_cap = -pin_cap;
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
if (net_wire_cap_map_ == nullptr)
|
2018-11-26 18:15:52 +01:00
|
|
|
net_wire_cap_map_ = new NetWireCapMap[corners_->count()];
|
2019-09-07 21:37:05 +02:00
|
|
|
bool make_drvr_entry = !net_wire_cap_map_[corner->index()].hasKey(net);
|
2018-11-26 18:15:52 +01:00
|
|
|
MinMaxFloatValues &values = net_wire_cap_map_[corner->index()][net];
|
2018-09-28 17:54:21 +02:00
|
|
|
values.setValue(min_max, wire_cap);
|
|
|
|
|
|
2018-11-26 18:15:52 +01:00
|
|
|
// Only need to do this when there is new net_wire_cap_map_ entry.
|
|
|
|
|
if (make_drvr_entry) {
|
2019-09-07 21:37:05 +02:00
|
|
|
for (Pin *pin : *network_->drivers(net)) {
|
|
|
|
|
if (drvr_pin_wire_cap_map_ == nullptr)
|
|
|
|
|
drvr_pin_wire_cap_map_ = new PinWireCapMap[corners_->count()];
|
|
|
|
|
drvr_pin_wire_cap_map_[corner->index()][pin] = &values;
|
2018-11-26 18:15:52 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::hasNetWireCap(Net *net) const
|
|
|
|
|
{
|
|
|
|
|
if (net_wire_cap_map_) {
|
|
|
|
|
for (int i = 0; i < corners_->count(); i++) {
|
|
|
|
|
if (net_wire_cap_map_[i].hasKey(net))
|
|
|
|
|
return true;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
2018-11-26 18:15:52 +01:00
|
|
|
return false;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::connectedCap(const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const OperatingConditions *op_cond,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
// Return values.
|
|
|
|
|
float &pin_cap,
|
|
|
|
|
float &wire_cap,
|
|
|
|
|
float &fanout,
|
|
|
|
|
bool &has_set_load) const
|
|
|
|
|
{
|
|
|
|
|
netCaps(pin, tr, op_cond, corner, min_max,
|
|
|
|
|
pin_cap, wire_cap, fanout, has_set_load);
|
|
|
|
|
float net_wire_cap;
|
|
|
|
|
bool has_net_wire_cap;
|
2018-11-26 18:15:52 +01:00
|
|
|
drvrPinWireCap(pin, corner, min_max, net_wire_cap, has_net_wire_cap);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (has_net_wire_cap) {
|
|
|
|
|
wire_cap += net_wire_cap;
|
|
|
|
|
has_set_load = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
Sdc::connectedPinCap(const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const OperatingConditions *op_cond,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
float pin_cap, wire_cap, fanout;
|
|
|
|
|
bool has_set_load;
|
|
|
|
|
connectedCap(pin, tr, op_cond, corner, min_max,
|
|
|
|
|
pin_cap, wire_cap, fanout, has_set_load);
|
|
|
|
|
return pin_cap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class FindNetCaps : public PinVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
FindNetCaps(const TransRiseFall *tr,
|
|
|
|
|
const OperatingConditions *op_cond,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &pin_cap,
|
|
|
|
|
float &wire_cap,
|
|
|
|
|
float &fanout,
|
|
|
|
|
bool &has_set_load,
|
|
|
|
|
const Sdc *sdc);
|
|
|
|
|
virtual void operator()(Pin *pin);
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
const TransRiseFall *tr_;
|
|
|
|
|
const OperatingConditions *op_cond_;
|
|
|
|
|
const Corner *corner_;
|
|
|
|
|
const MinMax *min_max_;
|
|
|
|
|
float &pin_cap_;
|
|
|
|
|
float &wire_cap_;
|
|
|
|
|
float &fanout_;
|
|
|
|
|
bool &has_set_load_;
|
|
|
|
|
const Sdc *sdc_;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(FindNetCaps);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
FindNetCaps::FindNetCaps(const TransRiseFall *tr,
|
|
|
|
|
const OperatingConditions *op_cond,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &pin_cap,
|
|
|
|
|
float &wire_cap,
|
|
|
|
|
float &fanout,
|
|
|
|
|
bool &has_set_load,
|
|
|
|
|
const Sdc *sdc) :
|
|
|
|
|
PinVisitor(),
|
|
|
|
|
tr_(tr),
|
|
|
|
|
op_cond_(op_cond),
|
|
|
|
|
corner_(corner),
|
|
|
|
|
min_max_(min_max),
|
|
|
|
|
pin_cap_(pin_cap),
|
|
|
|
|
wire_cap_(wire_cap),
|
|
|
|
|
fanout_(fanout),
|
|
|
|
|
has_set_load_(has_set_load),
|
|
|
|
|
sdc_(sdc)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
FindNetCaps::operator()(Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
sdc_->pinCaps(pin, tr_, op_cond_, corner_, min_max_,
|
2018-11-26 18:15:52 +01:00
|
|
|
pin_cap_, wire_cap_, fanout_, has_set_load_);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Capacitances for all pins connected to drvr_pin's net.
|
|
|
|
|
void
|
|
|
|
|
Sdc::netCaps(const Pin *drvr_pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const OperatingConditions *op_cond,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
// Return values.
|
|
|
|
|
float &pin_cap,
|
|
|
|
|
float &wire_cap,
|
|
|
|
|
float &fanout,
|
|
|
|
|
bool &has_set_load) const
|
|
|
|
|
{
|
|
|
|
|
pin_cap = 0.0;
|
|
|
|
|
wire_cap = 0.0;
|
|
|
|
|
fanout = 0.0;
|
|
|
|
|
has_set_load = false;
|
|
|
|
|
FindNetCaps visitor(tr, op_cond, corner, min_max, pin_cap,
|
|
|
|
|
wire_cap, fanout, has_set_load, this);
|
|
|
|
|
network_->visitConnectedPins(const_cast<Pin*>(drvr_pin), visitor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::pinCaps(const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const OperatingConditions *op_cond,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
// Return values.
|
|
|
|
|
float &pin_cap,
|
|
|
|
|
float &wire_cap,
|
|
|
|
|
float &fanout,
|
|
|
|
|
bool &has_set_load) const
|
|
|
|
|
{
|
|
|
|
|
if (network_->isTopLevelPort(pin)) {
|
|
|
|
|
Port *port = network_->port(pin);
|
|
|
|
|
bool is_output = network_->direction(port)->isAnyOutput();
|
|
|
|
|
float port_pin_cap, port_wire_cap;
|
|
|
|
|
int port_fanout;
|
|
|
|
|
bool has_pin_cap, has_wire_cap, has_fanout;
|
|
|
|
|
portExtCap(port, tr, min_max,
|
|
|
|
|
port_pin_cap, has_pin_cap,
|
|
|
|
|
port_wire_cap, has_wire_cap,
|
|
|
|
|
port_fanout, has_fanout);
|
|
|
|
|
if (has_pin_cap)
|
|
|
|
|
pin_cap += port_pin_cap;
|
|
|
|
|
if (has_wire_cap)
|
|
|
|
|
wire_cap += port_wire_cap;
|
|
|
|
|
if (is_output) {
|
|
|
|
|
if (has_fanout)
|
|
|
|
|
fanout += port_fanout;
|
|
|
|
|
// Output port counts as a fanout.
|
|
|
|
|
fanout++;
|
|
|
|
|
}
|
|
|
|
|
has_set_load |= has_pin_cap || has_wire_cap;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
LibertyPort *port = network_->libertyPort(pin);
|
|
|
|
|
if (port) {
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
pin_cap += portCapacitance(inst, port, tr, op_cond, corner, min_max);
|
|
|
|
|
if (port->direction()->isAnyInput())
|
|
|
|
|
fanout++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
Sdc::portCapacitance(Instance *inst,
|
|
|
|
|
LibertyPort *port,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const OperatingConditions *op_cond,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMax *min_max) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
Pvt *inst_pvt = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (inst)
|
|
|
|
|
inst_pvt = pvt(inst, min_max);
|
|
|
|
|
LibertyPort *corner_port = port->cornerPort(corner->libertyIndex(min_max));
|
|
|
|
|
return corner_port->capacitance(tr, min_max, op_cond, inst_pvt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float
|
|
|
|
|
Sdc::pinCapacitance(const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const OperatingConditions *op_cond,
|
|
|
|
|
const Corner *corner,
|
|
|
|
|
const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
LibertyPort *port = network_->libertyPort(pin);
|
|
|
|
|
if (port) {
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
return portCapacitance(inst, port, tr, op_cond, corner, min_max);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setResistance(Net *net,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
float res)
|
|
|
|
|
{
|
|
|
|
|
MinMaxFloatValues &values = net_res_map_[net];
|
|
|
|
|
values.setValue(min_max, res);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::resistance(Net *net,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
float &res,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
res = 0.0;
|
|
|
|
|
MinMaxFloatValues values;
|
|
|
|
|
net_res_map_.findKey(net, values, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
values.value(min_max, res, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setPortExtFanout(Port *port,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
int fanout)
|
|
|
|
|
{
|
|
|
|
|
PortExtCap *port_cap = ensurePortExtPinCap(port);
|
|
|
|
|
port_cap->setFanout(fanout, min_max);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::portExtFanout(Port *port,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
// Return values.
|
|
|
|
|
int &fanout,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
2018-11-26 18:15:52 +01:00
|
|
|
PortExtCap *port_cap = portExtCap(port);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (port_cap)
|
|
|
|
|
port_cap->fanout(min_max, fanout, exists);
|
|
|
|
|
else {
|
|
|
|
|
fanout = 0.0;
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
Sdc::portExtFanout(Port *port,
|
|
|
|
|
const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
int fanout;
|
|
|
|
|
bool exists;
|
|
|
|
|
portExtFanout(port, min_max, fanout, exists);
|
|
|
|
|
if (exists)
|
|
|
|
|
return fanout;
|
|
|
|
|
else
|
|
|
|
|
return 0.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PortExtCap *
|
|
|
|
|
Sdc::ensurePortExtPinCap(Port *port)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if (port_cap_map_ == nullptr)
|
2018-11-26 18:15:52 +01:00
|
|
|
port_cap_map_ = new PortExtCapMap;
|
|
|
|
|
PortExtCap *port_cap = port_cap_map_->findKey(port);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (port_cap == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
port_cap = new PortExtCap(port);
|
2018-11-26 18:15:52 +01:00
|
|
|
(*port_cap_map_)[port] = port_cap;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
return port_cap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disable(LibertyCell *cell,
|
|
|
|
|
LibertyPort *from,
|
|
|
|
|
LibertyPort *to)
|
|
|
|
|
{
|
|
|
|
|
DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (disabled_cell == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
disabled_cell = new DisabledCellPorts(cell);
|
|
|
|
|
disabled_cell_ports_[cell] = disabled_cell;
|
|
|
|
|
}
|
|
|
|
|
if (from && to) {
|
|
|
|
|
disabled_cell->setDisabledFromTo(from, to);
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellTimingArcSetIterator arc_iter(cell, from, to);
|
|
|
|
|
while (arc_iter.hasNext()) {
|
|
|
|
|
TimingArcSet *arc_set = arc_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
arc_set->setIsDisabledConstraint(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (from) {
|
|
|
|
|
disabled_cell->setDisabledFrom(from);
|
|
|
|
|
from->setIsDisabledConstraint(true);
|
|
|
|
|
}
|
|
|
|
|
else if (to) {
|
|
|
|
|
disabled_cell->setDisabledTo(to);
|
|
|
|
|
to->setIsDisabledConstraint(true);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
disabled_cell->setDisabledAll();
|
|
|
|
|
cell->setIsDisabledConstraint(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDisable(LibertyCell *cell,
|
|
|
|
|
LibertyPort *from,
|
|
|
|
|
LibertyPort *to)
|
|
|
|
|
{
|
|
|
|
|
DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell);
|
|
|
|
|
if (disabled_cell) {
|
|
|
|
|
if (from && to) {
|
|
|
|
|
disabled_cell->removeDisabledFromTo(from, to);
|
2018-12-05 23:18:41 +01:00
|
|
|
LibertyCellTimingArcSetIterator arc_iter(cell, from, to);
|
|
|
|
|
while (arc_iter.hasNext()) {
|
|
|
|
|
TimingArcSet *arc_set = arc_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
arc_set->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (from) {
|
|
|
|
|
disabled_cell->removeDisabledFrom(from);
|
|
|
|
|
from->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
else if (to) {
|
|
|
|
|
disabled_cell->removeDisabledTo(to);
|
|
|
|
|
to->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
disabled_cell->removeDisabledAll();
|
|
|
|
|
cell->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disable(TimingArcSet *arc_set)
|
|
|
|
|
{
|
|
|
|
|
LibertyCell *cell = arc_set->libertyCell();
|
|
|
|
|
DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (disabled_cell == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
disabled_cell = new DisabledCellPorts(cell);
|
|
|
|
|
disabled_cell_ports_[cell] = disabled_cell;
|
|
|
|
|
}
|
|
|
|
|
disabled_cell->setDisabled(arc_set);
|
|
|
|
|
arc_set->setIsDisabledConstraint(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDisable(TimingArcSet *arc_set)
|
|
|
|
|
{
|
|
|
|
|
LibertyCell *cell = arc_set->libertyCell();
|
|
|
|
|
DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell);
|
|
|
|
|
if (disabled_cell) {
|
|
|
|
|
disabled_cell->removeDisabled(arc_set);
|
|
|
|
|
arc_set->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disable(LibertyPort *port)
|
|
|
|
|
{
|
|
|
|
|
disabled_lib_ports_.insert(port);
|
|
|
|
|
port->setIsDisabledConstraint(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDisable(LibertyPort *port)
|
|
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
disabled_lib_ports_.erase(port);
|
2018-09-28 17:54:21 +02:00
|
|
|
port->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disable(Port *port)
|
|
|
|
|
{
|
|
|
|
|
disabled_ports_.insert(port);
|
|
|
|
|
if (graph_) {
|
|
|
|
|
Pin *pin = network_->findPin(network_->topInstance(), port);
|
|
|
|
|
annotateGraphDisabled(pin, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDisable(Port *port)
|
|
|
|
|
{
|
|
|
|
|
if (graph_) {
|
|
|
|
|
Pin *pin = network_->findPin(network_->topInstance(), port);
|
|
|
|
|
annotateGraphDisabled(pin, false);
|
|
|
|
|
}
|
2019-04-11 05:36:48 +02:00
|
|
|
disabled_ports_.erase(port);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disable(Instance *inst,
|
|
|
|
|
LibertyPort *from,
|
|
|
|
|
LibertyPort *to)
|
|
|
|
|
{
|
|
|
|
|
DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (disabled_inst == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
disabled_inst = new DisabledInstancePorts(inst);
|
|
|
|
|
disabled_inst_ports_[inst] = disabled_inst;
|
|
|
|
|
}
|
|
|
|
|
if (from && to)
|
|
|
|
|
disabled_inst->setDisabledFromTo(from, to);
|
|
|
|
|
else if (from)
|
|
|
|
|
disabled_inst->setDisabledFrom(from);
|
|
|
|
|
else if (to)
|
|
|
|
|
disabled_inst->setDisabledTo(to);
|
|
|
|
|
else
|
|
|
|
|
disabled_inst->setDisabledAll();
|
|
|
|
|
|
|
|
|
|
if (graph_)
|
|
|
|
|
setEdgeDisabledInstPorts(disabled_inst, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDisable(Instance *inst,
|
|
|
|
|
LibertyPort *from,
|
|
|
|
|
LibertyPort *to)
|
|
|
|
|
{
|
|
|
|
|
DisabledInstancePorts *disabled_inst = disabled_inst_ports_.findKey(inst);
|
|
|
|
|
if (disabled_inst) {
|
|
|
|
|
if (graph_)
|
|
|
|
|
setEdgeDisabledInstPorts(disabled_inst, false);
|
|
|
|
|
if (from && to)
|
|
|
|
|
disabled_inst->removeDisabledFromTo(from, to);
|
|
|
|
|
else if (from)
|
|
|
|
|
disabled_inst->removeDisabledFrom(from);
|
|
|
|
|
else if (to)
|
|
|
|
|
disabled_inst->removeDisabledTo(to);
|
|
|
|
|
else
|
|
|
|
|
disabled_inst->removeDisabledAll();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disable(Pin *from,
|
|
|
|
|
Pin *to)
|
|
|
|
|
{
|
|
|
|
|
PinPair probe(from, to);
|
|
|
|
|
if (!disabled_wire_edges_.hasKey(&probe)) {
|
|
|
|
|
PinPair *pair = new PinPair(from, to);
|
|
|
|
|
disabled_wire_edges_.insert(pair);
|
|
|
|
|
if (graph_)
|
|
|
|
|
annotateGraphDisabledWireEdge(from, to, true, graph_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDisable(Pin *from,
|
|
|
|
|
Pin *to)
|
|
|
|
|
{
|
|
|
|
|
annotateGraphDisabledWireEdge(from, to, false, graph_);
|
|
|
|
|
PinPair probe(from, to);
|
2019-04-11 05:36:48 +02:00
|
|
|
disabled_wire_edges_.erase(&probe);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disable(Edge *edge)
|
|
|
|
|
{
|
|
|
|
|
disabled_edges_.insert(edge);
|
|
|
|
|
edge->setIsDisabledConstraint(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDisable(Edge *edge)
|
|
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
disabled_edges_.erase(edge);
|
2018-09-28 17:54:21 +02:00
|
|
|
edge->setIsDisabledConstraint(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isDisabled(Edge *edge)
|
|
|
|
|
{
|
|
|
|
|
return disabled_edges_.hasKey(edge);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class DisableEdgesThruHierPin : public HierPinThruVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DisableEdgesThruHierPin(PinPairSet *pairs,
|
|
|
|
|
Graph *graph);
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
virtual void visit(Pin *drvr,
|
|
|
|
|
Pin *load);
|
|
|
|
|
|
|
|
|
|
PinPairSet *pairs_;
|
|
|
|
|
Graph *graph_;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(DisableEdgesThruHierPin);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DisableEdgesThruHierPin::DisableEdgesThruHierPin(PinPairSet *pairs,
|
|
|
|
|
Graph *graph) :
|
|
|
|
|
HierPinThruVisitor(),
|
|
|
|
|
pairs_(pairs),
|
|
|
|
|
graph_(graph)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DisableEdgesThruHierPin::visit(Pin *drvr,
|
|
|
|
|
Pin *load)
|
|
|
|
|
{
|
|
|
|
|
PinPair probe(drvr, load);
|
|
|
|
|
if (!pairs_->hasKey(&probe)) {
|
|
|
|
|
PinPair *pair = new PinPair(drvr, load);
|
|
|
|
|
pairs_->insert(pair);
|
|
|
|
|
if (graph_)
|
|
|
|
|
annotateGraphDisabledWireEdge(drvr, load, true, graph_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disable(Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
if (network_->isHierarchical(pin)) {
|
|
|
|
|
// Add leaf pins thru hierarchical pin to disabled_edges_.
|
|
|
|
|
DisableEdgesThruHierPin visitor(&disabled_wire_edges_, graph_);
|
|
|
|
|
visitDrvrLoadsThruHierPin(pin, network_, &visitor);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
disabled_pins_.insert(pin);
|
|
|
|
|
if (graph_)
|
|
|
|
|
annotateGraphDisabled(pin, true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class RemoveDisableEdgesThruHierPin : public HierPinThruVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
RemoveDisableEdgesThruHierPin(PinPairSet *pairs, Graph *graph);
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
virtual void visit(Pin *drvr, Pin *load);
|
|
|
|
|
|
|
|
|
|
PinPairSet *pairs_;
|
|
|
|
|
Graph *graph_;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(RemoveDisableEdgesThruHierPin);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
RemoveDisableEdgesThruHierPin::RemoveDisableEdgesThruHierPin(PinPairSet *pairs,
|
|
|
|
|
Graph *graph) :
|
|
|
|
|
HierPinThruVisitor(),
|
|
|
|
|
pairs_(pairs),
|
|
|
|
|
graph_(graph)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
RemoveDisableEdgesThruHierPin::visit(Pin *drvr,
|
|
|
|
|
Pin *load)
|
|
|
|
|
{
|
|
|
|
|
if (graph_)
|
|
|
|
|
annotateGraphDisabledWireEdge(drvr, load, false, graph_);
|
|
|
|
|
PinPair probe(drvr, load);
|
|
|
|
|
PinPair *pair = pairs_->findKey(&probe);
|
|
|
|
|
if (pair) {
|
2019-04-11 05:36:48 +02:00
|
|
|
pairs_->erase(pair);
|
2018-09-28 17:54:21 +02:00
|
|
|
delete pair;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDisable(Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
if (network_->isHierarchical(pin)) {
|
|
|
|
|
// Remove leaf pins thru hierarchical pin from disabled_edges_.
|
|
|
|
|
RemoveDisableEdgesThruHierPin visitor(&disabled_wire_edges_, graph_);
|
|
|
|
|
visitDrvrLoadsThruHierPin(pin, network_, &visitor);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (graph_)
|
|
|
|
|
annotateGraphDisabled(pin, false);
|
2019-04-11 05:36:48 +02:00
|
|
|
disabled_pins_.erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isDisabled(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
Port *port = network_->port(pin);
|
|
|
|
|
LibertyPort *lib_port = network_->libertyPort(pin);
|
|
|
|
|
return disabled_pins_.hasKey(const_cast<Pin*>(pin))
|
|
|
|
|
|| disabled_ports_.hasKey(port)
|
|
|
|
|
|| disabled_lib_ports_.hasKey(lib_port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isDisabled(const Instance *inst,
|
|
|
|
|
const Pin *from_pin,
|
|
|
|
|
const Pin *to_pin,
|
|
|
|
|
const TimingRole *role) const
|
|
|
|
|
{
|
|
|
|
|
if (role == TimingRole::wire()) {
|
|
|
|
|
// Hierarchical thru pin disables.
|
|
|
|
|
PinPair pair(const_cast<Pin*>(from_pin), const_cast<Pin*>(to_pin));
|
|
|
|
|
return disabled_wire_edges_.hasKey(&pair);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
LibertyCell *cell = network_->libertyCell(inst);
|
|
|
|
|
LibertyPort *from_port = network_->libertyPort(from_pin);
|
|
|
|
|
LibertyPort *to_port = network_->libertyPort(to_pin);
|
|
|
|
|
DisabledInstancePorts *disabled_inst =
|
|
|
|
|
disabled_inst_ports_.findKey(const_cast<Instance*>(inst));
|
|
|
|
|
DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell);
|
|
|
|
|
return (disabled_inst
|
|
|
|
|
&& disabled_inst->isDisabled(from_port, to_port, role))
|
|
|
|
|
|| (disabled_cell
|
|
|
|
|
&& disabled_cell->isDisabled(from_port, to_port, role));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isDisabled(TimingArcSet *arc_set) const
|
|
|
|
|
{
|
|
|
|
|
LibertyCell *cell = arc_set->libertyCell();
|
|
|
|
|
if (cell) {
|
|
|
|
|
DisabledCellPorts *disabled_cell = disabled_cell_ports_.findKey(cell);
|
|
|
|
|
return disabled_cell
|
|
|
|
|
&& disabled_cell->isDisabled(arc_set);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DisabledInstancePortsMap *
|
|
|
|
|
Sdc::disabledInstancePorts()
|
|
|
|
|
{
|
|
|
|
|
return &disabled_inst_ports_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DisabledCellPortsMap *
|
|
|
|
|
Sdc::disabledCellPorts()
|
|
|
|
|
{
|
|
|
|
|
return &disabled_cell_ports_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disableClockGatingCheck(Instance *inst)
|
|
|
|
|
{
|
|
|
|
|
disabled_clk_gating_checks_inst_.insert(inst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disableClockGatingCheck(Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
disabled_clk_gating_checks_pin_.insert(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDisableClockGatingCheck(Instance *inst)
|
|
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
disabled_clk_gating_checks_inst_.erase(inst);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeDisableClockGatingCheck(Pin *pin)
|
|
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
disabled_clk_gating_checks_pin_.erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isDisableClockGatingCheck(const Instance *inst)
|
|
|
|
|
{
|
|
|
|
|
return disabled_clk_gating_checks_inst_.hasKey(const_cast<Instance*>(inst));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isDisableClockGatingCheck(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
return disabled_clk_gating_checks_pin_.hasKey(const_cast<Pin*>(pin));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setLogicValue(Pin *pin,
|
|
|
|
|
LogicValue value)
|
|
|
|
|
{
|
|
|
|
|
logic_value_map_[pin] = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::logicValue(const Pin *pin,
|
|
|
|
|
LogicValue &value,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
logic_value_map_.findKey(pin, value, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setCaseAnalysis(Pin *pin,
|
|
|
|
|
LogicValue value)
|
|
|
|
|
{
|
|
|
|
|
case_value_map_[pin] = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::removeCaseAnalysis(Pin *pin)
|
|
|
|
|
{
|
2019-04-11 05:36:48 +02:00
|
|
|
case_value_map_.erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::caseLogicValue(const Pin *pin,
|
|
|
|
|
LogicValue &value,
|
|
|
|
|
bool &exists)
|
|
|
|
|
{
|
|
|
|
|
case_value_map_.findKey(pin, value, exists);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::hasLogicValue(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
return case_value_map_.hasKey(pin)
|
|
|
|
|
|| logic_value_map_.hasKey(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
ExceptionFrom *
|
|
|
|
|
Sdc::makeExceptionFrom(PinSet *from_pins,
|
|
|
|
|
ClockSet *from_clks,
|
|
|
|
|
InstanceSet *from_insts,
|
|
|
|
|
const TransRiseFallBoth *from_tr)
|
|
|
|
|
{
|
|
|
|
|
if ((from_pins && !from_pins->empty())
|
|
|
|
|
|| (from_clks && !from_clks->empty())
|
|
|
|
|
|| (from_insts && !from_insts->empty()))
|
|
|
|
|
return new ExceptionFrom(from_pins, from_clks, from_insts, from_tr, true);
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ExceptionThru *
|
|
|
|
|
Sdc::makeExceptionThru(PinSet *pins,
|
|
|
|
|
NetSet *nets,
|
|
|
|
|
InstanceSet *insts,
|
|
|
|
|
const TransRiseFallBoth *tr)
|
|
|
|
|
{
|
|
|
|
|
if ((pins && !pins->empty())
|
|
|
|
|
|| (nets && !nets->empty())
|
|
|
|
|
|| (insts && !insts->empty()))
|
|
|
|
|
return new ExceptionThru(pins, nets, insts, tr, true, network_);
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ExceptionTo *
|
|
|
|
|
Sdc::makeExceptionTo(PinSet *pins,
|
|
|
|
|
ClockSet *clks,
|
|
|
|
|
InstanceSet *insts,
|
|
|
|
|
const TransRiseFallBoth *tr,
|
|
|
|
|
const TransRiseFallBoth *end_tr)
|
|
|
|
|
{
|
|
|
|
|
if ((pins && !pins->empty())
|
|
|
|
|
|| (clks && !clks->empty())
|
|
|
|
|
|| (insts && !insts->empty())
|
|
|
|
|
|| (tr != TransRiseFallBoth::riseFall())
|
|
|
|
|
|| (end_tr != TransRiseFallBoth::riseFall()))
|
|
|
|
|
return new ExceptionTo(pins, clks, insts, tr, end_tr, true);
|
|
|
|
|
else
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Valid endpoints include gated clock enables which are not
|
|
|
|
|
// known until clock arrivals are determined.
|
|
|
|
|
bool
|
|
|
|
|
Sdc::exceptionToInvalid(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
Net *net = network_->net(pin);
|
|
|
|
|
// Floating pins are invalid.
|
2019-03-13 01:25:53 +01:00
|
|
|
if ((net == nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
&& !network_->isTopLevelPort(pin))
|
|
|
|
|
|| (net
|
|
|
|
|
// Pins connected to power/ground are invalid.
|
|
|
|
|
&& (network_->isPower(net)
|
|
|
|
|
|| network_->isGround(net)))
|
|
|
|
|
// Hierarchical pins are invalid.
|
|
|
|
|
|| network_->isHierarchical(pin))
|
|
|
|
|
return true;
|
|
|
|
|
// Register/latch Q pins are invalid.
|
|
|
|
|
LibertyPort *port = network_->libertyPort(pin);
|
|
|
|
|
if (port) {
|
|
|
|
|
LibertyCell *cell = port->libertyCell();
|
2019-03-13 01:25:53 +01:00
|
|
|
LibertyCellTimingArcSetIterator set_iter(cell, nullptr, port);
|
2018-12-05 23:18:41 +01:00
|
|
|
while (set_iter.hasNext()) {
|
|
|
|
|
TimingArcSet *set = set_iter.next();
|
2018-09-28 17:54:21 +02:00
|
|
|
TimingRole *role = set->role();
|
2018-12-05 23:18:41 +01:00
|
|
|
if (role->genericRole() == TimingRole::regClkToQ())
|
2018-09-28 17:54:21 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeFalsePath(ExceptionFrom *from,
|
|
|
|
|
ExceptionThruSeq *thrus,
|
|
|
|
|
ExceptionTo *to,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
const char *comment)
|
|
|
|
|
{
|
|
|
|
|
checkFromThrusTo(from, thrus, to);
|
|
|
|
|
FalsePath *exception = new FalsePath(from, thrus, to, min_max, true,
|
|
|
|
|
comment);
|
|
|
|
|
addException(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeMulticyclePath(ExceptionFrom *from,
|
|
|
|
|
ExceptionThruSeq *thrus,
|
|
|
|
|
ExceptionTo *to,
|
|
|
|
|
const MinMaxAll *min_max,
|
|
|
|
|
bool use_end_clk,
|
|
|
|
|
int path_multiplier,
|
|
|
|
|
const char *comment)
|
|
|
|
|
{
|
|
|
|
|
checkFromThrusTo(from, thrus, to);
|
|
|
|
|
MultiCyclePath *exception = new MultiCyclePath(from, thrus, to,
|
|
|
|
|
min_max, use_end_clk,
|
|
|
|
|
path_multiplier, true,
|
|
|
|
|
comment);
|
|
|
|
|
addException(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makePathDelay(ExceptionFrom *from,
|
|
|
|
|
ExceptionThruSeq *thrus,
|
|
|
|
|
ExceptionTo *to,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
bool ignore_clk_latency,
|
|
|
|
|
float delay,
|
|
|
|
|
const char *comment)
|
|
|
|
|
{
|
|
|
|
|
checkFromThrusTo(from, thrus, to);
|
|
|
|
|
PathDelay *exception = new PathDelay(from, thrus, to, min_max,
|
|
|
|
|
ignore_clk_latency, delay, true,
|
|
|
|
|
comment);
|
|
|
|
|
addException(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordPathDelayInternalStartpoints(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionFrom *from = exception->from();
|
|
|
|
|
if (from
|
|
|
|
|
&& from->hasPins()) {
|
|
|
|
|
PinSet::Iterator pin_iter(from->pins());
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
if (!(network_->isRegClkPin(pin)
|
|
|
|
|
|| network_->isTopLevelPort(pin))) {
|
2019-03-13 01:25:53 +01:00
|
|
|
if (path_delay_internal_startpoints_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
path_delay_internal_startpoints_ = new PinSet;
|
|
|
|
|
path_delay_internal_startpoints_->insert(pin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordPathDelayInternalStartpoints(ExceptionFrom *from)
|
|
|
|
|
{
|
|
|
|
|
if (from
|
|
|
|
|
&& from->hasPins()
|
|
|
|
|
&& path_delay_internal_startpoints_) {
|
|
|
|
|
PinSet::Iterator pin_iter(from->pins());
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
if (!(network_->isRegClkPin(pin)
|
|
|
|
|
|| network_->isTopLevelPort(pin))
|
|
|
|
|
&& !pathDelayFrom(pin))
|
2019-04-11 05:36:48 +02:00
|
|
|
path_delay_internal_startpoints_->erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::pathDelayFrom(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
if (first_from_pin_exceptions_) {
|
|
|
|
|
ExceptionPathSet *exceptions = first_from_pin_exceptions_->findKey(pin);
|
|
|
|
|
ExceptionPathSet::ConstIterator exception_iter(exceptions);
|
|
|
|
|
while (exception_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *exception = exception_iter.next();
|
|
|
|
|
if (exception->isPathDelay())
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isPathDelayInternalStartpoint(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
return path_delay_internal_startpoints_
|
|
|
|
|
&& path_delay_internal_startpoints_->hasKey(const_cast<Pin*>(pin));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PinSet *
|
|
|
|
|
Sdc::pathDelayInternalStartpoints() const
|
|
|
|
|
{
|
|
|
|
|
return path_delay_internal_startpoints_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordPathDelayInternalEndpoints(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionTo *to = exception->to();
|
|
|
|
|
if (to
|
|
|
|
|
&& to->hasPins()) {
|
|
|
|
|
PinSet::Iterator pin_iter(to->pins());
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
if (!(hasLibertyChecks(pin)
|
|
|
|
|
|| network_->isTopLevelPort(pin))) {
|
2019-03-13 01:25:53 +01:00
|
|
|
if (path_delay_internal_endpoints_ == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
path_delay_internal_endpoints_ = new PinSet;
|
|
|
|
|
path_delay_internal_endpoints_->insert(pin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordPathDelayInternalEndpoints(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionTo *to = exception->to();
|
|
|
|
|
if (to
|
|
|
|
|
&& to->hasPins()
|
|
|
|
|
&& path_delay_internal_endpoints_) {
|
|
|
|
|
PinSet::Iterator pin_iter(to->pins());
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
if (!(hasLibertyChecks(pin)
|
|
|
|
|
|| network_->isTopLevelPort(pin))
|
|
|
|
|
&& !pathDelayTo(pin))
|
2019-04-11 05:36:48 +02:00
|
|
|
path_delay_internal_endpoints_->erase(pin);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::hasLibertyChecks(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
const Instance *inst = network_->instance(pin);
|
|
|
|
|
LibertyCell *cell = network_->libertyCell(inst);
|
|
|
|
|
if (cell) {
|
|
|
|
|
LibertyPort *port = network_->libertyPort(pin);
|
|
|
|
|
if (port) {
|
2019-03-13 01:25:53 +01:00
|
|
|
LibertyCellTimingArcSetIterator timing_iter(cell, nullptr, port);
|
2018-12-05 23:18:41 +01:00
|
|
|
while (timing_iter.hasNext()) {
|
|
|
|
|
TimingArcSet *arc_set = timing_iter.next();
|
|
|
|
|
if (arc_set->role()->isTimingCheck())
|
2018-09-28 17:54:21 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::pathDelayTo(const Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
if (first_to_pin_exceptions_) {
|
|
|
|
|
ExceptionPathSet *exceptions = first_to_pin_exceptions_->findKey(pin);
|
|
|
|
|
ExceptionPathSet::ConstIterator exception_iter(exceptions);
|
|
|
|
|
while (exception_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *exception = exception_iter.next();
|
|
|
|
|
if (exception->isPathDelay())
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isPathDelayInternalEndpoint(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
return path_delay_internal_endpoints_
|
|
|
|
|
&& path_delay_internal_endpoints_->hasKey(const_cast<Pin*>(pin));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clearGroupPathMap()
|
|
|
|
|
{
|
|
|
|
|
// GroupPath exceptions are deleted with other exceptions.
|
|
|
|
|
// Delete group_path name strings.
|
|
|
|
|
GroupPathIterator group_path_iter(group_path_map_);
|
|
|
|
|
while (group_path_iter.hasNext()) {
|
|
|
|
|
const char *name;
|
|
|
|
|
GroupPathSet *groups;
|
|
|
|
|
group_path_iter.next(name, groups);
|
|
|
|
|
stringDelete(name);
|
|
|
|
|
delete groups;
|
|
|
|
|
}
|
|
|
|
|
group_path_map_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeGroupPath(const char *name,
|
|
|
|
|
bool is_default,
|
|
|
|
|
ExceptionFrom *from,
|
|
|
|
|
ExceptionThruSeq *thrus,
|
|
|
|
|
ExceptionTo *to,
|
|
|
|
|
const char *comment)
|
|
|
|
|
{
|
|
|
|
|
checkFromThrusTo(from, thrus, to);
|
|
|
|
|
if (name && is_default)
|
|
|
|
|
internalError("group path name and is_default are mutually exclusive.");
|
|
|
|
|
else if (name) {
|
|
|
|
|
GroupPath *group_path = new GroupPath(name, is_default, from, thrus, to,
|
|
|
|
|
true, comment);
|
|
|
|
|
addException(group_path);
|
|
|
|
|
// A named group path can have multiple exceptions.
|
|
|
|
|
GroupPathSet *groups = group_path_map_.findKey(name);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (groups == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
groups = new GroupPathSet;
|
|
|
|
|
group_path_map_[stringCopy(name)] = groups;
|
|
|
|
|
}
|
|
|
|
|
groups->insert(group_path);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// is_default
|
|
|
|
|
GroupPath *group_path = new GroupPath(name, is_default, from, thrus, to,
|
|
|
|
|
true, comment);
|
|
|
|
|
addException(group_path);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isGroupPathName(const char *group_name)
|
|
|
|
|
{
|
|
|
|
|
return group_path_map_.hasKey(group_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupPathIterator *
|
|
|
|
|
Sdc::groupPathIterator()
|
|
|
|
|
{
|
|
|
|
|
return new GroupPathIterator(group_path_map_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
FilterPath *
|
|
|
|
|
Sdc::makeFilterPath(ExceptionFrom *from,
|
|
|
|
|
ExceptionThruSeq *thrus,
|
|
|
|
|
ExceptionTo *to)
|
|
|
|
|
{
|
|
|
|
|
checkFromThrusTo(from, thrus, to);
|
|
|
|
|
FilterPath *exception = new FilterPath(from, thrus, to, true);
|
|
|
|
|
addException(exception);
|
|
|
|
|
// This is the only type of exception that can be returned.
|
|
|
|
|
// There is only one of them, so it shouldn't merge.
|
|
|
|
|
return exception;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeLoopExceptions()
|
|
|
|
|
{
|
|
|
|
|
GraphLoopSeq::Iterator loop_iter(levelize_->loops());
|
|
|
|
|
while (loop_iter.hasNext()) {
|
|
|
|
|
GraphLoop *loop = loop_iter.next();
|
|
|
|
|
makeLoopExceptions(loop);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make a -thru pin false path from every edge entering the loop
|
|
|
|
|
// around the loop and back.
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeLoopExceptions(GraphLoop *loop)
|
|
|
|
|
{
|
|
|
|
|
debugPrint0(debug_, "loop", 2, "Loop false path\n");
|
|
|
|
|
EdgeSeq::Iterator loop_edge_iter(loop->edges());
|
|
|
|
|
while (loop_edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = loop_edge_iter.next();
|
|
|
|
|
Vertex *from_vertex = edge->from(graph_);
|
|
|
|
|
Vertex *to_vertex = edge->to(graph_);
|
|
|
|
|
Pin *from_pin = from_vertex->pin();
|
|
|
|
|
Pin *to_pin = to_vertex->pin();
|
|
|
|
|
// Find edges entering the loop.
|
|
|
|
|
VertexInEdgeIterator in_edge_iter(to_vertex, graph_);
|
|
|
|
|
while (in_edge_iter.hasNext()) {
|
|
|
|
|
Edge *in_edge = in_edge_iter.next();
|
|
|
|
|
if (in_edge != edge) {
|
|
|
|
|
Pin *loop_input_pin = in_edge->from(graph_)->pin();
|
|
|
|
|
makeLoopException(loop_input_pin, to_pin, from_pin);
|
|
|
|
|
// Prevent sub-loops by blocking paths on the main loop also.
|
|
|
|
|
makeLoopException(from_pin, to_pin, loop_input_pin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeLoopException(Pin *loop_input_pin,
|
|
|
|
|
Pin *loop_pin,
|
|
|
|
|
Pin *loop_prev_pin)
|
|
|
|
|
{
|
|
|
|
|
ExceptionThruSeq *thrus = new ExceptionThruSeq;
|
|
|
|
|
makeLoopExceptionThru(loop_input_pin, thrus);
|
|
|
|
|
makeLoopExceptionThru(loop_pin, thrus);
|
|
|
|
|
makeLoopExceptionThru(loop_prev_pin, thrus);
|
|
|
|
|
makeLoopExceptionThru(loop_pin, thrus);
|
|
|
|
|
makeLoopPath(thrus);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeLoopPath(ExceptionThruSeq *thrus)
|
|
|
|
|
{
|
|
|
|
|
FalsePath *exception = new LoopPath(thrus, true);
|
|
|
|
|
addException(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::makeLoopExceptionThru(Pin *pin,
|
|
|
|
|
ExceptionThruSeq *thrus)
|
|
|
|
|
{
|
|
|
|
|
debugPrint1(debug_, "levelize", 2, " %s\n", network_->pathName(pin));
|
|
|
|
|
PinSet *pins = new PinSet;
|
|
|
|
|
pins->insert(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionThru *thru = makeExceptionThru(pins, nullptr, nullptr,
|
2018-09-28 17:54:21 +02:00
|
|
|
TransRiseFallBoth::riseFall());
|
|
|
|
|
thrus->push_back(thru);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteLoopExceptions()
|
|
|
|
|
{
|
|
|
|
|
ExceptionPathSet::Iterator except_iter(exceptions_);
|
|
|
|
|
while (except_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *except = except_iter.next();
|
|
|
|
|
if (except->isLoop())
|
|
|
|
|
deleteException(except);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::addException(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
debugPrint1(debug_, "exception_merge", 1, "add exception for %s\n",
|
|
|
|
|
exception->asString(network_));
|
|
|
|
|
|
|
|
|
|
if (exception->isPathDelay()) {
|
|
|
|
|
recordPathDelayInternalStartpoints(exception);
|
|
|
|
|
recordPathDelayInternalEndpoints(exception);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (exception->to() == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
path_delays_without_to_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check to see if the exception has from/to mixed object types.
|
|
|
|
|
// If so, the priority of the exception is mixed.
|
|
|
|
|
// Split it into separate exceptions that have consistent priority.
|
|
|
|
|
ExceptionFrom *from = exception->from();
|
|
|
|
|
if (from
|
|
|
|
|
&& (from->hasPins() || from->hasInstances())
|
|
|
|
|
&& from->hasClocks()) {
|
2019-03-13 01:25:53 +01:00
|
|
|
PinSet *pins1 = from->pins() ? new PinSet(*from->pins()) : nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
InstanceSet *insts1 =
|
2019-03-13 01:25:53 +01:00
|
|
|
from->instances() ? new InstanceSet(*from->instances()) : nullptr;
|
|
|
|
|
ExceptionFrom *from1 = new ExceptionFrom(pins1, nullptr, insts1,
|
2018-09-28 17:54:21 +02:00
|
|
|
from->transition(), true);
|
|
|
|
|
ExceptionThruSeq *thrus1 = exceptionThrusClone(exception->thrus(), network_);
|
|
|
|
|
ExceptionTo *to = exception->to();
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionTo *to1 = to ? to->clone() : nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true);
|
|
|
|
|
debugPrint1(debug_, "exception_merge", 1, " split exception for %s\n",
|
|
|
|
|
exception1->asString(network_));
|
|
|
|
|
addException1(exception1);
|
|
|
|
|
|
|
|
|
|
ClockSet *clks2 = new ClockSet(*from->clks());
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionFrom *from2 = new ExceptionFrom(nullptr, clks2, nullptr,
|
2018-09-28 17:54:21 +02:00
|
|
|
from->transition(), true);
|
|
|
|
|
ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_);
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionTo *to2 = to ? to->clone() : nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true);
|
|
|
|
|
debugPrint1(debug_, "exception_merge", 1, " split exception for %s\n",
|
|
|
|
|
exception2->asString(network_));
|
|
|
|
|
addException1(exception2);
|
|
|
|
|
|
|
|
|
|
delete exception;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
addException1(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::addException1(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionTo *to = exception->to();
|
|
|
|
|
if (to
|
|
|
|
|
&& (to->hasPins() || to->hasInstances())
|
|
|
|
|
&& to->hasClocks()) {
|
|
|
|
|
ExceptionFrom *from1 = exception->from()->clone();
|
|
|
|
|
ExceptionThruSeq *thrus1 = exceptionThrusClone(exception->thrus(), network_);
|
2019-03-13 01:25:53 +01:00
|
|
|
PinSet *pins1 = to->pins() ? new PinSet(*to->pins()) : nullptr;
|
|
|
|
|
InstanceSet *insts1 = to->instances() ? new InstanceSet(*to->instances()) : nullptr;
|
|
|
|
|
ExceptionTo *to1 = new ExceptionTo(pins1, nullptr, insts1,
|
2018-09-28 17:54:21 +02:00
|
|
|
to->transition(),
|
|
|
|
|
to->endTransition(), true);
|
|
|
|
|
ExceptionPath *exception1 = exception->clone(from1, thrus1, to1, true);
|
|
|
|
|
debugPrint1(debug_, "exception_merge", 1, " split exception for %s\n",
|
|
|
|
|
exception1->asString(network_));
|
|
|
|
|
addException2(exception1);
|
|
|
|
|
|
|
|
|
|
ExceptionFrom *from2 = exception->from()->clone();
|
|
|
|
|
ExceptionThruSeq *thrus2 = exceptionThrusClone(exception->thrus(), network_);
|
|
|
|
|
ClockSet *clks2 = new ClockSet(*to->clks());
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionTo *to2 = new ExceptionTo(nullptr, clks2, nullptr, to->transition(),
|
2018-09-28 17:54:21 +02:00
|
|
|
to->endTransition(), true);
|
|
|
|
|
ExceptionPath *exception2 = exception->clone(from2, thrus2, to2, true);
|
|
|
|
|
debugPrint1(debug_, "exception_merge", 1, " split exception for %s\n",
|
|
|
|
|
exception2->asString(network_));
|
|
|
|
|
addException2(exception2);
|
|
|
|
|
|
|
|
|
|
delete exception;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
addException2(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::addException2(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
if (exception->isMultiCycle() || exception->isPathDelay())
|
|
|
|
|
deleteMatchingExceptions(exception);
|
|
|
|
|
recordException(exception);
|
|
|
|
|
mergeException(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If a path delay/multicycle exception is redefined with a different
|
|
|
|
|
// delay/cycle count, the new exception overrides the existing
|
|
|
|
|
// exception. Multiple related exceptions are merged to reduce the
|
|
|
|
|
// number of tags. To support overrides, relevant merged exceptions must be
|
|
|
|
|
// expanded to find and delete or override the new exception.
|
|
|
|
|
// For example, the exception
|
|
|
|
|
// set_multi_cycle_path -from {A B} -to {C D} 2
|
|
|
|
|
// is a merged representation of the following four exceptions:
|
|
|
|
|
// set_multi_cycle_path -from A -to C 2
|
|
|
|
|
// set_multi_cycle_path -from A -to D 2
|
|
|
|
|
// set_multi_cycle_path -from B -to C 2
|
|
|
|
|
// set_multi_cycle_path -from B -to D 2
|
|
|
|
|
// If the following exception is later defined,
|
|
|
|
|
// set_multi_cycle_path -from A -to C 3
|
|
|
|
|
// The cycle count of one of the merged exceptions changes.
|
|
|
|
|
// This prevents the original four exceptions from merging into one
|
|
|
|
|
// exception.
|
|
|
|
|
//
|
|
|
|
|
// This situation is handled by breaking the original merged exception
|
|
|
|
|
// into multiple smaller exceptions that exclude the new subset
|
|
|
|
|
// exception. This is NOT done by expanding the merged exception,
|
|
|
|
|
// since the number of exception points can be huge leading to serious
|
|
|
|
|
// run time problems.
|
|
|
|
|
//
|
|
|
|
|
// For the example above, the merged exception is broken down into the
|
|
|
|
|
// following set of exceptions that exclude the new subset exception.
|
|
|
|
|
//
|
|
|
|
|
// set_multi_cycle_path -from {B} -to {C D} 2
|
|
|
|
|
// set_multi_cycle_path -from {A} -to {D} 2
|
|
|
|
|
//
|
|
|
|
|
// In general, the merged exception is broken down as follows:
|
|
|
|
|
//
|
|
|
|
|
// -from {merged_from - subset_from} -thru merged_thru... -to merged_to
|
|
|
|
|
// -from merged_from -thru {merged_thru - subset_thru}... -to merged_to
|
|
|
|
|
// -from merged_from -thru merged_thru... -to {merged_to - subset_to}
|
|
|
|
|
//
|
|
|
|
|
// Where the {set1 - set2} is the set difference of of the from/thru/to
|
|
|
|
|
// objects of the merged/subset exception. If the set difference is empty,
|
|
|
|
|
// that group of exceptions matches the subset so it should not be included
|
|
|
|
|
// in the expansion.
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteMatchingExceptions(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
debugPrint1(debug_, "exception_merge", 1, "find matches for %s\n",
|
|
|
|
|
exception->asString(network_));
|
|
|
|
|
ExceptionPathSet matches;
|
|
|
|
|
findMatchingExceptions(exception, matches);
|
|
|
|
|
|
|
|
|
|
ExceptionPathSet expanded_matches;
|
|
|
|
|
ExceptionPathSet::Iterator match_iter1(matches);
|
|
|
|
|
while (match_iter1.hasNext()) {
|
|
|
|
|
ExceptionPath *match = match_iter1.next();
|
|
|
|
|
// Expand the matching exception into a set of exceptions that
|
|
|
|
|
// that do not cover the new exception. Do not record them
|
|
|
|
|
// to prevent merging with the match, which will be deleted.
|
|
|
|
|
expandExceptionExcluding(match, exception, expanded_matches);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ExceptionPathSet::Iterator match_iter2(matches);
|
|
|
|
|
while (match_iter2.hasNext()) {
|
|
|
|
|
ExceptionPath *match = match_iter2.next();
|
|
|
|
|
deleteException(match);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ExceptionPathSet::Iterator expanded_iter(expanded_matches);
|
|
|
|
|
while (expanded_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *expand = expanded_iter.next();
|
|
|
|
|
addException(expand);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::findMatchingExceptions(ExceptionPath *exception,
|
|
|
|
|
ExceptionPathSet &matches)
|
|
|
|
|
{
|
|
|
|
|
if (exception->from())
|
|
|
|
|
findMatchingExceptionsFirstFrom(exception, matches);
|
|
|
|
|
else if (exception->thrus())
|
|
|
|
|
findMatchingExceptionsFirstThru(exception, matches);
|
|
|
|
|
else if (exception->to())
|
|
|
|
|
findMatchingExceptionsFirstTo(exception, matches);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::findMatchingExceptionsFirstFrom(ExceptionPath *exception,
|
|
|
|
|
ExceptionPathSet &matches)
|
|
|
|
|
{
|
|
|
|
|
ExceptionFrom *from = exception->from();
|
|
|
|
|
if (first_from_pin_exceptions_)
|
|
|
|
|
findMatchingExceptionsPins(exception, from->pins(),
|
|
|
|
|
first_from_pin_exceptions_,
|
|
|
|
|
matches);
|
|
|
|
|
if (first_from_inst_exceptions_)
|
|
|
|
|
findMatchingExceptionsInsts(exception, from->instances(),
|
|
|
|
|
first_from_inst_exceptions_, matches);
|
|
|
|
|
if (first_from_clk_exceptions_)
|
|
|
|
|
findMatchingExceptionsClks(exception, from->clks(),
|
|
|
|
|
first_from_clk_exceptions_,
|
|
|
|
|
matches);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::findMatchingExceptionsFirstThru(ExceptionPath *exception,
|
|
|
|
|
ExceptionPathSet &matches)
|
|
|
|
|
{
|
|
|
|
|
ExceptionThru *thru = (*exception->thrus())[0];
|
|
|
|
|
findMatchingExceptionsPins(exception, thru->pins(),
|
|
|
|
|
first_thru_pin_exceptions_,
|
|
|
|
|
matches);
|
|
|
|
|
findMatchingExceptionsInsts(exception, thru->instances(),
|
|
|
|
|
first_thru_inst_exceptions_,
|
|
|
|
|
matches);
|
|
|
|
|
if (first_thru_net_exceptions_) {
|
|
|
|
|
NetSet::Iterator net_iter(thru->nets());
|
|
|
|
|
if (net_iter.hasNext()) {
|
|
|
|
|
Net *net = net_iter.next();
|
|
|
|
|
// Potential matches includes exceptions that match net that are not
|
|
|
|
|
// the first exception point.
|
|
|
|
|
ExceptionPathSet *potential_matches =
|
|
|
|
|
first_thru_net_exceptions_->findKey(net);
|
|
|
|
|
if (potential_matches) {
|
|
|
|
|
ExceptionPathSet::Iterator match_iter(potential_matches);
|
|
|
|
|
while (match_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *match = match_iter.next();
|
|
|
|
|
ExceptionThru *match_thru = (*match->thrus())[0];
|
|
|
|
|
if (match_thru->nets()->hasKey(net)
|
|
|
|
|
&& match->overrides(exception)
|
|
|
|
|
&& match->intersectsPts(exception))
|
|
|
|
|
matches.insert(match);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::findMatchingExceptionsFirstTo(ExceptionPath *exception,
|
|
|
|
|
ExceptionPathSet &matches)
|
|
|
|
|
{
|
|
|
|
|
ExceptionTo *to = exception->to();
|
|
|
|
|
findMatchingExceptionsPins(exception, to->pins(), first_to_pin_exceptions_,
|
|
|
|
|
matches);
|
|
|
|
|
findMatchingExceptionsInsts(exception, to->instances(),
|
|
|
|
|
first_to_inst_exceptions_,
|
|
|
|
|
matches);
|
|
|
|
|
findMatchingExceptionsClks(exception, to->clks(), first_to_clk_exceptions_,
|
|
|
|
|
matches);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::findMatchingExceptionsClks(ExceptionPath *exception,
|
|
|
|
|
ClockSet *clks,
|
|
|
|
|
ClockExceptionsMap *exception_map,
|
|
|
|
|
ExceptionPathSet &matches)
|
|
|
|
|
{
|
|
|
|
|
if (exception_map) {
|
|
|
|
|
ExceptionPathSet clks_matches;
|
|
|
|
|
ClockSet::Iterator clk_iter(clks);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
|
|
|
|
clks_matches.insertSet(exception_map->findKey(clk));
|
|
|
|
|
}
|
|
|
|
|
findMatchingExceptions(exception, &clks_matches, matches);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::findMatchingExceptionsPins(ExceptionPath *exception,
|
|
|
|
|
PinSet *pins,
|
|
|
|
|
PinExceptionsMap *exception_map,
|
|
|
|
|
ExceptionPathSet &matches)
|
|
|
|
|
{
|
|
|
|
|
if (exception_map) {
|
|
|
|
|
ExceptionPathSet pins_matches;
|
|
|
|
|
PinSet::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
const Pin *pin = pin_iter.next();
|
|
|
|
|
pins_matches.insertSet(exception_map->findKey(pin));
|
|
|
|
|
}
|
|
|
|
|
findMatchingExceptions(exception, &pins_matches, matches);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::findMatchingExceptionsInsts(ExceptionPath *exception,
|
|
|
|
|
InstanceSet *insts,
|
|
|
|
|
InstanceExceptionsMap *exception_map,
|
|
|
|
|
ExceptionPathSet &matches)
|
|
|
|
|
{
|
|
|
|
|
if (exception_map) {
|
|
|
|
|
ExceptionPathSet insts_matches;
|
|
|
|
|
InstanceSet::Iterator inst_iter(insts);
|
|
|
|
|
while (inst_iter.hasNext()) {
|
|
|
|
|
Instance *inst = inst_iter.next();
|
|
|
|
|
insts_matches.insertSet(exception_map->findKey(inst));
|
|
|
|
|
}
|
|
|
|
|
findMatchingExceptions(exception, &insts_matches, matches);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::findMatchingExceptions(ExceptionPath *exception,
|
|
|
|
|
ExceptionPathSet *potential_matches,
|
|
|
|
|
ExceptionPathSet &matches)
|
|
|
|
|
{
|
|
|
|
|
if (potential_matches) {
|
|
|
|
|
ExceptionPathSet::Iterator match_iter(potential_matches);
|
|
|
|
|
while (match_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *match = match_iter.next();
|
|
|
|
|
if (match->overrides(exception)
|
|
|
|
|
&& match->intersectsPts(exception))
|
|
|
|
|
matches.insert(match);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::expandExceptionExcluding(ExceptionPath *exception,
|
|
|
|
|
ExceptionPath *excluding,
|
|
|
|
|
ExceptionPathSet &expansions)
|
|
|
|
|
{
|
|
|
|
|
ExceptionFrom *from = exception->from();
|
|
|
|
|
ExceptionThruSeq *thrus = exception->thrus();
|
|
|
|
|
ExceptionTo *to = exception->to();
|
|
|
|
|
if (from) {
|
|
|
|
|
ExceptionFrom *from_cpy = from->clone();
|
|
|
|
|
from_cpy->deleteObjects(excluding->from());
|
|
|
|
|
if (from_cpy->hasObjects()) {
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionThruSeq *thrus_cpy = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (thrus)
|
|
|
|
|
thrus_cpy = clone(thrus, network_);
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionTo *to_cpy = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (to)
|
|
|
|
|
to_cpy = to->clone();
|
|
|
|
|
ExceptionPath *expand = exception->clone(from_cpy,thrus_cpy,to_cpy,true);
|
|
|
|
|
expansions.insert(expand);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
delete from_cpy;
|
|
|
|
|
}
|
|
|
|
|
if (thrus) {
|
|
|
|
|
ExceptionThruSeq::Iterator thru_iter(thrus);
|
|
|
|
|
ExceptionThruSeq::Iterator thru_iter2(excluding->thrus());
|
|
|
|
|
while (thru_iter.hasNext()
|
|
|
|
|
&& thru_iter2.hasNext()) {
|
|
|
|
|
ExceptionThru *thru = thru_iter.next();
|
|
|
|
|
ExceptionThru *thru2 = thru_iter2.next();
|
|
|
|
|
ExceptionThru *thru_cpy = thru->clone(network_);
|
|
|
|
|
thru_cpy->deleteObjects(thru2);
|
|
|
|
|
if (thru_cpy->hasObjects()) {
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionFrom *from_cpy = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (from)
|
|
|
|
|
from_cpy = from->clone();
|
|
|
|
|
ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq;
|
|
|
|
|
ExceptionThruSeq::Iterator thru_iter1(thrus);
|
|
|
|
|
while (thru_iter1.hasNext()) {
|
|
|
|
|
ExceptionThru *thru1 = thru_iter1.next();
|
|
|
|
|
if (thru1 == thru)
|
|
|
|
|
thrus_cpy->push_back(thru_cpy);
|
|
|
|
|
else {
|
|
|
|
|
ExceptionThru *thru_cpy = thru->clone(network_);
|
|
|
|
|
thrus_cpy->push_back(thru_cpy);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionTo *to_cpy = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (to)
|
|
|
|
|
to_cpy = to->clone();
|
|
|
|
|
ExceptionPath *expand = exception->clone(from_cpy, thrus_cpy, to_cpy,
|
|
|
|
|
true);
|
|
|
|
|
expansions.insert(expand);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
delete thru_cpy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (to) {
|
|
|
|
|
ExceptionTo *to_cpy = to->clone();
|
|
|
|
|
to_cpy->deleteObjects(excluding->to());
|
|
|
|
|
if (to_cpy->hasObjects()) {
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionFrom *from_cpy = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (from)
|
|
|
|
|
from_cpy = from->clone();
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionThruSeq *thrus_cpy = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (thrus)
|
|
|
|
|
thrus_cpy = clone(thrus, network_);
|
|
|
|
|
ExceptionPath *expand = exception->clone(from_cpy,thrus_cpy,to_cpy,true);
|
|
|
|
|
expansions.insert(expand);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
delete to_cpy;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ExceptionThruSeq *
|
|
|
|
|
clone(ExceptionThruSeq *thrus, Network *network)
|
|
|
|
|
{
|
|
|
|
|
ExceptionThruSeq *thrus_cpy = new ExceptionThruSeq;
|
|
|
|
|
ExceptionThruSeq::Iterator thru_iter(thrus);
|
|
|
|
|
while (thru_iter.hasNext()) {
|
|
|
|
|
ExceptionThru *thru = thru_iter.next();
|
|
|
|
|
ExceptionThru *thru_cpy = thru->clone(network);
|
|
|
|
|
thrus_cpy->push_back(thru_cpy);
|
|
|
|
|
}
|
|
|
|
|
return thrus_cpy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordException(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
exceptions_.insert(exception);
|
|
|
|
|
recordMergeHashes(exception);
|
|
|
|
|
recordExceptionFirstPts(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordMergeHashes(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionPtIterator missing_pt_iter(exception);
|
|
|
|
|
while (missing_pt_iter.hasNext()) {
|
|
|
|
|
ExceptionPt *missing_pt = missing_pt_iter.next();
|
|
|
|
|
recordMergeHash(exception, missing_pt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordMergeHash(ExceptionPath *exception,
|
|
|
|
|
ExceptionPt *missing_pt)
|
|
|
|
|
{
|
2019-08-08 23:13:02 +02:00
|
|
|
size_t hash = exception->hash(missing_pt);
|
2018-09-28 17:54:21 +02:00
|
|
|
debugPrint3(debug_, "exception_merge", 3,
|
2019-08-08 23:13:02 +02:00
|
|
|
"record merge hash %zu %s missing %s\n",
|
2018-09-28 17:54:21 +02:00
|
|
|
hash,
|
|
|
|
|
exception->asString(network_),
|
|
|
|
|
missing_pt->asString(network_));
|
|
|
|
|
ExceptionPathSet *set = exception_merge_hash_.findKey(hash);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (set == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
set = new ExceptionPathSet;
|
|
|
|
|
exception_merge_hash_[hash] = set;
|
|
|
|
|
}
|
|
|
|
|
set->insert(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Record a mapping from first pin/clock/instance's to a set of exceptions.
|
|
|
|
|
// The first exception point is when the exception becomes active.
|
|
|
|
|
// After it becomes active, its state changes as the other
|
|
|
|
|
// exception points are traversed.
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordExceptionFirstPts(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
if (exception->from())
|
|
|
|
|
recordExceptionFirstFrom(exception);
|
|
|
|
|
else if (exception->thrus())
|
|
|
|
|
recordExceptionFirstThru(exception);
|
|
|
|
|
else if (exception->to())
|
|
|
|
|
recordExceptionFirstTo(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordExceptionFirstFrom(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionFrom *from = exception->from();
|
|
|
|
|
recordExceptionPins(exception, from->pins(), first_from_pin_exceptions_);
|
|
|
|
|
recordExceptionInsts(exception, from->instances(),
|
|
|
|
|
first_from_inst_exceptions_);
|
|
|
|
|
recordExceptionClks(exception, from->clks(), first_from_clk_exceptions_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordExceptionFirstThru(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionThru *thru = (*exception->thrus())[0];
|
|
|
|
|
recordExceptionPins(exception, thru->pins(), first_thru_pin_exceptions_);
|
|
|
|
|
recordExceptionInsts(exception, thru->instances(),
|
|
|
|
|
first_thru_inst_exceptions_);
|
|
|
|
|
recordExceptionEdges(exception, thru->edges(), first_thru_edge_exceptions_);
|
|
|
|
|
ExceptionThruSeq::Iterator thru_iter(exception->thrus());
|
|
|
|
|
while (thru_iter.hasNext()) {
|
|
|
|
|
ExceptionThru *thru = thru_iter.next();
|
|
|
|
|
recordExceptionNets(exception, thru->nets(), first_thru_net_exceptions_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordExceptionFirstTo(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionTo *to = exception->to();
|
|
|
|
|
recordExceptionPins(exception, to->pins(), first_to_pin_exceptions_);
|
|
|
|
|
recordExceptionInsts(exception, to->instances(), first_to_inst_exceptions_);
|
|
|
|
|
recordExceptionClks(exception, to->clks(), first_to_clk_exceptions_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordExceptionClks(ExceptionPath *exception,
|
|
|
|
|
ClockSet *clks,
|
|
|
|
|
ClockExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
ClockSet::Iterator clk_iter(clks);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionPathSet *set = nullptr;
|
|
|
|
|
if (exception_map == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
exception_map = new ClockExceptionsMap;
|
|
|
|
|
else
|
|
|
|
|
set = exception_map->findKey(clk);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (set == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
set = new ExceptionPathSet;
|
|
|
|
|
(*exception_map)[clk] = set;
|
|
|
|
|
}
|
|
|
|
|
set->insert(exception);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordExceptionEdges(ExceptionPath *exception,
|
|
|
|
|
EdgePinsSet *edges,
|
|
|
|
|
EdgeExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
EdgePinsSet::Iterator edge_iter(edges);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
EdgePins *edge = edge_iter.next();
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionPathSet *set = nullptr;
|
|
|
|
|
if (exception_map == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
exception_map = new EdgeExceptionsMap;
|
|
|
|
|
else
|
|
|
|
|
set = exception_map->findKey(edge);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (set == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
set = new ExceptionPathSet;
|
|
|
|
|
// Copy the EdgePins so it is owned by the map.
|
|
|
|
|
edge = new EdgePins(*edge);
|
|
|
|
|
exception_map->insert(edge, set);
|
|
|
|
|
}
|
|
|
|
|
set->insert(exception);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordExceptionPins(ExceptionPath *exception,
|
|
|
|
|
PinSet *pins,
|
|
|
|
|
PinExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
PinSet::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
const Pin *pin = pin_iter.next();
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionPathSet *set = nullptr;
|
|
|
|
|
if (exception_map == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
exception_map = new PinExceptionsMap;
|
|
|
|
|
else
|
|
|
|
|
set = exception_map->findKey(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (set == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
set = new ExceptionPathSet;
|
|
|
|
|
exception_map->insert(pin, set);
|
|
|
|
|
}
|
|
|
|
|
set->insert(exception);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordExceptionHpin(ExceptionPath *exception,
|
|
|
|
|
Pin *pin,
|
|
|
|
|
PinExceptionsMap *&exception_map)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionPathSet *set = nullptr;
|
|
|
|
|
if (exception_map == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
exception_map = new PinExceptionsMap;
|
|
|
|
|
else
|
|
|
|
|
set = exception_map->findKey(pin);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (set == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
set = new ExceptionPathSet;
|
|
|
|
|
exception_map->insert(pin, set);
|
|
|
|
|
}
|
|
|
|
|
set->insert(exception);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordExceptionInsts(ExceptionPath *exception,
|
|
|
|
|
InstanceSet *insts,
|
|
|
|
|
InstanceExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
InstanceSet::Iterator inst_iter(insts);
|
|
|
|
|
while (inst_iter.hasNext()) {
|
|
|
|
|
Instance *inst = inst_iter.next();
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionPathSet *set = nullptr;
|
|
|
|
|
if (exception_map == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
exception_map = new InstanceExceptionsMap;
|
|
|
|
|
else
|
|
|
|
|
set = exception_map->findKey(inst);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (set == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
set = new ExceptionPathSet;
|
|
|
|
|
(*exception_map)[inst] = set;
|
|
|
|
|
}
|
|
|
|
|
set->insert(exception);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::recordExceptionNets(ExceptionPath *exception,
|
|
|
|
|
NetSet *nets,
|
|
|
|
|
NetExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
NetSet::Iterator net_iter(nets);
|
|
|
|
|
while (net_iter.hasNext()) {
|
|
|
|
|
const Net *net = net_iter.next();
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionPathSet *set = nullptr;
|
|
|
|
|
if (exception_map == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
exception_map = new NetExceptionsMap;
|
|
|
|
|
else
|
|
|
|
|
set = exception_map->findKey(net);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (set == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
set = new ExceptionPathSet;
|
|
|
|
|
(*exception_map)[net] = set;
|
|
|
|
|
}
|
|
|
|
|
set->insert(exception);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Exceptions of the same type can be merged if they differ in exactly
|
|
|
|
|
// one exception point (-from, -thru or -to).
|
|
|
|
|
// For example, the following exceptions:
|
|
|
|
|
// set_false_path -from {A B} -to C
|
|
|
|
|
// set_false_path -from {A B} -to D
|
|
|
|
|
// can be merged to form:
|
|
|
|
|
// set_false_path -from {A B} -to {C D}
|
|
|
|
|
//
|
|
|
|
|
// A hash is generated for each exception missing one exception point
|
|
|
|
|
// to find potential matches. If a match is found, the exceptions are
|
|
|
|
|
// merged. Next we try to merge the surviving exception until we run
|
|
|
|
|
// out of merges.
|
|
|
|
|
void
|
|
|
|
|
Sdc::mergeException(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionPath *merged = findMergeMatch(exception);
|
|
|
|
|
while (merged)
|
|
|
|
|
merged = findMergeMatch(merged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Return the merged result.
|
|
|
|
|
ExceptionPath *
|
|
|
|
|
Sdc::findMergeMatch(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
bool first_pt = true;
|
|
|
|
|
ExceptionPtIterator missing_pt_iter(exception);
|
|
|
|
|
while (missing_pt_iter.hasNext()) {
|
|
|
|
|
ExceptionPt *missing_pt = missing_pt_iter.next();
|
2019-08-08 23:13:02 +02:00
|
|
|
size_t hash = exception->hash(missing_pt);
|
2018-09-28 17:54:21 +02:00
|
|
|
ExceptionPathSet *matches = exception_merge_hash_.findKey(hash);
|
|
|
|
|
if (matches) {
|
|
|
|
|
ExceptionPathSet::Iterator match_iter(matches);
|
|
|
|
|
while (match_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *match = match_iter.next();
|
|
|
|
|
ExceptionPt *match_missing_pt;
|
|
|
|
|
if (match != exception
|
|
|
|
|
// Exceptions are not merged if their priorities are
|
|
|
|
|
// different. This allows exceptions to be pruned during
|
|
|
|
|
// search at the endpoint.
|
|
|
|
|
&& exception->mergeable(match)
|
|
|
|
|
&& match->mergeablePts(exception, missing_pt, match_missing_pt)) {
|
|
|
|
|
debugPrint1(debug_, "exception_merge", 1, "merge %s\n",
|
|
|
|
|
exception->asString(network_));
|
|
|
|
|
debugPrint1(debug_, "exception_merge", 1, " with %s\n",
|
|
|
|
|
match->asString(network_));
|
|
|
|
|
// Unrecord the exception that is being merged away.
|
|
|
|
|
unrecordException(exception);
|
|
|
|
|
unrecordMergeHashes(match);
|
|
|
|
|
missing_pt->mergeInto(match_missing_pt);
|
|
|
|
|
recordMergeHashes(match);
|
|
|
|
|
// First point maps only change if the exception point that
|
|
|
|
|
// is being merged is the first exception point.
|
|
|
|
|
if (first_pt)
|
|
|
|
|
recordExceptionFirstPts(match);
|
|
|
|
|
// Have to wait until after exception point merge to delete
|
|
|
|
|
// the exception.
|
|
|
|
|
delete exception;
|
|
|
|
|
return match;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
first_pt = false;
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
return nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteExceptions()
|
|
|
|
|
{
|
|
|
|
|
ExceptionPathSet::Iterator except_iter(exceptions_);
|
|
|
|
|
while (except_iter.hasNext()) {
|
|
|
|
|
delete except_iter.next();
|
|
|
|
|
}
|
|
|
|
|
exceptions_.clear();
|
|
|
|
|
|
|
|
|
|
deleteExceptionMap(first_from_pin_exceptions_);
|
|
|
|
|
deleteExceptionMap(first_from_clk_exceptions_);
|
|
|
|
|
deleteExceptionMap(first_from_inst_exceptions_);
|
|
|
|
|
deleteExceptionMap(first_to_pin_exceptions_);
|
|
|
|
|
deleteExceptionMap(first_to_clk_exceptions_);
|
|
|
|
|
deleteExceptionMap(first_to_inst_exceptions_);
|
|
|
|
|
deleteExceptionMap(first_thru_pin_exceptions_);
|
|
|
|
|
deleteExceptionMap(first_thru_inst_exceptions_);
|
|
|
|
|
deleteExceptionMap(first_thru_net_exceptions_);
|
|
|
|
|
deleteExceptionMap(first_thru_edge_exceptions_);
|
|
|
|
|
|
|
|
|
|
delete path_delay_internal_startpoints_;
|
2019-03-13 01:25:53 +01:00
|
|
|
path_delay_internal_startpoints_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
delete path_delay_internal_endpoints_;
|
2019-03-13 01:25:53 +01:00
|
|
|
path_delay_internal_endpoints_ = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
|
|
|
|
|
deleteExceptionPtHashMapSets(exception_merge_hash_);
|
|
|
|
|
exception_merge_hash_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteExceptionPtHashMapSets(ExceptionPathPtHash &map)
|
|
|
|
|
{
|
|
|
|
|
ExceptionPathPtHash::Iterator set_iter(map);
|
|
|
|
|
while (set_iter.hasNext())
|
|
|
|
|
delete set_iter.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteExceptionMap(PinExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
PinExceptionsMap::Iterator set_iter(exception_map);
|
|
|
|
|
while (set_iter.hasNext()) {
|
|
|
|
|
const Pin *pin;
|
|
|
|
|
ExceptionPathSet *set;
|
|
|
|
|
set_iter.next(pin, set);
|
|
|
|
|
delete set;
|
|
|
|
|
}
|
|
|
|
|
delete exception_map;
|
2019-03-13 01:25:53 +01:00
|
|
|
exception_map = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteExceptionMap(InstanceExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
InstanceExceptionsMap::Iterator set_iter(exception_map);
|
|
|
|
|
while (set_iter.hasNext()) {
|
|
|
|
|
const Instance *inst;
|
|
|
|
|
ExceptionPathSet *set;
|
|
|
|
|
set_iter.next(inst, set);
|
|
|
|
|
delete set;
|
|
|
|
|
}
|
|
|
|
|
delete exception_map;
|
2019-03-13 01:25:53 +01:00
|
|
|
exception_map = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteExceptionMap(NetExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
NetExceptionsMap::Iterator set_iter(exception_map);
|
|
|
|
|
while (set_iter.hasNext()) {
|
|
|
|
|
const Net *net;
|
|
|
|
|
ExceptionPathSet *set;
|
|
|
|
|
set_iter.next(net, set);
|
|
|
|
|
delete set;
|
|
|
|
|
}
|
|
|
|
|
delete exception_map;
|
2019-03-13 01:25:53 +01:00
|
|
|
exception_map = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteExceptionMap(ClockExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
ClockExceptionsMap::Iterator set_iter(exception_map);
|
|
|
|
|
while (set_iter.hasNext()) {
|
|
|
|
|
const Clock *clk;
|
|
|
|
|
ExceptionPathSet *set;
|
|
|
|
|
set_iter.next(clk, set);
|
|
|
|
|
delete set;
|
|
|
|
|
}
|
|
|
|
|
delete exception_map;
|
2019-03-13 01:25:53 +01:00
|
|
|
exception_map = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteExceptionMap(EdgeExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
EdgeExceptionsMap::Iterator set_iter(exception_map);
|
|
|
|
|
while (set_iter.hasNext()) {
|
|
|
|
|
const EdgePins *edge_pins;
|
|
|
|
|
ExceptionPathSet *set;
|
|
|
|
|
set_iter.next(edge_pins, set);
|
|
|
|
|
delete set;
|
|
|
|
|
delete edge_pins;
|
|
|
|
|
}
|
|
|
|
|
delete exception_map;
|
2019-03-13 01:25:53 +01:00
|
|
|
exception_map = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteExceptionsReferencing(Clock *clk)
|
|
|
|
|
{
|
|
|
|
|
ExceptionPathSet::ConstIterator exception_iter(exceptions_);
|
|
|
|
|
while (exception_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *exception = exception_iter.next();
|
|
|
|
|
bool deleted = false;
|
|
|
|
|
ExceptionFrom *from = exception->from();
|
|
|
|
|
if (from) {
|
|
|
|
|
ClockSet *clks = from->clks();
|
|
|
|
|
if (clks && clks->hasKey(clk)) {
|
|
|
|
|
unrecordException(exception);
|
|
|
|
|
from->deleteClock(clk);
|
|
|
|
|
if (from->hasObjects())
|
|
|
|
|
recordException(exception);
|
|
|
|
|
else {
|
|
|
|
|
deleteException(exception);
|
|
|
|
|
deleted = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!deleted) {
|
|
|
|
|
ExceptionTo *to = exception->to();
|
|
|
|
|
if (to) {
|
|
|
|
|
ClockSet *clks = to->clks();
|
|
|
|
|
if (clks && clks->hasKey(clk)) {
|
|
|
|
|
unrecordException(exception);
|
|
|
|
|
to->deleteClock(clk);
|
|
|
|
|
if (to->hasObjects())
|
|
|
|
|
recordException(exception);
|
|
|
|
|
else
|
|
|
|
|
deleteException(exception);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deleteException(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
debugPrint1(debug_, "exception_merge", 2, "delete %s\n",
|
|
|
|
|
exception->asString(network_));
|
|
|
|
|
unrecordException(exception);
|
|
|
|
|
delete exception;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordException(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
unrecordMergeHashes(exception);
|
|
|
|
|
unrecordExceptionFirstPts(exception);
|
2019-04-11 05:36:48 +02:00
|
|
|
exceptions_.erase(exception);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordMergeHashes(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionPtIterator missing_pt_iter(exception);
|
|
|
|
|
while (missing_pt_iter.hasNext()) {
|
|
|
|
|
ExceptionPt *missing_pt = missing_pt_iter.next();
|
|
|
|
|
unrecordMergeHash(exception, missing_pt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordMergeHash(ExceptionPath *exception,
|
|
|
|
|
ExceptionPt *missing_pt)
|
|
|
|
|
{
|
2019-08-08 23:13:02 +02:00
|
|
|
size_t hash = exception->hash(missing_pt);
|
2018-09-28 17:54:21 +02:00
|
|
|
debugPrint3(debug_, "exception_merge", 3,
|
2019-08-08 23:13:02 +02:00
|
|
|
"unrecord merge hash %zu %s missing %s\n",
|
2018-09-28 17:54:21 +02:00
|
|
|
hash,
|
|
|
|
|
exception->asString(network_),
|
|
|
|
|
missing_pt->asString(network_));
|
|
|
|
|
ExceptionPathSet *matches = exception_merge_hash_.findKey(hash);
|
|
|
|
|
if (matches)
|
2019-04-11 05:36:48 +02:00
|
|
|
matches->erase(exception);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordExceptionFirstPts(ExceptionPath *exception)
|
|
|
|
|
{
|
|
|
|
|
ExceptionFrom *from = exception->from();
|
|
|
|
|
ExceptionThruSeq *thrus = exception->thrus();
|
|
|
|
|
ExceptionTo *to = exception->to();
|
|
|
|
|
if (from) {
|
|
|
|
|
unrecordExceptionPins(exception, from->pins(), first_from_pin_exceptions_);
|
|
|
|
|
unrecordExceptionClks(exception, from->clks(), first_from_clk_exceptions_);
|
|
|
|
|
unrecordExceptionInsts(exception, from->instances(),
|
|
|
|
|
first_from_inst_exceptions_);
|
|
|
|
|
}
|
|
|
|
|
else if (thrus) {
|
|
|
|
|
ExceptionThru *thru = (*thrus)[0];
|
|
|
|
|
unrecordExceptionPins(exception, thru->pins(), first_thru_pin_exceptions_);
|
|
|
|
|
unrecordExceptionInsts(exception, thru->instances(),
|
|
|
|
|
first_thru_inst_exceptions_);
|
|
|
|
|
unrecordExceptionNets(exception, thru->nets(), first_thru_net_exceptions_);
|
|
|
|
|
unrecordExceptionEdges(exception, thru->edges(),
|
|
|
|
|
first_thru_edge_exceptions_);
|
|
|
|
|
}
|
|
|
|
|
else if (to) {
|
|
|
|
|
unrecordExceptionPins(exception, to->pins(), first_to_pin_exceptions_);
|
|
|
|
|
unrecordExceptionClks(exception, to->clks(), first_to_clk_exceptions_);
|
|
|
|
|
unrecordExceptionInsts(exception, to->instances(),
|
|
|
|
|
first_to_inst_exceptions_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordExceptionClks(ExceptionPath *exception,
|
|
|
|
|
ClockSet *clks,
|
|
|
|
|
ClockExceptionsMap *exception_map)
|
|
|
|
|
{
|
|
|
|
|
ClockSet::Iterator clk_iter(clks);
|
|
|
|
|
while (clk_iter.hasNext()) {
|
|
|
|
|
Clock *clk = clk_iter.next();
|
|
|
|
|
ExceptionPathSet *set = exception_map->findKey(clk);
|
|
|
|
|
if (set)
|
2019-04-11 05:36:48 +02:00
|
|
|
set->erase(exception);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordExceptionPins(ExceptionPath *exception,
|
|
|
|
|
PinSet *pins,
|
|
|
|
|
PinExceptionsMap *exception_map)
|
|
|
|
|
{
|
|
|
|
|
PinSet::Iterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
const Pin *pin = pin_iter.next();
|
|
|
|
|
ExceptionPathSet *set = exception_map->findKey(pin);
|
|
|
|
|
if (set)
|
2019-04-11 05:36:48 +02:00
|
|
|
set->erase(exception);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordExceptionInsts(ExceptionPath *exception,
|
|
|
|
|
InstanceSet *insts,
|
|
|
|
|
InstanceExceptionsMap *exception_map)
|
|
|
|
|
{
|
|
|
|
|
InstanceSet::Iterator inst_iter(insts);
|
|
|
|
|
while (inst_iter.hasNext()) {
|
|
|
|
|
Instance *inst = inst_iter.next();
|
|
|
|
|
ExceptionPathSet *set = exception_map->findKey(inst);
|
|
|
|
|
if (set)
|
2019-04-11 05:36:48 +02:00
|
|
|
set->erase(exception);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordExceptionEdges(ExceptionPath *exception,
|
|
|
|
|
EdgePinsSet *edges,
|
|
|
|
|
EdgeExceptionsMap *exception_map)
|
|
|
|
|
{
|
|
|
|
|
EdgePinsSet::Iterator edge_iter(edges);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
EdgePins *edge = edge_iter.next();
|
|
|
|
|
ExceptionPathSet *set = exception_map->findKey(edge);
|
|
|
|
|
if (set)
|
2019-04-11 05:36:48 +02:00
|
|
|
set->erase(exception);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordExceptionNets(ExceptionPath *exception,
|
|
|
|
|
NetSet *nets,
|
|
|
|
|
NetExceptionsMap *exception_map)
|
|
|
|
|
{
|
|
|
|
|
NetSet::Iterator net_iter(nets);
|
|
|
|
|
while (net_iter.hasNext()) {
|
|
|
|
|
const Net *net = net_iter.next();
|
|
|
|
|
ExceptionPathSet *set = exception_map->findKey(net);
|
|
|
|
|
if (set)
|
2019-04-11 05:36:48 +02:00
|
|
|
set->erase(exception);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::unrecordExceptionHpin(ExceptionPath *exception,
|
|
|
|
|
Pin *pin,
|
|
|
|
|
PinExceptionsMap *&exception_map)
|
|
|
|
|
{
|
|
|
|
|
ExceptionPathSet *set = exception_map->findKey(pin);
|
|
|
|
|
if (set)
|
2019-04-11 05:36:48 +02:00
|
|
|
set->erase(exception);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
class ExpandException : public ExpandedExceptionVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
ExpandException(ExceptionPath *exception,
|
|
|
|
|
ExceptionPathSet &expansions,
|
|
|
|
|
Network *network);
|
|
|
|
|
virtual void visit(ExceptionFrom *from, ExceptionThruSeq *thrus,
|
|
|
|
|
ExceptionTo *to);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
ExceptionPathSet &expansions_;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(ExpandException);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ExpandException::ExpandException(ExceptionPath *exception,
|
|
|
|
|
ExceptionPathSet &expansions,
|
|
|
|
|
Network *network) :
|
|
|
|
|
ExpandedExceptionVisitor(exception, network),
|
|
|
|
|
expansions_(expansions)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ExpandException::visit(ExceptionFrom *from,
|
|
|
|
|
ExceptionThruSeq *thrus,
|
|
|
|
|
ExceptionTo *to)
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionFrom *from_clone = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (from)
|
|
|
|
|
from_clone = from->clone();
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionThruSeq *thrus_clone = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (thrus) {
|
|
|
|
|
thrus_clone = new ExceptionThruSeq;
|
|
|
|
|
ExceptionThruSeq::Iterator thru_iter(thrus);
|
|
|
|
|
while (thru_iter.hasNext()) {
|
|
|
|
|
ExceptionThru *thru = thru_iter.next();
|
|
|
|
|
thrus_clone->push_back(thru->clone(network_));
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
ExceptionTo *to_clone = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
if (to)
|
|
|
|
|
to_clone = to->clone();
|
|
|
|
|
ExceptionPath *expand = exception_->clone(from_clone, thrus_clone,
|
|
|
|
|
to_clone, true);
|
|
|
|
|
expansions_.insert(expand);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Expand exception from/thrus/to sets so there is only one exception
|
|
|
|
|
// point in each from/thru/to.
|
|
|
|
|
void
|
|
|
|
|
Sdc::expandException(ExceptionPath *exception,
|
|
|
|
|
ExceptionPathSet &expansions)
|
|
|
|
|
{
|
|
|
|
|
ExpandException expander(exception, expansions, network_);
|
|
|
|
|
expander.visitExpansions();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::resetPath(ExceptionFrom *from,
|
|
|
|
|
ExceptionThruSeq *thrus,
|
|
|
|
|
ExceptionTo *to,
|
|
|
|
|
const MinMaxAll *min_max)
|
|
|
|
|
{
|
|
|
|
|
checkFromThrusTo(from, thrus, to);
|
|
|
|
|
ExceptionPathSet::Iterator except_iter(exceptions_);
|
|
|
|
|
while (except_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *match = except_iter.next();
|
|
|
|
|
if (match->resetMatch(from, thrus, to, min_max)) {
|
|
|
|
|
debugPrint1(debug_, "exception_match", 3, "reset match %s\n",
|
|
|
|
|
match->asString(network_));
|
|
|
|
|
ExceptionPathSet expansions;
|
|
|
|
|
expandException(match, expansions);
|
|
|
|
|
deleteException(match);
|
|
|
|
|
ExceptionPathSet::Iterator expand_iter(expansions);
|
|
|
|
|
while (expand_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *expand = expand_iter.next();
|
|
|
|
|
if (expand->resetMatch(from, thrus, to, min_max)) {
|
|
|
|
|
unrecordPathDelayInternalStartpoints(expand->from());
|
|
|
|
|
unrecordPathDelayInternalEndpoints(expand);
|
|
|
|
|
delete expand;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
addException(expand);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::exceptionFromStates(const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
ExceptionStateSet *&states) const
|
|
|
|
|
{
|
|
|
|
|
return exceptionFromStates(pin, tr, clk, clk_tr, min_max, true, states);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::exceptionFromStates(const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
bool include_filter,
|
|
|
|
|
ExceptionStateSet *&states) const
|
|
|
|
|
{
|
|
|
|
|
bool srch_from = true;
|
|
|
|
|
if (pin) {
|
|
|
|
|
if (srch_from && first_from_pin_exceptions_)
|
|
|
|
|
srch_from &= exceptionFromStates(first_from_pin_exceptions_->findKey(pin),
|
2019-03-13 01:25:53 +01:00
|
|
|
nullptr, tr, min_max, include_filter,
|
2018-09-28 17:54:21 +02:00
|
|
|
states);
|
|
|
|
|
if (srch_from && first_thru_pin_exceptions_)
|
|
|
|
|
srch_from &= exceptionFromStates(first_thru_pin_exceptions_->findKey(pin),
|
2019-03-13 01:25:53 +01:00
|
|
|
nullptr, tr, min_max, include_filter,
|
2018-09-28 17:54:21 +02:00
|
|
|
states);
|
|
|
|
|
|
|
|
|
|
if (srch_from
|
|
|
|
|
&& (first_from_inst_exceptions_ || first_thru_inst_exceptions_)) {
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
if (srch_from && first_from_inst_exceptions_)
|
|
|
|
|
srch_from &= exceptionFromStates(first_from_inst_exceptions_->findKey(inst),
|
|
|
|
|
pin, tr, min_max, include_filter,
|
|
|
|
|
states);
|
|
|
|
|
if (srch_from && first_thru_inst_exceptions_)
|
|
|
|
|
srch_from &= exceptionFromStates(first_thru_inst_exceptions_->findKey(inst),
|
|
|
|
|
pin, tr, min_max, include_filter,
|
|
|
|
|
states);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (srch_from && clk && first_from_clk_exceptions_)
|
|
|
|
|
srch_from &= exceptionFromStates(first_from_clk_exceptions_->findKey(clk),
|
|
|
|
|
pin, clk_tr, min_max, include_filter,
|
|
|
|
|
states);
|
|
|
|
|
if (!srch_from) {
|
|
|
|
|
delete states;
|
2019-03-13 01:25:53 +01:00
|
|
|
states = nullptr;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
return srch_from;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::exceptionFromStates(const ExceptionPathSet *exceptions,
|
|
|
|
|
const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
bool include_filter,
|
|
|
|
|
ExceptionStateSet *&states) const
|
|
|
|
|
{
|
|
|
|
|
if (exceptions) {
|
|
|
|
|
ExceptionPathSet::ConstIterator exception_iter(exceptions);
|
|
|
|
|
while (exception_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *exception = exception_iter.next();
|
|
|
|
|
if (exception->matches(min_max, false)
|
2019-03-13 01:25:53 +01:00
|
|
|
&& (exception->from() == nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
|| exception->from()->transition()->matches(tr))
|
|
|
|
|
&& (include_filter || !exception->isFilter())) {
|
|
|
|
|
ExceptionState *state = exception->firstState();
|
2019-03-13 01:25:53 +01:00
|
|
|
if (state->matchesNextThru(nullptr, pin, tr, min_max, network_))
|
2018-09-28 17:54:21 +02:00
|
|
|
// -from clk -thru reg/clk
|
|
|
|
|
state = state->nextState();
|
|
|
|
|
// If the exception is -from and has no -to transition it is
|
|
|
|
|
// complete out of the gate.
|
|
|
|
|
if (state->isComplete()
|
|
|
|
|
&& exception->isFalse()) {
|
|
|
|
|
// Leave the completed false path state as a marker on the tag,
|
|
|
|
|
// but flush all other exception states because they are lower
|
|
|
|
|
// priority.
|
2019-03-13 01:25:53 +01:00
|
|
|
if (states == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
states = new ExceptionStateSet;
|
|
|
|
|
states->clear();
|
|
|
|
|
states->insert(state);
|
|
|
|
|
// No need to examine other exceptions from this
|
|
|
|
|
// pin/clock/instance.
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-03-13 01:25:53 +01:00
|
|
|
if (states == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
states = new ExceptionStateSet;
|
|
|
|
|
states->insert(state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::exceptionFromClkStates(const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const Clock *clk,
|
|
|
|
|
const TransRiseFall *clk_tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
ExceptionStateSet *&states) const
|
|
|
|
|
{
|
|
|
|
|
if (pin) {
|
|
|
|
|
if (first_from_pin_exceptions_)
|
|
|
|
|
exceptionFromStates(first_from_pin_exceptions_->findKey(pin),
|
2019-03-13 01:25:53 +01:00
|
|
|
nullptr, tr, min_max, true, states);
|
2018-09-28 17:54:21 +02:00
|
|
|
if (first_from_inst_exceptions_) {
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
exceptionFromStates(first_from_inst_exceptions_->findKey(inst),
|
|
|
|
|
pin, tr, min_max, true, states);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (first_from_clk_exceptions_)
|
|
|
|
|
exceptionFromStates(first_from_clk_exceptions_->findKey(clk),
|
|
|
|
|
pin, clk_tr, min_max, true, states);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::filterRegQStates(const Pin *to_pin,
|
|
|
|
|
const TransRiseFall *to_tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
ExceptionStateSet *&states) const
|
|
|
|
|
{
|
|
|
|
|
if (first_from_pin_exceptions_) {
|
|
|
|
|
const ExceptionPathSet *exceptions =
|
|
|
|
|
first_from_pin_exceptions_->findKey(to_pin);
|
|
|
|
|
if (exceptions) {
|
|
|
|
|
ExceptionPathSet::ConstIterator exception_iter(exceptions);
|
|
|
|
|
while (exception_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *exception = exception_iter.next();
|
|
|
|
|
// Hack for filter -from reg/Q.
|
|
|
|
|
if (exception->isFilter()
|
|
|
|
|
&& exception->matchesFirstPt(to_tr, min_max)) {
|
|
|
|
|
ExceptionState *state = exception->firstState();
|
2019-03-13 01:25:53 +01:00
|
|
|
if (states == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
states = new ExceptionStateSet;
|
|
|
|
|
states->insert(state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::exceptionThruStates(const Pin *from_pin,
|
|
|
|
|
const Pin *to_pin,
|
|
|
|
|
const TransRiseFall *to_tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
ExceptionStateSet *&states) const
|
|
|
|
|
{
|
|
|
|
|
if (first_thru_pin_exceptions_)
|
|
|
|
|
exceptionThruStates(first_thru_pin_exceptions_->findKey(to_pin),
|
|
|
|
|
to_tr, min_max, states);
|
|
|
|
|
if (first_thru_edge_exceptions_) {
|
|
|
|
|
EdgePins edge_pins(const_cast<Pin*>(from_pin), const_cast<Pin*>(to_pin));
|
|
|
|
|
exceptionThruStates(first_thru_edge_exceptions_->findKey(&edge_pins),
|
|
|
|
|
to_tr, min_max, states);
|
|
|
|
|
}
|
|
|
|
|
if (first_thru_inst_exceptions_
|
|
|
|
|
&& (network_->direction(to_pin)->isAnyOutput()
|
|
|
|
|
|| network_->isLatchData(to_pin))) {
|
|
|
|
|
const Instance *to_inst = network_->instance(to_pin);
|
|
|
|
|
exceptionThruStates(first_thru_inst_exceptions_->findKey(to_inst),
|
|
|
|
|
to_tr, min_max, states);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::exceptionThruStates(const ExceptionPathSet *exceptions,
|
|
|
|
|
const TransRiseFall *to_tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
// Return value.
|
|
|
|
|
ExceptionStateSet *&states) const
|
|
|
|
|
{
|
|
|
|
|
if (exceptions) {
|
|
|
|
|
ExceptionPathSet::ConstIterator exception_iter(exceptions);
|
|
|
|
|
while (exception_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *exception = exception_iter.next();
|
|
|
|
|
if (exception->matchesFirstPt(to_tr, min_max)) {
|
|
|
|
|
ExceptionState *state = exception->firstState();
|
2019-03-13 01:25:53 +01:00
|
|
|
if (states == nullptr)
|
2018-09-28 17:54:21 +02:00
|
|
|
states = new ExceptionStateSet;
|
|
|
|
|
states->insert(state);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::exceptionTo(ExceptionPathType type,
|
|
|
|
|
const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const ClockEdge *clk_edge,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
bool match_min_max_exactly,
|
|
|
|
|
// Return values.
|
|
|
|
|
ExceptionPath *&hi_priority_exception,
|
|
|
|
|
int &hi_priority) const
|
|
|
|
|
{
|
|
|
|
|
if (first_to_inst_exceptions_) {
|
|
|
|
|
Instance *inst = network_->instance(pin);
|
|
|
|
|
exceptionTo(first_to_inst_exceptions_->findKey(inst), type, pin, tr,
|
|
|
|
|
clk_edge, min_max, match_min_max_exactly,
|
|
|
|
|
hi_priority_exception, hi_priority);
|
|
|
|
|
}
|
|
|
|
|
if (first_to_pin_exceptions_)
|
|
|
|
|
exceptionTo(first_to_pin_exceptions_->findKey(pin), type, pin, tr,
|
|
|
|
|
clk_edge, min_max, match_min_max_exactly,
|
|
|
|
|
hi_priority_exception, hi_priority);
|
|
|
|
|
if (clk_edge && first_to_clk_exceptions_)
|
|
|
|
|
exceptionTo(first_to_clk_exceptions_->findKey(clk_edge->clock()),
|
|
|
|
|
type, pin, tr, clk_edge, min_max, match_min_max_exactly,
|
|
|
|
|
hi_priority_exception, hi_priority);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::exceptionTo(const ExceptionPathSet *to_exceptions,
|
|
|
|
|
ExceptionPathType type,
|
|
|
|
|
const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const ClockEdge *clk_edge,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
bool match_min_max_exactly,
|
|
|
|
|
// Return values.
|
|
|
|
|
ExceptionPath *&hi_priority_exception,
|
|
|
|
|
int &hi_priority) const
|
|
|
|
|
{
|
|
|
|
|
if (to_exceptions) {
|
|
|
|
|
ExceptionPathSet::ConstIterator exception_iter(to_exceptions);
|
|
|
|
|
while (exception_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *exception = exception_iter.next();
|
|
|
|
|
exceptionTo(exception, type, pin, tr, clk_edge,
|
|
|
|
|
min_max, match_min_max_exactly,
|
|
|
|
|
hi_priority_exception, hi_priority);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::exceptionTo(ExceptionPath *exception,
|
|
|
|
|
ExceptionPathType type,
|
|
|
|
|
const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const ClockEdge *clk_edge,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
bool match_min_max_exactly,
|
|
|
|
|
// Return values.
|
|
|
|
|
ExceptionPath *&hi_priority_exception,
|
|
|
|
|
int &hi_priority) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
if ((type == ExceptionPathType::any
|
2018-09-28 17:54:21 +02:00
|
|
|
|| exception->type() == type)
|
|
|
|
|
&& exceptionMatchesTo(exception, pin, tr, clk_edge, min_max,
|
|
|
|
|
match_min_max_exactly, false)) {
|
|
|
|
|
int priority = exception->priority(min_max);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (hi_priority_exception == nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
|| priority > hi_priority
|
|
|
|
|
|| (priority == hi_priority
|
|
|
|
|
&& exception->tighterThan(hi_priority_exception))) {
|
|
|
|
|
hi_priority = priority;
|
|
|
|
|
hi_priority_exception = exception;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::exceptionMatchesTo(ExceptionPath *exception,
|
|
|
|
|
const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const ClockEdge *clk_edge,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
bool match_min_max_exactly,
|
|
|
|
|
bool require_to_pin) const
|
|
|
|
|
{
|
|
|
|
|
ExceptionTo *to = exception->to();
|
|
|
|
|
return exception->matches(min_max, match_min_max_exactly)
|
2019-03-13 01:25:53 +01:00
|
|
|
&& ((to == nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
&& !require_to_pin)
|
|
|
|
|
|| (to
|
|
|
|
|
&& to->matches(pin, clk_edge, tr, network_)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isCompleteTo(ExceptionState *state,
|
|
|
|
|
const Pin *pin,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const ClockEdge *clk_edge,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
bool match_min_max_exactly,
|
|
|
|
|
bool require_to_pin) const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return state->nextThru() == nullptr
|
2018-09-28 17:54:21 +02:00
|
|
|
&& exceptionMatchesTo(state->exception(), pin, tr, clk_edge,
|
|
|
|
|
min_max, match_min_max_exactly, require_to_pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
Wireload *
|
|
|
|
|
Sdc::wireloadDefaulted(const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
Wireload *wireload1 = wireload(min_max);
|
2019-03-13 01:25:53 +01:00
|
|
|
if (wireload1 == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
LibertyLibrary *default_lib = network_->defaultLibertyLibrary();
|
|
|
|
|
if (default_lib)
|
|
|
|
|
wireload1 = default_lib->defaultWireload();
|
|
|
|
|
}
|
|
|
|
|
return wireload1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Wireload *
|
|
|
|
|
Sdc::wireload(const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
return wireload_[min_max->index()];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setWireload(Wireload *wireload,
|
|
|
|
|
const MinMaxAll *min_max)
|
|
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto mm_index : min_max->rangeIndex())
|
|
|
|
|
wireload_[mm_index] = wireload;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setWireloadMode(WireloadMode mode)
|
|
|
|
|
{
|
|
|
|
|
wireload_mode_ = mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WireloadMode
|
|
|
|
|
Sdc::wireloadMode()
|
|
|
|
|
{
|
|
|
|
|
return wireload_mode_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const WireloadSelection *
|
|
|
|
|
Sdc::wireloadSelection(const MinMax *min_max)
|
|
|
|
|
{
|
|
|
|
|
const WireloadSelection *sel = wireload_selection_[min_max->index()];
|
2019-03-13 01:25:53 +01:00
|
|
|
if (sel == nullptr) {
|
2018-09-28 17:54:21 +02:00
|
|
|
// Look for a default.
|
|
|
|
|
LibertyLibrary *lib = network_->defaultLibertyLibrary();
|
|
|
|
|
if (lib) {
|
|
|
|
|
WireloadSelection *default_sel = lib->defaultWireloadSelection();
|
|
|
|
|
if (default_sel) {
|
|
|
|
|
sel = default_sel;
|
|
|
|
|
setWireloadSelection(default_sel, MinMaxAll::all());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sel;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setWireloadSelection(WireloadSelection *selection,
|
|
|
|
|
const MinMaxAll *min_max)
|
|
|
|
|
{
|
2019-07-18 15:19:00 +02:00
|
|
|
for (auto mm_index : min_max->rangeIndex())
|
|
|
|
|
wireload_selection_[mm_index] = selection;
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::crprEnabled() const
|
|
|
|
|
{
|
|
|
|
|
return crpr_enabled_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setCrprEnabled(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
crpr_enabled_ = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CrprMode
|
|
|
|
|
Sdc::crprMode() const
|
|
|
|
|
{
|
|
|
|
|
return crpr_mode_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setCrprMode(CrprMode mode)
|
|
|
|
|
{
|
|
|
|
|
crpr_mode_ = mode;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::crprActive() const
|
|
|
|
|
{
|
2019-03-13 01:25:53 +01:00
|
|
|
return analysis_type_ == AnalysisType::ocv
|
2018-09-28 17:54:21 +02:00
|
|
|
&& crpr_enabled_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::propagateGatedClockEnable() const
|
|
|
|
|
{
|
|
|
|
|
return propagate_gated_clock_enable_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setPropagateGatedClockEnable(bool enable)
|
|
|
|
|
{
|
|
|
|
|
propagate_gated_clock_enable_ = enable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::presetClrArcsEnabled() const
|
|
|
|
|
{
|
|
|
|
|
return preset_clr_arcs_enabled_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setPresetClrArcsEnabled(bool enable)
|
|
|
|
|
{
|
|
|
|
|
preset_clr_arcs_enabled_ = enable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::condDefaultArcsEnabled() const
|
|
|
|
|
{
|
|
|
|
|
return cond_default_arcs_enabled_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setCondDefaultArcsEnabled(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
cond_default_arcs_enabled_ = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::isDisabledCondDefault(Edge *edge) const
|
|
|
|
|
{
|
|
|
|
|
return !cond_default_arcs_enabled_
|
|
|
|
|
&& edge->timingArcSet()->isCondDefault();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::bidirectInstPathsEnabled() const
|
|
|
|
|
{
|
|
|
|
|
return bidirect_inst_paths_enabled_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setBidirectInstPathsEnabled(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
bidirect_inst_paths_enabled_ = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Delay calculation propagates slews from a bidirect driver
|
|
|
|
|
// to the bidirect port and back through the bidirect driver when
|
|
|
|
|
// sta_bidirect_inst_paths_enabled_ is true.
|
|
|
|
|
bool
|
|
|
|
|
Sdc::bidirectDrvrSlewFromLoad(const Pin *pin) const
|
|
|
|
|
{
|
|
|
|
|
return bidirect_inst_paths_enabled_
|
|
|
|
|
&& network_->direction(pin)->isBidirect()
|
|
|
|
|
&& network_->isTopLevelPort(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::bidirectNetPathsEnabled() const
|
|
|
|
|
{
|
|
|
|
|
return bidirect_inst_paths_enabled_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setBidirectNetPathsEnabled(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
bidirect_inst_paths_enabled_ = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::recoveryRemovalChecksEnabled() const
|
|
|
|
|
{
|
|
|
|
|
return recovery_removal_checks_enabled_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setRecoveryRemovalChecksEnabled(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
recovery_removal_checks_enabled_ = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::gatedClkChecksEnabled() const
|
|
|
|
|
{
|
|
|
|
|
return gated_clk_checks_enabled_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setGatedClkChecksEnabled(bool enabled)
|
|
|
|
|
{
|
|
|
|
|
gated_clk_checks_enabled_ = enabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::dynamicLoopBreaking() const
|
|
|
|
|
{
|
|
|
|
|
return dynamic_loop_breaking_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setDynamicLoopBreaking(bool enable)
|
|
|
|
|
{
|
|
|
|
|
if (dynamic_loop_breaking_ != enable) {
|
|
|
|
|
if (levelize_->levelized()) {
|
|
|
|
|
if (enable)
|
|
|
|
|
makeLoopExceptions();
|
|
|
|
|
else
|
|
|
|
|
deleteLoopExceptions();
|
|
|
|
|
}
|
|
|
|
|
dynamic_loop_breaking_ = enable;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::propagateAllClocks() const
|
|
|
|
|
{
|
|
|
|
|
return propagate_all_clks_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setPropagateAllClocks(bool prop)
|
|
|
|
|
{
|
|
|
|
|
propagate_all_clks_ = prop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::clkThruTristateEnabled() const
|
|
|
|
|
{
|
|
|
|
|
return clk_thru_tristate_enabled_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setClkThruTristateEnabled(bool enable)
|
|
|
|
|
{
|
|
|
|
|
clk_thru_tristate_enabled_ = enable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClockEdge *
|
|
|
|
|
Sdc::defaultArrivalClockEdge() const
|
|
|
|
|
{
|
|
|
|
|
return default_arrival_clk_->edge(TransRiseFall::rise());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
Sdc::useDefaultArrivalClock()
|
|
|
|
|
{
|
|
|
|
|
return use_default_arrival_clock_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setUseDefaultArrivalClock(bool enable)
|
|
|
|
|
{
|
|
|
|
|
use_default_arrival_clock_ = enable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::connectPinAfter(Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
PinSet *drvrs = network_->drivers(pin);
|
|
|
|
|
ExceptionPathSet::Iterator except_iter(exceptions_);
|
|
|
|
|
while (except_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *exception = except_iter.next();
|
|
|
|
|
ExceptionPt *first_pt = exception->firstPt();
|
|
|
|
|
ExceptionThruSeq::Iterator thru_iter(exception->thrus());
|
|
|
|
|
while (thru_iter.hasNext()) {
|
|
|
|
|
ExceptionThru *thru = thru_iter.next();
|
|
|
|
|
if (thru->edges()) {
|
|
|
|
|
thru->connectPinAfter(drvrs, network_);
|
|
|
|
|
if (first_pt == thru)
|
|
|
|
|
recordExceptionEdges(exception, thru->edges(),
|
|
|
|
|
first_thru_edge_exceptions_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::disconnectPinBefore(Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
ExceptionPathSet::Iterator except_iter(exceptions_);
|
|
|
|
|
while (except_iter.hasNext()) {
|
|
|
|
|
ExceptionPath *exception = except_iter.next();
|
|
|
|
|
ExceptionPt *first_pt = exception->firstPt();
|
|
|
|
|
ExceptionThruSeq::Iterator thru_iter(exception->thrus());
|
|
|
|
|
while (thru_iter.hasNext()) {
|
|
|
|
|
ExceptionThru *thru = thru_iter.next();
|
|
|
|
|
if (thru->edges()) {
|
|
|
|
|
thru->disconnectPinBefore(pin, network_);
|
|
|
|
|
if (thru == first_pt)
|
|
|
|
|
recordExceptionEdges(exception, thru->edges(),
|
|
|
|
|
first_thru_edge_exceptions_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clkHpinDisablesChanged(Pin *pin)
|
|
|
|
|
{
|
|
|
|
|
if (isVertexPinClock(pin))
|
|
|
|
|
clkHpinDisablesInvalid();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// Annotate constraints to the timing graph.
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateGraph(bool annotate)
|
|
|
|
|
{
|
|
|
|
|
Stats stats(debug_);
|
|
|
|
|
// All output pins are considered constrained because
|
|
|
|
|
// they may be downstream from a set_min/max_delay -from that
|
|
|
|
|
// does not have a set_output_delay.
|
|
|
|
|
annotateGraphConstrainOutputs();
|
|
|
|
|
annotateDisables(annotate);
|
|
|
|
|
annotateGraphOutputDelays(annotate);
|
|
|
|
|
annotateGraphDataChecks(annotate);
|
|
|
|
|
annotateHierClkLatency(annotate);
|
|
|
|
|
stats.report("Annotate constraints to graph");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateGraphConstrainOutputs()
|
|
|
|
|
{
|
|
|
|
|
Instance *top_inst = network_->topInstance();
|
|
|
|
|
InstancePinIterator *pin_iter = network_->pinIterator(top_inst);
|
|
|
|
|
while (pin_iter->hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter->next();
|
|
|
|
|
if (network_->direction(pin)->isAnyOutput())
|
|
|
|
|
annotateGraphConstrained(pin, true);
|
|
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateDisables(bool annotate)
|
|
|
|
|
{
|
|
|
|
|
PinSet::Iterator pin_iter(disabled_pins_);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter.next();
|
|
|
|
|
annotateGraphDisabled(pin, annotate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Instance *top_inst = network_->topInstance();
|
|
|
|
|
PortSet::Iterator port_iter(disabled_ports_);
|
|
|
|
|
while (port_iter.hasNext()) {
|
|
|
|
|
Port *port = port_iter.next();
|
|
|
|
|
Pin *pin = network_->findPin(top_inst, port);
|
|
|
|
|
annotateGraphDisabled(pin, annotate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PinPairSet::Iterator pair_iter(disabled_wire_edges_);
|
|
|
|
|
while (pair_iter.hasNext()) {
|
|
|
|
|
PinPair *pair = pair_iter.next();
|
|
|
|
|
annotateGraphDisabledWireEdge(pair->first, pair->second, annotate, graph_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EdgeSet::Iterator edge_iter(disabled_edges_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
edge->setIsDisabledConstraint(annotate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DisabledInstancePortsMap::Iterator disable_inst_iter(disabled_inst_ports_);
|
|
|
|
|
while (disable_inst_iter.hasNext()) {
|
|
|
|
|
DisabledInstancePorts *disabled_inst = disable_inst_iter.next();
|
|
|
|
|
setEdgeDisabledInstPorts(disabled_inst, annotate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class DisableHpinEdgeVisitor : public HierPinThruVisitor
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
DisableHpinEdgeVisitor(bool annotate, Graph *graph);
|
|
|
|
|
virtual void visit(Pin *from_pin,
|
|
|
|
|
Pin *to_pin);
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
bool annotate_;
|
|
|
|
|
Graph *graph_;
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(DisableHpinEdgeVisitor);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
DisableHpinEdgeVisitor::DisableHpinEdgeVisitor(bool annotate,
|
|
|
|
|
Graph *graph) :
|
|
|
|
|
HierPinThruVisitor(),
|
|
|
|
|
annotate_(annotate),
|
|
|
|
|
graph_(graph)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
DisableHpinEdgeVisitor::visit(Pin *from_pin,
|
|
|
|
|
Pin *to_pin)
|
|
|
|
|
{
|
|
|
|
|
annotateGraphDisabledWireEdge(from_pin, to_pin, annotate_, graph_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
annotateGraphDisabledWireEdge(Pin *from_pin,
|
|
|
|
|
Pin *to_pin,
|
|
|
|
|
bool annotate,
|
|
|
|
|
Graph *graph)
|
|
|
|
|
{
|
|
|
|
|
Vertex *from_vertex = graph->pinDrvrVertex(from_pin);
|
|
|
|
|
Vertex *to_vertex = graph->pinLoadVertex(to_pin);
|
|
|
|
|
if (from_vertex && to_vertex) {
|
|
|
|
|
VertexOutEdgeIterator edge_iter(from_vertex, graph);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
if (edge->isWire()
|
|
|
|
|
&& edge->to(graph) == to_vertex)
|
|
|
|
|
edge->setIsDisabledConstraint(annotate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateGraphDisabled(const Pin *pin,
|
|
|
|
|
bool annotate)
|
|
|
|
|
{
|
|
|
|
|
Vertex *vertex, *bidirect_drvr_vertex;
|
|
|
|
|
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
|
|
|
|
|
vertex->setIsDisabledConstraint(annotate);
|
|
|
|
|
if (bidirect_drvr_vertex)
|
|
|
|
|
bidirect_drvr_vertex->setIsDisabledConstraint(annotate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setEdgeDisabledInstPorts(DisabledInstancePorts *disabled_inst,
|
|
|
|
|
bool annotate)
|
|
|
|
|
{
|
|
|
|
|
setEdgeDisabledInstPorts(disabled_inst, disabled_inst->instance(), annotate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setEdgeDisabledInstPorts(DisabledPorts *disabled_port,
|
|
|
|
|
Instance *inst,
|
|
|
|
|
bool annotate)
|
|
|
|
|
{
|
|
|
|
|
if (disabled_port->all()) {
|
|
|
|
|
InstancePinIterator *pin_iter = network_->pinIterator(inst);
|
|
|
|
|
while (pin_iter->hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter->next();
|
|
|
|
|
// set_disable_timing instance does not disable timing checks.
|
|
|
|
|
setEdgeDisabledInstFrom(pin, false, annotate);
|
|
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Disable from pins.
|
|
|
|
|
LibertyPortSet::Iterator from_iter(disabled_port->from());
|
|
|
|
|
while (from_iter.hasNext()) {
|
|
|
|
|
LibertyPort *from_port = from_iter.next();
|
|
|
|
|
Pin *from_pin = network_->findPin(inst, from_port);
|
|
|
|
|
if (from_pin)
|
|
|
|
|
setEdgeDisabledInstFrom(from_pin, true, annotate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Disable to pins.
|
|
|
|
|
LibertyPortSet::Iterator to_iter(disabled_port->to());
|
|
|
|
|
while (to_iter.hasNext()) {
|
|
|
|
|
LibertyPort *to_port = to_iter.next();
|
|
|
|
|
Pin *to_pin = network_->findPin(inst, to_port);
|
|
|
|
|
if (to_pin) {
|
|
|
|
|
if (network_->direction(to_pin)->isAnyOutput()) {
|
|
|
|
|
Vertex *vertex = graph_->pinDrvrVertex(to_pin);
|
|
|
|
|
if (vertex) {
|
|
|
|
|
VertexInEdgeIterator edge_iter(vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
edge->setIsDisabledConstraint(annotate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Disable from/to pins.
|
|
|
|
|
LibertyPortPairSet::Iterator from_to_iter(disabled_port->fromTo());
|
|
|
|
|
while (from_to_iter.hasNext()) {
|
|
|
|
|
LibertyPortPair *pair = from_to_iter.next();
|
2018-12-05 23:18:41 +01:00
|
|
|
const LibertyPort *from_port = pair->first;
|
|
|
|
|
const LibertyPort *to_port = pair->second;
|
2018-09-28 17:54:21 +02:00
|
|
|
Pin *from_pin = network_->findPin(inst, from_port);
|
|
|
|
|
Pin *to_pin = network_->findPin(inst, to_port);
|
|
|
|
|
if (from_pin && network_->direction(from_pin)->isAnyInput()
|
|
|
|
|
&& to_pin) {
|
|
|
|
|
Vertex *from_vertex = graph_->pinLoadVertex(from_pin);
|
|
|
|
|
Vertex *to_vertex = graph_->pinDrvrVertex(to_pin);
|
|
|
|
|
if (from_vertex && to_vertex) {
|
|
|
|
|
VertexOutEdgeIterator edge_iter(from_vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
if (edge->to(graph_) == to_vertex)
|
|
|
|
|
edge->setIsDisabledConstraint(annotate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::setEdgeDisabledInstFrom(Pin *from_pin,
|
|
|
|
|
bool disable_checks,
|
|
|
|
|
bool annotate)
|
|
|
|
|
{
|
|
|
|
|
if (network_->direction(from_pin)->isAnyInput()) {
|
|
|
|
|
Vertex *from_vertex = graph_->pinLoadVertex(from_pin);
|
|
|
|
|
if (from_vertex) {
|
|
|
|
|
VertexOutEdgeIterator edge_iter(from_vertex, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
if (disable_checks
|
|
|
|
|
|| !edge->role()->isTimingCheck())
|
|
|
|
|
edge->setIsDisabledConstraint(annotate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateGraphOutputDelays(bool annotate)
|
|
|
|
|
{
|
|
|
|
|
OutputDelayMap::Iterator output_iter(output_delay_map_);
|
|
|
|
|
while (output_iter.hasNext()) {
|
|
|
|
|
OutputDelay *output_delay = output_iter.next();
|
|
|
|
|
while (output_delay) {
|
|
|
|
|
OutputDelayVertexPinIterator vpin_iter(output_delay);
|
|
|
|
|
while (vpin_iter.hasNext()) {
|
|
|
|
|
Pin *vpin = vpin_iter.next();
|
|
|
|
|
annotateGraphConstrained(vpin, annotate);
|
|
|
|
|
}
|
|
|
|
|
output_delay = output_delay->next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateGraphDataChecks(bool annotate)
|
|
|
|
|
{
|
|
|
|
|
DataChecksMap::Iterator data_checks_iter(data_checks_to_map_);
|
|
|
|
|
while (data_checks_iter.hasNext()) {
|
|
|
|
|
DataCheckSet *checks = data_checks_iter.next();
|
|
|
|
|
DataCheckSet::Iterator check_iter(checks);
|
|
|
|
|
// There may be multiple data checks on a single pin,
|
|
|
|
|
// but we only need to mark it as constrained once.
|
|
|
|
|
if (check_iter.hasNext()) {
|
|
|
|
|
DataCheck *check = check_iter.next();
|
|
|
|
|
annotateGraphConstrained(check->to(), annotate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateGraphConstrained(const PinSet *pins,
|
|
|
|
|
bool annotate)
|
|
|
|
|
{
|
|
|
|
|
PinSet::ConstIterator pin_iter(pins);
|
|
|
|
|
while (pin_iter.hasNext()) {
|
|
|
|
|
const Pin *pin = pin_iter.next();
|
|
|
|
|
annotateGraphConstrained(pin, annotate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateGraphConstrained(const InstanceSet *insts,
|
|
|
|
|
bool annotate)
|
|
|
|
|
{
|
|
|
|
|
InstanceSet::ConstIterator inst_iter(insts);
|
|
|
|
|
while (inst_iter.hasNext()) {
|
|
|
|
|
const Instance *inst = inst_iter.next();
|
|
|
|
|
annotateGraphConstrained(inst, annotate);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateGraphConstrained(const Instance *inst,
|
|
|
|
|
bool annotate)
|
|
|
|
|
{
|
|
|
|
|
InstancePinIterator *pin_iter = network_->pinIterator(inst);
|
|
|
|
|
while (pin_iter->hasNext()) {
|
|
|
|
|
Pin *pin = pin_iter->next();
|
|
|
|
|
if (network_->direction(pin)->isAnyInput())
|
|
|
|
|
annotateGraphConstrained(pin, annotate);
|
|
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateGraphConstrained(const Pin *pin,
|
|
|
|
|
bool annotate)
|
|
|
|
|
{
|
|
|
|
|
Vertex *vertex, *bidirect_drvr_vertex;
|
|
|
|
|
graph_->pinVertices(pin, vertex, bidirect_drvr_vertex);
|
|
|
|
|
// Pin may be hierarchical and have no vertex.
|
|
|
|
|
if (vertex)
|
|
|
|
|
vertex->setIsConstrained(annotate);
|
|
|
|
|
if (bidirect_drvr_vertex)
|
|
|
|
|
bidirect_drvr_vertex->setIsConstrained(annotate);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateHierClkLatency(bool annotate)
|
|
|
|
|
{
|
|
|
|
|
if (annotate) {
|
|
|
|
|
ClockLatencies::Iterator latency_iter(clk_latencies_);
|
|
|
|
|
while (latency_iter.hasNext()) {
|
|
|
|
|
ClockLatency *latency = latency_iter.next();
|
|
|
|
|
const Pin *pin = latency->pin();
|
|
|
|
|
if (pin && network_->isHierarchical(pin))
|
|
|
|
|
annotateHierClkLatency(pin, latency);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
edge_clk_latency_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::annotateHierClkLatency(const Pin *hpin,
|
|
|
|
|
ClockLatency *latency)
|
|
|
|
|
{
|
|
|
|
|
EdgesThruHierPinIterator edge_iter(hpin, network_, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
|
|
|
|
edge_clk_latency_[edge] = latency;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::deannotateHierClkLatency(const Pin *hpin)
|
|
|
|
|
{
|
|
|
|
|
EdgesThruHierPinIterator edge_iter(hpin, network_, graph_);
|
|
|
|
|
while (edge_iter.hasNext()) {
|
|
|
|
|
Edge *edge = edge_iter.next();
|
2019-04-11 05:36:48 +02:00
|
|
|
edge_clk_latency_.erase(edge);
|
2018-09-28 17:54:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClockLatency *
|
|
|
|
|
Sdc::clockLatency(Edge *edge) const
|
|
|
|
|
{
|
|
|
|
|
return edge_clk_latency_.findKey(edge);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
Sdc::clockLatency(Edge *edge,
|
|
|
|
|
const TransRiseFall *tr,
|
|
|
|
|
const MinMax *min_max,
|
|
|
|
|
// Return values.
|
|
|
|
|
float &latency,
|
|
|
|
|
bool &exists) const
|
|
|
|
|
{
|
|
|
|
|
ClockLatency *latencies = edge_clk_latency_.findKey(edge);
|
|
|
|
|
if (latencies)
|
|
|
|
|
latencies->delay(tr, min_max, latency, exists);
|
|
|
|
|
else {
|
|
|
|
|
latency = 0.0;
|
|
|
|
|
exists = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
InputDelayVertexPinsIterator::InputDelayVertexPinsIterator(Sdc *sdc) :
|
|
|
|
|
InputDelayVertexPinsIterator(sdc->input_delay_map_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
InputDelayVertexPinsIterator::InputDelayVertexPinsIterator(InputDelayMap
|
|
|
|
|
&input_delay_map) :
|
|
|
|
|
input_iter_(input_delay_map)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
InputDelayVertexPinsIterator::hasNext()
|
|
|
|
|
{
|
|
|
|
|
return input_iter_.hasNext();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const Pin *
|
|
|
|
|
InputDelayVertexPinsIterator::next()
|
|
|
|
|
{
|
|
|
|
|
const Pin *pin;
|
|
|
|
|
InputDelay *input_delay;
|
|
|
|
|
input_iter_.next(pin, input_delay);
|
|
|
|
|
return pin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// Find the graph vertex pins corresponding to pin.
|
|
|
|
|
// If the pin is hierarchical, the vertex pins are:
|
|
|
|
|
// hierarchical input - load pins inside the hierarchical instance
|
|
|
|
|
// hierarchical output - load pins outside the hierarchical instance
|
|
|
|
|
void
|
|
|
|
|
findVertexLoadPins(Pin *pin, const Network *network, PinSet *vertex_pins)
|
|
|
|
|
{
|
|
|
|
|
if (network->isHierarchical(pin)) {
|
|
|
|
|
PortDirection *dir = network->direction(pin);
|
|
|
|
|
bool is_input = dir->isAnyInput();
|
|
|
|
|
bool is_output = dir->isAnyOutput();
|
|
|
|
|
const Instance *hinst = network->instance(pin);
|
|
|
|
|
PinConnectedPinIterator *pin_iter = network->connectedPinIterator(pin);
|
|
|
|
|
while (pin_iter->hasNext()) {
|
|
|
|
|
Pin *pin1 = pin_iter->next();
|
|
|
|
|
bool is_inside = network->isInside(pin1, hinst);
|
|
|
|
|
if (((is_input && is_inside)
|
|
|
|
|
|| (is_output && !is_inside))
|
|
|
|
|
&& network->isLoad(pin1))
|
|
|
|
|
vertex_pins->insert(pin1);
|
|
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
vertex_pins->insert(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find the graph vertex pins corresponding to pin.
|
|
|
|
|
// If the pin is hierarchical, the vertex pins are:
|
|
|
|
|
// hierarchical input - driver pins outside the hierarchical instance
|
|
|
|
|
// hierarchical output - driver pins inside the hierarchical instance
|
|
|
|
|
void
|
|
|
|
|
findVertexDriverPins(Pin *pin,
|
|
|
|
|
const Network *network,
|
|
|
|
|
PinSet *vertex_pins)
|
|
|
|
|
{
|
|
|
|
|
if (network->isHierarchical(pin)) {
|
|
|
|
|
PortDirection *dir = network->direction(pin);
|
|
|
|
|
bool is_input = dir->isAnyInput();
|
|
|
|
|
bool is_output = dir->isAnyOutput();
|
|
|
|
|
const Instance *hinst = network->instance(pin);
|
|
|
|
|
PinConnectedPinIterator *pin_iter = network->connectedPinIterator(pin);
|
|
|
|
|
while (pin_iter->hasNext()) {
|
|
|
|
|
Pin *pin1 = pin_iter->next();
|
|
|
|
|
bool is_inside = network->isInside(pin1, hinst);
|
|
|
|
|
if (((is_input && !is_inside)
|
|
|
|
|
|| (is_output && is_inside))
|
|
|
|
|
&& network->isDriver(pin1))
|
|
|
|
|
vertex_pins->insert(pin1);
|
|
|
|
|
}
|
|
|
|
|
delete pin_iter;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
vertex_pins->insert(pin);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
InputDelayIterator::InputDelayIterator(Sdc *sdc) :
|
|
|
|
|
InputDelayIterator(sdc->input_delay_map_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
InputDelayIterator::InputDelayIterator(InputDelayMap &input_delay_map) :
|
|
|
|
|
input_iter_(input_delay_map),
|
|
|
|
|
next_(NULL)
|
|
|
|
|
{
|
|
|
|
|
findNext();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
InputDelayIterator::hasNext()
|
|
|
|
|
{
|
|
|
|
|
return next_ != NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InputDelay *
|
|
|
|
|
InputDelayIterator::next()
|
|
|
|
|
{
|
|
|
|
|
InputDelay *next = next_;
|
|
|
|
|
findNext();
|
|
|
|
|
return next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
InputDelayIterator::findNext()
|
|
|
|
|
{
|
|
|
|
|
if (next_)
|
|
|
|
|
next_ = next_->next();
|
|
|
|
|
if (next_ == NULL && input_iter_.hasNext())
|
|
|
|
|
next_ = input_iter_.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
OutputDelayIterator::OutputDelayIterator(Sdc *sdc) :
|
|
|
|
|
OutputDelayIterator(sdc->output_delay_map_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
OutputDelayIterator::OutputDelayIterator(OutputDelayMap &output_delay_map) :
|
|
|
|
|
output_iter_(output_delay_map),
|
|
|
|
|
next_(NULL)
|
|
|
|
|
{
|
|
|
|
|
findNext();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
OutputDelayIterator::hasNext()
|
|
|
|
|
{
|
|
|
|
|
return next_ != NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputDelay *
|
|
|
|
|
OutputDelayIterator::next()
|
|
|
|
|
{
|
|
|
|
|
OutputDelay *next = next_;
|
|
|
|
|
findNext();
|
|
|
|
|
return next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
OutputDelayIterator::findNext()
|
|
|
|
|
{
|
|
|
|
|
if (next_)
|
|
|
|
|
next_ = next_->next();
|
|
|
|
|
if (next_ == NULL && output_iter_.hasNext())
|
|
|
|
|
next_ = output_iter_.next();
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 01:25:53 +01:00
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
ClockIterator::ClockIterator(Sdc *sdc) :
|
|
|
|
|
ClockSeq::Iterator(sdc->clocks())
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClockIterator::ClockIterator(ClockSeq &clocks) :
|
|
|
|
|
ClockSeq::Iterator(clocks)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
ClockGroupIterator::ClockGroupIterator(Sdc *sdc) :
|
|
|
|
|
ClockGroupsNameMap::Iterator(sdc->clk_groups_name_map_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ClockGroupIterator::ClockGroupIterator(ClockGroupsNameMap &clk_groups_name_map) :
|
|
|
|
|
ClockGroupsNameMap::Iterator(clk_groups_name_map)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
GroupPathIterator::GroupPathIterator(Sdc *sdc) :
|
|
|
|
|
GroupPathIterator(sdc->group_path_map_)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
GroupPathIterator::GroupPathIterator(GroupPathMap &group_path_map) :
|
|
|
|
|
GroupPathMap::Iterator(group_path_map)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 17:54:21 +02:00
|
|
|
} // namespace
|